Compare commits

...

389 Commits

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

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

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

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

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

Inline usage examples with the method they demonstrate

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

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

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

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

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

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

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

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

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

Separate Openslide as input vs Deep Zoom as output

Split tile-based tests into new file

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

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

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

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

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

withMetadata exports profile, should not convert

Convert one fixture to sRGB to help test

Discovered while investigating #125
2014-11-25 18:54:49 +00:00
Lovell Fuller
02b6016390 Add link to Dockerfile for libvips
Thanks @marcbachmann
2014-11-25 10:33:43 +00:00
Lovell Fuller
4e01d63195 Add hasProfile attribute to metadata response
At the very least will be useful investigating #125
2014-11-24 17:24:29 +00:00
Lovell Fuller
94b47508c0 imagemagick-native now supports async and filter 2014-11-24 15:13:47 +00:00
Lovell Fuller
328cda82c5 Updates for 7.42 stable release of libvips 2014-11-24 12:19:44 +00:00
Lovell Fuller
118b17aa2f Apply less blur before affine reduction #121 2014-11-24 11:52:48 +00:00
Lovell Fuller
b7c7fc22f3 Ensure correct Gaussian blur before affine #121
Use double sigma instead of int radius for blur
2014-11-20 13:59:39 +00:00
Lovell Fuller
177a4f574c Minimum version of libvips now 7.40.0 #74 2014-11-17 12:08:05 +00:00
Lovell Fuller
e22d093002 Ubuntu 14 now compiles 7.40.x from source
as packaged version is the outdated 7.38.x

Added support for Ubuntu 15 and Mint 17.1
2014-11-12 20:30:13 +00:00
Lovell Fuller
e7f6d49bc1 Additional blur radii tests #108 2014-11-12 20:11:28 +00:00
Lovell Fuller
b886db4b0d Add bounds checks on blur/sharpen parameters #108 2014-11-12 20:06:28 +00:00
Lovell Fuller
ee513ac7a7 Less C, more C++ e.g. namespace, enum class
Improve image reference handling
2014-11-11 18:28:23 +00:00
Lovell Fuller
e465306d97 Disable libvips cache for unit tests
Aids memory leak detection
2014-11-11 18:09:48 +00:00
Lovell Fuller
32d9bc204a Add 'fast' blur and Gaussian blur feature #108 2014-11-10 22:38:13 +00:00
Lovell Fuller
df5cf402e3 Ensure leak check tests child processes 2014-11-10 22:35:22 +00:00
Lovell Fuller
86681100b7 Control level of sharpening via radius/flat/jagged #108 2014-11-10 16:20:04 +00:00
Lovell Fuller
47927ef47d Shrink less, affine more, maintain performance #75
Affects interpolators with 4x4+ window size

e.g. Bicubic, LBB, Nohalo

Introduces blur before large affine

to improve large PNG reductions
2014-11-08 12:08:27 +00:00
Lovell Fuller
7537adf399 Add features from libvips 7.40+
Load TIFF from Buffer/Stream

Interlaced PNG output no longer needs tilecache

Option to disable PNG adaptive row filtering
2014-11-08 12:08:27 +00:00
Lovell Fuller
740838b47c Prevent auto-rotate fail for libmagick formats #117 2014-11-08 12:07:47 +00:00
Lovell Fuller
f7c2a839ad Add support for profile-less CMYK images #99 2014-11-06 11:30:19 +00:00
Lovell Fuller
62fcfb3dba Add link to gulp-responsive module 2014-11-04 11:23:40 +00:00
Lovell Fuller
333e8789f4 Include cmath, erroneously removed in 3a9a137f 2014-10-29 13:32:19 +00:00
Lovell Fuller
3a9a137f40 Modify C++ 11 to be MS-friendly #19 2014-10-29 13:16:16 +00:00
Lovell Fuller
5c51612982 Version bumps ahead of v0.7.1 2014-10-28 11:12:40 +00:00
Lovell Fuller
a472adeb74 Fail fast for Buffer with unsupported format #105 2014-10-28 10:53:11 +00:00
Lovell Fuller
2e61839387 Prepare preinstall.sh for inclusion in master 2014-10-28 09:59:28 +00:00
Lovell Fuller
51805ef657 Add root check and support for Debian 7, Mint 13 2014-10-27 22:32:39 +00:00
Lovell Fuller
5856e41a62 Merge pull request #111 from papandreou/preinstallOnDebianWheezy
Build from source on Debian Wheezy
2014-10-27 15:47:29 +00:00
Andreas Lind
ffbe6b7d76 Build from source on Debian Wheezy. 2014-10-27 15:23:40 +01:00
Lovell Fuller
ed6a966534 Add shell script to install libvips 2014-10-24 10:57:45 +01:00
Lovell Fuller
97fc2a2a3a Ensure resize error from C++ is wrapped
Thanks to @papandreou
2014-10-23 09:39:37 +01:00
Lovell Fuller
3e1be7a33a Merge pull request #107 from papandreou/doNotUnrefUnparsableImages
Do not call g_object_unref when imageType comes out as UNKNOWN.
2014-10-23 09:23:37 +01:00
Lovell Fuller
4a4dd7f987 Merge pull request #106 from papandreou/errorInstances
Pass errors as Error instances rather than strings.
2014-10-23 09:22:15 +01:00
Andreas Lind
005c628352 Do not call g_object_unref when imageType comes out as UNKNOWN.
Avoids "(sharp:23220): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed" dumped to the console when an unsupported or invalid image is loaded.
2014-10-23 08:32:23 +02:00
Andreas Lind
1bd316de80 Pass errors as Error instances rather than strings. 2014-10-23 08:27:10 +02:00
Lovell Fuller
49b44d8238 Ensure compilation on RHEL/Centos 6 gcc 2014-10-21 22:01:43 +01:00
Lovell Fuller
8bc1981891 Refactor C++ code
Split features into their own file

Style is becoming less C, more C++
2014-10-21 21:38:19 +01:00
Lovell Fuller
db6dc6431b Add support for mirroring #62 2014-10-21 14:47:08 +01:00
Lovell Fuller
f214673c3c Add Coveralls badge 2014-10-21 12:57:24 +01:00
Lovell Fuller
ffe00ee398 Travis CI + Coveralls integration 2014-10-21 12:40:11 +01:00
Lovell Fuller
6cade5bd7f Increase unit test coverage to ~95% 2014-10-21 12:22:23 +01:00
Lovell Fuller
a531b5917e Add details about running leak and bench tests 2014-10-20 15:54:44 +01:00
Lovell Fuller
ca561daedf Test code refactor #63
Use mocha for feature-specific unit tests

Add test coverage report via istanbul
2014-10-20 15:32:56 +01:00
Lovell Fuller
f4cb577cb4 Code lint of new features 2014-10-17 12:26:53 +01:00
Lovell Fuller
91be57cbce Code lint of secondary tests 2014-10-17 12:26:38 +01:00
Lovell Fuller
78596545b0 Dependency version bumps ahead of 0.7.0 2014-10-17 12:15:31 +01:00
Lovell Fuller
9f6cc33858 Memory leak mop-up of new feature spillage 2014-10-13 22:53:43 +01:00
Lovell Fuller
d82de45b7e Thank you Brandon 2014-10-13 17:51:48 +01:00
Lovell Fuller
b7bbf58624 Document new extract method 2014-10-13 15:41:10 +01:00
Lovell Fuller
945d941c7b Convert image to sRGB sooner for embed 2014-10-13 15:39:52 +01:00
Lovell Fuller
2605bf966f Merge pull request #102 from brandonaaron/extract
Add support for stand-alone, pre-resize, and post-resize extraction
2014-10-07 15:19:41 +01:00
Brandon Aaron
83b72a1ede Add support for stand-alone, pre-resize, and post-resize extraction with a topOffset, leftOffset, width, and height. 2014-10-07 09:56:55 -04:00
Lovell Fuller
6190ca4307 Ensure conversion to sRGB occurs before background #99 2014-10-07 10:17:20 +01:00
Lovell Fuller
c2fcf7fc4a Add libvips install info for Debian Jessie #100 2014-10-06 11:27:29 +01:00
Lovell Fuller
37cb4339e2 Import embedded colour profile, if present #99 2014-10-06 10:45:58 +01:00
Lovell Fuller
46f229e308 Use own Angle enum to wrap jcupitt/libvips@154796a 2014-10-06 10:22:45 +01:00
Lovell Fuller
7f8f38f666 PNG compression level range is 0-9 #95 2014-10-03 14:50:15 +01:00
Lovell Fuller
fb0769a327 Improve image reference handling 2014-10-03 11:45:06 +01:00
Lovell Fuller
b84cc3d49e Unit test hardening 2014-10-03 11:42:10 +01:00
Lovell Fuller
0cba506bc4 Allow embed with rgba background #86 #89
Small memory leak mop-up related to #94
2014-10-02 20:02:14 +01:00
Lovell Fuller
5cdfbba55c Merge pull request #94 from gasi/background-flatten
Add `background` and `flatten` APIs
2014-10-01 20:51:52 +01:00
Daniel Gasienica
6145231936 Add background and flatten APIs 2014-09-30 17:58:57 -07:00
Lovell Fuller
513b07ddcf Add hasAlpha attribute to metadata API
Provides internal sharp_image_has_alpha method

Hopefully this helps with #91
2014-09-30 13:35:42 +01:00
Lovell Fuller
150971fa92 Add details of gulp.js plugin 2014-09-30 13:35:10 +01:00
Lovell Fuller
ac85d88c9c Update code style, jshint checks and EOLs 2014-09-19 11:55:52 +01:00
Lovell Fuller
1c79d6fb5d Add support for greyscale conversion #43 2014-09-16 11:22:26 +01:00
Lovell Fuller
d41321254a Update benchmarks using c3.xlarge AWS instance 2014-09-13 20:37:57 +01:00
Lovell Fuller
515b4656e6 Improve Stream error handling #88 2014-09-12 19:30:57 +01:00
Lovell Fuller
34c96ff925 Correct ...magick... test dependencies 2014-09-11 13:45:34 +01:00
Lovell Fuller
b8a04cc4ef Add link to Centos 6.5 CI via snap-ci 2014-09-08 23:58:13 +01:00
Lovell Fuller
ddc3f6e9c6 Add RHEL libvips installation - close #78 2014-09-08 23:53:01 +01:00
Lovell Fuller
3699e61c20 Improve support for extension-less output filenames 2014-09-07 08:59:11 +01:00
Lovell Fuller
2820218609 Add format property to output info
Output format may be derived from input format
2014-09-04 11:38:05 +01:00
Lovell Fuller
eb3e739f7b Expose libvips thread pool size 2014-09-03 19:52:03 +01:00
Lovell Fuller
87f6e83988 Add memory leak check #21
Runs unit tests via valgrind
2014-09-02 13:59:35 +01:00
Lovell Fuller
5728efd32b Add support for gamma correction #43 2014-08-30 14:12:42 +01:00
Lovell Fuller
bac367b005 Notify V8 GC of libvips cache size #21 2014-08-26 23:28:58 +01:00
Lovell Fuller
0d89131f66 Prevent small memory leak with non-empty argv0 #21 2014-08-26 15:25:36 +01:00
Lovell Fuller
8380be4be3 Add nearest neighbour interpolation
Suitable for image enlargement
2014-08-26 09:38:27 +01:00
Lovell Fuller
d0f51363bf Re-order output option methods
Add explicit list of and links to contributors
2014-08-26 09:09:01 +01:00
Lovell Fuller
15160d3b61 Merge pull request #83 from julianojulio/master
Add support for maintaining metadata in output image, thanks @julianojulio #83
2014-08-26 08:31:58 +01:00
Juliano Julio Costa
c5efb77bad New withMetadata([boolean]) to keep metadata in the generated images. 2014-08-25 15:59:21 -04:00
Lovell Fuller
b877751b2d Add support for C++11 on Mac 2014-08-24 21:41:09 +01:00
Lovell Fuller
40db482fd8 Version bumps 2014-08-24 21:40:23 +01:00
Lovell Fuller
98554e919c Small doc improvements ahead of v0.6.0 2014-08-22 16:57:05 +01:00
Lovell Fuller
017bf1e905 Expose libvips interpolators #69 2014-08-22 16:50:24 +01:00
Lovell Fuller
6498fc3a9e Ignore libmagick's inconsistent GIF colour spaces 2014-08-22 14:31:34 +01:00
Lovell Fuller
8ba71c94f4 Add usage example of metadata method 2014-08-22 14:27:46 +01:00
Lovell Fuller
f2f3eb76e1 Add method for fast access to image metadata #32 2014-08-22 14:17:43 +01:00
Lovell Fuller
5fe945fca8 Add gravity support to crop #45 2014-08-21 11:01:25 +01:00
Lovell Fuller
8ef0851a49 Expose libvips' op-cache max items #76 2014-08-19 20:30:21 +01:00
Lovell Fuller
e45956db6c Correct usage examples for new Stream-based API 2014-08-18 19:34:55 +01:00
Lovell Fuller
7cc9f7e2e0 Add Xcode Command Line Tools install info #80 2014-08-18 19:11:53 +01:00
Lovell Fuller
df3903532d Initial Duplex Stream support 2014-08-07 14:00:34 +01:00
Lovell Fuller
46456c9a2a Clear cached warnings after success 2014-08-06 11:34:16 +01:00
Lovell Fuller
e98f2fc013 Small documentation improvement plus version bump 2014-07-20 22:46:26 +01:00
Lovell Fuller
7df7a505ee Merge pull request #68 from chanon/master
Pass additional info (width, height) to output callback
2014-07-20 22:22:34 +01:00
Chanon
d40bdcc6ac Sends width and height as another parameter to the callback. Fixes issue #67 2014-07-20 23:52:28 +07:00
Lovell Fuller
1cce56b024 Use tile cache for interlace output
Doubles performance of Adam7 interlaced PNG output
2014-07-09 23:47:04 +01:00
Lovell Fuller
2126f9afc1 Ensure xcode/clang uses cflags
Silence pedantic warnings triggered by V8 in 0.11
2014-07-09 21:14:40 +01:00
Lovell Fuller
41420eedcf Add details of Heroku buildpack, thanks @alex88 #57 2014-07-06 13:20:41 +01:00
Lovell Fuller
1b6ab19b6d Remove fftw dependency when compiling libvips from src #57 2014-07-03 21:12:18 +01:00
Lovell Fuller
fbe5c18762 Expose depth of task queue 2014-06-25 23:21:39 +01:00
Lovell Fuller
8acb0ed5d0 Enable libvips C++ interpolators in Travis CI 2014-06-18 21:08:09 +01:00
Lovell Fuller
430e04d894 Update (and reduce) dependencies ahead of v0.5.1 2014-06-17 20:24:22 +01:00
Lovell Fuller
012edb4379 Support existing path to pkg-config lib for Heroku #57 2014-06-17 19:55:41 +01:00
Lovell Fuller
11ead360a9 Add Promises benchmark 2014-06-13 19:20:39 +01:00
Lovell Fuller
84a059d7e3 Rotate should throw Error, not String.
Minor JSLint and whitespace fixes.
2014-06-13 18:57:54 +01:00
Lovell Fuller
b1b070ae5c Revert "use native Promise if available"
This reverts commit 261a90c8a2.
2014-06-13 18:46:36 +01:00
Lovell Fuller
4eb910fec9 Add support for bicubic and Nohalo interpolation #41 2014-06-08 20:46:03 +01:00
Lovell Fuller
f0a9d82bf7 Always convert to sRGB colourspace to prevent libwebp segfault #58 2014-06-07 22:49:15 +01:00
Lovell Fuller
6d20a1ca81 Simplify gyp to support non-standard brew installs #56 2014-06-05 22:19:02 +01:00
Lovell Fuller
eca2787213 Add @jonathanong to contributors - thank you!
Bump versions
2014-06-05 22:18:47 +01:00
Lovell Fuller
8d146accf3 Correct docs - callback is optional for png 2014-06-05 22:00:39 +01:00
Lovell Fuller
b635d015cd Merge pull request #55 from jonathanong/native-promises
use native Promise if available
2014-06-04 20:48:17 +01:00
Jonathan Ong
261a90c8a2 use native Promise if available 2014-06-04 09:03:37 -07:00
Lovell Fuller
4ae22b3425 Merge pull request #51 from jonathanong/deprecate-write
show deprecation warning for .write()
2014-06-04 10:43:59 +01:00
Lovell Fuller
c9aa9c7723 Merge pull request #49 from jonathanong/strings-are-not-errors
fix error types, support promises on convenience methods
2014-06-04 10:35:06 +01:00
Jonathan Ong
5e0b5969da show deprecation warning for .write() 2014-06-03 20:53:37 -07:00
Jonathan Ong
ae6d5e69b1 fix error types, support promises on convenience methods
closes #48
2014-06-03 20:41:15 -07:00
Lovell Fuller
5ccc2bca97 Warn about liborc 0.4.19 buffer overflows #21 2014-06-02 22:18:00 +01:00
Lovell Fuller
46b701c85c Fail fast when input Buffer is empty #37 2014-06-02 21:11:25 +01:00
Lovell Fuller
7319533969 Add support for Promises/A+ #33 2014-06-01 11:27:30 +01:00
Lovell Fuller
9a05684302 Add withoutEnlargement option #36 2014-05-29 20:01:43 +01:00
Lovell Fuller
906311d403 Replace write() with toFile() to allow streams in the future #30 2014-05-29 19:54:43 +01:00
Lovell Fuller
4de9a2435f Correct typo 2014-05-29 16:05:53 +01:00
Lovell Fuller
a94dd2b354 Add support for image rotation including EXIF auto-orient 2014-05-29 00:48:58 +01:00
Lovell Fuller
bc3311cbad Version bump 2014-05-26 09:23:41 +01:00
Lovell Fuller
7b03eb89d7 Merge pull request #39 from pierreinglebert/update-nan
update to nan 1.1.0
2014-05-26 09:15:35 +01:00
Pierre Inglebert
15a519ebd9 update to nan 1.1.0 2014-05-26 09:51:53 +02:00
Lovell Fuller
3f8e9f6487 Merge pull request #38 from pierreinglebert/fix-max-cropping-29
fix max factor #29
2014-05-24 11:54:59 +01:00
Pierre Inglebert
39688371a8 fix max factor #29 2014-05-24 10:29:33 +02:00
Lovell Fuller
e32faac17a Prevent writing output file over input file - closes #28 2014-05-22 22:27:02 +01:00
Lovell Fuller
6c96bd0d37 Version bumps 2014-05-22 00:18:43 +01:00
Lovell Fuller
6b5f2028b7 Add support for 32 bit Linux #27 2014-05-21 23:46:10 +01:00
Lovell Fuller
276ba5228b Add usage example and further unit test for new max option
Simplify max vs crop logic
2014-05-19 21:52:47 +01:00
Lovell Fuller
ad7735a0a6 Merge pull request #18 from pierreinglebert/master
Add 'max' canvas option to specify the maximum width and/or height.
Approximately equivalent to GraphicsMagick's geometry option.
2014-05-19 21:03:04 +01:00
Pierre Inglebert
88edad3fae add max option #18 2014-05-19 20:18:26 +02:00
Lovell Fuller
308d1971d8 Recalculate residual float value for affine after shrink #26
Switch to static casts for double values

Add unit tests that previously would have failed
2014-05-18 11:39:05 +01:00
Lovell Fuller
6622045172 Merge pull request #25 from pierreinglebert/fix-progressive-typo
Fix progressive typo
2014-05-14 11:17:41 +01:00
Pierre Inglebert
f68ba8ea57 fix progressive typo 2014-05-14 08:56:12 +02:00
Lovell Fuller
2e427bb28a Remove liborc from CI config (to match README) 2014-05-13 16:47:57 +01:00
Lovell Fuller
efc7504961 Merge branch 'pierreinglebert-0.11-wip' 2014-05-13 16:45:32 +01:00
Lovell Fuller
8118613fa0 Merge branch '0.11-wip' of https://github.com/pierreinglebert/sharp into pierreinglebert-0.11-wip
Conflicts:
	package.json
2014-05-13 16:45:12 +01:00
Pierre Inglebert
eb6a221cee use master branch of image-magick-native until the 0.11 compatible is out 2014-05-13 08:03:24 +02:00
Pierre Inglebert
acdfe02502 make travis test on node 0.11 2014-05-12 21:30:44 +02:00
Lovell Fuller
2e106f8e2e Version bumps 2014-05-12 21:15:23 +02:00
Lovell Fuller
10496881f1 Add quality and compressionLevel options for output image. #24 2014-05-12 21:15:23 +02:00
Lovell Fuller
e275f6f5dd Add warning about liborc memory leaks. #21 2014-05-12 21:15:23 +02:00
Lovell Fuller
d635c297a2 Replace use of deprecated libvips conv method.
Ensure unref of mask to fix minor memory leak of ~150 bytes/image.
2014-05-12 21:15:23 +02:00
Pierre Inglebert
817c0a2a5a do not publish tests files 2014-05-12 21:15:23 +02:00
Pierre Inglebert
92fd34c627 remove unnecessary gitignore file 2014-05-12 21:15:23 +02:00
Lovell Fuller
43086cf134 Merge branch 'master' into quality-option 2014-05-11 18:26:16 +01:00
Lovell Fuller
e607bac31c Version bumps 2014-05-10 20:23:08 +01:00
Lovell Fuller
f8338e7c4f Add quality and compressionLevel options for output image. #24 2014-05-10 19:45:12 +01:00
Lovell Fuller
8322b442e0 Add warning about liborc memory leaks. #21 2014-05-10 14:39:34 +01:00
Lovell Fuller
afc51df4d8 Replace use of deprecated libvips conv method.
Ensure unref of mask to fix minor memory leak of ~150 bytes/image.
2014-05-03 22:14:21 +01:00
Lovell Fuller
e3a70c1075 Merge pull request #22 from pierreinglebert/add-npmignore
Add npmignore to minimise published package size
2014-04-24 14:09:42 +01:00
Pierre Inglebert
e3ee2b2976 do not publish tests files 2014-04-24 11:28:02 +02:00
Pierre Inglebert
cb285a6fb3 remove unnecessary gitignore file 2014-04-24 11:27:34 +02:00
Pierre Inglebert
aed3ca63b3 use NanNull, NanNew & NanSymbol for 0.11.11+ compat 2014-04-23 10:03:11 +02:00
Lovell Fuller
cbcf5e0dcc Update instructions for libvips installation on Ubuntu 2014-04-18 13:09:45 +01:00
Lovell Fuller
59f5c2d31b Update async dependency 2014-04-18 09:11:01 +01:00
Lovell Fuller
d1b47ef419 Add details of performance test task - closes #12 2014-04-16 21:55:22 +01:00
Lovell Fuller
0954ca6adf Update performance test results 2014-04-15 22:57:27 +01:00
Lovell Fuller
c9d7f43bd9 Add gm buffer-input perf tests 2014-04-15 22:57:07 +01:00
Lovell Fuller
481741315d Correct README markdown format 2014-04-14 22:09:08 +01:00
Lovell Fuller
cae1dbdb89 Add imagemagick-native comparison to and disable vips caching in perf tests. 2014-04-14 22:03:48 +01:00
Lovell Fuller
200d5a9312 Major version increase ahead of incoming API additions. Add link to Travis CI status. 2014-04-08 23:11:01 +01:00
Lovell Fuller
e9ca25cb45 Correct markdown typo 2014-04-08 22:40:34 +01:00
Lovell Fuller
33f24d41e7 Add Travis CI config
Uses a PPA with back-ported libwebp to work around https://bugs.launchpad.net/ubuntu/+source/libwebp/+bug/1108731
2014-04-08 22:35:42 +01:00
Lovell Fuller
45d5f12a63 Add details of Ubuntu 12.04 oddities. Closes #16. 2014-04-08 22:35:20 +01:00
Lovell Fuller
8785ca4331 Merge pull request #15 from pierreinglebert/feature-movefixtures
Move test fixtures to their own directory.
2014-04-02 22:54:05 +01:00
Pierre Inglebert
1ecdf97bdb Move fixtures to a proper directory
Add test for gif (libmagick)
2014-04-02 23:42:05 +02:00
Lovell Fuller
3703ee41aa Merge pull request #14 from pierreinglebert/feature-magickloader
Add support for many other input files via the *magick libraries
2014-04-02 22:26:38 +01:00
Lovell Fuller
c8f023d8ba Support Node.js v0.10+ only. Add @pierreinglebert credit. 2014-04-02 22:19:53 +01:00
Lovell Fuller
fe773733cd Merge pull request #11 from pierreinglebert/master
Add support for Node.js v0.11+ via nan. Remove epeg test dependency as it fails to compile on v0.11+.
2014-04-02 22:08:21 +01:00
Pierre Inglebert
19bec9346e add libmagick load support 2014-04-02 22:17:41 +02:00
Pierre Inglebert
9bd335079f check input files with vips foreign lib 2014-04-02 21:04:01 +02:00
Pierre Inglebert
b6dc179551 remove all references to epeg 2014-04-02 18:51:19 +02:00
Pierre Inglebert
e96fd8b9de 0.11 compat using nan 2014-04-02 00:10:42 +02:00
Lovell Fuller
6a2816e917 Add help for compiling against the gettext dependency of libvips on Mac OS #9 2014-03-13 22:16:47 +00:00
Lovell Fuller
06d88de5a2 Version bump 2014-03-12 21:11:25 +00:00
Lovell Fuller
a292e1fe8e Add path to homebrew-installed pkgconfig for Mac OS 10.8 (10.9 is symlinked to 10.8) #9 2014-03-12 20:45:35 +00:00
Lovell Fuller
08bb35e7af Add test dependency installation details 2014-03-11 22:44:05 +00:00
Lovell Fuller
0e89a5bbf2 Add summary of JPEG and PNG optimisation features 2014-03-10 22:32:14 +00:00
Lovell Fuller
2a0d79a78b Minor markdown layout fix 2014-03-09 21:54:31 +00:00
Lovell Fuller
764b57022b Minor markdown layout fix 2014-03-09 21:53:09 +00:00
Lovell Fuller
5f61331d1a Breaking change to API to become more expressive (see #8). Add support for upscaling. 2014-03-09 21:44:03 +00:00
Lovell Fuller
d0e6a4c0f3 Support identity transform when both width and height are not specified 2014-03-04 22:44:53 +00:00
Lovell Fuller
f99e42d447 Add support for auto-scaling of width and height 2014-03-03 23:24:09 +00:00
Lovell Fuller
9b4387be97 Improve installation instructions for libvips 2014-02-28 23:11:05 +00:00
Lovell Fuller
9c3631ecb7 Add comparative performance tests using random dimensions 2014-02-28 22:39:02 +00:00
Lovell Fuller
31ca68fb14 Improve documentation of cache method 2014-02-28 22:37:59 +00:00
Lovell Fuller
d5d85a8697 Expose vips internal cache settings and status 2014-02-25 23:31:33 +00:00
Lovell Fuller
ae9a8b0f57 Remove unnecessary temporary copy of input buffer. 2014-02-23 10:52:40 +00:00
Lovell Fuller
0899252a72 Add support for WebP and TIFF image formats. Closes #7. 2014-02-22 21:48:00 +00:00
Lovell Fuller
16551bc058 Take a temporary copy of buffers provided as input. Soak testing suggests this prevents the problems seen in #6. 2014-02-12 22:36:13 +00:00
171 changed files with 9765 additions and 865 deletions

23
.gitignore vendored
View File

@@ -1,18 +1,9 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
build
node_modules
tests/output.jpg
tests/output.png
npm-debug.log
coverage
test/bench/node_modules
test/fixtures/output*
test/leak/libvips.supp
lib
include
.DS_Store

3
.jshintignore Normal file
View File

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

11
.jshintrc Normal file
View File

@@ -0,0 +1,11 @@
{
"strict": true,
"node": true,
"maxparams": 4,
"maxcomplexity": 13,
"globals": {
"before": true,
"describe": true,
"it": true
}
}

12
.npmignore Normal file
View File

@@ -0,0 +1,12 @@
build
node_modules
coverage
.jshintignore
.jshintrc
.gitignore
test
.travis.yml
appveyor.yml
mkdocs.yml
lib
include

19
.travis.yml Normal file
View File

@@ -0,0 +1,19 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "iojs-v2"
- "iojs-v3"
- "4"
sudo: 9000
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_install:
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90
- curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
after_success:
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js

90
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,90 @@
# Contributing to sharp
Hello, thank you for your interest in helping!
## Submit a new bug report
Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem.
New bugs are assigned a `triage` label whilst under investigation.
If you're having problems with `npm install sharp`, please include the output of the following commands, perhaps as a [gist](https://gist.github.com/):
```sh
vips -v
pkg-config --print-provides vips
npm install --verbose sharp
```
## Submit a new feature request
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists, it's probably fastest to add a comment to it about your requirement.
Implementation is usually straightforward if _libvips_ [already supports](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/ch03.html) the feature you need.
## Submit a Pull Request to fix a bug
Thank you! To prevent the problem occurring again, please add unit tests that would have failed.
Please select the `master` branch as the destination for your Pull Request so your fix can be included in the next minor release.
Please squash your changes into a single commit using a command like `git rebase -i upstream/master`.
To test C++ changes, you can compile the module using `npm install` and then run the tests using `npm test`.
## Submit a Pull Request with a new feature
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/master/test/unit) to cover your new feature.
A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
Where possible, the functional tests use gradient-based perceptual hashes
based on [dHash](http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html)
to compare expected vs actual images.
You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/master/package.json#L5).
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
| Release | WIP branch |
| ------: | :--------- |
| v0.11.0 | knife |
| v0.12.0 | look |
| v0.13.0 | mind |
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
### Add a new public method
The API tries to be as fluent as possible. Image processing concepts follow the naming conventions from _libvips_ and, to a lesser extent, _ImageMagick_.
Most methods have optional parameters and assume sensible defaults. Methods with mandatory parameters often have names like `doSomethingWith(X)`.
Please ensure backwards compatibility where possible. Methods to modify previously default behaviour often have names like `withoutOptionY()` or `withExtraZ()`.
Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to gather feedback on a potential API change.
### Remove an existing public method
A method to be removed should be deprecated in the next major version then removed in the following major version.
By way of example, the [bilinearInterpolation method](https://github.com/lovell/sharp/blob/v0.6.0/index.js#L155) present in v0.5.0 was deprecated in v0.6.0 and removed in v0.7.0.
## Run the tests
### Functional tests and static code analysis
```sh
npm test
```
### Memory leak tests
Requires [Valgrind](http://valgrind.org/).
```sh
npm run test-leak
```
## Finally
Please feel free to ask any questions via a [new issue](https://github.com/lovell/sharp/issues/new) or contact me by [e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4).

161
README.md Executable file → Normal file
View File

@@ -1,161 +1,30 @@
# sharp
_adj_
The typical use case for this high speed Node.js module
is to convert large images of many formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
1. clearly defined; distinct: a sharp photographic image.
2. quick, brisk, or spirited.
3. shrewd or astute: a sharp bargainer.
4. (Informal.) very stylish: a sharp dresser; a sharp jacket.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
The typical use case for this high speed Node.js module is to convert large JPEG and PNG images to smaller JPEG and PNG images of varying dimensions.
### Documentation
The performance of JPEG resizing is typically 15x-25x faster than ImageMagick and GraphicsMagick, based mainly on the number of CPU cores available.
Visit [sharp.dimens.io](http://sharp.dimens.io/) for
complete installation instructions, API documentation,
benchmark tests and a changelog.
Under the hood you'll find the blazingly fast [libvips](https://github.com/jcupitt/libvips) image processing library, originally created in 1989 at Birkbeck College and currently maintained by John Cupitt.
### Contributing
## Prerequisites
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
* Node.js v0.8+
* [libvips](https://github.com/jcupitt/libvips) v7.38+
### Licence
For the sharpest results, please compile libvips from source.
## Install
npm install sharp
## Usage
var sharp = require("sharp");
### resize(input, output, width, height, [options], callback)
Scale and crop to `width` x `height` calling `callback` when complete.
`input` can either be a filename String or a Buffer. When using a filename libvips will `mmap` the file for improved performance.
`output` can either be a filename String or one of `sharp.buffer.jpeg` or `sharp.buffer.png` to pass a Buffer containing image data to `callback`.
`width` is the Number of pixels wide the resultant image should be.
`height` is the Number of pixels high the resultant image should be.
`options` is optional, and can contain one or more of:
* `canvas` can be one of `sharp.canvas.crop`, `sharp.canvas.embedWhite` or `sharp.canvas.embedBlack`. Defaults to `sharp.canvas.crop`.
* `sharpen` when set to true will perform a mild sharpen of the resultant image. This typically reduces performance by 30%.
* `progressive` when set will use progressive (interlace) scan for the output. This typically reduces performance by 30%.
* `sequentialRead` is an advanced setting that, when set, switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant image data when a Buffer is requested.
### Examples
```javascript
sharp.resize("input.jpg", "output.jpg", 300, 200, function(err) {
if (err) {
throw err;
}
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
```
```javascript
sharp.resize("input.jpg", sharp.buffer.jpeg, 300, 200, {progressive: true}, function(err, buffer) {
if (err) {
throw err;
}
// buffer contains progressive JPEG image data
});
```
```javascript
sharp.resize("input.jpg", sharp.buffer.png, 300, 200, {sharpen: true}, function(err, buffer) {
if (err) {
throw err;
}
// buffer contains sharpened PNG image data (converted from JPEG)
});
```
```javascript
sharp.resize(buffer, "output.jpg", 200, 300, {canvas: sharp.canvas.embedWhite}, function(err) {
if (err) {
throw err;
}
// output.jpg is a 200 pixels wide and 300 pixels high image containing a scaled version
// of the image data contained in buffer embedded on a white canvas
});
```
```javascript
sharp.resize("input.jpg", sharp.buffer.jpeg, 200, 300, {canvas: sharp.canvas.embedBlack}, function(err, buffer) {
if (err) {
throw err;
}
// buffer contains JPEG image data of a 200 pixels wide and 300 pixels high image
// containing a scaled version of input.png embedded on a black canvas
});
```
## Testing
npm test
## Performance
Test environment:
* AMD Athlon 4 core 3.3GHz 512KB L2 CPU 1333 DDR3
* libvips 7.37
* libjpeg-turbo8 1.3.0
* libpng 1.6.6
* zlib1g 1.2.7
`-file-buffer` indicates read from file and write to buffer, `-buffer-file` indicates read from buffer and write to file etc.
`-sharpen`, `-progressive` etc. demonstrate the negative effect of options on performance.
### JPEG
* imagemagick x 5.53 ops/sec ±0.62% (31 runs sampled)
* gm-file-file x 4.10 ops/sec ±0.41% (25 runs sampled)
* gm-file-buffer x 4.10 ops/sec ±0.36% (25 runs sampled)
* epeg-file-file x 23.82 ops/sec ±0.18% (60 runs sampled)
* epeg-file-buffer x 23.98 ops/sec ±0.16% (61 runs sampled)
* sharp-buffer-file x 20.76 ops/sec ±0.55% (54 runs sampled)
* sharp-buffer-buffer x 20.90 ops/sec ±0.26% (54 runs sampled)
* sharp-file-file x 91.78 ops/sec ±0.38% (88 runs sampled)
* sharp-file-buffer x __93.05 ops/sec__ ±0.61% (76 runs sampled)
* sharp-file-buffer-sharpen x 63.09 ops/sec ±5.58% (63 runs sampled)
* sharp-file-buffer-progressive x 61.68 ops/sec ±0.53% (76 runs sampled)
* sharp-file-buffer-sequentialRead x 60.66 ops/sec ±0.38% (75 runs sampled)
### PNG
* imagemagick x 4.27 ops/sec ±0.21% (25 runs sampled)
* gm-file-file x 8.33 ops/sec ±0.19% (44 runs sampled)
* gm-file-buffer x 7.45 ops/sec ±0.16% (40 runs sampled)
* sharp-buffer-file x 4.94 ops/sec ±118.46% (26 runs sampled)
* sharp-buffer-buffer x 12.59 ops/sec ±0.55% (64 runs sampled)
* sharp-file-file x 44.06 ops/sec ±6.86% (75 runs sampled)
* sharp-file-buffer x __46.29 ops/sec__ ±0.38% (76 runs sampled)
* sharp-file-buffer-sharpen x 38.86 ops/sec ±0.22% (65 runs sampled)
* sharp-file-buffer-progressive x 46.35 ops/sec ±0.20% (76 runs sampled)
* sharp-file-buffer-sequentialRead x 29.02 ops/sec ±0.62% (72 runs sampled)
## Licence
Copyright 2013, 2014 Lovell Fuller
Copyright 2013, 2014, 2015 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

28
appveyor.yml Normal file
View File

@@ -0,0 +1,28 @@
os: Visual Studio 2015
version: "{build}"
build: off
platform: x64
environment:
VIPS_VERSION_MAJOR_MINOR: 8.1
VIPS_VERSION_PATCH: 1
VIPS_WARNING: 0
matrix:
- nodejs_version: "0.12"
nodejs_exec: "node"
- nodejs_version: "2"
nodejs_exec: "iojs"
- nodejs_version: "3"
nodejs_exec: "iojs"
- nodejs_version: "4"
nodejs_exec: "node"
install:
- ps: $env:VIPS_VERSION = "$env:VIPS_VERSION_MAJOR_MINOR.$env:VIPS_VERSION_PATCH"
- ps: Write-Output "Fetching http://www.vips.ecs.soton.ac.uk/supported/$env:VIPS_VERSION_MAJOR_MINOR/win32/vips-dev-$env:VIPS_VERSION.zip"
- ps: Start-FileDownload http://www.vips.ecs.soton.ac.uk/supported/$env:VIPS_VERSION_MAJOR_MINOR/win32/vips-dev-$env:VIPS_VERSION.zip -FileName c:\vips-dev-$env:VIPS_VERSION.zip
- ps: Invoke-Expression "& 7z -y x c:\vips-dev-$env:VIPS_VERSION.zip -oc:\ | FIND /V `"ing `""
- ps: $env:VIPS_HOME = "c:\vips-dev-$env:VIPS_VERSION"
- ps: $env:PATH = "$env:VIPS_HOME\bin;$env:PATH"
- ps: Install-Product node $env:nodejs_version x86
- npm install --arch=ia32
test_script:
- npm run-script test-win32-%nodejs_exec%

View File

@@ -1,19 +1,96 @@
{
'targets': [{
'target_name': 'sharp',
'sources': ['src/sharp.cc'],
'libraries': [
'<!@(PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" pkg-config --libs vips)',
'<!@(PKG_CONFIG_PATH="/usr/lib/pkgconfig" pkg-config --libs vips)'
'variables': {
'runtime_link%':'shared',
},
'sources': [
'src/common.cc',
'src/metadata.cc',
'src/operations.cc',
'src/pipeline.cc',
'src/sharp.cc',
'src/utilities.cc'
],
'include_dirs': [
'/usr/local/include/glib-2.0',
'/usr/local/lib/glib-2.0/include',
'/usr/include/glib-2.0',
'/usr/lib/glib-2.0/include',
'/usr/lib/x86_64-linux-gnu/glib-2.0/include'
'conditions': [
['OS=="win"', {
'library_dirs': [
'$(VIPS_HOME)/lib'
],
'libraries': [
'libvips.dll.a',
'glib-2.0.lib',
'gobject-2.0.lib',
'gthread-2.0.lib',
'gmodule-2.0.lib',
'liblcms2.dll.a',
'libxml2.lib',
'intl.lib',
'libjpeg.dll.a',
'libexif.dll.a',
'libpng.lib',
'libtiff.dll.a',
'libMagickWand-6.Q16.dll.a',
'libMagickCore-6.Q16.dll.a',
'pango-1.0.lib',
'pangoft2-1.0.lib',
'libgsf-1.dll.a',
'libopenslide.dll.a',
'libfftw3.dll.a'
],
'include_dirs': [
'$(VIPS_HOME)/include',
'$(VIPS_HOME)/include/glib-2.0',
'$(VIPS_HOME)/lib/glib-2.0/include',
'<!(node -e "require(\'nan\')")'
]
}, {
'variables': {
'PKG_CONFIG_PATH': '<!(which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR || true):$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig'
},
'libraries': [
'<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --libs vips)'
],
'include_dirs': [
'<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --cflags vips glib-2.0)',
'<!(node -e "require(\'nan\')")'
],
'conditions': [
['runtime_link == "static"', {
'libraries': ['<!(PKG_CONFIG_PATH="<(PKG_CONFIG_PATH)" pkg-config --libs vips --static)']
}]
]
}]
],
'cflags': ['-fexceptions', '-O3'],
'cflags_cc': ['-fexceptions', '-O3']
'cflags_cc': [
'-std=c++0x',
'-fexceptions',
'-Wall',
'-O3'
],
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [
'-std=c++11',
'-stdlib=libc++',
'-fexceptions',
'-Wall',
'-O3'
],
'MACOSX_DEPLOYMENT_TARGET': '10.7'
},
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1 # /EHsc
}
},
'configurations': {
'Release': {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1,
}
}
}
},
}]
}

598
docs/api.md Normal file
View File

@@ -0,0 +1,598 @@
# API
```javascript
var sharp = require('sharp');
```
### Input
#### sharp([input])
Constructor to which further methods are chained. `input`, if present, can be one of:
* Buffer containing JPEG, PNG, WebP, GIF* or TIFF image data, or
* String containing the path to an image file, with most major formats supported.
The object returned implements the
[stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
JPEG, PNG, WebP, 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.
```javascript
sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg', function(err) {
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
```
#### 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`)
* `width`: Number of pixels wide
* `height`: Number of pixels high
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522)
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
* `orientation`: Number value of the EXIF Orientation header, if present
* `exif`: Buffer containing raw EXIF data, if present
* `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
A Promises/A+ promise is returned when `callback` is not provided.
```javascript
var image = sharp(inputJpg);
image
.metadata()
.then(function(metadata) {
return image
.resize(Math.round(metadata.width / 2))
.webp()
.toBuffer();
})
.then(function(data) {
// data contains a WebP image half the width and height of the original JPEG
});
```
#### clone()
Takes a "snapshot" of the instance, returning a new instance.
Cloned instances inherit the input of their parent instance.
This allows multiple output Streams
and therefore multiple processing pipelines
to share a single input Stream.
```javascript
var pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract(20, 20, 100, 100).pipe(secondWritableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
#### sequentialRead()
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
This will reduce memory usage and can improve performance on some systems.
#### limitInputPixels(pixels)
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).
### Resizing
#### resize([width], [height])
Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified.
`width` is the integral Number of pixels wide the resultant image should be, between 1 and 16383 (0x3FFF). Use `null` or `undefined` to auto-scale the width to match the height.
`height` 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])
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`.
Possible values are `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` and `centre`.
The default gravity is `center`/`centre`.
```javascript
var transformer = sharp()
.resize(300, 200)
.crop(sharp.gravity.north)
.on('error', function(err) {
console.log(err);
});
// Read image data from readableStream, resize and write image data to writableStream
readableStream.pipe(transformer).pipe(writableStream);
```
#### embed()
Preserving aspect ratio, resize the image to the
maximum `width` or `height` specified
then embed on a background of the exact
`width` and `height` specified.
If the background contains an alpha value
then WebP and PNG format output images will
contain an alpha channel,
even when the input image does not.
```javascript
sharp('input.gif')
.resize(200, 300)
.background({r: 0, g: 0, b: 0, a: 0})
.embed()
.toFormat(sharp.format.webp)
.toBuffer(function(err, outputBuffer) {
if (err) {
throw err;
}
// outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
// containing a scaled version, embedded on a transparent canvas, of input.gif
});
```
#### max()
Preserving aspect ratio,
resize the image to be as large as possible
while ensuring its dimensions are less than or equal to
the `width` and `height` specified.
Both `width` and `height` must be provided via
`resize` otherwise the behaviour will default to `crop`.
```javascript
sharp(inputBuffer)
.resize(200, 200)
.max()
.toFormat('jpeg')
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains JPEG image data no wider than 200 pixels and no higher
// than 200 pixels regardless of the inputBuffer image dimensions
});
```
#### min()
Preserving aspect ratio,
resize the image to be as small as possible
while ensuring its dimensions are greater than or equal to
the `width` and `height` specified.
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
#### withoutEnlargement()
Do not enlarge the output image
if the input image width *or* height
are already less than the required dimensions.
This is equivalent to GraphicsMagick's `>` geometry option:
"*change the dimensions of the image only
if its width or height exceeds the geometry specification*".
#### ignoreAspectRatio()
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`.
Possible interpolators, in order of performance, are:
* `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation), suitable for image enlargement only.
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), the default and fastest image reduction interpolation.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation), which typically reduces performance by 5%.
* `vertexSplitQuadraticBasisSpline`: Use [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48), which prevents "staircasing" and typically reduces performance by 5%.
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" and typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance and typically reduces performance by a factor of 3.
```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(top, left, width, height)
Extract a region of the image. Can be used with or without a `resize` operation.
`top` and `left` are the offset, in pixels, from the top-left corner.
`width` and `height` are the dimensions of the extracted image.
Use `extract` before `resize` for pre-resize extraction. Use `extract` after `resize` for post-resize extraction. Use `extract` before and after for both.
```javascript
sharp(input)
.extract(top, left, width, height)
.toFile(output, function(err) {
// Extract a region of the input image, saving in the same format.
});
```
```javascript
sharp(input)
.extract(topOffsetPre, leftOffsetPre, widthPre, heightPre)
.resize(width, height)
.extract(topOffsetPost, leftOffsetPost, widthPost, heightPost)
.toFile(output, function(err) {
// Extract a region, resize, then extract from the resized image
});
```
#### background(rgba)
Set the background for the `embed` and `flatten` operations.
`rgba` is parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
The alpha value is a float between `0` (transparent) and `1` (opaque).
The default background is `{r: 0, g: 0, b: 0, a: 1}`, black without transparency.
#### flatten()
Merge alpha transparency channel, if any, with `background`.
#### rotate([angle])
Rotate the output image by either an explicit angle or auto-orient based on the EXIF `Orientation` tag.
`angle`, if present, is a Number with a value of `0`, `90`, `180` or `270`.
Use this method without `angle` to determine the angle from EXIF data. Mirroring is supported and may infer the use of a `flip` operation.
Method order is important when both rotating and extracting regions, for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
```javascript
var pipeline = sharp()
.rotate()
.resize(null, 200)
.progressive()
.toBuffer(function(err, outputBuffer, info) {
if (err) {
throw err;
}
// outputBuffer contains 200px high progressive JPEG image data,
// auto-rotated using EXIF Orientation tag
// info.width and info.height contain the dimensions of the resized image
});
readableStream.pipe(pipeline);
```
#### flip()
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
#### flop()
Flop the image about the horizontal X axis. This always occurs after rotation, if any.
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
#### blur([sigma])
When used without parameters, performs a fast, mild blur of the output image. This typically reduces performance by 10%.
When a `sigma` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 25%.
* `sigma`, if present, is a Number between 0.3 and 1000 representing the approximate blur radius in pixels.
#### sharpen([radius], [flat], [jagged])
When used without parameters, performs a fast, mild sharpen of the output image. This typically reduces performance by 10%.
When a `radius` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%.
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels.
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
* `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0.
#### gamma([gamma])
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` then increasing the encoding (brighten) post-resize at a factor of `gamma`.
`gamma`, if present, is a Number between 1 and 3. The default value is `2.2`, a suitable approximation for sRGB images.
This can improve the perceived brightness of a resized image in non-linear colour spaces.
JPEG input images will not take advantage of the shrink-on-load performance optimisation when applying a gamma correction.
#### grayscale() / greyscale()
Convert to 8-bit greyscale; 256 shades of grey.
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
The output image will still be web-friendly sRGB and contain three (identical) channels.
#### normalize() / normalise()
Enhance output image contrast by stretching its luminance to cover the full dynamic range. This typically reduces performance by 30%.
#### overlayWith(path)
_Experimental_
Alpha composite image at `path` over the processed (resized, extracted) image. The dimensions of the two images must match.
* `path` is a String containing the path to an image file with an alpha channel.
```javascript
sharp('input.png')
.rotate(180)
.resize(300)
.flatten()
.background('#ff6600')
.overlayWith('overlay.png')
.sharpen()
.withMetadata()
.quality(90)
.webp()
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png, sharpened,
// with metadata, 90% quality WebP image data. Phew!
});
```
### 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.
`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`.
A Promises/A+ promise is returned when `callback` is not provided.
#### toBuffer([callback])
Write image data to a Buffer, the format of which will match the input image by default. JPEG, PNG and WebP are supported.
`callback`, if present, gets three arguments `(err, buffer, info)` where:
* `err` is an error message, if any.
* `buffer` is the output image data.
* `info` contains the output image `format`, `size` (bytes), `width` and `height`.
A Promises/A+ promise is returned when `callback` is not provided.
#### jpeg()
Use JPEG format for the output image.
#### png()
Use PNG format for the output image.
#### webp()
Use WebP format for the output image.
#### raw()
_Requires libvips 7.42.0+_
Provide raw, uncompressed uint8 (unsigned char) image data for Buffer and Stream based output.
The number of channels depends on the input image and selected options.
* 1 channel for images converted to `greyscale()`, with each byte representing one pixel.
* 3 channels for colour images without alpha transparency, with bytes ordered \[red, green, blue, red, green, blue, etc.\]).
* 4 channels for colour images with alpha transparency, with bytes ordered \[red, green, blue, alpha, red, green, blue, alpha, etc.\].
#### toFormat(format)
Convenience method for the above output format methods, where `format` is either:
* an attribute of the `sharp.format` Object e.g. `sharp.format.jpeg`, or
* a String containing `jpeg`, `png`, `webp` or `raw`.
#### quality(quality)
The output quality to use for lossy JPEG, WebP and TIFF output formats. The default quality is `80`.
`quality` is a Number between 1 and 100.
#### progressive()
Use progressive (interlace) scan for JPEG and PNG output. This typically reduces compression performance by 30% but results in an image that can be rendered sooner when decompressed.
#### withMetadata([metadata])
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
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.
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])
The size and overlap, in pixels, of square Deep Zoom image pyramid tiles.
* `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.
```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
});
```
#### withoutChromaSubsampling()
Disable the use of [chroma subsampling](http://en.wikipedia.org/wiki/Chroma_subsampling) with JPEG output (4:4:4).
This can improve colour representation at higher quality settings (90+),
but usually increases output file size and typically reduces performance by 25%.
The default behaviour is to use chroma subsampling (4:2:0).
#### compressionLevel(compressionLevel)
An advanced setting for the _zlib_ compression level of the lossless PNG output format. The default level is `6`.
`compressionLevel` is a Number between 0 and 9.
#### withoutAdaptiveFiltering()
_Requires libvips 7.42.0+_
An advanced setting to disable adaptive row filtering for the lossless PNG output format.
#### trellisQuantisation() / trellisQuantization()
_Requires libvips 8.0.0+ compiled against mozjpeg 3.0+_
An advanced setting to apply the use of
[trellis quantisation](http://en.wikipedia.org/wiki/Trellis_quantization) with JPEG output.
Reduces file size and slightly increases relative quality at the cost of increased compression time.
#### overshootDeringing()
_Requires libvips 8.0.0+ compiled against mozjpeg 3.0+_
An advanced setting to reduce the effects of
[ringing](http://en.wikipedia.org/wiki/Ringing_%28signal%29) in JPEG output,
in particular where black text appears on a white background (or vice versa).
#### optimiseScans() / optimizeScans()
_Requires libvips 8.0.0+ compiled against mozjpeg 3.0+_
An advanced setting for progressive (interlace) JPEG output.
Calculates which spectrum of DCT coefficients uses the fewest bits.
Usually reduces file size at the cost of increased compression time.
### Attributes
#### format
An Object containing nested boolean values
representing the available input and output formats/methods,
for example:
```javascript
> console.dir(sharp.format);
{ jpeg: { id: 'jpeg',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: true, stream: true } },
png: { id: 'png',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: true, stream: true } },
webp: { id: 'webp',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: true, stream: true } },
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 } } }
```
#### queue
An EventEmitter that emits a `change` event when a task is either:
* queued, waiting for _libuv_ to provide a worker thread
* complete
```javascript
sharp.queue.on('change', function(queueLength) {
console.log('Queue contains ' + queueLength + ' task(s)');
});
```
### Utilities
#### sharp.cache([memory], [items])
If `memory` or `items` are provided, set the limits of _libvips'_ operation cache.
* `memory` is the maximum memory in MB to use for this cache, with a default value of 100
* `items` is the maximum number of operations to cache, with a default value of 500
This method always returns cache statistics, useful for determining how much working memory is required for a particular task.
```javascript
var stats = sharp.cache(); // { current: 75, high: 99, memory: 100, items: 500 }
sharp.cache(200); // { current: 75, high: 99, memory: 200, items: 500 }
sharp.cache(50, 200); // { current: 49, high: 99, memory: 50, items: 200}
```
#### sharp.concurrency([threads])
`threads`, if provided, is the Number of threads _libvips'_ should create for processing each image. The default value is the number of CPU cores. A value of `0` will reset to this default.
This method always returns the current concurrency.
```javascript
var threads = sharp.concurrency(); // 4
sharp.concurrency(2); // 2
sharp.concurrency(0); // 4
```
The maximum number of images that can be processed in parallel is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
#### sharp.counters()
Provides access to internal task counters.
* `queue` is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
* `process` is the number of resize tasks currently being processed.
```javascript
var counters = sharp.counters(); // { queue: 2, process: 4 }
```

101
docs/changelog.md Normal file
View File

@@ -0,0 +1,101 @@
# Changelog
### v0.11 - "*knife*"
#### v0.11.4 - 5<sup>th</sup> November 2015
* Add corners, e.g. `northeast`, to existing `gravity` option.
[#291](https://github.com/lovell/sharp/pull/291)
[@brandonaaron](https://github.com/brandonaaron)
* Ensure correct auto-rotation for EXIF Orientation values 2 and 4.
[#288](https://github.com/lovell/sharp/pull/288)
[@brandonaaron](https://github.com/brandonaaron)
* Make static linking possible via `--runtime_link` install option.
[#287](https://github.com/lovell/sharp/pull/287)
[@vlapo](https://github.com/vlapo)
#### v0.11.3 - 8<sup>th</sup> September 2015
* Intrepret blurSigma, sharpenFlat, and sharpenJagged as double precision.
[#263](https://github.com/lovell/sharp/pull/263)
[@chrisriley](https://github.com/chrisriley)
#### v0.11.2 - 28<sup>th</sup> August 2015
* Allow crop gravity to be provided as a String.
[#255](https://github.com/lovell/sharp/pull/255)
[@papandreou](https://github.com/papandreou)
* Add support for io.js v3 and Node v4.
[#246](https://github.com/lovell/sharp/issues/246)
#### v0.11.1 - 12<sup>th</sup> August 2015
* Silence MSVC warning: "C4530: C++ exception handler used, but unwind semantics are not enabled".
[#244](https://github.com/lovell/sharp/pull/244)
[@TheThing](https://github.com/TheThing)
* Suppress gamma correction for input image with alpha transparency.
[#249](https://github.com/lovell/sharp/issues/249)
[@compeak](https://github.com/compeak)
#### v0.11.0 - 15<sup>th</sup> July 2015
* Allow alpha transparency compositing via new `overlayWith` method.
[#97](https://github.com/lovell/sharp/issues/97)
[@gasi](https://github.com/gasi)
* Expose raw ICC profile data as a Buffer when using `metadata`.
[#129](https://github.com/lovell/sharp/issues/129)
[@homerjam](https://github.com/homerjam)
* Allow image header updates via a parameter passed to existing `withMetadata` method.
Provide initial support for EXIF `Orientation` tag,
which if present is now removed when using `rotate`, `flip` or `flop`.
[#189](https://github.com/lovell/sharp/issues/189)
[@h2non](https://github.com/h2non)
* Tighten constructor parameter checks.
[#221](https://github.com/lovell/sharp/issues/221)
[@mikemorris](https://github.com/mikemorris)
* Allow one input Stream to be shared with two or more output Streams via new `clone` method.
[#235](https://github.com/lovell/sharp/issues/235)
[@jaubourg](https://github.com/jaubourg)
* Use `round` instead of `floor` when auto-scaling dimensions to avoid floating-point rounding errors.
[#238](https://github.com/lovell/sharp/issues/238)
[@richardadjogah](https://github.com/richardadjogah)
### v0.10 - "*judgment*"
#### v0.10.1 - 1<sup>st</sup> June 2015
* Allow embed of image with alpha transparency onto non-transparent background.
[#204](https://github.com/lovell/sharp/issues/204)
[@mikemliu](https://github.com/mikemliu)
* Include C standard library for `atoi` as Xcode 6.3 appears to no longer do this.
[#228](https://github.com/lovell/sharp/issues/228)
[@doggan](https://github.com/doggan)
#### v0.10.0 - 23<sup>rd</sup> April 2015
* Add support for Windows (x86).
[#19](https://github.com/lovell/sharp/issues/19)
[@DullReferenceException](https://github.com/DullReferenceException)
[@itsananderson](https://github.com/itsananderson)
* Add support for Openslide input and DeepZoom output.
[#146](https://github.com/lovell/sharp/issues/146)
[@mvictoras](https://github.com/mvictoras)
* Allow arbitrary aspect ratios when resizing images via new `ignoreAspectRatio` method.
[#192](https://github.com/lovell/sharp/issues/192)
[@skedastik](https://github.com/skedastik)
* Enhance output image contrast by stretching its luminance to cover the full dynamic range via new `normalize` method.
[#194](https://github.com/lovell/sharp/issues/194)
[@bkw](https://github.com/bkw)
[@codingforce](https://github.com/codingforce)

103
docs/index.md Normal file
View File

@@ -0,0 +1,103 @@
# sharp
The typical use case for this high speed Node.js module
is to convert large images of many formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x faster than using
the quickest ImageMagick and GraphicsMagick settings.
[![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.
Output images can be in JPEG, PNG and WebP formats as well as uncompressed raw pixel data.
Streams, Buffer objects and the filesystem can be used for input and output.
A single input Stream can be split into multiple processing pipelines and output Streams.
Deep Zoom image pyramids can be generated,
suitable for use with "slippy map" tile viewers like
[OpenSeadragon](https://github.com/openseadragon/openseadragon)
and [Leaflet](https://github.com/turban/Leaflet.Zoomify).
### Features
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Colour spaces, embedded ICC profiles and alpha transparency channels
are all handled correctly.
### Fast
This module is powered by the blazingly fast
[libvips](https://github.com/jcupitt/libvips) image processing library,
originally created in 1989 at Birkbeck College
and currently maintained by
[John Cupitt](https://github.com/jcupitt).
Only small regions of uncompressed image data
are held in memory and processed at a time,
taking full advantage of multiple CPU cores and L1/L2/L3 cache.
Everything remains non-blocking thanks to _libuv_,
no child processes are spawned and Promises/A+ are supported.
### Optimal
Huffman tables are optimised when generating JPEG output images
without having to use separate command line tools like
[jpegoptim](https://github.com/tjko/jpegoptim) and
[jpegtran](http://jpegclub.org/jpegtran/).
PNG filtering can be disabled,
which for diagrams and line art often produces the same result
as [pngcrush](http://pmt.sourceforge.net/pngcrush/).
### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
### Credits
This module would never have been possible without
the help and code contributions of the following people:
* [John Cupitt](https://github.com/jcupitt)
* [Pierre Inglebert](https://github.com/pierreinglebert)
* [Jonathan Ong](https://github.com/jonathanong)
* [Chanon Sajjamanochai](https://github.com/chanon)
* [Juliano Julio](https://github.com/julianojulio)
* [Daniel Gasienica](https://github.com/gasi)
* [Julian Walker](https://github.com/julianwa)
* [Amit Pitaru](https://github.com/apitaru)
* [Brandon Aaron](https://github.com/brandonaaron)
* [Andreas Lind](https://github.com/papandreou)
* [Maurus Cuelenaere](https://github.com/mcuelenaere)
* [Linus Unnebäck](https://github.com/LinusU)
* [Victor Mateevitsi](https://github.com/mvictoras)
* [Alaric Holloway](https://github.com/skedastik)
* [Bernhard K. Weisshuhn](https://github.com/bkw)
Thank you!
### Licence
Copyright 2013, 2014, 2015 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

124
docs/install.md Normal file
View File

@@ -0,0 +1,124 @@
# Installation
```sh
npm install sharp
```
### Prerequisites
* Node.js v0.10+ or io.js
* [libvips](https://github.com/jcupitt/libvips) v7.40.0+ (8.1.1+ recommended)
* C++11 compatible compiler such as gcc 4.6+, clang 3.0+ or MSVC 2013 (Node v4+ requires gcc 4.8+)
### Linux
[![Ubuntu 12.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
[![Centos 6.5 Build Status](https://snap-ci.com/lovell/sharp/branch/master/build_image)](https://snap-ci.com/lovell/sharp/branch/master)
For a system-wide installation of the most suitable version of
libvips and its dependencies on the following Operating Systems:
* Debian 7, 8
* Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
* Mint 13, 17
* Elementary 0.3
* RHEL/Centos/Scientific 6, 7
* Fedora 21, 22
* Amazon Linux 2015.03, 2015.09
* OpenSuse 13
run the following as a user with `sudo` access:
```sh
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
```
or run the following as `root`:
```sh
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | bash -
```
The [preinstall.sh](https://github.com/lovell/sharp/blob/master/preinstall.sh) script requires `curl` and `pkg-config`.
Add `--with-openslide` to enable OpenSlide support:
```sh
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -s -- --with-openslide
```
#### Ubuntu LTS
libvips v7.40.6 is available via a PPA.
##### 12.04
```sh
sudo add-apt-repository -y ppa:lovell/precise-backport-vips
sudo apt-get update
sudo apt-get install -y libvips-dev libgsf-1-dev
```
##### 14.04
```sh
sudo add-apt-repository -y ppa:lovell/trusty-backport-vips
sudo apt-get update
sudo apt-get install -y libvips-dev libgsf-1-dev
```
### 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)
Install libvips via homebrew:
```sh
brew install homebrew/science/vips --with-webp --with-graphicsmagick
```
A missing or incorrectly configured _Xcode Command Line Tools_ installation
[can lead](https://github.com/lovell/sharp/issues/80) to a
`library not found for -ljpeg` error.
If so, please try: `xcode-select --install`.
The _gettext_ dependency of _libvips_
[can lead](https://github.com/lovell/sharp/issues/9)
to a `library not found for -lintl` error.
If so, please try `brew link gettext --force`.
### Windows
[![Windows Server 2012 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
Requires x86 32-bit Node.js or io.js (use `iojs.exe` rather than `node.exe`).
The WebP format is currently unsupported.
1. Ensure the [node-gyp prerequisites](https://github.com/TooTallNate/node-gyp#installation) are met.
2. [Download](http://www.vips.ecs.soton.ac.uk/supported/current/win32/) and unzip `vips-dev.x.y.z.zip`.
3. Set the `VIPS_HOME` environment variable to the full path of the `vips-dev-x.y.z` directory.
4. Add `vips-dev-x.y.z\bin` to `PATH`.
Versions of MSVC more recent than 2013 may require the use of `npm install --arch=ia32 --msvs_version=2013`.
### Heroku
[Alessandro Tagliapietra](https://github.com/alex88) maintains an
[Heroku buildpack for libvips](https://github.com/alex88/heroku-buildpack-vips)
and its dependencies.
### Docker
[Marc Bachmann](https://github.com/marcbachmann) maintains a
[Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
```sh
docker pull marcbachmann/libvips
```
### 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)

65
docs/performance.md Normal file
View File

@@ -0,0 +1,65 @@
# Performance
### Test environment
* AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4)
* Amazon Linux AMI 2015.09
* Node.js v4.1.2
### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.8 - Image processing in pure JavaScript.
* [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)~~ - Wrapper around libmagick++, supports Buffers only. Does not currently work with Node.js v4.
* [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.20.0 - Fully featured wrapper around GraphicsMagick's `convert` command line utility.
* sharp v0.11.3 / libvips v8.1.0 - Caching within libvips disabled to ensure a fair comparison.
### The task
Decompress a 2725x2225 JPEG image, resize to 720x480 using bilinear interpolation, then compress to JPEG.
### Results
| Module | Input | Output | Ops/sec | Speed-up |
| :---------- | :----- | :----- | ------: | -------: |
| jimp | file | file | 0.94 | 1.0 |
| jimp | buffer | buffer | 1.12 | 1.2 |
| lwip | file | file | 1.12 | 1.2 |
| lwip | buffer | buffer | 1.13 | 1.2 |
| gm | buffer | buffer | 5.50 | 5.9 |
| gm | file | file | 5.55 | 5.9 |
| imagemagick | file | file | 8.66 | 9.2 |
| sharp | stream | stream | 16.33 | 17.4 |
| sharp | file | file | 16.88 | 18.0 |
| sharp | file | buffer | 16.90 | 18.1 |
| sharp | buffer | file | 16.99 | 18.1 |
| sharp | buffer | buffer | 17.06 | 18.1 |
Greater performance can be expected with caching enabled (default) and using 8+ core machines.
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
### Benchmark test prerequisites
Requires both _ImageMagick_ and _GraphicsMagick_:
```sh
brew install imagemagick
brew install graphicsmagick
```
```sh
sudo apt-get install imagemagick graphicsmagick libmagick++-dev
```
### Running the benchmark test
```sh
git clone https://github.com/lovell/sharp.git
cd sharp
npm install
cd test/bench
npm install
npm test
```

BIN
icc/USWebCoatedSWOP.icc Normal file

Binary file not shown.

Binary file not shown.

900
index.js
View File

@@ -1,64 +1,836 @@
var sharp = require("./build/Release/sharp");
module.exports.buffer = {
jpeg: "__jpeg",
png: "__png"
};
module.exports.canvas = {
crop: "c",
embedWhite: "w",
embedBlack: "b"
};
module.exports.resize = function(input, output, width, height, options, callback) {
"use strict";
if (typeof options === 'function') {
callback = options;
options = {};
} else {
options = options || {};
}
if (typeof input === 'string') {
options.inFile = input;
} else if (typeof input ==='object' && input instanceof Buffer) {
options.inBuffer = input;
} else {
callback("Unsupported input " + typeof input);
return;
}
if (!output || output.length === 0) {
callback("Invalid output");
return;
}
var outWidth = Number(width);
if (Number.isNaN(outWidth)) {
callback("Invalid width " + width);
return;
}
var outHeight = Number(height);
if (Number.isNaN(outHeight)) {
callback("Invalid height " + height);
return;
}
var canvas = options.canvas || "c";
if (canvas.length !== 1 || "cwb".indexOf(canvas) === -1) {
callback("Invalid canvas " + canvas);
return;
}
var sharpen = !!options.sharpen;
var progessive = !!options.progessive;
var sequentialRead = !!options.sequentialRead;
sharp.resize(options.inFile, options.inBuffer, output, width, height, canvas, sharpen, progessive, sequentialRead, callback);
};
/* Deprecated v0.0.x methods */
module.exports.crop = function(input, output, width, height, sharpen, callback) {
sharp.resize(input, output, width, height, {canvas: "c", sharpen: true}, callback);
};
module.exports.embedWhite = function(input, output, width, height, callback) {
sharp.resize(input, output, width, height, {canvas: "w", sharpen: true}, callback);
};
module.exports.embedBlack = function(input, output, width, height, callback) {
sharp.resize(input, output, width, height, {canvas: "b", sharpen: true}, callback);
};
'use strict';
var path = require('path');
var util = require('util');
var stream = require('stream');
var events = require('events');
var semver = require('semver');
var color = require('color');
var BluebirdPromise = require('bluebird');
var sharp = require('./build/Release/sharp');
var libvipsVersion = sharp.libvipsVersion();
var maximum = {
width: 0x3FFF,
height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2)
};
var Sharp = function(input) {
if (!(this instanceof Sharp)) {
return new Sharp(input);
}
stream.Duplex.call(this);
this.options = {
// input options
bufferIn: null,
streamIn: false,
sequentialRead: false,
limitInputPixels: maximum.pixels,
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
widthPre: -1,
heightPre: -1,
topOffsetPost: -1,
leftOffsetPost: -1,
widthPost: -1,
heightPost: -1,
width: -1,
height: -1,
canvas: 'crop',
gravity: 0,
angle: 0,
rotateBeforePreExtract: false,
flip: false,
flop: false,
withoutEnlargement: false,
interpolator: 'bilinear',
// operations
background: [0, 0, 0, 255],
flatten: false,
blurSigma: 0,
sharpenRadius: 0,
sharpenFlat: 1,
sharpenJagged: 2,
gamma: 0,
greyscale: false,
normalize: 0,
// overlay
overlayPath: '',
// output options
output: '__input',
progressive: false,
quality: 80,
compressionLevel: 6,
withoutAdaptiveFiltering: false,
withoutChromaSubsampling: false,
trellisQuantisation: false,
overshootDeringing: false,
optimiseScans: false,
streamOut: false,
withMetadata: false,
withMetadataOrientation: -1,
tileSize: 256,
tileOverlap: 0,
// Function to notify of queue length changes
queueListener: function(queueLength) {
module.exports.queue.emit('change', queueLength);
}
};
if (typeof input === 'string') {
// input=file
this.options.fileIn = input;
} else if (typeof input === 'object' && input instanceof Buffer) {
// input=buffer
this.options.bufferIn = input;
} else if (typeof input === 'undefined') {
// input=stream
this.options.streamIn = true;
} else {
throw new Error('Unsupported input ' + typeof input);
}
return this;
};
module.exports = Sharp;
util.inherits(Sharp, stream.Duplex);
/*
EventEmitter singleton emits queue length 'change' events
*/
module.exports.queue = new events.EventEmitter();
/*
Supported image formats
*/
module.exports.format = sharp.format();
/*
Handle incoming chunk on Writable Stream
*/
Sharp.prototype._write = function(chunk, encoding, callback) {
/*jslint unused: false */
if (this.options.streamIn) {
if (typeof chunk === 'object' && chunk instanceof Buffer) {
if (this.options.bufferIn instanceof Buffer) {
// Append to existing Buffer
this.options.bufferIn = Buffer.concat(
[this.options.bufferIn, chunk],
this.options.bufferIn.length + chunk.length
);
} else {
// Create new Buffer
this.options.bufferIn = new Buffer(chunk.length);
chunk.copy(this.options.bufferIn);
}
callback();
} else {
callback(new Error('Non-Buffer data on Writable Stream'));
}
} else {
callback(new Error('Unexpected data on Writable Stream'));
}
};
// Crop this part of the resized image (Center/Centre, North, East, South, West)
module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'south': 3, 'west': 4, 'northeast': 5, 'southeast': 6, 'southwest': 7, 'northwest': 8};
Sharp.prototype.crop = function(gravity) {
this.options.canvas = 'crop';
if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 8) {
this.options.gravity = gravity;
} else if (typeof gravity === 'string' && typeof module.exports.gravity[gravity] === 'number') {
this.options.gravity = module.exports.gravity[gravity];
} else {
throw new Error('Unsupported crop gravity ' + gravity);
}
return this;
};
Sharp.prototype.extract = function(topOffset, leftOffset, width, height) {
/*jslint unused: false */
var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
var values = arguments;
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) {
if (typeof values[index] === 'number' && !Number.isNaN(values[index]) && (values[index] % 1 === 0) && values[index] >= 0) {
this.options[name + suffix] = values[index];
} else {
throw new Error('Non-integer value for ' + name + ' of ' + values[index]);
}
}.bind(this));
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && this.options.angle !== 0) {
this.options.rotateBeforePreExtract = true;
}
return this;
};
/*
Set the background colour for embed and flatten operations.
Delegates to the 'Color' module, which can throw an Error
but is liberal in what it accepts, clamping values to sensible min/max.
*/
Sharp.prototype.background = function(rgba) {
var colour = color(rgba);
this.options.background = colour.rgbArray();
this.options.background.push(colour.alpha() * 255);
return this;
};
Sharp.prototype.embed = function() {
this.options.canvas = 'embed';
return this;
};
Sharp.prototype.max = function() {
this.options.canvas = 'max';
return this;
};
Sharp.prototype.min = function() {
this.options.canvas = 'min';
return this;
};
/*
Ignoring the aspect ratio of the input, stretch the image to
the exact width and/or height provided via the resize method.
*/
Sharp.prototype.ignoreAspectRatio = function() {
this.options.canvas = 'ignore_aspect';
return this;
};
Sharp.prototype.flatten = function(flatten) {
this.options.flatten = (typeof flatten === 'boolean') ? flatten : true;
return this;
};
Sharp.prototype.overlayWith = function(overlayPath) {
if (typeof overlayPath !== 'string') {
throw new Error('The overlay path must be a string');
}
if (overlayPath === '') {
throw new Error('The overlay path cannot be empty');
}
this.options.overlayPath = overlayPath;
return this;
};
/*
Rotate output image by 0, 90, 180 or 270 degrees
Auto-rotation based on the EXIF Orientation tag is represented by an angle of -1
*/
Sharp.prototype.rotate = function(angle) {
if (typeof angle === 'undefined') {
this.options.angle = -1;
} else if (!Number.isNaN(angle) && [0, 90, 180, 270].indexOf(angle) !== -1) {
this.options.angle = angle;
} else {
throw new Error('Unsupported angle (0, 90, 180, 270) ' + angle);
}
return this;
};
/*
Flip the image vertically, about the Y axis
*/
Sharp.prototype.flip = function(flip) {
this.options.flip = (typeof flip === 'boolean') ? flip : true;
return this;
};
/*
Flop the image horizontally, about the X axis
*/
Sharp.prototype.flop = function(flop) {
this.options.flop = (typeof flop === 'boolean') ? flop : true;
return this;
};
/*
Do not enlarge the output if the input width *or* height are already less than the required dimensions
This is equivalent to GraphicsMagick's ">" geometry option:
"change the dimensions of the image only if its width or height exceeds the geometry specification"
*/
Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
this.options.withoutEnlargement = (typeof withoutEnlargement === 'boolean') ? withoutEnlargement : true;
return this;
};
/*
Blur the output image.
Call without a sigma to use a fast, mild blur.
Call with a sigma to use a slower, more accurate Gaussian blur.
*/
Sharp.prototype.blur = function(sigma) {
if (typeof sigma === 'undefined') {
// No arguments: default to mild blur
this.options.blurSigma = -1;
} else if (typeof sigma === 'boolean') {
// Boolean argument: apply mild blur?
this.options.blurSigma = sigma ? -1 : 0;
} else if (typeof sigma === 'number' && !Number.isNaN(sigma) && sigma >= 0.3 && sigma <= 1000) {
// Numeric argument: specific sigma
this.options.blurSigma = sigma;
} else {
throw new Error('Invalid blur sigma (0.3 to 1000.0) ' + sigma);
}
return this;
};
/*
Sharpen the output image.
Call without a radius to use a fast, mild sharpen.
Call with a radius to use a slow, accurate sharpen using the L of LAB colour space.
radius - size of mask in pixels, must be integer
flat - level of "flat" area sharpen, default 1
jagged - level of "jagged" area sharpen, default 2
*/
Sharp.prototype.sharpen = function(radius, flat, jagged) {
if (typeof radius === 'undefined') {
// No arguments: default to mild sharpen
this.options.sharpenRadius = -1;
} else if (typeof radius === 'boolean') {
// Boolean argument: apply mild sharpen?
this.options.sharpenRadius = radius ? -1 : 0;
} else if (typeof radius === 'number' && !Number.isNaN(radius) && (radius % 1 === 0) && radius >= 1) {
// Numeric argument: specific radius
this.options.sharpenRadius = radius;
// Control over flat areas
if (typeof flat !== 'undefined' && flat !== null) {
if (typeof flat === 'number' && !Number.isNaN(flat) && flat >= 0) {
this.options.sharpenFlat = flat;
} else {
throw new Error('Invalid sharpen level for flat areas ' + flat + ' (expected >= 0)');
}
}
// Control over jagged areas
if (typeof jagged !== 'undefined' && jagged !== null) {
if (typeof jagged === 'number' && !Number.isNaN(jagged) && jagged >= 0) {
this.options.sharpenJagged = jagged;
} else {
throw new Error('Invalid sharpen level for jagged areas ' + jagged + ' (expected >= 0)');
}
}
} else {
throw new Error('Invalid sharpen radius ' + radius + ' (expected integer >= 1)');
}
return this;
};
/*
Set the interpolator to use for the affine transformation
*/
module.exports.interpolator = {
nearest: 'nearest',
bilinear: 'bilinear',
bicubic: 'bicubic',
nohalo: 'nohalo',
locallyBoundedBicubic: 'lbb',
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
Sharp.prototype.interpolateWith = function(interpolator) {
var isValid = false;
for (var key in module.exports.interpolator) {
if (module.exports.interpolator[key] === interpolator) {
isValid = true;
break;
}
}
if (isValid) {
this.options.interpolator = interpolator;
} else {
throw new Error('Invalid interpolator ' + interpolator);
}
return this;
};
/*
Darken image pre-resize (1/gamma) and brighten post-resize (gamma).
Improves brightness of resized image in non-linear colour spaces.
*/
Sharp.prototype.gamma = function(gamma) {
if (typeof gamma === 'undefined') {
// Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2;
} else if (!Number.isNaN(gamma) && gamma >= 1 && gamma <= 3) {
this.options.gamma = gamma;
} else {
throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
}
return this;
};
/*
Enhance output image contrast by stretching its luminance to cover the full dynamic range
*/
Sharp.prototype.normalize = function(normalize) {
if (process.platform !== 'win32') {
this.options.normalize = (typeof normalize === 'boolean') ? normalize : true;
} else {
console.error('normalize unavailable on win32 platform');
}
return this;
};
Sharp.prototype.normalise = Sharp.prototype.normalize;
/*
Convert to greyscale
*/
Sharp.prototype.greyscale = function(greyscale) {
this.options.greyscale = (typeof greyscale === 'boolean') ? greyscale : true;
return this;
};
Sharp.prototype.grayscale = Sharp.prototype.greyscale;
Sharp.prototype.progressive = function(progressive) {
this.options.progressive = (typeof progressive === 'boolean') ? progressive : true;
return this;
};
Sharp.prototype.sequentialRead = function(sequentialRead) {
this.options.sequentialRead = (typeof sequentialRead === 'boolean') ? sequentialRead : true;
return this;
};
Sharp.prototype.quality = function(quality) {
if (!Number.isNaN(quality) && quality >= 1 && quality <= 100 && quality % 1 === 0) {
this.options.quality = quality;
} else {
throw new Error('Invalid quality (1 to 100) ' + quality);
}
return this;
};
/*
zlib compression level for PNG output
*/
Sharp.prototype.compressionLevel = function(compressionLevel) {
if (!Number.isNaN(compressionLevel) && compressionLevel >= 0 && compressionLevel <= 9) {
this.options.compressionLevel = compressionLevel;
} else {
throw new Error('Invalid compressionLevel (0 to 9) ' + compressionLevel);
}
return this;
};
/*
Disable the use of adaptive row filtering for PNG output - requires libvips 7.42.0+
*/
Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
if (semver.gte(libvipsVersion, '7.42.0')) {
this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
} else {
console.error('withoutAdaptiveFiltering requires libvips 7.41.0+');
}
return this;
};
/*
Disable the use of chroma subsampling for JPEG output
*/
Sharp.prototype.withoutChromaSubsampling = function(withoutChromaSubsampling) {
this.options.withoutChromaSubsampling = (typeof withoutChromaSubsampling === 'boolean') ? withoutChromaSubsampling : true;
return this;
};
/*
Apply trellis quantisation to JPEG output - requires libvips 8.0.0+ compiled against mozjpeg 3.0+
*/
Sharp.prototype.trellisQuantisation = function(trellisQuantisation) {
if (semver.gte(libvipsVersion, '8.0.0')) {
this.options.trellisQuantisation = (typeof trellisQuantisation === 'boolean') ? trellisQuantisation : true;
} else {
console.error('trellisQuantisation requires libvips 8.0.0+');
}
return this;
};
Sharp.prototype.trellisQuantization = Sharp.prototype.trellisQuantisation;
/*
Apply overshoot deringing to JPEG output - requires libvips 8.0.0+ compiled against mozjpeg 3.0+
*/
Sharp.prototype.overshootDeringing = function(overshootDeringing) {
if (semver.gte(libvipsVersion, '8.0.0')) {
this.options.overshootDeringing = (typeof overshootDeringing === 'boolean') ? overshootDeringing : true;
} else {
console.error('overshootDeringing requires libvips 8.0.0+');
}
return this;
};
/*
Optimise scans in progressive JPEG output - requires libvips 8.0.0+ compiled against mozjpeg 3.0+
*/
Sharp.prototype.optimiseScans = function(optimiseScans) {
if (semver.gte(libvipsVersion, '8.0.0')) {
this.options.optimiseScans = (typeof optimiseScans === 'boolean') ? optimiseScans : true;
if (this.options.optimiseScans) {
this.progressive();
}
} else {
console.error('optimiseScans requires libvips 8.0.0+');
}
return this;
};
Sharp.prototype.optimizeScans = Sharp.prototype.optimiseScans;
/*
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image
Optionally provide an Object with attributes to update:
orientation: numeric value for EXIF Orientation tag
*/
Sharp.prototype.withMetadata = function(withMetadata) {
this.options.withMetadata = (typeof withMetadata === 'boolean') ? withMetadata : true;
if (typeof withMetadata === 'object') {
if ('orientation' in withMetadata) {
if (
typeof withMetadata.orientation === 'number' &&
!Number.isNaN(withMetadata.orientation) &&
withMetadata.orientation % 1 === 0 &&
withMetadata.orientation >=0 &&
withMetadata.orientation <= 7
) {
this.options.withMetadataOrientation = withMetadata.orientation;
} else {
throw new Error('Invalid orientation (0 to 7) ' + withMetadata.orientation);
}
}
}
return this;
};
/*
Tile size and overlap for Deep Zoom output
*/
Sharp.prototype.tile = function(size, overlap) {
// Size of square tiles, in pixels
if (typeof size !== 'undefined' && size !== null) {
if (!Number.isNaN(size) && size % 1 === 0 && size >= 1 && size <= 8192) {
this.options.tileSize = size;
} else {
throw new Error('Invalid tile size (1 to 8192) ' + size);
}
}
// Overlap of tiles, in pixels
if (typeof overlap !== 'undefined' && overlap !== null) {
if (!Number.isNaN(overlap) && overlap % 1 === 0 && overlap >=0 && overlap <= 8192) {
if (overlap > this.options.tileSize) {
throw new Error('Tile overlap ' + overlap + ' cannot be larger than tile size ' + this.options.tileSize);
}
this.options.tileOverlap = overlap;
} else {
throw new Error('Invalid tile overlap (0 to 8192) ' + overlap);
}
}
return this;
};
Sharp.prototype.resize = function(width, height) {
if (!width) {
this.options.width = -1;
} else {
if (typeof width === 'number' && !Number.isNaN(width) && width % 1 === 0 && width > 0 && width <= maximum.width) {
this.options.width = width;
} else {
throw new Error('Invalid width (1 to ' + maximum.width + ') ' + width);
}
}
if (!height) {
this.options.height = -1;
} else {
if (typeof height === 'number' && !Number.isNaN(height) && height % 1 === 0 && height > 0 && height <= maximum.height) {
this.options.height = height;
} else {
throw new Error('Invalid height (1 to ' + maximum.height + ') ' + height);
}
}
return this;
};
/*
Limit the total number of pixels for input images
Assumes the image dimensions contained in the file header can be trusted
*/
Sharp.prototype.limitInputPixels = function(limit) {
if (typeof limit === 'number' && !Number.isNaN(limit) && limit % 1 === 0 && limit > 0) {
this.options.limitInputPixels = limit;
} else {
throw new Error('Invalid pixel limit (1 to ' + maximum.pixels + ') ' + limit);
}
return this;
};
/*
Write output image data to a file
*/
Sharp.prototype.toFile = function(output, callback) {
if (!output || output.length === 0) {
var errOutputInvalid = new Error('Invalid output');
if (typeof callback === 'function') {
callback(errOutputInvalid);
} else {
return BluebirdPromise.reject(errOutputInvalid);
}
} else {
if (this.options.fileIn === output) {
var errOutputIsInput = new Error('Cannot use same file for input and output');
if (typeof callback === 'function') {
callback(errOutputIsInput);
} else {
return BluebirdPromise.reject(errOutputIsInput);
}
} else {
this.options.output = output;
return this._pipeline(callback);
}
}
return this;
};
/*
Write output to a Buffer
*/
Sharp.prototype.toBuffer = function(callback) {
return this._pipeline(callback);
};
/*
Force JPEG output
*/
Sharp.prototype.jpeg = function() {
this.options.output = '__jpeg';
return this;
};
/*
Force PNG output
*/
Sharp.prototype.png = function() {
this.options.output = '__png';
return this;
};
/*
Force WebP output
*/
Sharp.prototype.webp = function() {
this.options.output = '__webp';
return this;
};
/*
Force raw, uint8 output
*/
Sharp.prototype.raw = function() {
var supportsRawOutput = module.exports.format.raw.output;
if (supportsRawOutput.file || supportsRawOutput.buffer || supportsRawOutput.stream) {
this.options.output = '__raw';
} else {
console.error('Raw output requires libvips 7.42.0+');
}
return this;
};
/*
Force output to a given format
@param format is either the id as a String or an Object with an 'id' attribute
*/
Sharp.prototype.toFormat = function(format) {
var id = format;
if (typeof format === 'object') {
id = format.id;
}
if (typeof id === 'string' && typeof module.exports.format[id] === 'object' && typeof this[id] === 'function') {
this[id]();
} else {
throw new Error('Unsupported format ' + format);
}
return this;
};
/*
Used by a Writable Stream to notify that it is ready for data
*/
Sharp.prototype._read = function() {
if (!this.options.streamOut) {
this.options.streamOut = true;
this._pipeline();
}
};
/*
Invoke the C++ image processing pipeline
Supports callback, stream and promise variants
*/
Sharp.prototype._pipeline = function(callback) {
var that = this;
if (typeof callback === 'function') {
// output=file/buffer
if (this.options.streamIn) {
// output=file/buffer, input=stream
this.on('finish', function() {
sharp.pipeline(that.options, callback);
});
} else {
// output=file/buffer, input=file/buffer
sharp.pipeline(this.options, callback);
}
return this;
} else if (this.options.streamOut) {
// output=stream
if (this.options.streamIn) {
// output=stream, input=stream
this.on('finish', function() {
sharp.pipeline(that.options, function(err, data) {
if (err) {
that.emit('error', err);
} else {
that.push(data);
}
that.push(null);
});
});
} else {
// output=stream, input=file/buffer
sharp.pipeline(this.options, function(err, data) {
if (err) {
that.emit('error', err);
} else {
that.push(data);
}
that.push(null);
});
}
return this;
} else {
// output=promise
if (this.options.streamIn) {
// output=promise, input=stream
return new BluebirdPromise(function(resolve, reject) {
that.on('finish', function() {
sharp.pipeline(that.options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
});
} else {
// output=promise, input=file/buffer
return new BluebirdPromise(function(resolve, reject) {
sharp.pipeline(that.options, function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
}
};
/*
Reads the image header and returns metadata
Supports callback, stream and promise variants
*/
Sharp.prototype.metadata = function(callback) {
var that = this;
if (typeof callback === 'function') {
if (this.options.streamIn) {
this.on('finish', function() {
sharp.metadata(that.options, callback);
});
} else {
sharp.metadata(this.options, callback);
}
return this;
} else {
if (this.options.streamIn) {
return new BluebirdPromise(function(resolve, reject) {
that.on('finish', function() {
sharp.metadata(that.options, function(err, metadata) {
if (err) {
reject(err);
} else {
resolve(metadata);
}
});
});
});
} else {
return new BluebirdPromise(function(resolve, reject) {
sharp.metadata(that.options, function(err, metadata) {
if (err) {
reject(err);
} else {
resolve(metadata);
}
});
});
}
}
};
/*
Clone new instance using existing options.
Cloned instances share the same input.
*/
Sharp.prototype.clone = function() {
// Clone existing options
var clone = new Sharp();
util._extend(clone.options, this.options);
clone.streamIn = false;
// Pass 'finish' event to clone for Stream-based input
this.on('finish', function() {
// Clone inherits input data
clone.options.bufferIn = this.options.bufferIn;
clone.emit('finish');
});
return clone;
};
/*
Get and set cache memory and item limits
*/
module.exports.cache = function(memory, items) {
if (typeof memory !== 'number' || Number.isNaN(memory)) {
memory = null;
}
if (typeof items !== 'number' || Number.isNaN(items)) {
items = null;
}
return sharp.cache(memory, items);
};
/*
Get and set size of thread pool
*/
module.exports.concurrency = function(concurrency) {
if (typeof concurrency !== 'number' || Number.isNaN(concurrency)) {
concurrency = null;
}
return sharp.concurrency(concurrency);
};
/*
Get internal counters
*/
module.exports.counters = function() {
return sharp.counters();
};
/*
Get the version of the libvips library
*/
module.exports.libvipsVersion = function() {
return libvipsVersion;
};

17
mkdocs.yml Normal file
View File

@@ -0,0 +1,17 @@
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.
copyright: <a href="https://dimens.io/">dimens.io</a>
google_analytics: ['UA-13034748-12', 'sharp.dimens.io']
theme: readthedocs
markdown_extensions:
- toc:
permalink: True
dev_addr: 0.0.0.0:10101
pages:
- Home: index.md
- Installation: install.md
- API: api.md
- Performance: performance.md
- Changelog: changelog.md

67
package.json Executable file → Normal file
View File

@@ -1,10 +1,32 @@
{
"name": "sharp",
"version": "0.1.4",
"author": "Lovell Fuller",
"description": "High performance module to resize JPEG and PNG images using the libvips image processing library",
"version": "0.11.4",
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
"Jonathan Ong <jonathanrichardong@gmail.com>",
"Chanon Sajjamanochai <chanon.s@gmail.com>",
"Juliano Julio <julianojulio@gmail.com>",
"Daniel Gasienica <daniel@gasienica.ch>",
"Julian Walker <julian@fiftythree.com>",
"Amit Pitaru <pitaru.amit@gmail.com>",
"Brandon Aaron <hello.brandon@aaron.sh>",
"Andreas Lind <andreas@one.com>",
"Maurus Cuelenaere <mcuelenaere@gmail.com>",
"Linus Unnebäck <linus@folkdatorn.se>",
"Victor Mateevitsi <mvictoras@gmail.com>",
"Alaric Holloway <alaric.holloway@gmail.com>",
"Bernhard K. Weisshuhn <bkw@codingforce.com>",
"Chris Riley <criley@primedia.com>"
],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
"scripts": {
"test": "node tests/perf.js"
"clean": "rm -rf test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=20000 ./test/unit/*.js",
"test-clean": "npm run clean && npm install && npm test",
"test-leak": "./test/leak/leak.sh",
"test-win32-node": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-win32-iojs": "iojs ./node_modules/mocha/bin/mocha --slow=5000 --timeout=30000 ./test/unit/*.js"
},
"main": "index.js",
"repository": {
@@ -14,24 +36,35 @@
"keywords": [
"jpeg",
"png",
"webp",
"tiff",
"dzi",
"resize",
"thumbnail",
"sharpen",
"crop",
"embed",
"libvips",
"fast",
"buffer"
"vips"
],
"devDependencies": {
"imagemagick": "*",
"gm": "*",
"epeg": "*",
"async": "*",
"benchmark": "*"
"dependencies": {
"bluebird": "^3.0.5",
"color": "^0.10.1",
"nan": "^2.1.0",
"semver": "^5.0.3"
},
"license": "Apache 2.0",
"devDependencies": {
"async": "^1.5.0",
"coveralls": "^2.11.4",
"exif-reader": "1.0.0",
"icc": "^0.0.2",
"istanbul": "^0.4.0",
"mocha": "^2.3.3",
"mocha-jshint": "^2.2.5",
"node-cpplint": "^0.4.0",
"rimraf": "^2.4.3",
"bufferutil": "^1.2.1"
},
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
"node": ">=0.10"
}
}
}

326
preinstall.sh Executable file
View File

@@ -0,0 +1,326 @@
#!/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
# * Amazon Linux 2015.03, 2015.09
# * OpenSuse 13
vips_version_minimum=7.40.0
vips_version_latest_major_minor=8.1
vips_version_latest_patch=1
openslide_version_minimum=3.4.0
openslide_version_latest_major_minor=3.4
openslide_version_latest_patch=1
install_libvips_from_source() {
echo "Compiling libvips $vips_version_latest_major_minor.$vips_version_latest_patch 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
make
make install
cd ..
rm -rf vips-$vips_version_latest_major_minor.$vips_version_latest_patch
rm vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
ldconfig
echo "Installed libvips $vips_version_latest_major_minor.$vips_version_latest_patch"
}
install_libopenslide_from_source() {
echo "Compiling openslide $openslide_version_latest_major_minor.$openslide_version_latest_patch from source"
curl -O -L https://github.com/openslide/openslide/releases/download/v$openslide_version_latest_major_minor.$openslide_version_latest_patch/openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
tar xzvf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
cd openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
PKG_CONFIG_PATH=$pkg_config_path ./configure $1
make
make install
cd ..
rm -rf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
rm openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
ldconfig
echo "Installed libopenslide $openslide_version_latest_major_minor.$openslide_version_latest_patch"
}
sorry() {
echo "Sorry, I don't yet know how to install lib$1 on $2"
exit 1
}
pkg_config_path="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig"
check_if_library_exists() {
PKG_CONFIG_PATH=$pkg_config_path pkg-config --exists $1
if [ $? -eq 0 ]; then
version_found=$(PKG_CONFIG_PATH=$pkg_config_path pkg-config --modversion $1)
PKG_CONFIG_PATH=$pkg_config_path pkg-config --atleast-version=$2 $1
if [ $? -eq 0 ]; then
# Found suitable version of libvips
echo "Found lib$1 $version_found"
return 1
fi
echo "Found lib$1 $version_found but require $2"
else
echo "Could not find lib$1 using a PKG_CONFIG_PATH of '$pkg_config_path'"
fi
return 0
}
enable_openslide=0
# Is libvips already installed, and is it at least the minimum required version?
if [ $# -eq 1 ]; then
if [ "$1" = "--with-openslide" ]; then
echo "Installing vips with openslide support"
enable_openslide=1
else
echo "Sorry, $1 is not supported. Did you mean --with-openslide?"
exit 1
fi
fi
if ! type pkg-config >/dev/null; then
sorry "vips" "a system without pkg-config"
fi
openslide_exists=0
if [ $enable_openslide -eq 1 ]; then
check_if_library_exists "openslide" "$openslide_version_minimum"
openslide_exists=$?
fi
check_if_library_exists "vips" "$vips_version_minimum"
vips_exists=$?
if [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 1 ]; then
if [ $openslide_exists -eq 1 ]; then
# Check if vips compiled with openslide support
vips_with_openslide=`vips list classes | grep -i opensli`
if [ -z $vips_with_openslide ]; then
echo "Vips compiled without openslide support."
else
exit 0
fi
fi
elif [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 0 ]; then
exit 0
fi
# Verify root/sudo access
if [ "$(id -u)" -ne "0" ]; then
echo "Sorry, I need root/sudo access to continue"
exit 1
fi
# OS-specific installations of 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
if [ -f /etc/debian_version ]; then
# Debian Linux
DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in
jessie|vivid|wily)
# Debian 8, Ubuntu 15
echo "Installing libopenslide via apt-get"
apt-get install -y libopenslide-dev
;;
trusty|utopic|qiana|rebecca|rafaela|freya)
# Ubuntu 14, Mint 17
echo "Installing libopenslide dependencies via apt-get"
apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
install_libopenslide_from_source
;;
precise|wheezy|maya)
# Debian 7, Ubuntu 12.04, Mint 13
echo "Installing libopenslide dependencies via apt-get"
apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
install_libopenslide_from_source
;;
*)
# Unsupported Debian-based OS
sorry "openslide" "Debian-based $DISTRO"
;;
esac
elif [ -f /etc/redhat-release ]; then
# Red Hat Linux
RELEASE=$(cat /etc/redhat-release)
echo "Detected Red Hat Linux '$RELEASE'"
case $RELEASE in
"Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
# RHEL/CentOS 7
echo "Installing libopenslide dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
install_libopenslide_from_source "--prefix=/usr"
;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS 6
echo "Installing libopenslide dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
install_libopenslide_from_source "--prefix=/usr"
;;
"Fedora release 21 "*|"Fedora release 22 "*)
# Fedora 21, 22
echo "Installing libopenslide via yum"
yum install -y openslide-devel
;;
*)
# Unsupported RHEL-based OS
sorry "openslide" "$RELEASE"
;;
esac
elif [ -f /etc/os-release ]; then
RELEASE=$(cat /etc/os-release | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.2"*)
echo "Installing libopenslide via zypper"
zypper --gpg-auto-import-keys install -y libopenslide-devel
;;
esac
elif [ -f /etc/SuSE-brand ]; then
RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.1")
echo "Installing libopenslide dependencies via zypper"
zypper --gpg-auto-import-keys install -y --type pattern devel_basis
zypper --gpg-auto-import-keys install -y tar curl libpng16-devel libjpeg-turbo libjpeg8-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel libgdk_pixbuf-2_0-0 sqlite3-devel cairo-devel glib2-devel
install_libopenslide_from_source
;;
esac
else
# Unsupported OS
sorry "openslide" "$(uname -a)"
fi
fi
# OS-specific installations of libvips follows
if [ -f /etc/debian_version ]; then
# Debian Linux
DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in
jessie|vivid|wily)
# Debian 8, Ubuntu 15
if [ $enable_openslide -eq 1 ]; then
echo "Recompiling vips with openslide support"
install_libvips_from_source
else
echo "Installing libvips via apt-get"
apt-get install -y libvips-dev libgsf-1-dev
fi
;;
trusty|utopic|qiana|rebecca|rafaela|freya)
# Ubuntu 14, Mint 17
echo "Installing libvips dependencies via apt-get"
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
install_libvips_from_source
;;
precise|wheezy|maya)
# Debian 7, Ubuntu 12.04, Mint 13
echo "Installing libvips dependencies via apt-get"
add-apt-repository -y ppa:lyrasis/precise-backports
apt-get update
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
install_libvips_from_source
;;
*)
# Unsupported Debian-based OS
sorry "vips" "Debian-based $DISTRO"
;;
esac
elif [ -f /etc/redhat-release ]; then
# Red Hat Linux
RELEASE=$(cat /etc/redhat-release)
echo "Detected Red Hat Linux '$RELEASE'"
case $RELEASE in
"Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
# RHEL/CentOS 7
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr"
;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS 6
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel curl
yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
yum install -y --enablerepo=nux-dextop gobject-introspection-devel
yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
yum install -y --enablerepo=remi libwebp-devel
install_libvips_from_source "--prefix=/usr"
;;
"Fedora release 21 "*|"Fedora release 22 "*)
# Fedora 21, 22
if [ $enable_openslide -eq 1 ]; then
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gcc-c++ gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
echo "Compiling vips with openslide support"
install_libvips_from_source "--prefix=/usr"
else
echo "Installing libvips via yum"
yum install -y vips-devel
fi
;;
*)
# Unsupported RHEL-based OS
sorry "vips" "$RELEASE"
;;
esac
elif [ -f /etc/system-release ]; then
# Probably Amazon Linux
RELEASE=$(cat /etc/system-release)
case $RELEASE in
"Amazon Linux AMI release 2015.03"|"Amazon Linux AMI release 2015.09")
# Amazon Linux
echo "Detected '$RELEASE'"
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr"
;;
*)
# Unsupported Amazon Linux version
sorry "vips" "$RELEASE"
;;
esac
elif [ -f /etc/os-release ]; then
RELEASE=$(cat /etc/os-release | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.2"*)
echo "Installing libvips dependencies via zypper"
zypper --gpg-auto-import-keys install -y --type pattern devel_basis
zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
install_libvips_from_source
;;
esac
elif [ -f /etc/SuSE-brand ]; then
RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.1")
echo "Installing libvips dependencies via zypper"
zypper --gpg-auto-import-keys install -y --type pattern devel_basis
zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
install_libvips_from_source
;;
esac
else
# Unsupported OS
sorry "vips" "$(uname -a)"
fi

184
src/common.cc Executable file
View File

@@ -0,0 +1,184 @@
#include <cstdlib>
#include <string>
#include <string.h>
#include <vips/vips.h>
#include "common.h"
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 7 || (VIPS_MAJOR_VERSION == 7 && VIPS_MINOR_VERSION < 40))
#error libvips version 7.40.0+ required - see http://sharp.dimens.io/page/install
#endif
#ifdef _WIN64
#error Windows 64-bit is currently unsupported - see http://sharp.dimens.io/page/install#windows
#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"
namespace sharp {
// How many tasks are in the queue?
volatile int counterQueue = 0;
// How many tasks are being processed?
volatile int counterProcess = 0;
// Filename extension checkers
static bool EndsWith(std::string const &str, std::string const &end) {
return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);
}
bool IsJpeg(std::string const &str) {
return EndsWith(str, ".jpg") || EndsWith(str, ".jpeg") || EndsWith(str, ".JPG") || EndsWith(str, ".JPEG");
}
bool IsPng(std::string const &str) {
return EndsWith(str, ".png") || EndsWith(str, ".PNG");
}
bool IsWebp(std::string const &str) {
return EndsWith(str, ".webp") || EndsWith(str, ".WEBP");
}
bool IsTiff(std::string const &str) {
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
}
bool IsDz(std::string const &str) {
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
}
/*
Determine image format of a buffer.
*/
ImageType DetermineImageType(void *buffer, size_t const length) {
ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load_buffer(buffer, length);
if (load != NULL) {
std::string loader = load;
if (EndsWith(loader, "JpegBuffer")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "PngBuffer")) {
imageType = ImageType::PNG;
} else if (EndsWith(loader, "WebpBuffer")) {
imageType = ImageType::WEBP;
} else if (EndsWith(loader, "TiffBuffer")) {
imageType = ImageType::TIFF;
} else if (EndsWith(loader, "MagickBuffer")) {
imageType = ImageType::MAGICK;
}
}
return imageType;
}
/*
Initialise and return a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
*/
VipsImage* InitImage(void *buffer, size_t const length, VipsAccess const access) {
return vips_image_new_from_buffer(buffer, length, NULL, "access", access, NULL);
}
/*
Determine image format, reads the first few bytes of the file
*/
ImageType DetermineImageType(char const *file) {
ImageType imageType = ImageType::UNKNOWN;
char const *load = vips_foreign_find_load(file);
if (load != NULL) {
std::string loader = load;
if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) {
imageType = ImageType::PNG;
} else if (EndsWith(loader, "WebpFile")) {
imageType = ImageType::WEBP;
} else if (EndsWith(loader, "Openslide")) {
imageType = ImageType::OPENSLIDE;
} else if (EndsWith(loader, "TiffFile")) {
imageType = ImageType::TIFF;
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
imageType = ImageType::MAGICK;
}
}
return imageType;
}
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(char const *file, VipsAccess const access) {
return vips_image_new_from_file(file, "access", access, NULL);
}
/*
Does this image have an embedded profile?
*/
bool HasProfile(VipsImage *image) {
return (vips_image_get_typeof(image, 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) {
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)
);
}
/*
Get EXIF Orientation of image, if any.
*/
int ExifOrientation(VipsImage const *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]);
}
return orientation;
}
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VipsImage *image, int const orientation) {
char exif[3];
g_snprintf(exif, sizeof(exif), "%d", orientation);
vips_image_set_string(image, EXIF_IFD0_ORIENTATION, exif);
}
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VipsImage *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.
*/
int InterpolatorWindowSize(char const *name) {
VipsInterpolate *interpolator = vips_interpolate_new(name);
if (interpolator == NULL) {
return -1;
}
int window_size = vips_interpolate_get_window_size(interpolator);
g_object_unref(interpolator);
return window_size;
}
} // namespace sharp

85
src/common.h Executable file
View File

@@ -0,0 +1,85 @@
#ifndef SRC_COMMON_H_
#define SRC_COMMON_H_
#include <string>
namespace sharp {
enum class ImageType {
UNKNOWN,
JPEG,
PNG,
WEBP,
TIFF,
MAGICK,
OPENSLIDE
};
// How many tasks are in the queue?
extern volatile int counterQueue;
// How many tasks are being processed?
extern volatile int counterProcess;
// Filename extension checkers
bool IsJpeg(std::string const &str);
bool IsPng(std::string const &str);
bool IsWebp(std::string const &str);
bool IsTiff(std::string const &str);
bool IsDz(std::string const &str);
/*
Determine image format of a buffer.
*/
ImageType DetermineImageType(void *buffer, size_t const length);
/*
Determine image format of a file.
*/
ImageType DetermineImageType(char const *file);
/*
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);
/*
Initialise and return a VipsImage from a file.
*/
VipsImage* InitImage(char const *file, VipsAccess const access);
/*
Does this image have an embedded profile?
*/
bool HasProfile(VipsImage *image);
/*
Does this image have an alpha channel?
Uses colour space interpretation with number of channels to guess this.
*/
bool HasAlpha(VipsImage *image);
/*
Get EXIF Orientation of image, if any.
*/
int ExifOrientation(VipsImage const *image);
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VipsImage *image, int const orientation);
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VipsImage *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.
*/
int InterpolatorWindowSize(char const *name);
} // namespace sharp
#endif // SRC_COMMON_H_

232
src/metadata.cc Executable file
View File

@@ -0,0 +1,232 @@
#include <node.h>
#include <vips/vips.h>
#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::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) {}
};
/*
Delete input char[] buffer and notify V8 of memory deallocation
Used as the callback function for the "postclose" signal
*/
static void DeleteBuffer(VipsObject *object, char *buffer) {
if (buffer != NULL) {
delete[] buffer;
}
}
class MetadataWorker : public AsyncWorker {
public:
MetadataWorker(Callback *callback, MetadataBaton *baton) : AsyncWorker(callback), baton(baton) {}
~MetadataWorker() {}
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&counterQueue);
ImageType imageType = ImageType::UNKNOWN;
VipsImage *image = NULL;
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 != NULL) {
// Listen for "postclose" signal to delete input buffer
g_signal_connect(image, "postclose", G_CALLBACK(DeleteBuffer), baton->bufferIn);
} else {
(baton->err).append("Input buffer has corrupt header");
imageType = ImageType::UNKNOWN;
DeleteBuffer(NULL, baton->bufferIn);
}
} else {
(baton->err).append("Input buffer contains unsupported image format");
DeleteBuffer(NULL, baton->bufferIn);
}
} else {
// From file
imageType = DetermineImageType(baton->fileIn.c_str());
if (imageType != ImageType::UNKNOWN) {
image = InitImage(baton->fileIn.c_str(), VIPS_ACCESS_RANDOM);
if (image == NULL) {
(baton->err).append("Input file has corrupt header");
imageType = ImageType::UNKNOWN;
}
} else {
(baton->err).append("Input file is of an unsupported image format");
}
}
if (image != NULL && imageType != 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;
}
// 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);
// Derived attributes
baton->hasAlpha = HasAlpha(image);
baton->orientation = ExifOrientation(image);
// EXIF
if (vips_image_get_typeof(image, VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
void* exif;
size_t exifLength;
if (!vips_image_get_blob(image, VIPS_META_EXIF_NAME, &exif, &exifLength)) {
baton->exifLength = exifLength;
baton->exif = static_cast<char*>(malloc(exifLength));
memcpy(baton->exif, exif, exifLength);
}
}
// ICC profile
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME) == VIPS_TYPE_BLOB) {
void* icc;
size_t iccLength;
if (!vips_image_get_blob(image, VIPS_META_ICC_NAME, &icc, &iccLength)) {
baton->iccLength = iccLength;
baton->icc = static_cast<char*>(malloc(iccLength));
memcpy(baton->icc, icc, iccLength);
}
}
// Drop image reference
g_object_unref(image);
}
// Clean up
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback () {
HandleScope();
Local<Value> argv[2] = { Null(), Null() };
if (!baton->err.empty()) {
// Error
argv[0] = Error(baton->err.c_str());
} 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));
if (baton->orientation > 0) {
Set(info, New("orientation").ToLocalChecked(), New<Number>(baton->orientation));
}
if (baton->exifLength > 0) {
Set(info, New("exif").ToLocalChecked(), NewBuffer(baton->exif, baton->exifLength).ToLocalChecked());
}
if (baton->iccLength > 0) {
Set(info, New("icc").ToLocalChecked(), NewBuffer(baton->icc, baton->iccLength).ToLocalChecked());
}
argv[1] = info;
}
delete baton;
// Return to JavaScript
callback->Call(2, argv);
}
private:
MetadataBaton* baton;
};
/*
metadata(options, callback)
*/
NAN_METHOD(metadata) {
HandleScope();
// V8 objects are converted to non-V8 types held in the baton struct
MetadataBaton *baton = new MetadataBaton;
Local<Object> options = info[0].As<Object>();
// Input filename
baton->fileIn = *Utf8String(Get(options, New("fileIn").ToLocalChecked()).ToLocalChecked());
// Input Buffer object
if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) {
Local<Object> buffer = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>();
// Take a copy of the input Buffer to avoid problems with V8 heap compaction
baton->bufferInLength = node::Buffer::Length(buffer);
baton->bufferIn = new char[baton->bufferInLength];
memcpy(baton->bufferIn, node::Buffer::Data(buffer), baton->bufferInLength);
}
// Join queue for worker thread
Callback *callback = new Callback(info[1].As<Function>());
AsyncQueueWorker(new MetadataWorker(callback, baton));
// Increment queued task counter
g_atomic_int_inc(&counterQueue);
}

8
src/metadata.h Executable file
View File

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

333
src/operations.cc Executable file
View File

@@ -0,0 +1,333 @@
#include <vips/vips.h>
#include "common.h"
#include "operations.h"
namespace sharp {
/*
Alpha composite src over dst
Assumes alpha channels are already premultiplied and will be unpremultiplied after
*/
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out) {
using sharp::HasAlpha;
// Split src into non-alpha and alpha
VipsImage *srcWithoutAlpha;
if (vips_extract_band(src, &srcWithoutAlpha, 0, "n", src->Bands - 1, NULL))
return -1;
vips_object_local(context, srcWithoutAlpha);
VipsImage *srcAlpha;
if (vips_extract_band(src, &srcAlpha, src->Bands - 1, "n", 1, NULL))
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, NULL)) {
return -1;
}
vips_object_local(context, dstWithoutAlpha);
// Alpha: Extract last channel
if (vips_extract_band(dst, &dstAlpha, dst->Bands - 1, "n", 1, NULL)) {
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, NULL)) {
return -1;
}
vips_object_local(context, black);
if (vips_invert(black, &dstAlpha, NULL)) {
return -1;
}
vips_object_local(context, dstAlpha);
}
// Compute normalized input alpha channels:
VipsImage *srcAlphaNormalized;
if (vips_linear1(srcAlpha, &srcAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
return -1;
vips_object_local(context, srcAlphaNormalized);
VipsImage *dstAlphaNormalized;
if (vips_linear1(dstAlpha, &dstAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
return -1;
vips_object_local(context, dstAlphaNormalized);
//
// Compute normalized output alpha channel:
//
// References:
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
// - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826
//
// out_a = src_a + dst_a * (1 - src_a)
// ^^^^^^^^^^^
// t0
// ^^^^^^^^^^^^^^^^^^^
// t1
VipsImage *t0;
if (vips_linear1(srcAlphaNormalized, &t0, -1.0, 1.0, NULL))
return -1;
vips_object_local(context, t0);
VipsImage *t1;
if (vips_multiply(dstAlphaNormalized, t0, &t1, NULL))
return -1;
vips_object_local(context, t1);
VipsImage *outAlphaNormalized;
if (vips_add(srcAlphaNormalized, t1, &outAlphaNormalized, NULL))
return -1;
vips_object_local(context, outAlphaNormalized);
//
// Compute output RGB channels:
//
// Wikipedia:
// out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a
// ^^^^^^^^^^^
// t0
//
// Omit division by `out_a` since `Compose` is supposed to output a
// premultiplied RGBA image as reversal of premultiplication is handled
// externally.
//
VipsImage *t2;
if (vips_multiply(dstWithoutAlpha, t0, &t2, NULL))
return -1;
vips_object_local(context, t2);
VipsImage *outRGBPremultiplied;
if (vips_add(srcWithoutAlpha, t2, &outRGBPremultiplied, NULL))
return -1;
vips_object_local(context, outRGBPremultiplied);
// Denormalize output alpha channel:
VipsImage *outAlpha;
if (vips_linear1(outAlphaNormalized, &outAlpha, 255.0, 0.0, NULL))
return -1;
vips_object_local(context, outAlpha);
// Combine RGB and alpha channel into output image:
return vips_bandjoin2(outRGBPremultiplied, outAlpha, out, NULL);
}
/*
* Premultiply alpha channel of `image`.
*/
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
return vips_premultiply(image, out, NULL);
#else
VipsImage *imageRGB;
if (vips_extract_band(image, &imageRGB, 0, "n", image->Bands - 1, NULL))
return -1;
vips_object_local(context, imageRGB);
VipsImage *imageAlpha;
if (vips_extract_band(image, &imageAlpha, image->Bands - 1, "n", 1, NULL))
return -1;
vips_object_local(context, imageAlpha);
VipsImage *imageAlphaNormalized;
if (vips_linear1(imageAlpha, &imageAlphaNormalized, 1.0 / 255.0, 0.0, NULL))
return -1;
vips_object_local(context, imageAlphaNormalized);
VipsImage *imageRGBPremultiplied;
if (vips_multiply(imageRGB, imageAlphaNormalized, &imageRGBPremultiplied, NULL))
return -1;
vips_object_local(context, imageRGBPremultiplied);
return vips_bandjoin2(imageRGBPremultiplied, imageAlpha, out, NULL);
#endif
}
/*
* Unpremultiply alpha channel of `image`.
*/
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out) {
#if (VIPS_MAJOR_VERSION >= 9 || (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 1))
return vips_unpremultiply(image, out, NULL);
#else
VipsImage *imageRGBPremultipliedTransformed;
if (vips_extract_band(image, &imageRGBPremultipliedTransformed, 0, "n", image->Bands - 1, NULL))
return -1;
vips_object_local(context, imageRGBPremultipliedTransformed);
VipsImage *imageAlphaTransformed;
if (vips_extract_band(image, &imageAlphaTransformed, image->Bands - 1, "n", 1, NULL))
return -1;
vips_object_local(context, imageAlphaTransformed);
VipsImage *imageAlphaNormalizedTransformed;
if (vips_linear1(imageAlphaTransformed, &imageAlphaNormalizedTransformed, 1.0 / 255.0, 0.0, NULL))
return -1;
vips_object_local(context, imageAlphaNormalizedTransformed);
VipsImage *imageRGBUnpremultipliedTransformed;
if (vips_divide(imageRGBPremultipliedTransformed, imageAlphaNormalizedTransformed, &imageRGBUnpremultipliedTransformed, NULL))
return -1;
vips_object_local(context, imageRGBUnpremultipliedTransformed);
return vips_bandjoin2(imageRGBUnpremultipliedTransformed, imageAlphaTransformed, out, NULL);
#endif
}
/*
* Stretch luminance to cover full dynamic range.
*/
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) {
#ifndef _WIN32
// Get original colourspace
VipsInterpretation typeBeforeNormalize = image->Type;
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
typeBeforeNormalize = VIPS_INTERPRETATION_sRGB;
}
// Convert to LAB colourspace
VipsImage *lab;
if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, NULL)) {
return -1;
}
vips_object_local(context, lab);
// Extract luminance
VipsImage *luminance;
if (vips_extract_band(lab, &luminance, 0, "n", 1, NULL)) {
return -1;
}
vips_object_local(context, luminance);
// Extract chroma
VipsImage *chroma;
if (vips_extract_band(lab, &chroma, 1, "n", 2, NULL)) {
return -1;
}
vips_object_local(context, chroma);
// Find luminance range
VipsImage *stats;
if (vips_stats(luminance, &stats, NULL)) {
return -1;
}
vips_object_local(context, stats);
double min = *VIPS_MATRIX(stats, 0, 0);
double max = *VIPS_MATRIX(stats, 1, 0);
if (min != max) {
double f = 100.0 / (max - min);
double a = -(min * f);
// Scale luminance
VipsImage *luminance100;
if (vips_linear1(luminance, &luminance100, f, a, NULL)) {
return -1;
}
vips_object_local(context, luminance100);
// Join scaled luminance to chroma
VipsImage *normalizedLab;
if (vips_bandjoin2(luminance100, chroma, &normalizedLab, NULL)) {
return -1;
}
vips_object_local(context, normalizedLab);
// Convert to original colourspace
VipsImage *normalized;
if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, NULL)) {
return -1;
}
vips_object_local(context, normalized);
// 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, NULL)) {
return -1;
}
vips_object_local(context, alpha);
// Join alpha channel to normalised image
VipsImage *normalizedAlpha;
if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, NULL)) {
return -1;
}
vips_object_local(context, normalizedAlpha);
*out = normalizedAlpha;
} else {
*out = normalized;
}
} else {
// Cannot normalise zero-range image
*out = image;
}
#else
// The normalize operation is currently unsupported on Windows
// See https://github.com/lovell/sharp/issues/152
*out = image;
#endif
return 0;
}
/*
* Gaussian blur (use sigma <0 for fast blur)
*/
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma) {
VipsImage *blurred;
if (sigma < 0.0) {
// Fast, mild blur - averages neighbouring pixels
VipsImage *blur = vips_image_new_matrixv(3, 3,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, 1.0);
vips_image_set_double(blur, "scale", 9);
vips_object_local(context, blur);
if (vips_conv(image, &blurred, blur, NULL)) {
return -1;
}
} else {
// Slower, accurate Gaussian blur
// Create Gaussian function for standard deviation
VipsImage *gaussian;
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) {
return -1;
}
vips_object_local(context, gaussian);
// Apply Gaussian function
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
return -1;
}
}
vips_object_local(context, blurred);
*out = blurred;
return 0;
}
/*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
*/
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged) {
VipsImage *sharpened;
if (radius == -1) {
// Fast, mild sharpen
VipsImage *sharpen = vips_image_new_matrixv(3, 3,
-1.0, -1.0, -1.0,
-1.0, 32.0, -1.0,
-1.0, -1.0, -1.0);
vips_image_set_double(sharpen, "scale", 24);
vips_object_local(context, sharpen);
if (vips_conv(image, &sharpened, sharpen, NULL)) {
return -1;
}
} 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, NULL)) {
return -1;
}
}
vips_object_local(context, sharpened);
*out = sharpened;
return 0;
}
} // namespace sharp

39
src/operations.h Executable file
View File

@@ -0,0 +1,39 @@
#ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_
namespace sharp {
/*
Composite images `src` and `dst` with premultiplied alpha channel and output
image with premultiplied alpha.
*/
int Composite(VipsObject *context, VipsImage *src, VipsImage *dst, VipsImage **out);
/*
* Premultiply alpha channel of `image`.
*/
int Premultiply(VipsObject *context, VipsImage *image, VipsImage **out);
/*
* Unpremultiply alpha channel of `image`.
*/
int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out);
/*
* Stretch luminance to cover full dynamic range.
*/
int Normalize(VipsObject *context, VipsImage *image, VipsImage **out);
/*
* Gaussian blur. Use sigma of -1 for fast blur.
*/
int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma);
/*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
*/
int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged);
} // namespace sharp
#endif // SRC_OPERATIONS_H_

1259
src/pipeline.cc Executable file

File diff suppressed because it is too large Load Diff

8
src/pipeline.h Executable file
View File

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

View File

@@ -1,291 +1,37 @@
#include <node.h>
#include <node_buffer.h>
#include <math.h>
#include <string>
#include <string.h>
#include <vips/vips.h>
using namespace v8;
using namespace node;
#include "nan.h"
struct resize_baton {
std::string file_in;
void* buffer_in;
size_t buffer_in_len;
std::string file_out;
void* buffer_out;
size_t buffer_out_len;
int width;
int height;
bool crop;
VipsExtend extend;
bool sharpen;
bool progessive;
VipsAccess access_method;
std::string err;
Persistent<Function> callback;
#include "common.h"
#include "metadata.h"
#include "pipeline.h"
#include "utilities.h"
resize_baton(): buffer_in_len(0), buffer_out_len(0) {}
};
NAN_MODULE_INIT(init) {
vips_init("sharp");
typedef enum {
JPEG,
PNG
} ImageType;
// Set libvips operation cache limits
vips_cache_set_max_mem(100 * 1024 * 1024); // 100 MB
vips_cache_set_max(500); // 500 operations
unsigned char MARKER_JPEG[] = {0xff, 0xd8};
unsigned char MARKER_PNG[] = {0x89, 0x50};
bool ends_with(std::string const &str, std::string const &end) {
return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);
// Methods available to JavaScript
Nan::Set(target, Nan::New("metadata").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked());
Nan::Set(target, Nan::New("pipeline").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(pipeline)).ToLocalChecked());
Nan::Set(target, Nan::New("cache").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(cache)).ToLocalChecked());
Nan::Set(target, Nan::New("concurrency").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(concurrency)).ToLocalChecked());
Nan::Set(target, Nan::New("counters").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(counters)).ToLocalChecked());
Nan::Set(target, Nan::New("libvipsVersion").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(libvipsVersion)).ToLocalChecked());
Nan::Set(target, Nan::New("format").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(format)).ToLocalChecked());
Nan::Set(target, Nan::New("_maxColourDistance").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(_maxColourDistance)).ToLocalChecked());
}
bool is_jpeg(std::string const &str) {
return ends_with(str, ".jpg") || ends_with(str, ".jpeg");
}
bool is_png(std::string const &str) {
return ends_with(str, ".png");
}
void resize_error(resize_baton *baton, VipsImage *unref) {
(baton->err).append(vips_error_buffer());
vips_error_clear();
g_object_unref(unref);
vips_thread_shutdown();
return;
}
void resize_async(uv_work_t *work) {
resize_baton* baton = static_cast<resize_baton*>(work->data);
ImageType inputImageType = JPEG;
VipsImage *in = vips_image_new();
if (baton->buffer_in_len > 1) {
if (memcmp(MARKER_JPEG, baton->buffer_in, 2) == 0) {
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
return resize_error(baton, in);
}
} else if(memcmp(MARKER_PNG, baton->buffer_in, 2) == 0) {
inputImageType = PNG;
if (vips_pngload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
return resize_error(baton, in);
}
}
} else if (is_jpeg(baton->file_in)) {
if (vips_jpegload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
return resize_error(baton, in);
}
} else if (is_png(baton->file_in)) {
inputImageType = PNG;
if (vips_pngload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
return resize_error(baton, in);
}
} else {
resize_error(baton, in);
(baton->err).append("Unsupported input " + baton->file_in);
return;
}
double xfactor = static_cast<double>(in->Xsize) / std::max(baton->width, 1);
double yfactor = static_cast<double>(in->Ysize) / std::max(baton->height, 1);
double factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
factor = std::max(factor, 1.0);
int shrink = floor(factor);
double residual = shrink / factor;
// Try to use libjpeg shrink-on-load
int shrink_on_load = 1;
if (inputImageType == JPEG) {
if (shrink >= 8) {
factor = factor / 8;
shrink_on_load = 8;
} else if (shrink >= 4) {
factor = factor / 4;
shrink_on_load = 4;
} else if (shrink >= 2) {
factor = factor / 2;
shrink_on_load = 2;
}
if (shrink_on_load > 1) {
// Recalculate integral shrink and double residual
factor = std::max(factor, 1.0);
shrink = floor(factor);
residual = shrink / factor;
// Reload input using shrink-on-load
g_object_unref(in);
in = vips_image_new();
if (baton->buffer_in_len > 1) {
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "shrink", shrink_on_load, NULL)) {
return resize_error(baton, in);
}
} else {
if (vips_jpegload((baton->file_in).c_str(), &in, "shrink", shrink_on_load, NULL)) {
return resize_error(baton, in);
}
}
}
}
VipsImage *shrunk = vips_image_new();
if (shrink > 1) {
// Use vips_shrink with the integral reduction
if (vips_shrink(in, &shrunk, shrink, shrink, NULL)) {
return resize_error(baton, in);
}
} else {
vips_copy(in, &shrunk, NULL);
}
g_object_unref(in);
// Use vips_affine with the remaining float part using bilinear interpolation
VipsImage *affined = vips_image_new();
if (residual > 0) {
if (vips_affine(shrunk, &affined, residual, 0, 0, residual, "interpolate", vips_interpolate_bilinear_static(), NULL)) {
return resize_error(baton, shrunk);
}
} else {
vips_copy(shrunk, &affined, NULL);
}
g_object_unref(shrunk);
VipsImage *canvased = vips_image_new();
if (affined->Xsize != baton->width || affined->Ysize != baton->height) {
if (baton->crop) {
// Crop
int width = std::min(affined->Xsize, baton->width);
int height = std::min(affined->Ysize, baton->height);
int left = (affined->Xsize - width + 1) / 2;
int top = (affined->Ysize - height + 1) / 2;
if (vips_extract_area(affined, &canvased, left, top, width, height, NULL)) {
return resize_error(baton, affined);
}
} else {
// Embed
int left = (baton->width - affined->Xsize) / 2;
int top = (baton->height - affined->Ysize) / 2;
if (vips_embed(affined, &canvased, left, top, baton->width, baton->height, "extend", baton->extend, NULL)) {
return resize_error(baton, affined);
}
}
} else {
vips_copy(affined, &canvased, NULL);
}
g_object_unref(affined);
// Mild sharpen
VipsImage *sharpened = vips_image_new();
if (baton->sharpen) {
INTMASK* sharpen = im_create_imaskv("sharpen", 3, 3,
-1, -1, -1,
-1, 32, -1,
-1, -1, -1);
sharpen->scale = 24;
if (im_conv(canvased, sharpened, sharpen)) {
return resize_error(baton, canvased);
}
} else {
vips_copy(canvased, &sharpened, NULL);
}
g_object_unref(canvased);
if (baton->file_out == "__jpeg") {
// Write JPEG to buffer
if (vips_jpegsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
return resize_error(baton, sharpened);
}
} else if (baton->file_out == "__png") {
// Write PNG to buffer
if (vips_pngsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) {
return resize_error(baton, sharpened);
}
} else if (is_jpeg(baton->file_out)) {
// Write JPEG to file
if (vips_jpegsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
return resize_error(baton, sharpened);
}
} else if (is_png(baton->file_out)) {
// Write PNG to file
if (vips_pngsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) {
return resize_error(baton, sharpened);
}
} else {
(baton->err).append("Unsupported output " + baton->file_out);
}
g_object_unref(sharpened);
vips_thread_shutdown();
}
void resize_async_after(uv_work_t *work, int status) {
HandleScope scope;
resize_baton *baton = static_cast<resize_baton*>(work->data);
Handle<Value> argv[2] = { Null(), Null() };
if (!baton->err.empty()) {
// Error
argv[0] = String::New(baton->err.data(), baton->err.size());
} else if (baton->buffer_out_len > 0) {
// Buffer
Buffer *slowBuffer = Buffer::New(baton->buffer_out_len);
memcpy(Buffer::Data(slowBuffer), baton->buffer_out, baton->buffer_out_len);
Local<Object> globalObj = Context::GetCurrent()->Global();
Local<Function> bufferConstructor = Local<Function>::Cast(globalObj->Get(String::New("Buffer")));
Handle<Value> constructorArgs[3] = { slowBuffer->handle_, v8::Integer::New(baton->buffer_out_len), v8::Integer::New(0) };
argv[1] = bufferConstructor->NewInstance(3, constructorArgs);
g_free(baton->buffer_out);
}
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
baton->callback.Dispose();
delete baton;
delete work;
}
Handle<Value> resize(const Arguments& args) {
HandleScope scope;
resize_baton *baton = new resize_baton;
baton->file_in = *String::Utf8Value(args[0]->ToString());
if (args[1]->IsObject()) {
Local<Object> buffer = args[1]->ToObject();
baton->buffer_in = Buffer::Data(buffer);
baton->buffer_in_len = Buffer::Length(buffer);
}
baton->file_out = *String::Utf8Value(args[2]->ToString());
baton->width = args[3]->Int32Value();
baton->height = args[4]->Int32Value();
Local<String> canvas = args[5]->ToString();
if (canvas->Equals(String::NewSymbol("c"))) {
baton->crop = true;
} else if (canvas->Equals(String::NewSymbol("w"))) {
baton->crop = false;
baton->extend = VIPS_EXTEND_WHITE;
} else if (canvas->Equals(String::NewSymbol("b"))) {
baton->crop = false;
baton->extend = VIPS_EXTEND_BLACK;
}
baton->sharpen = args[6]->BooleanValue();
baton->progessive = args[7]->BooleanValue();
baton->access_method = args[8]->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
baton->callback = Persistent<Function>::New(Local<Function>::Cast(args[9]));
uv_work_t *work = new uv_work_t;
work->data = baton;
uv_queue_work(uv_default_loop(), work, resize_async, (uv_after_work_cb)resize_async_after);
return scope.Close(Undefined());
}
static void at_exit(void* arg) {
HandleScope scope;
vips_shutdown();
}
extern "C" void init(Handle<Object> target) {
HandleScope scope;
vips_init("");
AtExit(at_exit);
NODE_SET_METHOD(target, "resize", resize);
};
NODE_MODULE(sharp, init);
NODE_MODULE(sharp, init)

258
src/utilities.cc Executable file
View File

@@ -0,0 +1,258 @@
#include <node.h>
#include <vips/vips.h>
#include "nan.h"
#include "common.h"
#include "operations.h"
#include "utilities.h"
using v8::Local;
using v8::Object;
using v8::Number;
using v8::String;
using v8::Boolean;
using Nan::HandleScope;
using Nan::New;
using Nan::Set;
using Nan::ThrowError;
using Nan::To;
using Nan::Utf8String;
/*
Get and set cache memory and item limits
*/
NAN_METHOD(cache) {
HandleScope();
// Set cache memory limit
if (info[0]->IsInt32()) {
vips_cache_set_max_mem(To<int32_t>(info[0]).FromJust() * 1048576);
}
// Set cache items limit
if (info[1]->IsInt32()) {
vips_cache_set_max(To<int32_t>(info[1]).FromJust());
}
// Get cache statistics
Local<Object> cache = New<Object>();
Set(cache, New("current").ToLocalChecked(), New<Number>(vips_tracked_get_mem() / 1048576));
Set(cache, New("high").ToLocalChecked(), New<Number>(vips_tracked_get_mem_highwater() / 1048576));
Set(cache, New("memory").ToLocalChecked(), New<Number>(vips_cache_get_max_mem() / 1048576));
Set(cache, New("items").ToLocalChecked(), New<Number>(vips_cache_get_max()));
info.GetReturnValue().Set(cache);
}
/*
Get and set size of thread pool
*/
NAN_METHOD(concurrency) {
HandleScope();
// Set concurrency
if (info[0]->IsInt32()) {
vips_concurrency_set(To<int32_t>(info[0]).FromJust());
}
// Get concurrency
info.GetReturnValue().Set(New<Number>(vips_concurrency_get()));
}
/*
Get internal counters (queued tasks, processing tasks)
*/
NAN_METHOD(counters) {
using sharp::counterProcess;
using sharp::counterQueue;
HandleScope();
Local<Object> counters = New<Object>();
Set(counters, New("queue").ToLocalChecked(), New<Number>(counterQueue));
Set(counters, New("process").ToLocalChecked(), New<Number>(counterProcess));
info.GetReturnValue().Set(counters);
}
/*
Get libvips version
*/
NAN_METHOD(libvipsVersion) {
HandleScope();
char version[9];
g_snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
info.GetReturnValue().Set(New(version).ToLocalChecked());
}
/*
Get available input/output file/buffer/stream formats
*/
NAN_METHOD(format) {
HandleScope();
// Attribute names
Local<String> attrId = New("id").ToLocalChecked();
Local<String> attrInput = New("input").ToLocalChecked();
Local<String> attrOutput = New("output").ToLocalChecked();
Local<String> attrFile = New("file").ToLocalChecked();
Local<String> attrBuffer = New("buffer").ToLocalChecked();
Local<String> attrStream = New("stream").ToLocalChecked();
// Which load/save operations are available for each compressed format?
Local<Object> format = New<Object>();
for (std::string f : {"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz"}) {
// Input
Local<Boolean> hasInputFile =
New<Boolean>(vips_type_find("VipsOperation", (f + "load").c_str()));
Local<Boolean> hasInputBuffer =
New<Boolean>(vips_type_find("VipsOperation", (f + "load_buffer").c_str()));
Local<Object> input = New<Object>();
Set(input, attrFile, hasInputFile);
Set(input, attrBuffer, hasInputBuffer);
Set(input, attrStream, hasInputBuffer);
// Output
Local<Boolean> hasOutputFile =
New<Boolean>(vips_type_find("VipsOperation", (f + "save").c_str()));
Local<Boolean> hasOutputBuffer =
New<Boolean>(vips_type_find("VipsOperation", (f + "save_buffer").c_str()));
Local<Object> output = New<Object>();
Set(output, attrFile, hasOutputFile);
Set(output, attrBuffer, hasOutputBuffer);
Set(output, attrStream, hasOutputBuffer);
// Other attributes
Local<Object> container = New<Object>();
Local<String> formatId = New(f).ToLocalChecked();
Set(container, attrId, formatId);
Set(container, attrInput, input);
Set(container, attrOutput, output);
// Add to set of formats
Set(format, formatId, container);
}
// Raw, uncompressed data
Local<Object> raw = New<Object>();
Local<String> rawId = New("raw").ToLocalChecked();
Set(raw, attrId, rawId);
Set(format, rawId, raw);
// No support for raw input yet, so always false
Local<Boolean> unsupported = New<Boolean>(false);
Local<Object> rawInput = New<Object>();
Set(rawInput, attrFile, unsupported);
Set(rawInput, attrBuffer, unsupported);
Set(rawInput, attrStream, unsupported);
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(raw, attrOutput, rawOutput);
info.GetReturnValue().Set(format);
}
/*
Synchronous, internal-only method used by some of the functional tests.
Calculates the maximum colour distance using the DE2000 algorithm
between two images of the same dimensions and number of channels.
*/
NAN_METHOD(_maxColourDistance) {
using sharp::Premultiply;
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 = NULL;
ImageType imageType1 = DetermineImageType(*Utf8String(info[0]));
if (imageType1 != ImageType::UNKNOWN) {
image1 = InitImage(*Utf8String(info[0]), VIPS_ACCESS_SEQUENTIAL);
if (image1 == NULL) {
g_object_unref(hook);
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 = NULL;
ImageType imageType2 = DetermineImageType(*Utf8String(info[1]));
if (imageType2 != ImageType::UNKNOWN) {
image2 = InitImage(*Utf8String(info[1]), VIPS_ACCESS_SEQUENTIAL);
if (image2 == NULL) {
g_object_unref(hook);
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);
return ThrowError("mismatchedBands");
}
// Ensure same dimensions
if (image1->Xsize != image2->Xsize || image1->Ysize != image2->Ysize) {
g_object_unref(hook);
return ThrowError("mismatchedDimensions");
}
// Premultiply and remove alpha
if (HasAlpha(image1)) {
VipsImage *imagePremultiplied1;
if (Premultiply(hook, image1, &imagePremultiplied1)) {
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, NULL)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, imagePremultipliedNoAlpha1);
image1 = imagePremultipliedNoAlpha1;
}
if (HasAlpha(image2)) {
VipsImage *imagePremultiplied2;
if (Premultiply(hook, image2, &imagePremultiplied2)) {
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, NULL)) {
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, NULL)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
vips_object_local(hook, difference);
// Extract maximum distance
double maxColourDistance;
if (vips_max(difference, &maxColourDistance, NULL)) {
g_object_unref(hook);
return ThrowError(vips_error_buffer());
}
g_object_unref(hook);
info.GetReturnValue().Set(New<Number>(maxColourDistance));
}

13
src/utilities.h Executable file
View File

@@ -0,0 +1,13 @@
#ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_
#include "nan.h"
NAN_METHOD(cache);
NAN_METHOD(concurrency);
NAN_METHOD(counters);
NAN_METHOD(libvipsVersion);
NAN_METHOD(format);
NAN_METHOD(_maxColourDistance);
#endif // SRC_UTILITIES_H_

24
test/bench/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "sharp-benchmark",
"version": "0.0.1",
"private": true,
"author": "Lovell Fuller <npm@lovell.info>",
"description": "Benchmark and performance tests for sharp",
"scripts": {
"test": "node perf && node random && node parallel"
},
"devDependencies": {
"async": "^1.4.2",
"benchmark": "^1.0.0",
"gm": "^1.20.0",
"imagemagick": "^0.1.3",
"imagemagick-native": "^1.8.0",
"jimp": "^0.2.8",
"lwip": "^0.0.8",
"semver": "^5.0.3"
},
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
}

41
test/bench/parallel.js Executable file
View File

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

791
test/bench/perf.js Executable file
View File

@@ -0,0 +1,791 @@
'use strict';
var fs = require('fs');
var async = require('async');
var assert = require('assert');
var Benchmark = require('benchmark');
var semver = require('semver');
// Contenders
var gm = require('gm');
var imagemagick = require('imagemagick');
var jimp = require('jimp');
var sharp = require('../../index');
var imagemagickNative;
try {
imagemagickNative = require('imagemagick-native');
} catch (err) {
console.log('Excluding imagemagick-native');
}
var lwip;
try {
lwip = require('lwip');
} catch (err) {
console.log('Excluding lwip');
}
var fixtures = require('../fixtures');
var width = 720;
var height = 480;
// Approximately equivalent to fast bilinear
var magickFilter = 'Triangle';
// Disable libvips cache to ensure tests are as fair as they can be
sharp.cache(0);
async.series({
jpeg: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg');
// jimp
jpegSuite.add('jimp-buffer-buffer', {
defer: true,
fn: function(deferred) {
new jimp(inputJpgBuffer, function(err) {
if (err) {
throw err;
} else {
this
.resize(width, height)
.quality(80)
.getBuffer(jimp.MIME_JPEG, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
}
}).add('jimp-file-file', {
defer: true,
fn: function(deferred) {
new jimp(fixtures.inputJpg, function(err) {
if (err) {
throw err;
} else {
this
.resize(width, height)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
}
});
// 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, 'linear', 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, 'linear', 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: magickFilter
}, 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: magickFilter
}, 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(magickFilter)
.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(magickFilter)
.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(magickFilter)
.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(magickFilter)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// sharp
jpegSuite.add('sharp-buffer-file', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).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).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-file-file', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg).resize(width, height).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);
readable.pipe(pipeline).pipe(writable);
}
}).add('sharp-file-buffer', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg).resize(width, height).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-promise', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).toBuffer().then(function(buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
}
}).add('sharp-sharpen-mild', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).sharpen().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-sharpen-radius', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).sharpen(3, 1, 3).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-blur-mild', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).blur().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-blur-radius', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).blur(3).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).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-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-nohalo', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).interpolateWith(sharp.interpolator.nohalo).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-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-gamma', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).gamma().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-normalise', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).normalise().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-greyscale', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).greyscale().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-greyscale-gamma', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).gamma().greyscale().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-progressive', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).progressive().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-without-chroma-subsampling', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).withoutChromaSubsampling().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-rotate', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).rotate(90).resize(width, height).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-sequentialRead', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer).resize(width, height).sequentialRead().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).on('cycle', function(event) {
console.log('jpeg ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
}).run();
},
png: function(callback) {
var inputPngBuffer = fs.readFileSync(fixtures.inputPng);
var pngSuite = new Benchmark.Suite('png');
// jimp
pngSuite.add('jimp-buffer-buffer', {
defer: true,
fn: function(deferred) {
new jimp(inputPngBuffer, function(err) {
if (err) {
throw err;
} else {
this
.resize(width, height)
.getBuffer(jimp.MIME_PNG, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
}
}).add('jimp-file-file', {
defer: true,
fn: function(deferred) {
new jimp(fixtures.inputPng, function(err) {
if (err) {
throw err;
} else {
this
.resize(width, height)
.write(fixtures.outputPng, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
}
});
// lwip
if (typeof lwip !== 'undefined') {
pngSuite.add('lwip-buffer-buffer', {
defer: true,
fn: function(deferred) {
lwip.open(inputPngBuffer, 'png', function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
if (err) {
throw err;
}
image.toBuffer('png', function (err, buffer) {
if (err) {
throw err;
}
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
});
});
}
});
}
// imagemagick
pngSuite.add('imagemagick-file-file', {
defer: true,
fn: function(deferred) {
imagemagick.resize({
srcPath: fixtures.inputPng,
dstPath: fixtures.outputPng,
width: width,
height: height,
filter: magickFilter
}, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
// imagemagick-native
if (typeof imagemagickNative !== 'undefined') {
pngSuite.add('imagemagick-native-buffer-buffer', {
defer: true,
fn: function(deferred) {
imagemagickNative.convert({
srcData: inputPngBuffer,
width: width,
height: height,
format: 'PNG',
filter: magickFilter
});
deferred.resolve();
}
});
}
// gm
pngSuite.add('gm-file-file', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputPng)
.resize(width, height)
.filter(magickFilter)
.write(fixtures.outputPng, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-file-buffer', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputPng)
.resize(width, height)
.filter(magickFilter)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// sharp
pngSuite.add('sharp-buffer-file', {
defer: true,
fn: function(deferred) {
sharp(inputPngBuffer).resize(width, height).toFile(fixtures.outputPng, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-buffer-buffer', {
defer: true,
fn: function(deferred) {
sharp(inputPngBuffer).resize(width, height).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-file-file', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputPng).resize(width, height).toFile(fixtures.outputPng, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-file-buffer', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputPng).resize(width, height).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-progressive', {
defer: true,
fn: function(deferred) {
sharp(inputPngBuffer).resize(width, height).progressive().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
if (semver.gte(sharp.libvipsVersion(), '7.41.0')) {
pngSuite.add('sharp-withoutAdaptiveFiltering', {
defer: true,
fn: function(deferred) {
sharp(inputPngBuffer).resize(width, height).withoutAdaptiveFiltering().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
}
pngSuite.on('cycle', function(event) {
console.log(' png ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
}).run();
},
webp: function(callback) {
var inputWebPBuffer = fs.readFileSync(fixtures.inputWebP);
(new Benchmark.Suite('webp')).add('sharp-buffer-file', {
defer: true,
fn: function(deferred) {
sharp(inputWebPBuffer).resize(width, height).toFile(fixtures.outputWebP, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-buffer-buffer', {
defer: true,
fn: function(deferred) {
sharp(inputWebPBuffer).resize(width, height).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-file-file', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputWebP).resize(width, height).toFile(fixtures.outputWebP, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-file-buffer', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputWebp).resize(width, height).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).on('cycle', function(event) {
console.log('webp ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').pluck('name'));
}).run();
}
}, function(err, results) {
assert(!err, err);
Object.keys(results).forEach(function(format) {
if (results[format].toString().substr(0, 5) !== 'sharp') {
console.log('sharp was slower than ' + results[format] + ' for ' + format);
}
});
console.dir(sharp.cache());
});

74
test/bench/random.js Executable file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

BIN
test/fixtures/4.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

View File

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
test/fixtures/CMU-1-Small-Region.svs vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
test/fixtures/Crash_test.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

BIN
test/fixtures/G31D.TIF vendored Normal file

Binary file not shown.

BIN
test/fixtures/Landscape_1.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
test/fixtures/Landscape_2.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_3.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test/fixtures/Landscape_4.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
test/fixtures/Landscape_5.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_6.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
test/fixtures/Landscape_7.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
test/fixtures/Landscape_8.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
test/fixtures/Portrait_1.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
test/fixtures/Portrait_2.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_3.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_4.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
test/fixtures/Portrait_5.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
test/fixtures/Portrait_6.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
test/fixtures/Portrait_7.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
test/fixtures/Portrait_8.jpg vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

17
test/fixtures/Wikimedia-logo.svg vendored Normal file
View File

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

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
test/fixtures/alpha-layer-2-ink.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

BIN
test/fixtures/blackbug.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
test/fixtures/corrupt-header.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 KiB

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