Compare commits

...

454 Commits

Author SHA1 Message Date
Lovell Fuller
18afcf5f90 Release v0.22.0 2019-03-18 23:26:39 +00:00
Lovell Fuller
87a422942d Pin prebuild due to breaking change in 8.2.0 2019-03-18 23:10:33 +00:00
Lovell Fuller
ac515121e5 Release v0.22.0 2019-03-18 21:31:46 +00:00
Lovell Fuller
2bfea0ad76 Docs: refresh usage examples 2019-03-18 21:29:17 +00:00
Lovell Fuller
83cdb558f6 Allow Stream-based input of raw pixel data #1579 2019-03-18 20:15:18 +00:00
Lovell Fuller
9ee377963e Improve error message if libvips tarball is corrupt 2019-03-17 23:07:58 +00:00
Lovell Fuller
9cc06c887b Add support for pages option for multi-page input #1566 2019-03-17 16:37:27 +00:00
Lovell Fuller
7457b50373 Remove unused shared library 2019-03-15 15:58:25 +00:00
Lovell Fuller
6387fb79b1 Small improvements to input and install docs, bump deps 2019-03-15 15:48:55 +00:00
Lovell Fuller
54e5514b9a Bump dependencies to latest 2019-03-10 18:14:43 +00:00
Lovell Fuller
1e4597c284 Changelog entry for #1595 (plus add GIF) 2019-03-10 17:26:26 +00:00
Lovell Fuller
7cafd4386c Add composite op, supporting multiple images and blend modes #728 2019-03-09 22:46:23 +00:00
Lovell Fuller
e3549ba28c Remove functions previously deprecated in v0.21.0
background, crop, embed, ignoreAspectRatio, max, min, withoutEnlargement
2019-03-01 23:43:35 +00:00
Lovell Fuller
d1bbe62e52 Rename armv8 as arm64v8 to match Node's process.arch 2019-03-01 23:43:35 +00:00
Lovell Fuller
36af74a09b Upgrade to libvips v8.7.4 2019-03-01 23:43:35 +00:00
Fabrizio Ruggeri
5afe02be60 Allow page input option to be set for PDF (#1595) 2019-03-01 23:29:34 +00:00
Jack Cross
2262959673 Docs: add missing comma to extend example (#1588) 2019-02-27 11:03:02 +00:00
Lovell Fuller
ba3f914445 Document support for animated WebP in metadata pages 2019-01-27 21:01:49 +00:00
Lovell Fuller
770be35c44 Tests: add a couple of extra leak suppressions for Node 2019-01-27 20:48:17 +00:00
Lovell Fuller
cc9f2b90fd Docs: use absolute URL for logo 2019-01-19 15:15:22 +00:00
Lovell Fuller
4aff57b071 Release v0.21.3 2019-01-19 14:25:37 +00:00
Maxime BACONNAIS
1df8d82fe0 Docs: overlay parameter of overlayWith is optional (#1547) 2019-01-19 14:19:41 +00:00
Lovell Fuller
98e90784f4 Docs: overlay parameter of overlayWith is optional 2019-01-19 14:11:54 +00:00
Lovell Fuller
87ea54cc66 Bump devDependencies 2019-01-19 14:06:16 +00:00
Lovell Fuller
d5e98bc8ad Split file-based input errors into missing vs invalid #1542 2019-01-19 11:59:36 +00:00
Lovell Fuller
fa69ff773a Input image decoding fail fast by default 2019-01-18 19:25:55 +00:00
Lovell Fuller
a183bb1cac Add valgrind memory leak suppressions 2019-01-18 12:08:28 +00:00
Lovell Fuller
cf62372cab Install: log the fallback to build from source
https://github.com/lovell/sharp-libvips/issues/18
2019-01-14 19:33:01 +00:00
Lovell Fuller
56fa9c95a1 Release v0.21.2 2019-01-13 10:26:47 +00:00
Lovell Fuller
32a34a8841 Tests: separate IO suite into per-format unit files 2019-01-13 10:11:32 +00:00
Lovell Fuller
98797445de Expose PNG output options requiring libimagequant #1484 2019-01-13 09:06:05 +00:00
Lovell Fuller
bd377438b6 Ignore colour profiles in LAB images as they are already LAB 2019-01-12 18:13:43 +00:00
Lovell Fuller
9dd6510de6 Expose underlying error message for invalid input #1505 2019-01-12 16:10:25 +00:00
Lovell Fuller
93ad9d4a4a Ensure all metadata removed from PNG unless withMetadata used 2019-01-09 21:17:53 +00:00
Lovell Fuller
4c01a099ea Add ensureAlpha op, adds alpha channel if missing #1153 2019-01-05 21:12:33 +00:00
Lovell Fuller
8e70579e47 Docs: use HTTPS links where available 2019-01-04 21:25:21 +00:00
Lovell Fuller
ee8bfa3980 Add 2019 to list of years of copyright 2019-01-04 16:05:26 +00:00
Lovell Fuller
c5dfa49cae Docs: add sharp logo to readme 2019-01-04 15:56:22 +00:00
Lovell Fuller
0822404129 Docs: expand logo viewBox to prevent clipping 2019-01-04 15:54:33 +00:00
Lovell Fuller
144f39cd45 Docs: add sharp logo, CC BY-SA 4.0 2019-01-04 15:12:53 +00:00
Lovell Fuller
87f191fd05 Node 11 now supported by nodejs/nan 2019-01-03 14:01:18 +00:00
Lovell Fuller
37ed436202 Version bump of devDependencies 2019-01-03 13:26:56 +00:00
Lovell Fuller
88e490356d Test: remove stray console.log 2019-01-03 12:35:54 +00:00
Lovell Fuller
7c631c0787 Ensure shortest resized edge is >= 1px #1003 2019-01-03 12:01:55 +00:00
Lovell Fuller
f5d3721fe0 Doc refresh for #1205 2019-01-02 19:04:49 +00:00
Lovell Fuller
cc633589d9 Expose pages metadata for multi-page input images #1205 2019-01-01 22:10:27 +00:00
Lovell Fuller
cc1d4c1a6d Expose palette-bit-depth metadata, requires upcoming libvips v8.8.0 2019-01-01 21:02:00 +00:00
Lovell Fuller
30ca424942 Apply correct forced output when chaining #1528 2019-01-01 18:40:09 +00:00
Pascal Temel
813831acf0 Docs: Update deprecated overlayWith example (#1526) 2018-12-30 20:44:36 +00:00
Lovell Fuller
a54fe9f77c Prevent mutatation of jpeg options #1516 2018-12-21 19:54:33 +00:00
Amila Welihinda
8c6da5548a Docs: change repo badge to SVG (#1498) 2018-12-10 11:03:03 +01:00
Lovell Fuller
a2aa7d69e7 Add error handler to download stream lovell/sharp-libvips#14 2018-12-08 13:56:05 +00:00
Lovell Fuller
34d5252242 Release v0.21.1 2018-12-07 19:23:54 +00:00
Lovell Fuller
f31e4d2869 Changelog, credit and doc refresh for #1483 2018-12-06 21:58:14 +00:00
Michael B. Klein
c695c40abc Expose libvips pyramid/tile options for TIFF output (#1483) 2018-12-06 22:33:46 +01:00
Lovell Fuller
fd1ca1dbb2 Ensure the tests for #1477 pass on OS X 2018-12-04 23:58:02 +01:00
Lovell Fuller
f25dbd5f61 Ensure the tests for #1477 pass on OS X 2018-12-04 23:45:08 +01:00
Keith
541e7104fd Expose libvips recombination matrix operation #1477 2018-12-04 23:06:34 +01:00
Lovell Fuller
94945cf6ac Changelog and credit for #1475 2018-12-04 07:49:41 +00:00
Lovell Fuller
db76e655f8 Ensure licensing checker works on Windows 2018-11-29 10:15:13 +00:00
Lovell Fuller
d43c7b581d Add licensing checker for production dependencies 2018-11-29 09:53:00 +00:00
Julian Aubourg
383b933e26 Build prototype with Object.assign to allow minification (#1475) 2018-11-26 19:40:06 +01:00
Lovell Fuller
d26ccf6294 Docs: update Alpine repository URLs 2018-11-21 22:42:58 +00:00
Lovell Fuller
6f9699f605 Ensure correct channel info for raw, greyscale output #1425 2018-11-19 20:00:30 +00:00
Quinn Pan
1e9093d781 Docs: correct code example in extend operation 2018-11-19 20:35:16 +01:00
Lovell Fuller
9dc6492e52 Docs: correct code example in extend operation 2018-11-19 19:34:16 +00:00
Lovell Fuller
d22f7cae6a Silence cast-function-type warnings from GCC 8+ 2018-11-19 18:47:35 +00:00
Lovell Fuller
473afaab45 Install: detect missing libvips on OpenBSD and SunOS
See https://github.com/lovell/sharp-libvips/issues/12
2018-11-19 18:46:05 +00:00
Lovell Fuller
dcd68303a4 Docs: add installation details for Lambda without Docker 2018-11-16 10:28:34 +00:00
Lovell Fuller
03394556b5 Update semistandard linter to latest 2018-11-11 18:05:40 +00:00
Lovell Fuller
1c4f6f75f3 Add Node 11 to CI, experimental only, no prebuild
Hide deprecation warnings - see nodejs/nan#811
2018-11-11 17:55:35 +00:00
Lovell Fuller
f00928dedb Doc refresh for #1438 #1439 2018-11-11 17:40:19 +00:00
Daiz
a48f8fbb61 Allow separate parameters for gamma encoding and decoding (#1439) 2018-11-11 10:15:38 +01:00
Daiz
1fa388370e Add support for the "mitchell" kernel for image reductions (#1438) 2018-10-28 15:11:27 +00:00
Christoph Tavan
95ef6b3f71 Docs: update Alpine libvips installation instructions (#1429)
With version 8.7.0 of the vips-dev Alpine package the documented installation instructions no longer work and result in:

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

The fix was proposed in https://bugs.alpinelinux.org/issues/9561#note-2
2018-10-24 14:31:32 +01:00
Lovell Fuller
de11d36d00 Minor version bumps 2018-10-22 19:38:06 +01:00
Lovell Fuller
d77c2adabe Changelog and docs for #1422 2018-10-22 19:17:42 +01:00
SethWen
c89c055ae0 Install: add support for npm_config_sharp_dist_base_url (#1422) 2018-10-22 18:54:47 +01:00
Lovell Fuller
dac8117f32 Docs: ensure options are included for flatten op 2018-10-08 19:56:30 +01:00
Waylon Walker
937b091bab Docs: clear _libvips cache on Windows (#1403) 2018-10-07 16:49:05 +01:00
Lovell Fuller
019e6a1bfe Release v0.21.0 2018-10-04 13:03:30 +01:00
Lovell Fuller
1565e58fcf Update benchmark results ahead of v0.21.0
Remove lwip and images as they lack Node 10 support
2018-10-04 12:41:01 +01:00
Lovell Fuller
c22e2a17ef Update benchmark dependencies 2018-10-04 11:01:09 +01:00
Lovell Fuller
fd2a10ccea Threshold trim tests for non-turbo libjpeg 2018-10-02 20:33:26 +01:00
Lovell Fuller
0725378257 Add trimOffsetLeft, trimOffsetTop to trim response #914 2018-10-02 20:16:00 +01:00
Lovell Fuller
c431909f35 Refresh resize docs to ensure options are present 2018-10-02 18:45:08 +01:00
Lovell Fuller
db4df6f0b2 Add size to metadata response (Stream/Buffer only) #695 2018-10-02 18:05:08 +01:00
Lovell Fuller
17f942c802 Add chromaSubsampling and isProgressive to metadata #1186 2018-10-02 17:11:25 +01:00
Lovell Fuller
60438ebfe5 Ensure precision of trim threshold, update docs #914 2018-10-02 12:45:37 +01:00
Lovell Fuller
21fbe546b8 Switch from custom trim op to vips_find_trim #914 2018-10-02 11:24:32 +01:00
Lovell Fuller
11900945eb Bump dependency versions to latest 2018-10-02 11:23:49 +01:00
Lovell Fuller
ea5270221b Add new leak suppression for nodejs/libuv 2018-10-02 11:23:15 +01:00
Lovell Fuller
a64844689e Deprecate background, add op-specific prop to resize/extend/flatten #1392 2018-10-01 20:58:55 +01:00
Lovell Fuller
6007e13a22 Improve/increase installation error handling 2018-10-01 11:06:12 +01:00
Lovell Fuller
c3274e480b Deprecate crop, embed, ignoreAspectRatio, max, min, withoutEnlargement.
These become options of the resize operation instead. #1135
2018-09-30 20:16:27 +01:00
Miguel Aragón
3c54eeda5b Use more universal English to improve global understanding 2018-09-28 16:04:56 +01:00
Lovell Fuller
6236e4b97d Changelog entry and credit for #1385 2018-09-27 21:01:41 +01:00
freezy
796738da65 Add support for arbitrary rotation angle via vips_rotate (#1385) 2018-09-27 18:00:36 +01:00
freezy
37d385fafa Move background extraction into separate method (#1383) 2018-09-24 10:00:00 +01:00
Lovell Fuller
db2af42ee7 File extend, extract and trim ops under 'resize' #1135
Should make them easier to find in the docs
2018-09-22 14:52:08 +01:00
Lovell Fuller
24b42ef192 Tests: Move all setup to named file 2018-09-22 13:54:20 +01:00
Lovell Fuller
2ce166ab0a Update links to libvips, now in its own GitHub org 2018-09-21 20:33:01 +01:00
Lovell Fuller
71755b69e4 Remove duplicate libvips version/platform check 2018-09-21 20:20:40 +01:00
Lovell Fuller
1106aac2d8 Tests: tweak colour thresholds for (non-turbo) libjpeg compat 2018-09-21 19:51:47 +01:00
Lovell Fuller
93aac660a3 Tests: avoid shrink-on-load for (non-turbo) libjpeg compat 2018-09-21 19:34:52 +01:00
Lovell Fuller
0ce8ad7130 Enable CI on OS X 2018-09-20 10:41:24 +01:00
Lovell Fuller
deacd553bf Enable SIMD convolution by default #1213 2018-09-19 21:42:40 +01:00
Lovell Fuller
c8ff7e11a9 Upgrade to libvips v8.7.0
Drop Node 4 support
Add experimental musl prebuild for Node 8 and 10
2018-09-19 21:38:09 +01:00
Lovell Fuller
4cff62258c Improve smartcrop saliency testing/reporting 2018-09-05 22:49:31 +01:00
Lovell Fuller
0144358afb Release v0.20.8 2018-09-05 08:44:01 +01:00
Lovell Fuller
136097efe7 Downgrade nyc for continued Node 4 support 2018-09-04 17:07:10 +01:00
Lovell Fuller
374c6959d7 Changelog and credit for #1358 #1362 2018-09-04 16:39:24 +01:00
Axel Eirola
7d48a5ccf4 Allow floating point density input (#1362)
Metadata output will still remain integer
2018-09-01 08:58:30 +01:00
ajhool
bf3254cb16 Install: avoid race conditions when creating directories (#1358) 2018-08-29 09:20:26 +01:00
Lovell Fuller
5bed3a7d52 Release v0.20.7 2018-08-21 11:50:14 +01:00
Lovell Fuller
ece111280b Use copy+unlink if rename fails during install #1345 2018-08-20 15:14:31 +01:00
Lovell Fuller
a15a9b956b Release v0.20.6 2018-08-20 11:40:10 +01:00
Lovell Fuller
42860c2f83 Changelog, credit and doc refresh for #1342 2018-08-19 10:43:25 +01:00
Alun Davies
b5b95e5ae1 Expose depth option for tile-based output (#1342) 2018-08-18 15:09:53 +01:00
Lovell Fuller
d705cffdd6 Ensure extractChannel works with 16-bit images #1330 2018-08-12 20:22:39 +01:00
Rodrigo Alviani
23a4bc103e Docs: correct quality option in overlayWith example (#1325) 2018-08-08 08:42:18 +01:00
Lovell Fuller
c14434f9e7 Add removeAlpha op, removes alpha channel if any #1248 2018-08-07 20:32:11 +01:00
Lovell Fuller
25bd2cea3e Add experimental entropy field to stats response 2018-08-06 15:41:27 +01:00
Lovell Fuller
532de4ecab Cache libvips binaries to reduce re-install time #1301 2018-08-05 10:31:41 +01:00
Lovell Fuller
bfdd27eeef Doc refresh and dependency bumps 2018-08-05 09:42:09 +01:00
Lovell Fuller
bd9f238ab4 Improve install time error messages for FreeBSD #1310 2018-08-04 22:27:32 +01:00
Lovell Fuller
75556bb57c Ensure vendor platform mismatch throws error #1303 2018-08-04 21:34:11 +01:00
thegareth
2de062a34a Docs: update the "make a transparent image" example (#1316)
Alpha for colour is between 0-1, not 0-255.
2018-08-02 09:42:25 +01:00
Lovell Fuller
4589b15dea Changelog and credit for #1285 #1290 2018-07-10 16:12:16 +01:00
Sylvain Dumont
8b75ce6786 Allow full WebP alphaQuality range of 0-100 (#1290) 2018-07-10 15:58:17 +01:00
Espen Hovlandsdal
7bbc5176a1 Expose mozjpeg quant_table flag (#1285) 2018-07-10 15:56:05 +01:00
Lovell Fuller
5cb35485f1 Release v0.20.5 2018-06-27 08:44:31 +01:00
Lovell Fuller
80189ed689 Add changelog and credit for #1265 2018-06-27 07:54:41 +01:00
Muhammad Faheem Akhtar
3d7e8ef432 Docs: update new Buffer() to Buffer.from() (#1273) 2018-06-26 08:19:27 +01:00
Lovell Fuller
1999c7103c Docs: add CSS to improve left-hand nav nesting 2018-06-25 19:58:50 +01:00
Thomas Vantuycom
9c20ae383e Remove top of file table of contents in documentation (#1270) 2018-06-24 21:45:51 +01:00
Tom Lokhorst
76c41eaf05 Expose libjpeg optimize_coding flag (#1265) 2018-06-21 18:12:10 +01:00
Lovell Fuller
873aa6700f Release v0.20.4 2018-06-20 08:26:28 +01:00
Lovell Fuller
0d9590a9a0 Documentation refresh 2018-06-20 08:14:03 +01:00
Lovell Fuller
94607b585a Ensure extractChannel sets bw colourspace interp #1257 2018-06-19 22:47:52 +01:00
Lovell Fuller
da0b0348a2 Prevent rounding err with shrink-on-load and 90/270 rot #1241 2018-06-19 21:19:34 +01:00
Lovell Fuller
09263455b5 Release v0.20.3 2018-05-29 08:38:42 +01:00
Lovell Fuller
ddc23493d4 Add warning about possible concurrent tile race #1151 2018-05-25 19:53:23 +01:00
Lovell Fuller
54a71fc142 Fix tint op by ensuring LAB and allowing negative values #1235
Add test cases for more tint colours and input interpretations
2018-05-23 20:51:47 +01:00
Lovell Fuller
b1a9bf10a2 Pre-built binaries provided for even-numbered major versions 2018-05-21 11:44:34 +01:00
Lovell Fuller
97cfbe1b63 Bump dependency versions 2018-05-19 15:19:23 +01:00
Lovell Fuller
0ee8c63551 Replace use of libvips API deprecated since v8.6.1
See jcupitt/libvips@b085908
2018-05-19 15:03:50 +01:00
Lovell Fuller
0ac5a9ad82 Promote nw.js details to main installation docs - see 6e51f2d 2018-05-19 14:53:22 +01:00
Michael
6e51f2d608 rebuild doc for NW.js (#1229)
refers to https://github.com/lovell/sharp/issues/1223
2018-05-15 07:28:09 +01:00
Lovell Fuller
f23a8dc9dc Release v0.20.2 2018-04-28 18:29:32 +01:00
Lovell Fuller
d09fe6178c Add support for Node 10, drop support for Node 9 2018-04-28 18:12:10 +01:00
Lovell Fuller
ae2cfcc4f3 Version bumps, hold simple-get at v2 for Node 4 support 2018-04-28 18:11:20 +01:00
Lovell Fuller
853cc65e32 Changelog entry for #1208 2018-04-28 14:06:45 +01:00
Nathan Graves
5d140d949f Add support for Group4 (CCITTFAX4) compression to TIFF output (#1208) 2018-04-26 19:49:08 +01:00
Lovell Fuller
6d2da2b3ba Switch unzip test dependency for Node 10 support 2018-04-25 20:37:10 +01:00
Lovell Fuller
165e337e44 Changelog entry and doc refresh for #1204 2018-04-25 10:19:33 +01:00
Nathan Graves
b154cd0418 Add support for page selection with multi-page TIFF input (#1204) 2018-04-24 22:57:27 +01:00
Lovell Fuller
8ef1532691 Dependency version bumps 2018-04-17 20:46:22 +01:00
Lovell Fuller
771e44f2a7 Add error highlighting lack of Windows x86 node.exe support 2018-04-17 20:37:19 +01:00
Lovell Fuller
8933f1128d Changelog entry and credit for #1165 2018-04-11 20:30:16 +01:00
Rik Heywood
dbac4b9a63 Add tint operation to set image chroma 2018-04-11 20:05:48 +01:00
Lovell Fuller
bdac5b5807 Add code usage examples for output-related functions 2018-04-06 19:52:00 +01:00
Lovell Fuller
b0961b5213 Bump dependencies 2018-04-06 19:49:17 +01:00
Thomas Parisot
0d7c3fc4d8 Ignore global libvips during install via SHARP_IGNORE_GLOBAL_LIBVIPS (#1165) 2018-03-22 18:48:30 +00:00
Lovell Fuller
8dac256096 Release v0.20.1 2018-03-17 14:04:35 +00:00
Lovell Fuller
8320da39c3 Changelog entry and doc refresh for #1161 2018-03-17 11:12:43 +00:00
Andrea Bianco
875937e3d8 Expose libvips' median filter operation (#1161) 2018-03-17 10:52:44 +00:00
Lovell Fuller
f880adbaac Bump dependencies ahead of v0.20.1 2018-03-16 20:46:08 +00:00
Lovell Fuller
48c5f86adb Improve install when global libvips below min version #1148 2018-03-13 20:37:42 +00:00
Lovell Fuller
f60f7dab12 Prevent error when cumulative rounding below target #1154 2018-03-13 19:42:10 +00:00
Lovell Fuller
8f690236ed Release v0.20.0 2018-03-04 21:54:28 +00:00
Lovell Fuller
430a4297aa Add support for prebuilt sharp binaries on common platforms 2018-03-04 14:33:44 +00:00
Oskar Thornblad
69e3ab02c4 Mention EXIF orientation in metadata's jsdocs (#1142) 2018-03-04 14:19:53 +00:00
Lovell Fuller
b87a20b881 Release v0.19.1 2018-02-24 10:02:12 +00:00
Lovell Fuller
0e6f2d16ab Bump dependencies 2018-02-23 19:27:34 +00:00
Lovell Fuller
498b061819 Upgrade nan dependency, enables async hooks 2018-02-23 19:18:31 +00:00
Lovell Fuller
5ab6f599fb Prevent crop when at or below target dimensions #1134 2018-02-23 10:31:11 +00:00
Lovell Fuller
8b80626035 Changelog, credit and doc refresh for #1121 2018-02-18 19:14:37 +00:00
Andrea Bianco
f86ae79fdb Expose angle option in tile feature (#1121) 2018-02-18 20:00:08 +01:00
Lovell Fuller
1a4e68096f Changelog and credit for #1024, bump dependencies 2018-02-04 13:30:47 +00:00
Marcel
d599d1f29e Expose linear transform feature of libvips (#1024) 2018-02-04 10:36:04 +00:00
Lovell Fuller
73edfb3d2c Update list of files that npm can ignore 2018-01-13 10:28:31 +00:00
Lovell Fuller
ebae68d579 Doc refresh for #1077 2018-01-13 10:27:43 +00:00
Tony Mobily
573836e2b8 Add mention of max() to preserve ratio in withoutEnlargement (#1077) 2018-01-13 09:59:59 +00:00
Lovell Fuller
da5deb8177 Release v0.19.0 2018-01-11 22:31:51 +00:00
Lovell Fuller
8fca89e876 Update documentation hostname 2018-01-10 23:35:50 +00:00
Lovell Fuller
d5295a2d0c Doc refresh for #1079 2018-01-10 22:24:46 +00:00
Oleg Aleynik
c4df115948 Expose IPTC and XMP metadata when available (#1079) 2018-01-10 22:12:11 +00:00
Lovell Fuller
8afcb16d8e Update performance benchmark results for v0.19.0 2018-01-07 14:30:25 +00:00
Lovell Fuller
3764d63244 Add valgrind suppressions for Node 8 2018-01-06 13:39:26 +00:00
Lovell Fuller
c82914df30 Doc refresh and devDependencies version bumps 2018-01-06 00:07:35 +00:00
Lovell Fuller
84ba921f5b Unpin tar dep as race condition is fixed - see 053e727 2018-01-05 10:51:24 +00:00
Lovell Fuller
8e74668e3c Update linter plus related spacing fixes 2018-01-05 10:18:08 +00:00
Lovell Fuller
707c05b5f5 Update libvips dependency minimum to v8.6.1 2018-01-05 09:57:33 +00:00
Lovell Fuller
9bd2cec199 Update benchmark test dependencies 2018-01-02 20:19:11 +00:00
Lovell Fuller
358b8fe8b6 Refresh Lambda install docs to recommend use of Docker 2017-12-22 19:31:03 +00:00
Lovell Fuller
7115ae5375 Prevent toFile ext taking precedence over toFormat #1037 2017-12-20 20:33:08 +00:00
Lovell Fuller
13997ca653 Run unit test files sequentially through valgrind 2017-12-18 19:57:26 +00:00
Lovell Fuller
9fa04a0b93 Attach event listener to clone only for Stream #995 2017-12-12 21:53:27 +00:00
Lovell Fuller
0894145284 Changelog and doc refresh for #1038 2017-12-12 19:42:07 +00:00
Kenric D'Souza
927b77700d Add gravity support to embed feature (#1038) 2017-12-12 19:29:16 +00:00
Lovell Fuller
1d7a0ea99e Changelog and doc refresh for #915 2017-12-09 13:14:23 +00:00
Rahul Nanwani
d6aee8e5ba Add pixel-derived image statistics via vips_stats (#915) 2017-12-09 11:17:48 +00:00
Lovell Fuller
dfaa39fa5d Enable OS X CI 2017-12-09 10:47:02 +00:00
Lovell Fuller
8fe3b59efe Update test fixtures for e9b7231ac0 2017-12-08 17:54:39 +00:00
Lovell Fuller
47237b1f15 Add expected min libvips version to error messages 2017-12-07 23:16:33 +00:00
Lovell Fuller
053e727bd5 Pin tar dependency as latest version has a race condition with Node 4 2017-12-01 19:00:28 +00:00
Lovell Fuller
2ce9e81d80 Wait for close event before decompressing tarball
Should reduce the chance of a race condition
2017-11-30 20:29:18 +00:00
Lovell Fuller
50848ee462 Add unit test coverage report for C++ 2017-11-30 20:16:30 +00:00
Lovell Fuller
ef06d560cd Windows CI: retry npm install to workaround race condition 2017-11-30 20:12:14 +00:00
Lovell Fuller
2abf9f96c7 Force TIFF predictor=none to match fixture in squash tests 2017-11-27 19:55:20 +00:00
Lovell Fuller
a91686b4cd Temporarily revert to previous npm for Node 9 support 2017-11-27 19:38:44 +00:00
Lovell Fuller
1be424cd47 Add missing file from commit fc233ed 2017-11-27 19:35:08 +00:00
Lovell Fuller
929ea10f76 Re-enable test for 1200 DPI SVG rasterisation to PNG 2017-11-27 19:31:31 +00:00
Lovell Fuller
11d1f39e3d Add Node 9 to CI test environments 2017-11-27 19:15:09 +00:00
Lovell Fuller
fc233ed4ff Update upstream libvips source to v8.6.0-beta1 2017-11-27 19:14:30 +00:00
Lovell Fuller
efd2e893cf Ensure expected fixtures match libvips v8.6.0 behaviour 2017-11-27 19:13:39 +00:00
Lovell Fuller
2a18b9a8f7 Remove centreSampling option, update some expected test fixtures
See https://github.com/jcupitt/libvips/issues/705
2017-11-26 13:49:56 +00:00
Lovell Fuller
6aa214181b Version bump dependencies 2017-11-25 11:25:27 +00:00
Lovell Fuller
7067beda99 Docs refresh for cropOffsetLeft/cropOffsetTop 2017-11-25 11:24:53 +00:00
Lovell Fuller
e0f0baf164 Silence a couple of MSVC type conversion warnings 2017-11-17 20:10:10 +00:00
Lovell Fuller
1fec132dee Add tilecache before smartcrop to avoid over-computation 2017-11-17 19:53:50 +00:00
Lovell Fuller
ba521fccb4 Replace caw with its tunnel-agent dependency 2017-11-07 19:38:10 +00:00
Lovell Fuller
965a97105e Default TIFF predictor to horizontal to match libvips' behaviour 2017-10-19 19:40:07 +01:00
Lovell Fuller
3c88c84998 Doc refresh, changelog and dead code removal for #977 2017-10-19 11:05:43 +01:00
Jarda Kotěšovec
d0f66c3734 Switch to libvips' resize, make fastShrinkOnLoad optional (#977) 2017-10-19 11:05:43 +01:00
Lovell Fuller
ebc2a741f6 Ensure accessMethod is applied during shrink-on-load 2017-10-19 11:05:43 +01:00
Lovell Fuller
d46512af1c Doc refresh and changelog entry for #976 2017-10-19 11:05:43 +01:00
Matthew McEachen
b4d72bd544 Add failOnError option to fail-fast on bad input image data (#976) 2017-10-19 11:05:43 +01:00
Lovell Fuller
382d476271 Temporarily allow CI failures on OS X 2017-10-19 11:05:43 +01:00
Lovell Fuller
80c240b54a Add compile time check for libvips 8.6.0+ 2017-10-19 11:05:43 +01:00
Lovell Fuller
21f99d88ab Docs: possible to install libvips via pkg on FreeBSD 2017-10-19 11:05:43 +01:00
Lovell Fuller
d5873a00d5 Docs refresh 2017-10-19 11:05:43 +01:00
Lovell Fuller
99076edc89 Default PNG output to adaptiveFiltering=false, compressionLevel=9
PNG is mostly used for logos and line art, so these defaults will
produce the smallest files for most output most of the time.
2017-10-19 11:05:43 +01:00
Lovell Fuller
7b6c80327e Verify platform of vendor binaries at install and run time 2017-10-19 11:05:43 +01:00
Lovell Fuller
57946ed672 Upgrade to libvips v8.6.0
Expose offset coordinates of strategy-based crop
Switch to Github releases for prebuilt libvips
Move packaging scripts to lovell/sharp-libvips repo
2017-10-19 11:05:43 +01:00
Aditya Bharti
aad16ac50d Improved jsdocs for withoutEnlargement and sequentialRead #972 (#992) 2017-10-15 09:28:08 +01:00
Brooks Becton
bbe897e607 Added Coding tools section and added types link (#975) 2017-10-04 13:02:11 +01:00
Mika Fischer
eca0e66e23 Remove use of dynamic require to facilitate bundling (#960) 2017-09-25 21:00:40 +01:00
Lovell Fuller
3511723914 Release v0.18.4 2017-09-18 18:26:12 +01:00
Lovell Fuller
6a1c7b7588 Ensure input Buffer really is marked as Persistent #950 2017-09-16 20:17:32 +01:00
Lovell Fuller
18fd6ef119 Release v0.18.3 2017-09-13 10:51:08 +01:00
Matt Parrish
0004f5d2ff Migrate from got to simple-get (#945)
The simple-get module provides support for basic auth and
is already used by the future prebuild dependency.
2017-09-12 07:38:17 +01:00
Mario Viens
5f29d1ba9c Clarifying cache documentation (#919) 2017-08-19 10:43:36 +01:00
Jasper Blues
791fd35c35 Update README.md (#913) 2017-08-16 12:09:02 +01:00
Kleis Auke Wolthuizen
e0d622d347 Skip shrink-on-load when trimming (#887) (#888) 2017-07-28 21:02:30 +01:00
Lovell Fuller
6b34e8a804 Docs: FreeBSD gcc v4/v5 needs _GLIBCXX_USE_C99 for C++11 #873 2017-07-25 14:54:44 +01:00
Lovell Fuller
eb8773fe3e Use detect-libc instead of ldd output parsing 2017-07-17 12:11:13 +01:00
Lovell Fuller
b40e3fa1f1 Docs: Alpine vips-dev package requires fftw-dev also 2017-07-15 14:47:25 +01:00
Lovell Fuller
d25d761b55 Update benchmark deps, allow node-images to fail 2017-07-14 13:18:13 +01:00
Lovell Fuller
d6051dd714 Release v0.18.2 2017-07-01 09:52:47 +01:00
Lovell Fuller
53ff061efa Document SHARP_DIST_BASE_URL #841 2017-07-01 09:44:02 +01:00
Lovell Fuller
72b0efd393 Remove possible switch case fall through
Luckily there were no side effects in this... case
2017-06-25 19:09:20 +01:00
Lovell Fuller
df97ef23d9 Document/changelog for Solus Linux support #857 2017-06-25 17:42:03 +01:00
Ekrem Karaca
f6373971bd Add support for Solus OS (#857) 2017-06-24 13:48:03 +01:00
Lovell Fuller
ec617f2489 Document minimum Node dependency of v4.5.0 #847 2017-06-20 22:05:05 +01:00
Lovell Fuller
502ae78579 Allow binary download URL override via SHARP_DIST_BASE_URL #841 2017-06-20 21:40:46 +01:00
Lovell Fuller
49297d6afb Ensure flip and flop operations work with auto-rotate #837 2017-06-19 23:42:26 +01:00
Lovell Fuller
29354badd8 Docs: add Alpine Linux install details 2017-06-19 08:13:49 +01:00
Lovell Fuller
3c4de796c8 Dependency bumps and next point release 2017-06-04 19:55:45 +01:00
Lovell Fuller
c7f4488e77 Docs and changelog entry for #828 2017-06-04 19:51:22 +01:00
Yves Bos
d8765f955d Allow xres and yres to be set for TIFF output (#828) 2017-06-03 10:52:09 +01:00
Lovell Fuller
9f20037dad Release v0.18.1 2017-05-30 21:02:35 +01:00
Lovell Fuller
2ebb090df2 Update Travis CI to Ubuntu 16.04
Rollback Appveyor CI from Node 8 to 7
2017-05-30 20:50:32 +01:00
Lovell Fuller
110fff3ab9 Replace Node 7 with Node 8 in CI environment 2017-05-30 20:32:15 +01:00
Lovell Fuller
f42a1ceab7 Recalculate residual after adjusting shrink #831 2017-05-30 20:22:15 +01:00
Lovell Fuller
9e39a7fa95 Correct shrink calc, regression introduced in e398b47 #831 2017-05-30 17:16:41 +01:00
Lovell Fuller
c879df3b31 Release v0.18.0 2017-05-30 08:09:59 +01:00
Lovell Fuller
361ed98353 Remove previously-deprecated output format 'option' functions 2017-05-23 21:57:05 +01:00
Lovell Fuller
d45f8ef2d3 Document the cache-free nature of metadata extraction #796 2017-05-23 21:24:29 +01:00
Lovell Fuller
d6a63d11d7 Docs refresh 2017-05-22 21:49:37 +01:00
jingsam
4c6804eadc Add toFormat 'jpg' alias for 'jpeg' (#814) 2017-05-22 12:59:43 +01:00
Nicolas Coden
99810c0311 Add support for any rotation angle (#791)
Allow to provide any positive or negative multiple of 90 to `.rotate(...)`.
Negative angles and angles above 360 are converted to valid 0/90/180/270
rotations (0 rotations are still ignored).

Changes:
- [Node] Add `useExifOrientation` internal variable to know if the Exif
  orientation must be used instead of the provided angle. This allows to save a
  negative angle in the `angle` option, because the `-1` special case is not
  needed.

- [Node] Change check for planed-rotation in extract, to prepare a
  rotation before extraction: check with both `angle` and `useExifOrientation`
  options.
  I think this check contains a bit too much logics on rotation options. Maybe
  we could move this condition to a dedicated function.

- [C++] Separate `CalculateRotationAndFlip` into two generic functions:
  - `CalculateExifRotationAndFlip`: Calculate the angle of rotation and
    need-to-flip for the given Exif orientation.
  - `CalculateAngleRotation`: Calculate the rotation for the given angle.

  One or the other function is used to calculate the rotation, depending on
  wether the Exif orientation tag or the provided angle must be used.

- Add unit tests for `-3690`, `-450`, `-90`, `90`, `450`, `3690` and `-3780`,
  `-540`, `0`, `180`, `540`, `3780` rotations
- Add `320x240` fixture image for tests.

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

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

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

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
src/libvips/* linguist-vendored

14
.gitignore vendored
View File

@@ -1,11 +1,15 @@
build
node_modules
coverage
/coverage
test/bench/node_modules
test/fixtures/output*
test/leak/libvips.supp
lib
include
packaging/libvips*
packaging/*.log
test/saliency/report.json
test/saliency/Image*
test/saliency/[Uu]serData*
!test/saliency/userData.js
vendor
.gitattributes
.DS_Store
.nyc_output
package-lock.json

View File

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

View File

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

View File

@@ -2,15 +2,14 @@ build
node_modules
coverage
.editorconfig
.jshintignore
.jshintrc
.gitattributes
.gitignore
test
.travis.yml
appveyor.yml
circle.yml
mkdocs.yml
lib
include
packaging
preinstall.sh
docs/css/
vendor
.prebuildrc
.nyc_output
CONTRIBUTING.md

4
.prebuildrc Normal file
View File

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

View File

@@ -1,22 +1,79 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "4"
- "5"
os:
- linux
- osx
sudo: false
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
osx_image: xcode7.3
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install homebrew/science/vips; fi
after_success:
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
matrix:
include:
- name: "Linux (glibc) - Node 6"
os: linux
dist: trusty
sudo: false
language: node_js
node_js: "6"
- name: "Linux (glibc) - Node 8"
os: linux
dist: trusty
sudo: false
language: node_js
node_js: "8"
- name: "Linux (glibc) - Node 10"
os: linux
dist: trusty
sudo: false
language: node_js
node_js: "10"
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
- name: "Linux (glibc) - Node 11"
os: linux
dist: trusty
sudo: false
language: node_js
node_js: "11"
- name: "Linux (musl) - Node 8"
os: linux
dist: trusty
sudo: true
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:8-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux (musl) - Node 10"
os: linux
dist: trusty
sudo: true
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux (musl) - Node 11"
os: linux
dist: trusty
sudo: true
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:11-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "OS X - Node 6"
os: osx
osx_image: xcode9.2
language: node_js
node_js: "6"
- name: "OS X - Node 8"
os: osx
osx_image: xcode9.2
language: node_js
node_js: "8"
- name: "OS X - Node 10"
os: osx
osx_image: xcode9.2
language: node_js
node_js: "10"
- name: "OS X - Node 11"
os: osx
osx_image: xcode9.2
language: node_js
node_js: "11"

View File

@@ -14,7 +14,7 @@ New bugs are assigned a `triage` label whilst under investigation.
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.
Implementation is usually straightforward if _libvips_ [already supports](https://libvips.github.io/libvips/API/current/) the feature you need.
## Submit a Pull Request to fix a bug
@@ -41,9 +41,8 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch |
| ------: | :--------- |
| v0.15.0 | outfit |
| v0.16.0 | pencil |
| v0.17.0 | quill |
| v0.22.0 | uptake |
| v0.23.0 | vision |
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
@@ -63,6 +62,17 @@ A method to be removed should be deprecated in the next major version then remov
By way of example, the [bilinearInterpolation method](https://github.com/lovell/sharp/blob/v0.6.0/index.js#L155) present in v0.5.0 was deprecated in v0.6.0 and removed in v0.7.0.
## Documentation
The public API is documented with [JSDoc](http://usejsdoc.org/) annotated comments.
These can be converted to Markdown by running:
```sh
npm run docs
```
Please include documentation updates in any Pull Request that modifies the public API.
## Run the tests
### Functional tests and static code analysis
@@ -79,15 +89,11 @@ Requires [Valgrind](http://valgrind.org/).
npm run test-leak
```
### Packaging tests
Tests the installation on a number of Linux-based operating systems.
Requires docker.
```sh
npm run test-packaging
```
## Finally
Please feel free to ask any questions via a [new issue](https://github.com/lovell/sharp/issues/new) or contact me by [e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4).
Please feel free to ask any questions via a
[new issue](https://github.com/lovell/sharp/issues/new).
If you're unable to post details publicly, please
[e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4)
for private, paid consulting.

View File

@@ -1,47 +1,112 @@
# sharp
<img src="https://raw.githubusercontent.com/lovell/sharp/master/docs/image/sharp-logo.svg?sanitize=true" width="160" height="160" alt="sharp logo" align="right">
```sh
npm install sharp
```
The typical use case for this high speed Node.js module
is to convert large images of many formats to
is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x faster than using the
Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed.
Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most Windows (x64), Linux and ARMv6+ systems do not require
the installation of any external runtime dependencies.
Most modern 64-bit OS X, Windows and Linux systems running
Node versions 6, 8, 10 and 11
do not require any additional install or runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips`
to install the libvips dependency.
## Examples
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
```javascript
const sharp = require('sharp');
```
### Callback
```javascript
sharp(inputBuffer)
.resize(320, 240)
.toFile('output.webp', (err, info) => { ... });
```
### Promise
```javascript
sharp('input.jpg')
.rotate()
.resize(200)
.toBuffer()
.then( data => { ... })
.catch( err => { ... });
```
### Async/await
```javascript
const semiTransparentRedPng = await sharp({
create: {
width: 48,
height: 48,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
}
})
.png()
.toBuffer();
```
### Stream
```javascript
const roundedCorners = Buffer.from(
'<svg><rect x="0" y="0" width="200" height="200" rx="50" ry="50"/></svg>'
);
const roundedCornerResizer =
sharp()
.resize(200, 200)
.composite([{
input: roundedCorners,
blend: 'dest-in'
}])
.png();
readableStream
.pipe(roundedCornerResizer)
.pipe(writableStream);
```
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.svg?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
### Documentation
Visit [sharp.dimens.io](http://sharp.dimens.io/) for complete
[installation instructions](http://sharp.dimens.io/page/install),
[API documentation](http://sharp.dimens.io/page/api),
[benchmark tests](http://sharp.dimens.io/page/performance) and
[changelog](http://sharp.dimens.io/page/changelog).
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
[installation instructions](https://sharp.pixelplumbing.com/page/install),
[API documentation](https://sharp.pixelplumbing.com/page/api),
[benchmark tests](https://sharp.pixelplumbing.com/page/performance) and
[changelog](https://sharp.pixelplumbing.com/page/changelog).
### Contributing
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md)
covers reporting bugs, requesting features and submitting code changes.
### Licence
### Licensing
Copyright 2013, 2014, 2015, 2016 Lovell Fuller and contributors.
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

View File

@@ -3,13 +3,14 @@ version: "{build}"
build: off
platform: x64
environment:
VIPS_WARNING: 0
matrix:
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "5"
- nodejs_version: "6"
- nodejs_version: "8"
- nodejs_version: "10"
- nodejs_version: "11"
install:
- ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@5
- npm install
test_script:
- npm run-script test-win
- npm test

View File

@@ -5,11 +5,9 @@
['OS == "win"', {
# Build libvips C++ binding for Windows due to MSVC std library ABI changes
'type': 'shared_library',
'variables': {
'download_vips': '<!(node -e "require(\'./binding\').download_vips()")'
},
'defines': [
'VIPS_CPLUSPLUS_EXPORTS'
'VIPS_CPLUSPLUS_EXPORTS',
'_ALLOW_KEYWORD_MACROS'
],
'sources': [
'src/libvips/cplusplus/VError.cpp',
@@ -17,14 +15,14 @@
'src/libvips/cplusplus/VImage.cpp'
],
'include_dirs': [
'<(module_root_dir)/include',
'<(module_root_dir)/include/glib-2.0',
'<(module_root_dir)/lib/glib-2.0/include'
'vendor/include',
'vendor/include/glib-2.0',
'vendor/lib/glib-2.0/include'
],
'libraries': [
'<(module_root_dir)/lib/libvips.lib',
'<(module_root_dir)/lib/libglib-2.0.lib',
'<(module_root_dir)/lib/libgobject-2.0.lib'
'../vendor/lib/libvips.lib',
'../vendor/lib/libglib-2.0.lib',
'../vendor/lib/libgobject-2.0.lib'
],
'configurations': {
'Release': {
@@ -48,56 +46,32 @@
'dependencies': [
'libvips-cpp'
],
# Nested variables "pattern" borrowed from http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi
'variables': {
'variables': {
'variables': {
'conditions': [
['OS != "win"', {
# Build the PKG_CONFIG_PATH environment variable with all possible combinations
'pkg_config_path': '<!(which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR || true):$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig'
}, {
'pkg_config_path': ''
}]
],
},
'conditions': [
['OS != "win"', {
# Which version, if any, of libvips is available globally via pkg-config?
'global_vips_version': '<!(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --modversion vips-cpp 2>/dev/null || true)'
}, {
'global_vips_version': ''
}]
],
'pkg_config_path%': '<(pkg_config_path)'
},
'pkg_config_path%': '<(pkg_config_path)',
'runtime_link%': 'shared',
'conditions': [
['OS != "win"', {
# Does the globally available version of libvips, if any, meet the minimum version requirement?
'use_global_vips': '<!(GLOBAL_VIPS_VERSION="<(global_vips_version)" node -e "require(\'./binding\').use_global_vips()")'
'pkg_config_path': '<!(node -e "console.log(require(\'./lib/libvips\').pkgConfigPath())")',
'use_global_libvips': '<!(node -e "console.log(Boolean(require(\'./lib/libvips\').useGlobalLibvips()).toString())")'
}, {
'use_global_vips': ''
'pkg_config_path': '',
'use_global_libvips': ''
}]
]
},
'sources': [
'src/common.cc',
'src/metadata.cc',
'src/stats.cc',
'src/operations.cc',
'src/pipeline.cc',
'src/sharp.cc',
'src/utilities.cc'
],
'defines': [
'_GLIBCXX_USE_CXX11_ABI=0'
],
'include_dirs': [
'<!(node -e "require(\'nan\')")'
],
'conditions': [
['use_global_vips == "true"', {
['use_global_libvips == "true"', {
# Use pkg-config for include and lib
'include_dirs': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --cflags-only-I vips-cpp vips glib-2.0 | sed s\/-I//g)'],
'conditions': [
@@ -105,52 +79,84 @@
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs --static vips-cpp)']
}, {
'libraries': ['<!@(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs vips-cpp)']
}],
['OS == "linux"', {
'defines': [
# Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible.
'_GLIBCXX_USE_CXX11_ABI=<!(if readelf -Ws "$(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --variable libdir vips-cpp)/libvips-cpp.so" | c++filt | grep -qF __cxx11;then echo "1";else echo "0";fi)'
]
}]
]
}, {
# Attempt to download pre-built libvips and install locally within node_modules
# Use pre-built libvips stored locally within node_modules
'include_dirs': [
'<(module_root_dir)/include',
'<(module_root_dir)/include/glib-2.0',
'<(module_root_dir)/lib/glib-2.0/include'
'vendor/include',
'vendor/include/glib-2.0',
'vendor/lib/glib-2.0/include'
],
'conditions': [
['OS == "win"', {
'defines': [
'_ALLOW_KEYWORD_MACROS'
],
'libraries': [
'<(module_root_dir)/lib/libvips.lib',
'<(module_root_dir)/lib/libglib-2.0.lib',
'<(module_root_dir)/lib/libgobject-2.0.lib'
'../vendor/lib/libvips.lib',
'../vendor/lib/libglib-2.0.lib',
'../vendor/lib/libgobject-2.0.lib'
]
}],
['OS == "mac"', {
'libraries': [
'../vendor/lib/libvips-cpp.42.dylib',
'../vendor/lib/libvips.42.dylib',
'../vendor/lib/libglib-2.0.0.dylib',
'../vendor/lib/libgobject-2.0.0.dylib',
# Ensure runtime linking is relative to sharp.node
'-rpath \'@loader_path/../../vendor/lib\''
]
}],
['OS == "linux"', {
'variables': {
'download_vips': '<!(LDD_VERSION="<!(ldd --version 2>&1 || true)" node -e "require(\'./binding\').download_vips()")'
},
'defines': [
'_GLIBCXX_USE_CXX11_ABI=0'
],
'libraries': [
'<(module_root_dir)/lib/libvips-cpp.so',
'<(module_root_dir)/lib/libvips.so',
'<(module_root_dir)/lib/libglib-2.0.so',
'<(module_root_dir)/lib/libgobject-2.0.so',
'../vendor/lib/libvips-cpp.so',
'../vendor/lib/libvips.so',
'../vendor/lib/libglib-2.0.so',
'../vendor/lib/libgobject-2.0.so',
# Dependencies of dependencies, included for openSUSE support
'<(module_root_dir)/lib/libGraphicsMagick.so',
'<(module_root_dir)/lib/libGraphicsMagickWand.so',
'<(module_root_dir)/lib/libexif.so',
'<(module_root_dir)/lib/libgio-2.0.so',
'<(module_root_dir)/lib/libgmodule-2.0.so',
'<(module_root_dir)/lib/libgsf-1.so',
'<(module_root_dir)/lib/libjpeg.so',
'<(module_root_dir)/lib/libpng.so',
'<(module_root_dir)/lib/libtiff.so',
'<(module_root_dir)/lib/libwebp.so',
'<(module_root_dir)/lib/libz.so',
'<(module_root_dir)/lib/libffi.so',
'<(module_root_dir)/lib/libgthread-2.0.so',
'<(module_root_dir)/lib/liblcms2.so',
'<(module_root_dir)/lib/libpng16.so',
'<(module_root_dir)/lib/libxml2.so',
'<(module_root_dir)/lib/liborc-0.4.so',
'../vendor/lib/libcairo.so',
'../vendor/lib/libcroco-0.6.so',
'../vendor/lib/libexif.so',
'../vendor/lib/libexpat.so',
'../vendor/lib/libffi.so',
'../vendor/lib/libfontconfig.so',
'../vendor/lib/libfreetype.so',
'../vendor/lib/libfribidi.so',
'../vendor/lib/libgdk_pixbuf-2.0.so',
'../vendor/lib/libgif.so',
'../vendor/lib/libgio-2.0.so',
'../vendor/lib/libgmodule-2.0.so',
'../vendor/lib/libgsf-1.so',
'../vendor/lib/libgthread-2.0.so',
'../vendor/lib/libharfbuzz.so',
'../vendor/lib/libjpeg.so',
'../vendor/lib/liblcms2.so',
'../vendor/lib/liborc-0.4.so',
'../vendor/lib/libpango-1.0.so',
'../vendor/lib/libpangocairo-1.0.so',
'../vendor/lib/libpangoft2-1.0.so',
'../vendor/lib/libpixman-1.so',
'../vendor/lib/libpng.so',
'../vendor/lib/librsvg-2.so',
'../vendor/lib/libtiff.so',
'../vendor/lib/libwebp.so',
'../vendor/lib/libwebpdemux.so',
'../vendor/lib/libwebpmux.so',
'../vendor/lib/libxml2.so',
'../vendor/lib/libz.so',
# Ensure runtime linking is relative to sharp.node
'-Wl,-rpath=\'$${ORIGIN}/../../lib\''
'-Wl,--disable-new-dtags -Wl,-rpath=\'$${ORIGIN}/../../vendor/lib\''
]
}]
]
@@ -176,69 +182,25 @@
},
'configurations': {
'Release': {
'cflags_cc': [
'-Wno-cast-function-type',
'-Wno-deprecated-declarations'
],
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [
'-Wno-deprecated-declarations'
]
},
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'msvs_disabled_warnings': [
4275
4275,
4996
]
}
},
}, {
'target_name': 'win_copy_dlls',
'type': 'none',
'dependencies': [
'sharp'
],
'conditions': [
['OS == "win"', {
# Windows lacks support for rpath
'copies': [{
'destination': '<(module_root_dir)/build/Release',
'files': [
'<(module_root_dir)/lib/GNU.Gettext.dll',
'<(module_root_dir)/lib/libMagickCore-6.Q16-2.dll',
'<(module_root_dir)/lib/libMagickWand-6.Q16-2.dll',
'<(module_root_dir)/lib/libasprintf-0.dll',
'<(module_root_dir)/lib/libcairo-2.dll',
'<(module_root_dir)/lib/libcairo-gobject-2.dll',
'<(module_root_dir)/lib/libcairo-script-interpreter-2.dll',
'<(module_root_dir)/lib/libexif-12.dll',
'<(module_root_dir)/lib/libexpat-1.dll',
'<(module_root_dir)/lib/libffi-6.dll',
'<(module_root_dir)/lib/libfftw3-3.dll',
'<(module_root_dir)/lib/libfontconfig-1.dll',
'<(module_root_dir)/lib/libfreetype-6.dll',
'<(module_root_dir)/lib/libgcc_s_seh-1.dll',
'<(module_root_dir)/lib/libgdk_pixbuf-2.0-0.dll',
'<(module_root_dir)/lib/libgio-2.0-0.dll',
'<(module_root_dir)/lib/libglib-2.0-0.dll',
'<(module_root_dir)/lib/libgmodule-2.0-0.dll',
'<(module_root_dir)/lib/libgobject-2.0-0.dll',
'<(module_root_dir)/lib/libgsf-1-114.dll',
'<(module_root_dir)/lib/libgthread-2.0-0.dll',
'<(module_root_dir)/lib/libintl-8.dll',
'<(module_root_dir)/lib/libjpeg-62.dll',
'<(module_root_dir)/lib/liblcms2-2.dll',
'<(module_root_dir)/lib/libopenjp2.dll',
'<(module_root_dir)/lib/libopenslide-0.dll',
'<(module_root_dir)/lib/libpango-1.0-0.dll',
'<(module_root_dir)/lib/libpangocairo-1.0-0.dll',
'<(module_root_dir)/lib/libpangowin32-1.0-0.dll',
'<(module_root_dir)/lib/libpixman-1-0.dll',
'<(module_root_dir)/lib/libpng16-16.dll',
'<(module_root_dir)/lib/libquadmath-0.dll',
'<(module_root_dir)/lib/libsqlite3-0.dll',
'<(module_root_dir)/lib/libssp-0.dll',
'<(module_root_dir)/lib/libtiff-5.dll',
'<(module_root_dir)/lib/libvips-42.dll',
'<(module_root_dir)/lib/libxml2-2.dll',
'<(module_root_dir)/lib/zlib1.dll'
]
}]
}]
]
}]
}

View File

@@ -1,134 +0,0 @@
'use strict';
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');
var semver = require('semver');
var request = require('request');
var tar = require('tar');
var tmp = require('os').tmpdir();
var distBaseUrl = 'https://dl.bintray.com/lovell/sharp/';
// Use NPM-provided environment variable where available, falling back to require-based method for Electron
var minimumLibvipsVersion = process.env.npm_package_config_libvips || require('./package.json').config.libvips;
var vipsHeaderPath = path.join(__dirname, 'include', 'vips', 'vips.h');
// -- Helpers
// Does this file exist?
var isFile = function(file) {
var exists = false;
try {
exists = fs.statSync(file).isFile();
} catch (err) {}
return exists;
};
var unpack = function(tarPath, done) {
var extractor = tar.Extract({
path: __dirname
});
extractor.on('error', error);
extractor.on('end', function() {
if (!isFile(vipsHeaderPath)) {
error('Could not unpack ' + tarPath);
}
if (typeof done === 'function') {
done();
}
});
fs.createReadStream(tarPath).on('error', error)
.pipe(zlib.Unzip())
.pipe(extractor);
};
// Error
var error = function(msg) {
if (msg instanceof Error) {
msg = msg.message;
}
process.stderr.write('ERROR: ' + msg + '\n');
process.exit(1);
};
// -- Binary downloaders
module.exports.download_vips = function() {
// Has vips been installed locally?
if (!isFile(vipsHeaderPath)) {
// Ensure Intel 64-bit or ARM
if (process.arch === 'ia32') {
error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
// Ensure glibc >= 2.15
var lddVersion = process.env.LDD_VERSION;
if (lddVersion) {
if (/(glibc|gnu libc)/i.test(lddVersion)) {
var glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) {
error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
} else {
error(lddVersion.split(/\n/)[0] + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');
}
}
// Arch/platform-specific .tar.gz
var platform = (process.arch === 'arm') ? 'arm' : process.platform.substr(0, 3);
var tarFilename = ['libvips', minimumLibvipsVersion, platform].join('-') + '.tar.gz';
var tarPath = path.join(__dirname, 'packaging', tarFilename);
if (isFile(tarPath)) {
unpack(tarPath);
} else {
// Download to per-process temporary file
tarPath = path.join(tmp, process.pid + '-' + tarFilename);
var tmpFile = fs.createWriteStream(tarPath).on('finish', function() {
unpack(tarPath, function() {
// Attempt to remove temporary file
try {
fs.unlinkSync(tarPath);
} catch (err) {}
});
});
var options = {
url: distBaseUrl + tarFilename
};
if (process.env.npm_config_https_proxy) {
// Use the NPM-configured HTTPS proxy
options.proxy = process.env.npm_config_https_proxy;
}
request(options).on('response', function(response) {
if (response.statusCode !== 200) {
error(distBaseUrl + tarFilename + ' status code ' + response.statusCode);
}
}).on('error', function(err) {
error('Download from ' + distBaseUrl + tarFilename + ' failed: ' + err.message);
}).pipe(tmpFile);
}
}
};
module.exports.use_global_vips = function() {
var useGlobalVips = false;
var globalVipsVersion = process.env.GLOBAL_VIPS_VERSION;
if (globalVipsVersion) {
useGlobalVips = semver.gte(
globalVipsVersion,
minimumLibvipsVersion
);
}
if (process.platform === 'darwin' && !useGlobalVips) {
if (globalVipsVersion) {
error(
'Found libvips ' + globalVipsVersion + ' but require ' + minimumLibvipsVersion +
'\nPlease upgrade libvips by running: brew update && brew upgrade'
);
} else {
error('Please install libvips by running: brew install homebrew/science/vips --with-webp --with-graphicsmagick');
}
}
process.stdout.write(useGlobalVips ? 'true' : 'false');
};

View File

@@ -1,6 +0,0 @@
machine:
services:
- docker
test:
override:
- ./packaging/test.sh

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

@@ -0,0 +1,115 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## removeAlpha
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
### Examples
```javascript
sharp('rgba.png')
.removeAlpha()
.toFile('rgb.png', function(err, info) {
// rgb.png is a 3 channel image without an alpha channel
});
```
Returns **Sharp**
## ensureAlpha
Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
### Examples
```javascript
sharp('rgb.jpg')
.ensureAlpha()
.toFile('rgba.png', function(err, info) {
// rgba.png is a 4 channel image with a fully opaque alpha channel
});
```
Returns **Sharp**
## extractChannel
Extract a single channel from a multi-channel image.
### Parameters
- `channel` **([Number][1] \| [String][2])** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
### Examples
```javascript
sharp(input)
.extractChannel('green')
.toFile('input_green.jpg', function(err, info) {
// info.channels === 1
// input_green.jpg contains the green channel of the input image
});
```
- Throws **[Error][3]** Invalid channel
Returns **Sharp**
## joinChannel
Join one or more channels to the image.
The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
Channel ordering follows vips convention:
- sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
- CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
Buffers may be any of the image formats supported by sharp: JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data.
For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
### Parameters
- `images` **([Array][4]&lt;([String][2] \| [Buffer][5])> | [String][2] \| [Buffer][5])** one or more images (file paths, Buffers).
- `options` **[Object][6]** image options, see `sharp()` constructor.
- Throws **[Error][3]** Invalid parameters
Returns **Sharp**
## bandbool
Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
### Parameters
- `boolOp` **[String][2]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
### Examples
```javascript
sharp('3-channel-rgb-input.png')
.bandbool(sharp.bool.and)
.toFile('1-channel-output.png', function (err, info) {
// The output will be a single channel image where each pixel `P = R & G & B`.
// If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
// then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
});
```
- Throws **[Error][3]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[5]: https://nodejs.org/api/buffer.html
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object

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

@@ -0,0 +1,79 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## tint
Tint the image using the provided chroma while preserving the image luminance.
An alpha channel may be present and will be unchanged by the operation.
### Parameters
- `rgb` **([String][1] \| [Object][2])** parsed by the [color][3] module to extract chroma values.
- Throws **[Error][4]** Invalid parameter
Returns **Sharp**
## greyscale
Convert to 8-bit greyscale; 256 shades of grey.
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
By default the output image will be web-friendly sRGB and contain three (identical) color channels.
This may be overridden by other sharp operations such as `toColourspace('b-w')`,
which will produce an output image containing one color channel.
An alpha channel may be present, and will be unchanged by the operation.
### Parameters
- `greyscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp**
## grayscale
Alternative spelling of `greyscale`.
### Parameters
- `grayscale` **[Boolean][5]** (optional, default `true`)
Returns **Sharp**
## toColourspace
Set the output colourspace.
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
### Parameters
- `colourspace` **[String][1]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][6]
- Throws **[Error][4]** Invalid parameters
Returns **Sharp**
## toColorspace
Alternative spelling of `toColourspace`.
### Parameters
- `colorspace` **[String][1]?** output colorspace.
- Throws **[Error][4]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[3]: https://www.npmjs.org/package/color
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[6]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568

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

@@ -0,0 +1,81 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## composite
Composite image(s) over the processed (resized, extracted etc.) image.
The images to composite must be the same size or smaller than the processed image.
If both `top` and `left` options are provided, they take precedence over `gravity`.
The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
`dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
`xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
`colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
`hard-light`, `soft-light`, `difference`, `exclusion`.
More information about blend modes can be found at
[https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode][1]
and [https://www.cairographics.org/operators/][2]
### Parameters
- `images` **[Array][3]&lt;[Object][4]>** Ordered list of images to composite
- `images[].input` **([Buffer][5] \| [String][6])?** Buffer containing image data or String containing the path to an image file.
- `images[].blend` **[String][6]** how to blend this image with the image below. (optional, default `'over'`)
- `images[].gravity` **[String][6]** gravity at which to place the overlay. (optional, default `'centre'`)
- `images[].top` **[Number][7]?** the pixel offset from the top edge.
- `images[].left` **[Number][7]?** the pixel offset from the left edge.
- `images[].tile` **[Boolean][8]** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
- `images[].density` **[Number][7]** number representing the DPI for vector overlay image. (optional, default `72`)
- `images[].raw` **[Object][4]?** describes overlay when using raw pixel data.
- `images[].raw.width` **[Number][7]?**
- `images[].raw.height` **[Number][7]?**
- `images[].raw.channels` **[Number][7]?**
- `images[].create` **[Object][4]?** describes a blank overlay to be created.
- `images[].create.width` **[Number][7]?**
- `images[].create.height` **[Number][7]?**
- `images[].create.channels` **[Number][7]?** 3-4
- `images[].create.background` **([String][6] \| [Object][4])?** parsed by the [color][9] module to extract values for red, green, blue and alpha.
### Examples
```javascript
sharp('input.png')
.rotate(180)
.resize(300)
.flatten( { background: '#ff6600' } )
.composite([{ input: 'overlay.png', gravity: 'southeast' }])
.sharpen()
.withMetadata()
.webp( { quality: 90 } )
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});
```
- Throws **[Error][10]** Invalid parameters
Returns **Sharp**
[1]: https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
[2]: https://www.cairographics.org/operators/
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[5]: https://nodejs.org/api/buffer.html
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[9]: https://www.npmjs.org/package/color
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error

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

@@ -0,0 +1,121 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## Sharp
### Parameters
- `input` **([Buffer][1] \| [String][2])?** if present, can be
a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
- `options` **[Object][3]?** if present, is an Object with optional attributes.
- `options.failOnError` **[Boolean][4]** by default halt processing and raise an error when loading invalid images.
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
- `options.density` **[Number][5]** number representing the DPI for vector images. (optional, default `72`)
- `options.pages` **[Number][5]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
- `options.page` **[Number][5]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
- `options.raw` **[Object][3]?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.width` **[Number][5]?**
- `options.raw.height` **[Number][5]?**
- `options.raw.channels` **[Number][5]?** 1-4
- `options.create` **[Object][3]?** describes a new image to be created.
- `options.create.width` **[Number][5]?**
- `options.create.height` **[Number][5]?**
- `options.create.channels` **[Number][5]?** 3-4
- `options.create.background` **([String][2] \| [Object][3])?** parsed by the [color][6] module to extract values for red, green, blue and alpha.
### Examples
```javascript
sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg', function(err) {
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
```
```javascript
// Read image data from readableStream,
// resize to 300 pixels wide,
// emit an 'info' event with calculated dimensions
// and finally write image data to writableStream
var transformer = sharp()
.resize(300)
.on('info', function(info) {
console.log('Image height is ' + info.height);
});
readableStream.pipe(transformer).pipe(writableStream);
```
```javascript
// Create a blank 300x200 PNG image of semi-transluent red pixels
sharp({
create: {
width: 300,
height: 200,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
}
})
.png()
.toBuffer()
.then( ... );
```
- Throws **[Error][7]** Invalid parameters
Returns **[Sharp][8]**
### format
An Object containing nested boolean values representing the available input and output formats/methods.
#### Examples
```javascript
console.log(sharp.format);
```
Returns **[Object][3]**
### versions
An Object containing the version numbers of libvips and its dependencies.
#### Examples
```javascript
console.log(sharp.versions);
```
## queue
An EventEmitter that emits a `change` event when a task is either:
- queued, waiting for _libuv_ to provide a worker thread
- complete
### Examples
```javascript
sharp.queue.on('change', function(queueLength) {
console.log('Queue contains ' + queueLength + ' task(s)');
});
```
[1]: https://nodejs.org/api/buffer.html
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[6]: https://www.npmjs.org/package/color
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[8]: #sharp

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

@@ -0,0 +1,150 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## clone
Take a "snapshot" of the Sharp instance, returning a new instance.
Cloned instances inherit the input of their parent instance.
This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
### Examples
```javascript
const pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
Returns **Sharp**
## metadata
Fast access to (uncached) image metadata without decoding any compressed image data.
A `Promise` is returned when `callback` is not provided.
- `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
- `size`: Total size of image in bytes, for Stream and Buffer input only
- `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
- `height`: Number of pixels high (EXIF orientation is not taken into consideration)
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][1]
- `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...][2]
- `density`: Number of pixels per inch (DPI), if present
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
- `pages`: Number of pages/frames contained within the image, with support for TIFF, PDF, animated GIF and animated WebP
- `pageHeight`: Number of pixels high each page in this PDF image will be.
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
- `orientation`: Number value of the EXIF Orientation header, if present
- `exif`: Buffer containing raw EXIF data, if present
- `icc`: Buffer containing raw [ICC][3] profile data, if present
- `iptc`: Buffer containing raw IPTC data, if present
- `xmp`: Buffer containing raw XMP data, if present
### Parameters
- `callback` **[Function][4]?** called with the arguments `(err, metadata)`
### Examples
```javascript
const image = sharp(inputJpg);
image
.metadata()
.then(function(metadata) {
return image
.resize(Math.round(metadata.width / 2))
.webp()
.toBuffer();
})
.then(function(data) {
// data contains a WebP image half the width and height of the original JPEG
});
```
Returns **([Promise][5]&lt;[Object][6]> | Sharp)**
## stats
Access to pixel-derived image statistics for every channel in the image.
A `Promise` is returned when `callback` is not provided.
- `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
- `min` (minimum value in the channel)
- `max` (maximum value in the channel)
- `sum` (sum of all values in a channel)
- `squaresSum` (sum of squared values in a channel)
- `mean` (mean of the values in a channel)
- `stdev` (standard deviation for the values in a channel)
- `minX` (x-coordinate of one of the pixel where the minimum lies)
- `minY` (y-coordinate of one of the pixel where the minimum lies)
- `maxX` (x-coordinate of one of the pixel where the maximum lies)
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
- `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
### Parameters
- `callback` **[Function][4]?** called with the arguments `(err, stats)`
### Examples
```javascript
const image = sharp(inputJpg);
image
.stats()
.then(function(stats) {
// stats contains the channel-wise statistics array and the isOpaque value
});
```
Returns **[Promise][5]&lt;[Object][6]>**
## limitInputPixels
Do not process input images where the number of pixels (width x height) exceeds this limit.
Assumes image dimensions contained in the input metadata can be trusted.
The default limit is 268402689 (0x3FFF x 0x3FFF) pixels.
### Parameters
- `limit` **([Number][7] \| [Boolean][8])** an integral Number of pixels, zero or false to remove limit, true to use default limit.
- Throws **[Error][9]** Invalid limit
Returns **Sharp**
## sequentialRead
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
This will reduce memory usage and can improve performance on some systems.
The default behaviour _before_ function call is `false`, meaning the libvips access method is not sequential.
### Parameters
- `sequentialRead` **[Boolean][8]** (optional, default `true`)
Returns **Sharp**
[1]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636
[2]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672
[3]: https://www.npmjs.com/package/icc
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error

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

@@ -0,0 +1,304 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## rotate
Rotate the output image by either an explicit angle
or auto-orient based on the EXIF `Orientation` tag.
If an angle is provided, it is converted to a valid positive degree rotation.
For example, `-450` will produce a 270deg rotation.
When rotating by an angle other than a multiple of 90,
the background colour can be provided with the `background` option.
If no angle is provided, it is determined from the EXIF data.
Mirroring is supported and may infer the use of a flip operation.
The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
Method order is important when both rotating and extracting regions,
for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
### Parameters
- `angle` **[Number][1]** angle of rotation. (optional, default `auto`)
- `options` **[Object][2]?** if present, is an Object with optional attributes.
- `options.background` **([String][3] \| [Object][2])** parsed by the [color][4] module to extract values for red, green, blue and alpha. (optional, default `"#000000"`)
### Examples
```javascript
const pipeline = sharp()
.rotate()
.resize(null, 200)
.toBuffer(function (err, outputBuffer, info) {
// outputBuffer contains 200px high JPEG image data,
// auto-rotated using EXIF Orientation tag
// info.width and info.height contain the dimensions of the resized image
});
readableStream.pipe(pipeline);
```
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## flip
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
### Parameters
- `flip` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
## flop
Flop the image about the horizontal X axis. This always occurs after rotation, if any.
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
### Parameters
- `flop` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
## sharpen
Sharpen the image.
When used without parameters, performs a fast, mild sharpen of the output image.
When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
Separate control over the level of sharpening in "flat" and "jagged" areas is available.
### Parameters
- `sigma` **[Number][1]?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `flat` **[Number][1]** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
- `jagged` **[Number][1]** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## median
Apply median filter.
When used without parameters the default window is 3x3.
### Parameters
- `size` **[Number][1]** square mask size: size x size (optional, default `3`)
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## blur
Blur the image.
When used without parameters, performs a fast, mild blur of the output image.
When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
### Parameters
- `sigma` **[Number][1]?** a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## flatten
Merge alpha transparency channel, if any, with a background.
### Parameters
- `options` **[Object][2]?**
- `options.background` **([String][3] \| [Object][2])** background colour, parsed by the [color][4] module, defaults to black. (optional, default `{r:0,g:0,b:0}`)
Returns **Sharp**
## gamma
Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
then increasing the encoding (brighten) post-resize at a factor of `gamma`.
This can improve the perceived brightness of a resized image in non-linear colour spaces.
JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
when applying a gamma correction.
Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
### Parameters
- `gamma` **[Number][1]** value between 1.0 and 3.0. (optional, default `2.2`)
- `gammaOut` **[Number][1]?** value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## negate
Produce the "negative" of the image.
### Parameters
- `negate` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
## normalise
Enhance output image contrast by stretching its luminance to cover the full dynamic range.
### Parameters
- `normalise` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
## normalize
Alternative spelling of normalise.
### Parameters
- `normalize` **[Boolean][6]** (optional, default `true`)
Returns **Sharp**
## convolve
Convolve the image with the specified kernel.
### Parameters
- `kernel` **[Object][2]**
- `kernel.width` **[Number][1]** width of the kernel in pixels.
- `kernel.height` **[Number][1]** width of the kernel in pixels.
- `kernel.kernel` **[Array][7]&lt;[Number][1]>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number][1]** the scale of the kernel in pixels. (optional, default `sum`)
- `kernel.offset` **[Number][1]** the offset of the kernel in pixels. (optional, default `0`)
### Examples
```javascript
sharp(input)
.convolve({
width: 3,
height: 3,
kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1]
})
.raw()
.toBuffer(function(err, data, info) {
// data contains the raw pixel data representing the convolution
// of the input image with the horizontal Sobel operator
});
```
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## threshold
Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
### Parameters
- `threshold` **[Number][1]** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
- `options` **[Object][2]?**
- `options.greyscale` **[Boolean][6]** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean][6]** alternative spelling for greyscale. (optional, default `true`)
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## boolean
Perform a bitwise boolean operation with operand image.
This operation creates an output image where each pixel is the result of
the selected bitwise boolean `operation` between the corresponding pixels of the input images.
### Parameters
- `operand` **([Buffer][8] \| [String][3])** Buffer containing image data or String containing the path to an image file.
- `operator` **[String][3]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
- `options` **[Object][2]?**
- `options.raw` **[Object][2]?** describes operand when using raw pixel data.
- `options.raw.width` **[Number][1]?**
- `options.raw.height` **[Number][1]?**
- `options.raw.channels` **[Number][1]?**
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## linear
Apply the linear formula a \* input + b to the image (levels adjustment)
### Parameters
- `a` **[Number][1]** multiplier (optional, default `1.0`)
- `b` **[Number][1]** offset (optional, default `0.0`)
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
## recomb
Recomb the image with the specified matrix.
### Parameters
- `inputMatrix`
- `3x3` **[Array][7]&lt;[Array][7]&lt;[Number][1]>>** Recombination matrix
### Examples
```javascript
sharp(input)
.recomb([
[0.3588, 0.7044, 0.1368],
[0.2990, 0.5870, 0.1140],
[0.2392, 0.4696, 0.0912],
])
.raw()
.toBuffer(function(err, data, info) {
// data contains the raw pixel data after applying the recomb
// With this example input, a sepia filter has been applied
});
```
- Throws **[Error][5]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[4]: https://www.npmjs.org/package/color
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[8]: https://nodejs.org/api/buffer.html

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

@@ -0,0 +1,326 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## toFile
Write output image data to a file.
If an explicit output format is not selected, it will be inferred from the extension,
with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
Note that raw pixel data is only supported for buffer output.
A `Promise` is returned when `callback` is not provided.
### Parameters
- `fileOut` **[String][1]** the path to write the image data to.
- `callback` **[Function][2]?** called on completion with two arguments `(err, info)`.
`info` contains the output image `format`, `size` (bytes), `width`, `height`,
`channels` and `premultiplied` (indicating if premultiplication was used).
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
### Examples
```javascript
sharp(input)
.toFile('output.png', (err, info) => { ... });
```
```javascript
sharp(input)
.toFile('output.png')
.then(info => { ... })
.catch(err => { ... });
```
- Throws **[Error][3]** Invalid parameters
Returns **[Promise][4]&lt;[Object][5]>** when no callback is provided
## toBuffer
Write output to a Buffer.
JPEG, PNG, WebP, TIFF and RAW output are supported.
By default, the format will match the input image, except GIF and SVG input which become PNG output.
`callback`, if present, gets three arguments `(err, data, info)` where:
- `err` is an error, if any.
- `data` is the output image data.
- `info` contains the output image `format`, `size` (bytes), `width`, `height`,
`channels` and `premultiplied` (indicating if premultiplication was used).
When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
A `Promise` is returned when `callback` is not provided.
### Parameters
- `options` **[Object][5]?**
- `options.resolveWithObject` **[Boolean][6]?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function][2]?**
### Examples
```javascript
sharp(input)
.toBuffer((err, data, info) => { ... });
```
```javascript
sharp(input)
.toBuffer()
.then(data => { ... })
.catch(err => { ... });
```
```javascript
sharp(input)
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => { ... })
.catch(err => { ... });
```
Returns **[Promise][4]&lt;[Buffer][7]>** when no callback is provided
## withMetadata
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
This will also convert to and add a web-friendly sRGB ICC profile.
### Parameters
- `withMetadata` **[Object][5]?**
- `withMetadata.orientation` **[Number][8]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
### Examples
```javascript
sharp('input.jpg')
.withMetadata()
.toFile('output-with-metadata.jpg')
.then(info => { ... });
```
- Throws **[Error][3]** Invalid parameters
Returns **Sharp**
## jpeg
Use these JPEG options for output image.
### Parameters
- `options` **[Object][5]?** output options
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
- `options.chromaSubsampling` **[String][1]** set to '4:4:4' to prevent chroma subsampling when quality &lt;= 90 (optional, default `'4:2:0'`)
- `options.trellisQuantisation` **[Boolean][6]** apply trellis quantisation, requires mozjpeg (optional, default `false`)
- `options.overshootDeringing` **[Boolean][6]** apply overshoot deringing, requires mozjpeg (optional, default `false`)
- `options.optimiseScans` **[Boolean][6]** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`)
- `options.optimizeScans` **[Boolean][6]** alternative spelling of optimiseScans (optional, default `false`)
- `options.optimiseCoding` **[Boolean][6]** optimise Huffman coding tables (optional, default `true`)
- `options.optimizeCoding` **[Boolean][6]** alternative spelling of optimiseCoding (optional, default `true`)
- `options.quantisationTable` **[Number][8]** quantization table to use, integer 0-8, requires mozjpeg (optional, default `0`)
- `options.quantizationTable` **[Number][8]** alternative spelling of quantisationTable (optional, default `0`)
- `options.force` **[Boolean][6]** force JPEG output, otherwise attempt to use input format (optional, default `true`)
### Examples
```javascript
// Convert any input to very high quality JPEG output
const data = await sharp(input)
.jpeg({
quality: 100,
chromaSubsampling: '4:4:4'
})
.toBuffer();
```
- Throws **[Error][3]** Invalid options
Returns **Sharp**
## png
Use these PNG options for output image.
PNG output is always full colour at 8 or 16 bits per pixel.
Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
### Parameters
- `options` **[Object][5]?**
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
- `options.palette` **[Boolean][6]** quantise to a palette-based image with alpha transparency support, requires libimagequant (optional, default `false`)
- `options.quality` **[Number][8]** use the lowest number of colours needed to achieve given quality, requires libimagequant (optional, default `100`)
- `options.colours` **[Number][8]** maximum number of palette entries, requires libimagequant (optional, default `256`)
- `options.colors` **[Number][8]** alternative spelling of `options.colours`, requires libimagequant (optional, default `256`)
- `options.dither` **[Number][8]** level of Floyd-Steinberg error diffusion, requires libimagequant (optional, default `1.0`)
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
### Examples
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.png()
.toBuffer();
```
- Throws **[Error][3]** Invalid options
Returns **Sharp**
## webp
Use these WebP options for output image.
### Parameters
- `options` **[Object][5]?** output options
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
- `options.alphaQuality` **[Number][8]** quality of alpha layer, integer 0-100 (optional, default `100`)
- `options.lossless` **[Boolean][6]** use lossless compression mode (optional, default `false`)
- `options.nearLossless` **[Boolean][6]** use near_lossless compression mode (optional, default `false`)
- `options.force` **[Boolean][6]** force WebP output, otherwise attempt to use input format (optional, default `true`)
### Examples
```javascript
// Convert any input to lossless WebP output
const data = await sharp(input)
.webp({ lossless: true })
.toBuffer();
```
- Throws **[Error][3]** Invalid options
Returns **Sharp**
## tiff
Use these TIFF options for output image.
### Parameters
- `options` **[Object][5]?** output options
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
- `options.force` **[Boolean][6]** force TIFF output, otherwise attempt to use input format (optional, default `true`)
- `options.compression` **[Boolean][6]** compression options: lzw, deflate, jpeg, ccittfax4 (optional, default `'jpeg'`)
- `options.predictor` **[Boolean][6]** compression predictor options: none, horizontal, float (optional, default `'horizontal'`)
- `options.pyramid` **[Boolean][6]** write an image pyramid (optional, default `false`)
- `options.tile` **[Boolean][6]** write a tiled tiff (optional, default `false`)
- `options.tileWidth` **[Boolean][6]** horizontal tile size (optional, default `256`)
- `options.tileHeight` **[Boolean][6]** vertical tile size (optional, default `256`)
- `options.xres` **[Number][8]** horizontal resolution in pixels/mm (optional, default `1.0`)
- `options.yres` **[Number][8]** vertical resolution in pixels/mm (optional, default `1.0`)
- `options.squash` **[Boolean][6]** squash 8-bit images down to 1 bit (optional, default `false`)
### Examples
```javascript
// Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
sharp('input.svg')
.tiff({
compression: 'lzw',
squash: true
})
.toFile('1-bpp-output.tiff')
.then(info => { ... });
```
- Throws **[Error][3]** Invalid options
Returns **Sharp**
## raw
Force output to be raw, uncompressed uint8 pixel data.
### Examples
```javascript
// Extract raw RGB pixel data from JPEG input
const { data, info } = await sharp('input.jpg')
.raw()
.toBuffer({ resolveWithObject: true });
```
Returns **Sharp**
## toFormat
Force output to a given format.
### Parameters
- `format` **([String][1] \| [Object][5])** as a String or an Object with an 'id' attribute
- `options` **[Object][5]** output options
### Examples
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.toFormat('png')
.toBuffer();
```
- Throws **[Error][3]** unsupported format or options
Returns **Sharp**
## tile
Use tile-based deep zoom (image pyramid) output.
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
### Parameters
- `tile` **[Object][5]?**
- `tile.size` **[Number][8]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `tile.overlap` **[Number][8]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `tile.angle` **[Number][8]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
- `tile.depth` **[String][1]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
- `tile.container` **[String][1]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
- `tile.layout` **[String][1]** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
### Examples
```javascript
sharp('input.tiff')
.png()
.tile({
size: 512
})
.toFile('output.dz', function(err, info) {
// output.dzi is the Deep Zoom XML definition
// output_files contains 512x512 tiles grouped by zoom level
});
```
- Throws **[Error][3]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[7]: https://nodejs.org/api/buffer.html
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number

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

@@ -0,0 +1,234 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## resize
Resize image to `width`, `height` or `width x height`.
When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
- `cover`: Crop to cover both provided dimensions (the default).
- `contain`: Embed within both provided dimensions.
- `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
- `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
- `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
Some of these values are based on the [object-fit][1] CSS property.
When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
- `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
- `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
- `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
Some of these values are based on the [object-position][2] CSS property.
The experimental strategy-based approach resizes so one dimension is at its target length
then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
- `entropy`: focus on the region with the highest [Shannon entropy][3].
- `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
Possible interpolation kernels are:
- `nearest`: Use [nearest neighbour interpolation][4].
- `cubic`: Use a [Catmull-Rom spline][5].
- `mitchell`: Use a [Mitchell-Netravali spline][6].
- `lanczos2`: Use a [Lanczos kernel][7] with `a=2`.
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
### Parameters
- `width` **[Number][8]?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
- `height` **[Number][8]?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
- `options` **[Object][9]?**
- `options.width` **[String][10]?** alternative means of specifying `width`. If both are present this take priority.
- `options.height` **[String][10]?** alternative means of specifying `height`. If both are present this take priority.
- `options.fit` **[String][10]** how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`. (optional, default `'cover'`)
- `options.position` **[String][10]** position, gravity or strategy to use when `fit` is `cover` or `contain`. (optional, default `'centre'`)
- `options.background` **([String][10] \| [Object][9])** background colour when using a `fit` of `contain`, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
- `options.kernel` **[String][10]** the kernel to use for image reduction. (optional, default `'lanczos3'`)
- `options.withoutEnlargement` **[Boolean][12]** do not enlarge if the width _or_ height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option. (optional, default `false`)
- `options.fastShrinkOnLoad` **[Boolean][12]** take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images. (optional, default `true`)
### Examples
```javascript
sharp(input)
.resize({ width: 100 })
.toBuffer()
.then(data => {
// 100 pixels wide, auto-scaled height
});
```
```javascript
sharp(input)
.resize({ height: 100 })
.toBuffer()
.then(data => {
// 100 pixels high, auto-scaled width
});
```
```javascript
sharp(input)
.resize(200, 300, {
kernel: sharp.kernel.nearest,
fit: 'contain',
position: 'right top',
background: { r: 255, g: 255, b: 255, alpha: 0.5 }
})
.toFile('output.png')
.then(() => {
// output.png is a 200 pixels wide and 300 pixels high image
// containing a nearest-neighbour scaled version
// contained within the north-east corner of a semi-transparent white canvas
});
```
```javascript
const transformer = sharp()
.resize({
width: 200,
height: 200,
fit: sharp.fit.cover,
position: sharp.strategy.entropy
});
// Read image data from readableStream
// Write 200px square auto-cropped image data to writableStream
readableStream
.pipe(transformer)
.pipe(writableStream);
```
```javascript
sharp(input)
.resize(200, 200, {
fit: sharp.fit.inside,
withoutEnlargement: true
})
.toFormat('jpeg')
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains JPEG image data
// no wider and no higher than 200 pixels
// and no larger than the input image
});
```
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## extend
Extends/pads the edges of the image with the provided background colour.
This operation will always occur after resizing and extraction, if any.
### Parameters
- `extend` **([Number][8] \| [Object][9])** single pixel count to add to all edges or an Object with per-edge counts
- `extend.top` **[Number][8]?**
- `extend.left` **[Number][8]?**
- `extend.bottom` **[Number][8]?**
- `extend.right` **[Number][8]?**
- `extend.background` **([String][10] \| [Object][9])** background colour, parsed by the [color][11] module, defaults to black without transparency. (optional, default `{r:0,g:0,b:0,alpha:1}`)
### Examples
```javascript
// Resize to 140 pixels wide, then add 10 transparent pixels
// to the top, left and right edges and 20 to the bottom edge
sharp(input)
.resize(140)
.extend({
top: 10,
bottom: 20,
left: 10,
right: 10,
background: { r: 0, g: 0, b: 0, alpha: 0 }
})
...
```
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## extract
Extract a region of the image.
- Use `extract` before `resize` for pre-resize extraction.
- Use `extract` after `resize` for post-resize extraction.
- Use `extract` before and after for both.
### Parameters
- `options` **[Object][9]**
- `options.left` **[Number][8]** zero-indexed offset from left edge
- `options.top` **[Number][8]** zero-indexed offset from top edge
- `options.width` **[Number][8]** dimension of extracted image
- `options.height` **[Number][8]** dimension of extracted image
### Examples
```javascript
sharp(input)
.extract({ left: left, top: top, width: width, height: height })
.toFile(output, function(err) {
// Extract a region of the input image, saving in the same format.
});
```
```javascript
sharp(input)
.extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
.resize(width, height)
.extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
.toFile(output, function(err) {
// Extract a region, resize, then extract from the resized image
});
```
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
## trim
Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
### Parameters
- `threshold` **[Number][8]** the allowed difference from the top-left pixel, a number greater than zero. (optional, default `10`)
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
[1]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
[2]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-position
[3]: https://en.wikipedia.org/wiki/Entropy_%28information_theory%29
[4]: http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation
[5]: https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline
[6]: https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
[7]: https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[11]: https://www.npmjs.org/package/color
[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error

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

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

View File

@@ -1,737 +0,0 @@
# API
```javascript
var sharp = require('sharp');
```
### Input
#### sharp([input], [options])
Constructor to which further methods are chained.
`input`, if present, can be one of:
* Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* String containing the path to an image file, with most major formats supported.
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data
can be streamed into the object when `input` is `null` or `undefined`.
`options`, if present, is an Object with the following optional attributes:
* `density` an integral number representing the DPI for vector images, defaulting to 72.
* `raw` an Object containing `width`, `height` and `channels` when providing uncompressed data. See `raw()` for pixel ordering.
The object returned by the constructor implements the
[stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
JPEG, PNG or WebP format image data can be streamed out from this object.
When using Stream based output, derived attributes are available from the `info` event.
```javascript
sharp('input.jpg')
.resize(300, 200)
.toFile('output.jpg', function(err) {
// output.jpg is a 300 pixels wide and 200 pixels high image
// containing a scaled and cropped version of input.jpg
});
```
```javascript
// Read image data from readableStream,
// resize to 300 pixels wide,
// emit an 'info' event with calculated dimensions
// and finally write image data to writableStream
var transformer = sharp()
.resize(300)
.on('info', function(info) {
console.log('Image height is ' + info.height);
});
readableStream.pipe(transformer).pipe(writableStream);
```
#### 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`, `openslide`, `ppm`, `fits`)
* `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
* `density`: Number of pixels per inch (DPI), if present
* `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
* `orientation`: Number value of the EXIF Orientation header, if present
* `exif`: Buffer containing raw EXIF data, if present
* `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
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({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
#### 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 either an integral Number of pixels, with a value between 1 and the default 268402689 (0x3FFF * 0x3FFF) or
a boolean. `false` will disable checking while `true` will revert to the default limit.
### Resizing
#### resize([width], [height])
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([option])
Crop the resized image to the exact size specified, the default behaviour.
`option`, if present, is an attribute of:
* `sharp.gravity` e.g. `sharp.gravity.north`, to crop to an edge or corner, or
* `sharp.strategy` e.g. `sharp.strategy.entropy`, to crop dynamically.
Possible attributes of `sharp.gravity` are
`north`, `northeast`, `east`, `southeast`, `south`,
`southwest`, `west`, `northwest`, `center` and `centre`.
Possible attributes of the experimental `sharp.strategy` are:
* `entropy`: resize so one dimension is at its target size
then repeatedly remove pixels from the edge with the lowest
[Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29)
until it too reaches the target size.
The default crop option is a `center`/`centre` gravity.
```javascript
var transformer = sharp()
.resize(200, 200)
.crop(sharp.strategy.entropy)
.on('error', function(err) {
console.log(err);
});
// Read image data from readableStream
// Write 200px square auto-cropped image data to writableStream
readableStream.pipe(transformer).pipe(writableStream);
```
#### 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`.
The default interpolator is `bicubic`, providing a general-purpose interpolator that is both fast and of good quality.
Possible interpolators, in order of performance, are:
* `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation), suitable for image enlargement only.
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), faster than bicubic but with less smooth results.
* `vertexSplitQuadraticBasisSpline`: Use the smoother [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48) to prevent "staircasing" when enlarging.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default).
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance but typically reduces performance by a factor of 3.
[Compare the output of these interpolators](https://github.com/lovell/sharp/tree/master/test/interpolators)
```javascript
sharp(inputBuffer)
.resize(200, 300)
.interpolateWith(sharp.interpolator.nohalo)
.background('white')
.embed()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a nohalo scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
### Operations
#### extract({ left: left, top: top, width: width, height: height })
Extract a region of the image. Can be used with or without a `resize` operation.
`left` and `top` 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({ left: left, top: top, width: width, height: height })
.toFile(output, function(err) {
// Extract a region of the input image, saving in the same format.
});
```
```javascript
sharp(input)
.extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
.resize(width, height)
.extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
.toFile(output, function(err) {
// Extract a region, resize, then extract from the resized image
});
```
#### background(rgba)
Set the background for the `embed`, `flatten` and `extend` operations.
`rgba` is parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
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`.
#### extend(extension)
Extends/pads the edges of the image with `background`, where `extension` is one of:
* a Number representing the pixel count to add to each edge, or
* an Object containing `top`, `left`, `bottom` and `right` attributes, each a Number of pixels to add to that edge.
This operation will always occur after resizing and extraction, if any.
```javascript
// Resize to 140 pixels wide, then add 10 transparent pixels
// to the top, left and right edges and 20 to the bottom edge
sharp(input)
.resize(140)
.background({r: 0, g: 0, b: 0, a: 0})
.extend({top: 10, bottom: 20, left: 10, right: 10})
...
```
#### negate()
Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc.
#### 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.
#### threshold([threshold])
Converts all pixels in the image to greyscale white or black. Any pixel greather-than-or-equal-to the threshold (0..255) will be white. All others will be black.
* `threshold`, if present, is a Number, representing the level above which pixels will be forced to white.
#### 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(image, [options])
Overlay (composite) a image containing an alpha channel over the processed (resized, extracted etc.) image.
`image` is one of the following, and must be the same size or smaller than the processed image:
* Buffer containing PNG, WebP, GIF or SVG image data, or
* String containing the path to an image file, with most major transparency formats supported.
`options`, if present, is an Object with the following optional attributes:
* `gravity` is a String or an attribute of the `sharp.gravity` Object e.g. `sharp.gravity.north` at which to place the overlay, defaulting to `center`/`centre`.
```javascript
sharp('input.png')
.rotate(180)
.resize(300)
.flatten()
.background('#ff6600')
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
.sharpen()
.withMetadata()
.quality(90)
.webp()
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});
```
### Output
#### toFile(path, [callback])
`path` is a String containing the path to write the image data to.
If an explicit output format is not selected, it will be inferred from the extension, with JPEG, PNG, WebP, TIFF 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`, `height` and `channels`.
A Promises/A+ promise is returned when `callback` is not provided.
#### toBuffer([callback])
Write image data to a Buffer, the format of which will match the input image by default. JPEG, PNG and WebP are supported.
`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`, `height` and `channels`.
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()
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 1 and 8, used to update the value of the EXIF `Orientation` tag.
This has no effect if the input image does not have an EXIF `Orientation` tag.
The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
#### tile(options)
The size, overlap, container and directory layout to use when generating square Deep Zoom image pyramid tiles.
`options` is an Object with one or more of the following attributes:
* `size` is an integral Number between 1 and 8192. The default value is 256 pixels.
* `overlap` is an integral Number between 0 and 8192. The default value is 0 pixels.
* `container` is a String, with value `fs` or `zip`. The default value is `fs`.
* `layout` is a String, with value `dz`, `zoomify` or `google`. The default value is `dz`.
You can also use the file extension .zip or .szi to write to a ZIP container instead of the filesystem.
```javascript
sharp('input.tiff')
.tile({
size: 512
})
.toFile('output.dzi', function(err, info) {
// output.dzi is the Deep Zoom XML definition
// output_files contains 512x512 tiles grouped by zoom level
});
```
#### withoutChromaSubsampling()
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()
An advanced setting to disable adaptive row filtering for the lossless PNG output format.
#### trellisQuantisation() / trellisQuantization()
_Requires libvips to have been compiled with mozjpeg support_
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 to have been compiled with mozjpeg support_
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 to have been compiled with mozjpeg support_
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)');
});
```
#### versions
An Object containing the version numbers of libvips and, on Linux, its dependencies.
```javascript
> console.log(sharp.versions);
{ zlib: '1.2.8',
ffi: '3.2.1',
glib: '2.46.2',
xml: '2.9.2',
gsf: '1.14.34',
exif: '0.6.21',
jpeg: '1.4.2',
png: '1.6.19',
lcms: '2.7',
webp: '0.4.4',
tiff: '4.0.6',
magick: '6.9.2-6',
orc: '0.4.24',
vips: '8.1.1' }
```
### Utilities
#### sharp.cache([options])
If `options` is provided, sets the limits of _libvips'_ operation cache.
* `options.memory` is the maximum memory in MB to use for this cache, with a default value of 50
* `options.files` is the maximum number of files to hold open, with a default value of 20
* `options.items` is the maximum number of operations to cache, with a default value of 100
`options` can also be a boolean, where `true` enables the default cache settings and `false` disables all caching.
This method always returns cache statistics, useful for determining how much working memory is required for a particular task.
```javascript
var stats = sharp.cache();
```
```javascript
sharp.cache( { items: 200 } );
sharp.cache( { files: 0 } );
sharp.cache(false);
```
#### 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 }
```
#### sharp.simd([enable])
_Requires libvips to have been compiled with liborc support_
Improves the performance of `resize`, `blur` and `sharpen` operations
by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
* `enable`, if present, is a boolean where `true` enables and `false` disables the use of SIMD.
This method always returns the current state.
This feature is currently disabled by default
but future versions may enable it by default.
When enabled, versions of liborc prior to 0.4.24
and versions of libvips prior to 8.2.0
have been known to crash under heavy load.
```javascript
var simd = sharp.simd();
// simd is `true` if SIMD is currently enabled
```
```javascript
var simd = sharp.simd(true);
// attempts to enable the use of SIMD, returning true if available
```

View File

@@ -1,5 +1,639 @@
# Changelog
### v0.22 - "*uptake*"
Requires libvips v8.7.4.
#### v0.22.0 - 18<sup>th</sup> March 2019
* Remove functions previously deprecated in v0.21.0:
`background`, `crop`, `embed`, `ignoreAspectRatio`, `max`, `min` and `withoutEnlargement`.
* Add `composite` operation supporting multiple images and blend modes; deprecate `overlayWith`.
[#728](https://github.com/lovell/sharp/issues/728)
* Add support for `pages` input option for multi-page input.
[#1566](https://github.com/lovell/sharp/issues/1566)
* Allow Stream-based input of raw pixel data.
[#1579](https://github.com/lovell/sharp/issues/1579)
* Add support for `page` input option to GIF and PDF.
[#1595](https://github.com/lovell/sharp/pull/1595)
[@ramiel](https://github.com/ramiel)
### v0.21 - "*teeth*"
Requires libvips v8.7.0.
#### v0.21.3 - 19<sup>th</sup> January 2019
* Input image decoding now fails fast, set `failOnError` to change this behaviour.
* Failed filesystem-based input now separates missing file and invalid format errors.
[#1542](https://github.com/lovell/sharp/issues/1542)
#### v0.21.2 - 13<sup>th</sup> January 2019
* Ensure all metadata is removed from PNG output unless `withMetadata` used.
* Ensure shortest edge is at least one pixel after resizing.
[#1003](https://github.com/lovell/sharp/issues/1003)
* Add `ensureAlpha` operation to add an alpha channel, if missing.
[#1153](https://github.com/lovell/sharp/issues/1153)
* Expose `pages` and `pageHeight` metadata for multi-page input images.
[#1205](https://github.com/lovell/sharp/issues/1205)
* Expose PNG output options requiring libimagequant.
[#1484](https://github.com/lovell/sharp/issues/1484)
* Expose underlying error message for invalid input.
[#1505](https://github.com/lovell/sharp/issues/1505)
* Prevent mutatation of options passed to `jpeg`.
[#1516](https://github.com/lovell/sharp/issues/1516)
* Ensure forced output format applied correctly when output chaining.
[#1528](https://github.com/lovell/sharp/issues/1528)
#### v0.21.1 - 7<sup>th</sup> December 2018
* Install: support `sharp_dist_base_url` npm config, like existing `SHARP_DIST_BASE_URL`.
[#1422](https://github.com/lovell/sharp/pull/1422)
[@SethWen](https://github.com/SethWen)
* Ensure `channel` metadata is correct for raw, greyscale output.
[#1425](https://github.com/lovell/sharp/issues/1425)
* Add support for the "mitchell" kernel for image reductions.
[#1438](https://github.com/lovell/sharp/pull/1438)
[@Daiz](https://github.com/Daiz)
* Allow separate parameters for gamma encoding and decoding.
[#1439](https://github.com/lovell/sharp/pull/1439)
[@Daiz](https://github.com/Daiz)
* Build prototype with `Object.assign` to allow minification.
[#1475](https://github.com/lovell/sharp/pull/1475)
[@jaubourg](https://github.com/jaubourg)
* Expose libvips' recombination matrix operation.
[#1477](https://github.com/lovell/sharp/pull/1477)
[@fromkeith](https://github.com/fromkeith)
* Expose libvips' pyramid/tile options for TIFF output.
[#1483](https://github.com/lovell/sharp/pull/1483)
[@mbklein](https://github.com/mbklein)
#### v0.21.0 - 4<sup>th</sup> October 2018
* Deprecate the following resize-related functions:
`crop`, `embed`, `ignoreAspectRatio`, `max`, `min` and `withoutEnlargement`.
Access to these is now via options passed to the `resize` function.
For example:
`embed('north')` is now `resize(width, height, { fit: 'contain', position: 'north' })`,
`crop('attention')` is now `resize(width, height, { fit: 'cover', position: 'attention' })`,
`max().withoutEnlargement()` is now `resize(width, height, { fit: 'inside', withoutEnlargement: true })`.
[#1135](https://github.com/lovell/sharp/issues/1135)
* Deprecate the `background` function.
Per-operation `background` options added to `resize`, `extend` and `flatten` operations.
[#1392](https://github.com/lovell/sharp/issues/1392)
* Add `size` to `metadata` response (Stream and Buffer input only).
[#695](https://github.com/lovell/sharp/issues/695)
* Switch from custom trim operation to `vips_find_trim`.
[#914](https://github.com/lovell/sharp/issues/914)
* Add `chromaSubsampling` and `isProgressive` properties to `metadata` response.
[#1186](https://github.com/lovell/sharp/issues/1186)
* Drop Node 4 support.
[#1212](https://github.com/lovell/sharp/issues/1212)
* Enable SIMD convolution by default.
[#1213](https://github.com/lovell/sharp/issues/1213)
* Add experimental prebuilt binaries for musl-based Linux.
[#1379](https://github.com/lovell/sharp/issues/1379)
* Add support for arbitrary rotation angle via vips_rotate.
[#1385](https://github.com/lovell/sharp/pull/1385)
[@freezy](https://github.com/freezy)
### v0.20 - "*prebuild*"
Requires libvips v8.6.1.
#### v0.20.8 - 5<sup>th</sup> September 2018
* Avoid race conditions when creating directories during installation.
[#1358](https://github.com/lovell/sharp/pull/1358)
[@ajhool](https://github.com/ajhool)
* Accept floating point values for input density parameter.
[#1362](https://github.com/lovell/sharp/pull/1362)
[@aeirola](https://github.com/aeirola)
#### v0.20.7 - 21<sup>st</sup> August 2018
* Use copy+unlink if rename operation fails during installation.
[#1345](https://github.com/lovell/sharp/issues/1345)
#### v0.20.6 - 20<sup>th</sup> August 2018
* Add removeAlpha operation to remove alpha channel, if any.
[#1248](https://github.com/lovell/sharp/issues/1248)
* Expose mozjpeg quant_table flag.
[#1285](https://github.com/lovell/sharp/pull/1285)
[@rexxars](https://github.com/rexxars)
* Allow full WebP alphaQuality range of 0-100.
[#1290](https://github.com/lovell/sharp/pull/1290)
[@sylvaindumont](https://github.com/sylvaindumont)
* Cache libvips binaries to reduce re-install time.
[#1301](https://github.com/lovell/sharp/issues/1301)
* Ensure vendor platform mismatch throws error at install time.
[#1303](https://github.com/lovell/sharp/issues/1303)
* Improve install time error messages for FreeBSD users.
[#1310](https://github.com/lovell/sharp/issues/1310)
* Ensure extractChannel works with 16-bit images.
[#1330](https://github.com/lovell/sharp/issues/1330)
* Expose depth option for tile-based output.
[#1342](https://github.com/lovell/sharp/pull/1342)
[@alundavies](https://github.com/alundavies)
* Add experimental entropy field to stats response.
#### v0.20.5 - 27<sup>th</sup> June 2018
* Expose libjpeg optimize_coding flag.
[#1265](https://github.com/lovell/sharp/pull/1265)
[@tomlokhorst](https://github.com/tomlokhorst)
#### v0.20.4 - 20<sup>th</sup> June 2018
* Prevent possible rounding error when using shrink-on-load and 90/270 degree rotation.
[#1241](https://github.com/lovell/sharp/issues/1241)
[@anahit42](https://github.com/anahit42)
* Ensure extractChannel sets correct single-channel colour space interpretation.
[#1257](https://github.com/lovell/sharp/issues/1257)
[@jeremychone](https://github.com/jeremychone)
#### v0.20.3 - 29<sup>th</sup> May 2018
* Fix tint operation by ensuring LAB interpretation and allowing negative values.
[#1235](https://github.com/lovell/sharp/issues/1235)
[@wezside](https://github.com/wezside)
#### v0.20.2 - 28<sup>th</sup> April 2018
* Add tint operation to set image chroma.
[#825](https://github.com/lovell/sharp/pull/825)
[@rikh42](https://github.com/rikh42)
* Add environment variable to ignore globally-installed libvips.
[#1165](https://github.com/lovell/sharp/pull/1165)
[@oncletom](https://github.com/oncletom)
* Add support for page selection with multi-page input (GIF/TIFF).
[#1204](https://github.com/lovell/sharp/pull/1204)
[@woolite64](https://github.com/woolite64)
* Add support for Group4 (CCITTFAX4) compression with TIFF output.
[#1208](https://github.com/lovell/sharp/pull/1208)
[@woolite64](https://github.com/woolite64)
#### v0.20.1 - 17<sup>th</sup> March 2018
* Improve installation experience when a globally-installed libvips below the minimum required version is found.
[#1148](https://github.com/lovell/sharp/issues/1148)
* Prevent smartcrop error when cumulative rounding is below target size.
[#1154](https://github.com/lovell/sharp/issues/1154)
[@ralrom](https://github.com/ralrom)
* Expose libvips' median filter operation.
[#1161](https://github.com/lovell/sharp/pull/1161)
[@BiancoA](https://github.com/BiancoA)
#### v0.20.0 - 5<sup>th</sup> March 2018
* Add support for prebuilt sharp binaries on common platforms.
[#186](https://github.com/lovell/sharp/issues/186)
### v0.19 - "*suit*"
Requires libvips v8.6.1.
#### v0.19.1 - 24<sup>th</sup> February 2018
* Expose libvips' linear transform feature.
[#1024](https://github.com/lovell/sharp/pull/1024)
[@3epnm](https://github.com/3epnm)
* Expose angle option for tile-based output.
[#1121](https://github.com/lovell/sharp/pull/1121)
[@BiancoA](https://github.com/BiancoA)
* Prevent crop operation when image already at or below target dimensions.
[#1134](https://github.com/lovell/sharp/issues/1134)
[@pieh](https://github.com/pieh)
#### v0.19.0 - 11<sup>th</sup> January 2018
* Expose offset coordinates of strategy-based crop.
[#868](https://github.com/lovell/sharp/issues/868)
[@mirohristov-com](https://github.com/mirohristov-com)
* PNG output now defaults to adaptiveFiltering=false, compressionLevel=9
[#872](https://github.com/lovell/sharp/issues/872)
[@wmertens](https://github.com/wmertens)
* Add stats feature for pixel-derived image statistics.
[#915](https://github.com/lovell/sharp/pull/915)
[@rnanwani](https://github.com/rnanwani)
* Add failOnError option to fail-fast on bad input image data.
[#976](https://github.com/lovell/sharp/pull/976)
[@mceachen](https://github.com/mceachen)
* Resize: switch to libvips' implementation, make fastShrinkOnLoad optional, remove interpolator and centreSampling options.
[#977](https://github.com/lovell/sharp/pull/977)
[@jardakotesovec](https://github.com/jardakotesovec)
* Attach finish event listener to a clone only for Stream-based input.
[#995](https://github.com/lovell/sharp/issues/995)
[@whmountains](https://github.com/whmountains)
* Add tilecache before smartcrop to avoid over-computation of previous operations.
[#1028](https://github.com/lovell/sharp/issues/1028)
[@coffeebite](https://github.com/coffeebite)
* Prevent toFile extension taking precedence over requested format.
[#1037](https://github.com/lovell/sharp/issues/1037)
[@tomgallagher](https://github.com/tomgallagher)
* Add support for gravity option to existing embed feature.
[#1038](https://github.com/lovell/sharp/pull/1038)
[@AzureByte](https://github.com/AzureByte)
* Expose IPTC and XMP metadata when available.
[#1079](https://github.com/lovell/sharp/pull/1079)
[@oaleynik](https://github.com/oaleynik)
* TIFF output: switch default predictor from 'none' to 'horizontal' to match libvips' behaviour.
### v0.18 - "*ridge*"
Requires libvips v8.5.5.
#### v0.18.4 - 18<sup>th</sup> September 2017
* Ensure input Buffer really is marked as Persistent, prevents mark-sweep GC.
[#950](https://github.com/lovell/sharp/issues/950)
[@lfdoherty](https://github.com/lfdoherty)
#### v0.18.3 - 13<sup>th</sup> September 2017
* Skip shrink-on-load when trimming.
[#888](https://github.com/lovell/sharp/pull/888)
[@kleisauke](https://github.com/kleisauke)
* Migrate from got to simple-get for basic auth support.
[#945](https://github.com/lovell/sharp/pull/945)
[@pbomb](https://github.com/pbomb)
#### v0.18.2 - 1<sup>st</sup> July 2017
* Expose libvips' xres and yres properties for TIFF output.
[#828](https://github.com/lovell/sharp/pull/828)
[@YvesBos](https://github.com/YvesBos)
* Ensure flip and flop operations work with auto-rotate.
[#837](https://github.com/lovell/sharp/issues/837)
[@rexxars](https://github.com/rexxars)
* Allow binary download URL override via SHARP_DIST_BASE_URL env variable.
[#841](https://github.com/lovell/sharp/issues/841)
* Add support for Solus Linux.
[#857](https://github.com/lovell/sharp/pull/857)
[@ekremkaraca](https://github.com/ekremkaraca)
#### v0.18.1 - 30<sup>th</sup> May 2017
* Remove regression from #781 that could cause incorrect shrink calculation.
[#831](https://github.com/lovell/sharp/issues/831)
[@suprMax](https://github.com/suprMax)
#### v0.18.0 - 30<sup>th</sup> May 2017
* Remove the previously-deprecated output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
withoutChromaSubsampling, trellisQuantisation, trellisQuantization,
overshootDeringing, optimiseScans and optimizeScans.
* Ensure maximum output dimensions are based on the format to be used.
[#176](https://github.com/lovell/sharp/issues/176)
[@stephanebachelier](https://github.com/stephanebachelier)
* Avoid costly (un)premultiply when using overlayWith without alpha channel.
[#573](https://github.com/lovell/sharp/issues/573)
[@strarsis](https://github.com/strarsis)
* Include pixel depth (e.g. "uchar") when reading metadata.
[#577](https://github.com/lovell/sharp/issues/577)
[@moedusa](https://github.com/moedusa)
* Add support for Buffer and Stream-based TIFF output.
[#587](https://github.com/lovell/sharp/issues/587)
[@strarsis](https://github.com/strarsis)
* Expose warnings from libvips via NODE_DEBUG=sharp environment variable.
[#607](https://github.com/lovell/sharp/issues/607)
[@puzrin](https://github.com/puzrin)
* Switch to the libvips implementation of "attention" and "entropy" crop strategies.
[#727](https://github.com/lovell/sharp/issues/727)
* Improve performance and accuracy of nearest neighbour integral upsampling.
[#752](https://github.com/lovell/sharp/issues/752)
[@MrIbby](https://github.com/MrIbby)
* Constructor single argument API: allow plain object, reject null/undefined.
[#768](https://github.com/lovell/sharp/issues/768)
[@kub1x](https://github.com/kub1x)
* Ensure ARM64 pre-built binaries use correct C++11 ABI version.
[#772](https://github.com/lovell/sharp/issues/772)
[@ajiratech2](https://github.com/ajiratech2)
* Prevent aliasing by using dynamic values for shrink(-on-load).
[#781](https://github.com/lovell/sharp/issues/781)
[@kleisauke](https://github.com/kleisauke)
* Expose libvips' "squash" parameter to enable 1-bit TIFF output.
[#783](https://github.com/lovell/sharp/pull/783)
[@YvesBos](https://github.com/YvesBos)
* Add support for rotation using any multiple of +/-90 degrees.
[#791](https://github.com/lovell/sharp/pull/791)
[@ncoden](https://github.com/ncoden)
* Add "jpg" alias to toFormat as shortened form of "jpeg".
[#814](https://github.com/lovell/sharp/pull/814)
[@jingsam](https://github.com/jingsam)
### v0.17 - "*quill*"
Requires libvips v8.4.2.
#### v0.17.3 - 1<sup>st</sup> April 2017
* Allow toBuffer to optionally resolve a Promise with both info and data.
[#143](https://github.com/lovell/sharp/issues/143)
[@salzhrani](https://github.com/salzhrani)
* Create blank image of given width, height, channels and background.
[#470](https://github.com/lovell/sharp/issues/470)
[@pjarts](https://github.com/pjarts)
* Add support for the "nearest" kernel for image reductions.
[#732](https://github.com/lovell/sharp/pull/732)
[@alice0meta](https://github.com/alice0meta)
* Add support for TIFF compression and predictor options.
[#738](https://github.com/lovell/sharp/pull/738)
[@kristojorg](https://github.com/kristojorg)
#### v0.17.2 - 11<sup>th</sup> February 2017
* Ensure Readable side of Stream can start flowing after Writable side has finished.
[#671](https://github.com/lovell/sharp/issues/671)
[@danhaller](https://github.com/danhaller)
* Expose WebP alpha quality, lossless and near-lossless output options.
[#685](https://github.com/lovell/sharp/pull/685)
[@rnanwani](https://github.com/rnanwani)
#### v0.17.1 - 15<sup>th</sup> January 2017
* Improve error messages for invalid parameters.
[@spikeon](https://github.com/spikeon)
[#644](https://github.com/lovell/sharp/pull/644)
* Simplify expression for finding vips-cpp libdir.
[#656](https://github.com/lovell/sharp/pull/656)
* Allow HTTPS-over-HTTP proxy when downloading pre-compiled dependencies.
[@wangzhiwei1888](https://github.com/wangzhiwei1888)
[#679](https://github.com/lovell/sharp/issues/679)
#### v0.17.0 - 11<sup>th</sup> December 2016
* Drop support for versions of Node prior to v4.
* Deprecate the following output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
withoutChromaSubsampling, trellisQuantisation, trellisQuantization,
overshootDeringing, optimiseScans and optimizeScans.
Access to these is now via output format functions, for example `quality(n)`
is now `jpeg({quality: n})` and/or `webp({quality: n})`.
* Autoconvert GIF and SVG input to PNG output if no other format is specified.
* Expose libvips' "centre" resize option to mimic \*magick's +0.5px convention.
[#568](https://github.com/lovell/sharp/issues/568)
* Ensure support for embedded base64 PNG and JPEG images within an SVG.
[#601](https://github.com/lovell/sharp/issues/601)
[@dynamite-ready](https://github.com/dynamite-ready)
* Ensure premultiply operation occurs before box filter shrink.
[#605](https://github.com/lovell/sharp/issues/605)
[@CmdrShepardsPie](https://github.com/CmdrShepardsPie)
[@teroparvinen](https://github.com/teroparvinen)
* Add support for PNG and WebP tile-based output formats (in addition to JPEG).
[#622](https://github.com/lovell/sharp/pull/622)
[@ppaskaris](https://github.com/ppaskaris)
* Allow use of extend with greyscale input.
[#623](https://github.com/lovell/sharp/pull/623)
[@ppaskaris](https://github.com/ppaskaris)
* Allow non-RGB input to embed/extend onto background with an alpha channel.
[#646](https://github.com/lovell/sharp/issues/646)
[@DaGaMs](https://github.com/DaGaMs)
### v0.16 - "*pencil*"
Requires libvips v8.3.3
#### v0.16.2 - 22<sup>nd</sup> October 2016
* Restrict readelf usage to Linux only when detecting global libvips version.
[#602](https://github.com/lovell/sharp/issues/602)
[@caoko](https://github.com/caoko)
#### v0.16.1 - 13<sup>th</sup> October 2016
* C++11 ABI version is now auto-detected, remove sharp-cxx11 installation flag.
* Add experimental 'attention' crop strategy.
[#295](https://github.com/lovell/sharp/issues/295)
* Include .node extension for Meteor's require() implementation.
[#537](https://github.com/lovell/sharp/issues/537)
[@isjackwild](https://github.com/isjackwild)
* Ensure convolution kernel scale is clamped to a minimum value of 1.
[#561](https://github.com/lovell/sharp/issues/561)
[@abagshaw](https://github.com/abagshaw)
* Correct calculation of y-axis placement when overlaying image at a fixed point.
[#566](https://github.com/lovell/sharp/issues/566)
[@Nateowami](https://github.com/Nateowami)
#### v0.16.0 - 18<sup>th</sup> August 2016
* Add pre-compiled libvips for OS X, ARMv7 and ARMv8.
[#312](https://github.com/lovell/sharp/issues/312)
* Ensure boolean, bandbool, extractChannel ops occur before sRGB conversion.
[#504](https://github.com/lovell/sharp/pull/504)
[@mhirsch](https://github.com/mhirsch)
* Recalculate factors after WebP shrink-on-load to avoid round-to-zero errors.
[#508](https://github.com/lovell/sharp/issues/508)
[@asilvas](https://github.com/asilvas)
* Prevent boolean errors during extract operation.
[#511](https://github.com/lovell/sharp/pull/511)
[@mhirsch](https://github.com/mhirsch)
* Add joinChannel and toColourspace/toColorspace operations.
[#513](https://github.com/lovell/sharp/pull/513)
[@mhirsch](https://github.com/mhirsch)
* Add support for raw pixel data with boolean and withOverlay operations.
[#516](https://github.com/lovell/sharp/pull/516)
[@mhirsch](https://github.com/mhirsch)
* Prevent bandbool creating a single channel sRGB image.
[#519](https://github.com/lovell/sharp/pull/519)
[@mhirsch](https://github.com/mhirsch)
* Ensure ICC profiles are removed from PNG output unless withMetadata used.
[#521](https://github.com/lovell/sharp/issues/521)
[@ChrisPinewood](https://github.com/ChrisPinewood)
* Add alpha channels, if missing, to overlayWith images.
[#540](https://github.com/lovell/sharp/pull/540)
[@cmtt](https://github.com/cmtt)
* Remove deprecated interpolateWith method - use resize(w, h, { interpolator: ... })
[#310](https://github.com/lovell/sharp/issues/310)
### v0.15 - "*outfit*"
Requires libvips v8.3.1
#### v0.15.1 - 12<sup>th</sup> July 2016
* Concat Stream-based input in single operation for ~+3% perf and less GC.
[#429](https://github.com/lovell/sharp/issues/429)
[@papandreou](https://github.com/papandreou)
* Add alpha channel, if required, before extend operation.
[#439](https://github.com/lovell/sharp/pull/439)
[@frulo](https://github.com/frulo)
* Allow overlay image to be repeated across entire image via tile option.
[#443](https://github.com/lovell/sharp/pull/443)
[@lemnisk8](https://github.com/lemnisk8)
* Add cutout option to overlayWith feature, applies only the alpha channel of the overlay image.
[#448](https://github.com/lovell/sharp/pull/448)
[@kleisauke](https://github.com/kleisauke)
* Ensure scaling factors are calculated independently to prevent rounding errors.
[#452](https://github.com/lovell/sharp/issues/452)
[@puzrin](https://github.com/puzrin)
* Add --sharp-cxx11 flag to compile with gcc's new C++11 ABI.
[#456](https://github.com/lovell/sharp/pull/456)
[@kapouer](https://github.com/kapouer)
* Add top/left offset support to overlayWith operation.
[#473](https://github.com/lovell/sharp/pull/473)
[@rnanwani](https://github.com/rnanwani)
* Add convolve operation for kernel-based convolution.
[#479](https://github.com/lovell/sharp/pull/479)
[@mhirsch](https://github.com/mhirsch)
* Add greyscale option to threshold operation for colourspace conversion control.
[#480](https://github.com/lovell/sharp/pull/480)
[@mhirsch](https://github.com/mhirsch)
* Ensure ICC profiles are licenced for distribution.
[#486](https://github.com/lovell/sharp/issues/486)
[@kapouer](https://github.com/kapouer)
* Allow images with an alpha channel to work with LAB-colourspace based sharpen.
[#490](https://github.com/lovell/sharp/issues/490)
[@jwagner](https://github.com/jwagner)
* Add trim operation to remove "boring" edges.
[#492](https://github.com/lovell/sharp/pull/492)
[@kleisauke](https://github.com/kleisauke)
* Add bandbool feature for channel-wise boolean operations.
[#496](https://github.com/lovell/sharp/pull/496)
[@mhirsch](https://github.com/mhirsch)
* Add extractChannel operation to extract a channel from an image.
[#497](https://github.com/lovell/sharp/pull/497)
[@mhirsch](https://github.com/mhirsch)
* Add ability to read and write native libvips .v files.
[#500](https://github.com/lovell/sharp/pull/500)
[@mhirsch](https://github.com/mhirsch)
* Add boolean feature for bitwise image operations.
[#501](https://github.com/lovell/sharp/pull/501)
[@mhirsch](https://github.com/mhirsch)
#### v0.15.0 - 21<sup>st</sup> May 2016
* Use libvips' new Lanczos 3 kernel as default for image reduction.
Deprecate interpolateWith method, now provided as a resize option.
[#310](https://github.com/lovell/sharp/issues/310)
[@jcupitt](https://github.com/jcupitt)
* Take advantage of libvips v8.3 features.
Add support for libvips' new GIF and SVG loaders.
Pre-built binaries now include giflib and librsvg, exclude *magick.
Use shrink-on-load for WebP input.
Break existing sharpen API to accept sigma and improve precision.
[#369](https://github.com/lovell/sharp/issues/369)
* Remove unnecessary (un)premultiply operations when not resizing/compositing.
[#413](https://github.com/lovell/sharp/issues/413)
[@jardakotesovec](https://github.com/jardakotesovec)
### v0.14 - "*needle*"
Requires libvips v8.2.3
@@ -62,6 +696,9 @@ Requires libvips v8.2.3
[#387](https://github.com/lovell/sharp/issues/387)
[@kleisauke](https://github.com/kleisauke)
* Remove deprecated style of calling extract API. Breaks calls using positional arguments.
[#276](https://github.com/lovell/sharp/issues/276)
### v0.13 - "*mind*"
Requires libvips v8.2.2

5
docs/css/extra.css Normal file
View File

@@ -0,0 +1,5 @@
/* Nest document subheadings in navigation */
ul.subnav ul:not(.subnav) {
padding-left: 2em;
font-size: 80%;
}

View File

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

After

Width:  |  Height:  |  Size: 592 B

View File

@@ -1,32 +1,31 @@
# sharp
<img src="image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
The typical use case for this high speed Node.js module
is to convert large images of many formats to
is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
Resizing an image is typically 4x faster than using the
Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings.
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not sacrificed for speed.
Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most Windows (x64), Linux and ARMv6+ systems do not require
the installation of any external runtime dependencies.
Use with OS X is as simple as running `brew install homebrew/science/vips`
to install the libvips dependency.
Most modern 64-bit OS X, Windows and Linux systems running
Node versions 6, 8 and 10
do not require any additional install or runtime dependencies.
[![Test Coverage](https://coveralls.io/repos/lovell/sharp/badge.png?branch=master)](https://coveralls.io/r/lovell/sharp?branch=master)
### Formats
This module supports reading JPEG, PNG, WebP, TIFF, OpenSlide,
GIF and most other libmagick-supported formats.
This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images.
Output images can be in JPEG, PNG and WebP formats as well as uncompressed raw pixel data.
Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data.
Streams, Buffer objects and the filesystem can be used for input and output.
@@ -40,7 +39,7 @@ and [Leaflet](https://github.com/turban/Leaflet.Zoomify).
### Fast
This module is powered by the blazingly fast
[libvips](https://github.com/jcupitt/libvips) image processing library,
[libvips](https://github.com/libvips/libvips) image processing library,
originally created in 1989 at Birkbeck College
and currently maintained by
[John Cupitt](https://github.com/jcupitt).
@@ -50,7 +49,7 @@ 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.
no child processes are spawned and Promises/async/await are supported.
### Optimal
@@ -59,9 +58,9 @@ 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,
PNG filtering is disabled by default,
which for diagrams and line art often produces the same result
as [pngcrush](http://pmt.sourceforge.net/pngcrush/).
as [pngcrush](https://pmt.sourceforge.io/pngcrush/).
### Contributing
@@ -89,17 +88,53 @@ the help and code contributions of the following people:
* [Alaric Holloway](https://github.com/skedastik)
* [Bernhard K. Weisshuhn](https://github.com/bkw)
* [David A. Carley](https://github.com/dacarley)
* [John Tobin](https://github.com/jtobinisaniceguy)
* [Kenton Gray](https://github.com/kentongray)
* [Felix Bünemann](https://github.com/felixbuenemann)
* [Samy Al Zahrani](https://github.com/salzhrani)
* [Chintan Thakkar](https://github.com/lemnisk8)
* [F. Orlando Galashan](https://github.com/frulo)
* [Kleis Auke Wolthuizen](https://github.com/kleisauke)
* [Matt Hirsch](https://github.com/mhirsch)
* [Rahul Nanwani](https://github.com/rnanwani)
* [Matthias Thoemmes](https://github.com/cmtt)
* [Patrick Paskaris](https://github.com/ppaskaris)
* [Jérémy Lal](https://github.com/kapouer)
* [Alice Monday](https://github.com/alice0meta)
* [Kristo Jorgenson](https://github.com/kristojorg)
* [Yves Bos](https://github.com/YvesBos)
* [Nicolas Coden](https://github.com/ncoden)
* [Matt Parrish](https://github.com/pbomb)
* [Matthew McEachen](https://github.com/mceachen)
* [Jarda Kotěšovec](https://github.com/jardakotesovec)
* [Kenric D'Souza](https://github.com/AzureByte)
* [Oleh Aleinyk](https://github.com/oaleynik)
* [Marcel Bretschneider](https://github.com/3epnm)
* [Andrea Bianco](https://github.com/BiancoA)
* [Rik Heywood](https://github.com/rikh42)
* [Thomas Parisot](https://github.com/oncletom)
* [Nathan Graves](https://github.com/woolite64)
* [Tom Lokhorst](https://github.com/tomlokhorst)
* [Espen Hovlandsdal](https://github.com/rexxars)
* [Sylvain Dumont](https://github.com/sylvaindumont)
* [Alun Davies](https://github.com/alundavies)
* [Aidan Hoolachan](https://github.com/ajhool)
* [Axel Eirola](https://github.com/aeirola)
* [Freezy](https://github.com/freezy)
* [Julian Aubourg](https://github.com/jaubourg)
* [Keith Belovay](https://github.com/fromkeith)
* [Michael B. Klein](https://github.com/mbklein)
Thank you!
### Licence
### Licensing
Copyright 2013, 2014, 2015 Lovell Fuller and contributors.
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

View File

@@ -4,111 +4,126 @@
npm install sharp
```
### Prerequisites
```sh
yarn add sharp
```
* C++11 compatible compiler such as gcc 4.6+ (Node v4+ requires gcc 4.8+), clang 3.0+ or MSVC 2013
* [node-gyp](https://github.com/TooTallNate/node-gyp#installation)
## Prerequisites
* Node v4.5.0+
### Building from source
Pre-compiled binaries for sharp are provided for use with
Node versions 6, 8, 10 and 11 on
64-bit Windows, OS X and Linux platforms.
Sharp will be built from source at install time when:
* a globally-installed libvips is detected,
* pre-compiled binaries do not exist for the current platform and Node version, or
* when the `npm install --build-from-source` flag is used.
Building from source requires:
* C++11 compatible compiler such as gcc 4.8+, clang 3.0+ or MSVC 2013+
* [node-gyp](https://github.com/nodejs/node-gyp#installation) and its dependencies (includes Python 2.7)
## libvips
### Linux
[![Ubuntu 14.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
[![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp)
[![Ubuntu 16.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules/sharp/lib` during `npm install`.
This involves an automated HTTPS download of approximately 6MB.
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 9MB.
Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.:
Most Linux-based (glibc, musl) operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.:
* Debian 7, 8
* Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
* Centos 7
* Fedora 21, 22, 23
* openSUSE 13.2
* Archlinux 2015.06.01
* Debian 7+
* Ubuntu 14.04+
* Centos 7+
* Alpine 3.8+ (Node 8 and 10)
* Fedora
* openSUSE 13.2+
* Archlinux
* Raspbian Jessie
* Amazon Linux 2015.03, 2015.09
* Amazon Linux
* Solus
To use your own version of libvips instead of the provided binaries, make sure it is
at least the version listed under `config.libvips` in the `package.json` file that it
can be located using `pkg-config`. If you are using non-stadard paths (anything other
than `/usr` or `/usr/local`), you might need to set `PKG_CONFIG_PATH` during `npm install`
To use a globally-installed version of libvips instead of the provided binaries,
make sure it is at least the version listed under `config.libvips` in the `package.json` file
and that it can be located using `pkg-config --modversion vips-cpp`.
If you are using non-standard paths (anything other than `/usr` or `/usr/local`),
you might need to set `PKG_CONFIG_PATH` during `npm install`
and `LD_LIBRARY_PATH` at runtime.
You can print the detected vips version using: `pkg-config --modversion vips-cpp`
This allows the use of newer versions of libvips with older versions of sharp.
For older Linux-based operating systems and 32-bit Intel CPUs,
a system-wide installation of the most suitable version of
libvips and its dependencies can be achieved by running
the following command as a user with `sudo` access
(requires `curl` and `pkg-config`):
For 32-bit Intel CPUs and older Linux-based operating systems such as Centos 6,
compiling libvips from source is recommended.
[https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball](https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball)
#### Alpine Linux
libvips is available in the
[testing repository](https://pkgs.alpinelinux.org/packages?name=vips-dev):
```sh
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
apk add vips-dev fftw-dev build-base --update-cache \
--repository https://alpine.global.ssl.fastly.net/alpine/edge/testing/ \
--repository https://alpine.global.ssl.fastly.net/alpine/edge/main
```
For Linux-based operating systems such as Alpine that use musl libc,
the smaller stack size means libvips' cache should be disabled
The smaller stack size of musl libc means
libvips may need to be used without a cache
via `sharp.cache(false)` to avoid a stack overflow.
Beware of Linux OS upgrades that introduce v5.1+ of the `g++` compiler due to
[changes](https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html)
in the C++11 ABI.
This module assumes the previous behaviour, which can be enforced by setting the
`_GLIBCXX_USE_CXX11_ABI=0` environment variable at libvips' compile time.
### Mac OS
[![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
[![OS X 10.12 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
libvips must be installed before `npm install` is run.
This can be achieved via homebrew:
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 8MB.
```sh
brew install homebrew/science/vips
```
To use your own version of libvips instead of the provided binaries, make sure it is
at least the version listed under `config.libvips` in the `package.json` file and
that it can be located using `pkg-config --modversion vips-cpp`.
For GIF input and WebP output suppport use:
```sh
brew install homebrew/science/vips --with-imagemagick --with-webp
```
A missing or incorrectly configured _Xcode Command Line Tools_ installation
[can lead](https://github.com/lovell/sharp/issues/80) to a
`library not found for -ljpeg` error.
If so, please try: `xcode-select --install`.
The _gettext_ dependency of _libvips_
[can lead](https://github.com/lovell/sharp/issues/9)
to a `library not found for -lintl` error.
If so, please try `brew link gettext --force`.
### Windows
### Windows x64
[![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules\sharp` during `npm install`.
This involves an automated HTTPS download of approximately 9MB.
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 14MB.
If you are having issues during installation consider removing the directory
`C:\Users\[user]\AppData\Roaming\npm-cache\_libvips`.
Only 64-bit (x64) `node.exe` is supported.
The WebP format is currently unavailable on Windows.
### FreeBSD
libvips must be installed before `npm install` is run.
This can be achieved via [FreshPorts](https://www.freshports.org/graphics/vips/):
This can be achieved via package or ports:
```sh
pkg install -y pkgconf vips
```
```sh
cd /usr/ports/graphics/vips/ && make install clean
```
FreeBSD's gcc v4 and v5 need `CXXFLAGS=-D_GLIBCXX_USE_C99` set for C++11 support due to
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=193528
### Heroku
[Alessandro Tagliapietra](https://github.com/alex88) maintains an
[Heroku buildpack for libvips](https://github.com/alex88/heroku-buildpack-vips)
and its dependencies.
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager.
### Docker
@@ -126,35 +141,145 @@ docker pull marcbachmann/libvips
docker pull wjordan/libvips
```
[Tailor Brands](https://github.com/TailorBrands) maintain
[Debian-based Dockerfiles for libvips and nodejs](https://github.com/TailorBrands/docker-libvips).
```sh
docker pull tailor/docker-libvips
```
### AWS Lambda
In order to use sharp on AWS Lambda, you need to [create a deployment package](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html). Because sharp
downloads and links libraries for the current platform during `npm install` you have to
do this on a system similar to the [Lambda Execution Environment](http://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html). The easiest ways to do this, is to setup a
small t2.micro instance using the AMI ID listed in the previous link, ssh into it as ec2-user
and follow the instructions below.
Set the Lambda runtime to Node.js 8.10.
Install depencies:
The binaries in the `node_modules` directory of the
[deployment package](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html)
must be for the Linux x64 platform/architecture.
On non-Linux machines such as OS X and Windows run the following:
```sh
sudo yum-config-manager --enable epel
sudo yum install -y nodejs gcc-c++
curl -s https://www.npmjs.com/install.sh | sudo sh
rm -rf node_modules/sharp
npm install --arch=x64 --platform=linux --target=8.10.0 sharp
```
Copy your code and package.json to the instance using `scp` and create a deployment package:
Alternatively a Docker container closely matching the Lambda runtime can be used:
```sh
cd sharp-lambda-example
npm install
zip -ur9 ../sharp-lambda-example.zip index.js node_modules
rm -rf node_modules/sharp
docker run -v "$PWD":/var/task lambci/lambda:build-nodejs8.10 npm install sharp
```
You can now download your deployment ZIP using `scp` and upload it to Lambda.
To get the best performance select the largest memory available.
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
**Performance Tip:** To get the best performance on Lambda choose the largest memory available because this also gives you the most cpu time (a 1536 MB function is 12x faster than a 128 MB function).
### NW.js
Run the `nw-gyp` tool after installation.
```sh
cd node-modules/sharp
nw-gyp rebuild --arch=x64 --target=[your nw version]
node node_modules/sharp/install/dll-copy
```
[http://docs.nwjs.io/en/latest/For%20Users/Advanced/Use%20Native%20Node%20Modules/](http://docs.nwjs.io/en/latest/For%20Users/Advanced/Use%20Native%20Node%20Modules/)
### Build tools
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)
### Coding tools
* [Sharp TypeScript Types](https://www.npmjs.com/package/@types/sharp)
### CLI tools
* [sharp-cli](https://www.npmjs.com/package/sharp-cli)
### Security
Many users of this module process untrusted, user-supplied images,
but there are aspects of security to consider when doing so.
It is possible to compile libvips with support for various third-party image loaders.
Each of these libraries has undergone differing levels of security testing.
Whilst tools such as [American Fuzzy Lop](http://lcamtuf.coredump.cx/afl/)
and [Valgrind](http://valgrind.org/) have been used to test
the most popular web-based formats, as well as libvips itself,
you are advised to perform your own testing and sandboxing.
### Pre-compiled libvips binaries
This module will attempt to download a pre-compiled bundle of libvips
and its dependencies on Linux and Windows machines under either of these
conditions:
1. If a global installation of libvips that meets the
minimum version requirement cannot be found;
1. If `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable is set.
```sh
SHARP_IGNORE_GLOBAL_LIBVIPS=1 npm install sharp
```
Should you need to manually download and inspect these files,
you can do so via
[https://github.com/lovell/sharp-libvips/releases](https://github.com/lovell/sharp-libvips/releases)
Should you wish to install these from your own location,
set the `sharp_dist_base_url` npm config option, e.g.
```sh
npm config set sharp_dist_base_url "https://hostname/path/"
npm install sharp
```
or set the `SHARP_DIST_BASE_URL` environment variable, e.g.
```sh
SHARP_DIST_BASE_URL="https://hostname/path/" npm install sharp
```
to use `https://hostname/path/libvips-x.y.z-platform.tar.gz`.
### Licences
This module is licensed under the terms of the
[Apache 2.0 Licence](https://github.com/lovell/sharp/blob/master/LICENSE).
The libraries downloaded and used by this module
are done so under the terms of the following licences,
all of which are compatible with the Apache 2.0 Licence.
Use of libraries under the terms of the LGPLv3 is via the
"any later version" clause of the LGPLv2 or LGPLv2.1.
| Library | Used under the terms of |
|---------------|----------------------------------------------------------------------------------------------------------|
| cairo | Mozilla Public License 2.0 |
| expat | MIT Licence |
| fontconfig | [fontconfig Licence](https://cgit.freedesktop.org/fontconfig/tree/COPYING) (BSD-like) |
| freetype | [freetype Licence](http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) |
| fribidi | LGPLv3 |
| gettext | LGPLv3 |
| giflib | MIT Licence |
| glib | LGPLv3 |
| harfbuzz | MIT Licence |
| lcms | MIT Licence |
| libcroco | LGPLv3 |
| libexif | LGPLv3 |
| libffi | MIT Licence |
| libgsf | LGPLv3 |
| libjpeg-turbo | [zlib License, IJG License](https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/LICENSE.md) |
| libpng | [libpng License](http://www.libpng.org/pub/png/src/libpng-LICENSE.txt) |
| librsvg | LGPLv3 |
| libtiff | [libtiff License](http://www.libtiff.org/misc.html) (BSD-like) |
| libvips | LGPLv3 |
| libwebp | New BSD License |
| libxml2 | MIT Licence |
| pango | LGPLv3 |
| pixman | MIT Licence |
| zlib | [zlib Licence](https://github.com/madler/zlib/blob/master/zlib.h) |

View File

@@ -2,60 +2,59 @@
### Test environment
* AWS EC2 [c4.xlarge](http://aws.amazon.com/ec2/instance-types/#c4) (4x E5-2666 v3 @2.90GHz)
* Amazon Linux 2015.09.1
* Node.js v5.5.0
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
* Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
* Node.js v10.11.0
### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.2.20 - Image processing in pure JavaScript. Bilinear interpolation only.
* [lwip](https://www.npmjs.com/package/lwip) v0.0.8 - Wrapper around CImg, compiles dependencies from source.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) @47c7329 - Wrapper around libmagick++, supports Buffers only.
* [jimp](https://www.npmjs.com/package/jimp) v0.5.3 - Image processing in pure JavaScript. Provides bicubic interpolation.
* [mapnik](https://www.npmjs.org/package/mapnik) v4.0.1 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
* [imagemagick-native](https://www.npmjs.com/package/imagemagick-native) v1.9.3 - Wrapper around libmagick++, supports Buffers only.
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
* [gm](https://www.npmjs.com/package/gm) v1.21.0 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.13.0 / libvips v8.2.2 - Caching within libvips disabled to ensure a fair comparison.
* [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.21.0 / libvips v8.7.0 - Caching within libvips disabled to ensure a fair comparison.
### The task
Decompress a 2725x2225 JPEG image, resize to 720x480 using bicubic interpolation (where available), then compress to JPEG.
Decompress a 2725x2225 JPEG image,
resize to 720x588 using Lanczos 3 resampling (where available),
then compress to JPEG at a "quality" setting of 80.
### Results
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp (bilinear) | file | file | 1.04 | 1.0 |
| jimp (bilinear) | buffer | buffer | 1.07 | 1.0 |
| lwip | file | file | 1.13 | 1.1 |
| lwip | buffer | buffer | 1.13 | 1.1 |
| imagemagick-native | buffer | buffer | 1.65 | 1.6 |
| imagemagick | file | file | 5.02 | 4.8 |
| gm | buffer | buffer | 5.36 | 5.2 |
| gm | file | file | 5.39 | 5.2 |
| sharp | stream | stream | 22.00 | 21.2 |
| sharp | file | file | 22.87 | 22.0 |
| sharp | file | buffer | 23.03 | 22.1 |
| sharp | buffer | file | 23.10 | 22.2 |
| sharp | buffer | buffer | 23.21 | 22.3 |
| jimp | buffer | buffer | 0.71 | 1.0 |
| mapnik | buffer | buffer | 3.32 | 4.7 |
| gm | buffer | buffer | 3.97 | 5.6 |
| imagemagick-native | buffer | buffer | 4.06 | 5.7 |
| imagemagick | file | file | 4.24 | 6.0 |
| sharp | stream | stream | 25.30 | 35.6 |
| sharp | file | file | 26.17 | 36.9 |
| sharp | buffer | buffer | 26.45 | 37.3 |
Greater performance can be expected with caching enabled (default) and using 8+ core machines.
Greater libvips performance can be expected with caching enabled (default)
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
### Benchmark test prerequisites
Requires both _ImageMagick_ and _GraphicsMagick_:
Requires _ImageMagick_, _GraphicsMagick_ and _Mapnik_:
```sh
brew install imagemagick
brew install graphicsmagick
brew install mapnik
```
```sh
sudo apt-get install imagemagick libmagick++-dev graphicsmagick
sudo apt-get install imagemagick libmagick++-dev graphicsmagick libmapnik-dev
```
```sh
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick mapnik-devel
```
### Running the benchmark test

Binary file not shown.

1028
index.js

File diff suppressed because it is too large Load Diff

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

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

109
install/libvips.js Normal file
View File

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

37
lib/agent.js Normal file
View File

@@ -0,0 +1,37 @@
'use strict';
const url = require('url');
const tunnelAgent = require('tunnel-agent');
const is = require('./is');
const proxies = [
'HTTPS_PROXY',
'https_proxy',
'HTTP_PROXY',
'http_proxy',
'npm_config_https_proxy',
'npm_config_proxy'
];
function env (key) {
return process.env[key];
}
module.exports = function () {
try {
const proxy = url.parse(proxies.map(env).find(is.string));
const tunnel = proxy.protocol === 'https:'
? tunnelAgent.httpsOverHttps
: tunnelAgent.httpsOverHttp;
return tunnel({
proxy: {
port: Number(proxy.port),
host: proxy.hostname,
proxyAuth: proxy.auth
}
});
} catch (err) {
return null;
}
};

147
lib/channel.js Normal file
View File

@@ -0,0 +1,147 @@
'use strict';
const is = require('./is');
/**
* Boolean operations for bandbool.
* @private
*/
const bool = {
and: 'and',
or: 'or',
eor: 'eor'
};
/**
* Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
*
* @example
* sharp('rgba.png')
* .removeAlpha()
* .toFile('rgb.png', function(err, info) {
* // rgb.png is a 3 channel image without an alpha channel
* });
*
* @returns {Sharp}
*/
function removeAlpha () {
this.options.removeAlpha = true;
return this;
}
/**
* Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
*
* @example
* sharp('rgb.jpg')
* .ensureAlpha()
* .toFile('rgba.png', function(err, info) {
* // rgba.png is a 4 channel image with a fully opaque alpha channel
* });
*
* @returns {Sharp}
*/
function ensureAlpha () {
this.options.ensureAlpha = true;
return this;
}
/**
* Extract a single channel from a multi-channel image.
*
* @example
* sharp(input)
* .extractChannel('green')
* .toFile('input_green.jpg', function(err, info) {
* // info.channels === 1
* // input_green.jpg contains the green channel of the input image
* });
*
* @param {Number|String} channel - zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively.
* @returns {Sharp}
* @throws {Error} Invalid channel
*/
function extractChannel (channel) {
if (channel === 'red') {
channel = 0;
} else if (channel === 'green') {
channel = 1;
} else if (channel === 'blue') {
channel = 2;
}
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
this.options.extractChannel = channel;
} else {
throw new Error('Cannot extract invalid channel ' + channel);
}
return this;
}
/**
* Join one or more channels to the image.
* The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
* By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
* Channel ordering follows vips convention:
* - sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
* - CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
*
* Buffers may be any of the image formats supported by sharp: JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data.
* For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
*
* @param {Array<String|Buffer>|String|Buffer} images - one or more images (file paths, Buffers).
* @param {Object} options - image options, see `sharp()` constructor.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function joinChannel (images, options) {
if (Array.isArray(images)) {
images.forEach(function (image) {
this.options.joinChannelIn.push(this._createInputDescriptor(image, options));
}, this);
} else {
this.options.joinChannelIn.push(this._createInputDescriptor(images, options));
}
return this;
}
/**
* Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
*
* @example
* sharp('3-channel-rgb-input.png')
* .bandbool(sharp.bool.and)
* .toFile('1-channel-output.png', function (err, info) {
* // The output will be a single channel image where each pixel `P = R & G & B`.
* // If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
* // then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
* });
*
* @param {String} boolOp - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function bandbool (boolOp) {
if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) {
this.options.bandBoolOp = boolOp;
} else {
throw new Error('Invalid bandbool operation ' + boolOp);
}
return this;
}
/**
* Decorate the Sharp prototype with channel-related functions.
* @private
*/
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Public instance functions
removeAlpha,
ensureAlpha,
extractChannel,
joinChannel,
bandbool
});
// Class attributes
Sharp.bool = bool;
};

119
lib/colour.js Normal file
View File

@@ -0,0 +1,119 @@
'use strict';
const color = require('color');
const is = require('./is');
/**
* Colourspaces.
* @private
*/
const colourspace = {
multiband: 'multiband',
'b-w': 'b-w',
bw: 'b-w',
cmyk: 'cmyk',
srgb: 'srgb'
};
/**
* Tint the image using the provided chroma while preserving the image luminance.
* An alpha channel may be present and will be unchanged by the operation.
*
* @param {String|Object} rgb - parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values.
* @returns {Sharp}
* @throws {Error} Invalid parameter
*/
function tint (rgb) {
const colour = color(rgb);
this.options.tintA = colour.a();
this.options.tintB = colour.b();
return this;
}
/**
* Convert to 8-bit greyscale; 256 shades of grey.
* This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
* By default the output image will be web-friendly sRGB and contain three (identical) color channels.
* This may be overridden by other sharp operations such as `toColourspace('b-w')`,
* which will produce an output image containing one color channel.
* An alpha channel may be present, and will be unchanged by the operation.
* @param {Boolean} [greyscale=true]
* @returns {Sharp}
*/
function greyscale (greyscale) {
this.options.greyscale = is.bool(greyscale) ? greyscale : true;
return this;
}
/**
* Alternative spelling of `greyscale`.
* @param {Boolean} [grayscale=true]
* @returns {Sharp}
*/
function grayscale (grayscale) {
return this.greyscale(grayscale);
}
/**
* Set the output colourspace.
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
* @param {String} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColourspace (colourspace) {
if (!is.string(colourspace)) {
throw new Error('Invalid output colourspace ' + colourspace);
}
this.options.colourspace = colourspace;
return this;
}
/**
* Alternative spelling of `toColourspace`.
* @param {String} [colorspace] - output colorspace.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColorspace (colorspace) {
return this.toColourspace(colorspace);
}
/**
* Update a colour attribute of the this.options Object.
* @private
* @param {String} key
* @param {String|Object} val
* @throws {Error} Invalid key
*/
function _setColourOption (key, val) {
if (is.object(val) || is.string(val)) {
const colour = color(val);
this.options[key] = [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
}
}
/**
* Decorate the Sharp prototype with colour-related functions.
* @private
*/
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Public
tint,
greyscale,
grayscale,
toColourspace,
toColorspace,
// Private
_setColourOption
});
// Class attributes
Sharp.colourspace = colourspace;
Sharp.colorspace = colourspace;
};

174
lib/composite.js Normal file
View File

@@ -0,0 +1,174 @@
'use strict';
const deprecate = require('util').deprecate;
const is = require('./is');
/**
* Blend modes.
* @member
* @private
*/
const blend = {
clear: 'clear',
source: 'source',
over: 'over',
in: 'in',
out: 'out',
atop: 'atop',
dest: 'dest',
'dest-over': 'dest-over',
'dest-in': 'dest-in',
'dest-out': 'dest-out',
'dest-atop': 'dest-atop',
xor: 'xor',
add: 'add',
saturate: 'saturate',
multiply: 'multiply',
screen: 'screen',
overlay: 'overlay',
darken: 'darken',
lighten: 'lighten',
'colour-dodge': 'colour-dodge',
'color-dodge': 'colour-dodge',
'colour-burn': 'colour-burn',
'color-burn': 'colour-burn',
'hard-light': 'hard-light',
'soft-light': 'soft-light',
difference: 'difference',
exclusion: 'exclusion'
};
/**
* Composite image(s) over the processed (resized, extracted etc.) image.
*
* The images to composite must be the same size or smaller than the processed image.
* If both `top` and `left` options are provided, they take precedence over `gravity`.
*
* The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
* `dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
* `xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
* `colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
* `hard-light`, `soft-light`, `difference`, `exclusion`.
*
* More information about blend modes can be found at
* https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
* and https://www.cairographics.org/operators/
*
* @example
* sharp('input.png')
* .rotate(180)
* .resize(300)
* .flatten( { background: '#ff6600' } )
* .composite([{ input: 'overlay.png', gravity: 'southeast' }])
* .sharpen()
* .withMetadata()
* .webp( { quality: 90 } )
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains upside down, 300px wide, alpha channel flattened
* // onto orange background, composited with overlay.png with SE gravity,
* // sharpened, with metadata, 90% quality WebP image data. Phew!
* });
*
* @param {Object[]} images - Ordered list of images to composite
* @param {Buffer|String} [images[].input] - Buffer containing image data or String containing the path to an image file.
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
* @param {Number} [images[].top] - the pixel offset from the top edge.
* @param {Number} [images[].left] - the pixel offset from the left edge.
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
* @param {Number} [images[].raw.width]
* @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels]
* @param {Object} [images[].create] - describes a blank overlay to be created.
* @param {Number} [images[].create.width]
* @param {Number} [images[].create.height]
* @param {Number} [images[].create.channels] - 3-4
* @param {String|Object} [images[].create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function composite (images) {
if (!Array.isArray(images)) {
throw is.invalidParameterError('images to composite', 'array', images);
}
this.options.composite = images.map(image => {
if (!is.object(image)) {
throw is.invalidParameterError('image to composite', 'object', image);
}
const { raw, density } = image;
const inputOptions = (raw || density) ? { raw, density } : undefined;
const composite = {
input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }),
blend: 'over',
tile: false,
left: -1,
top: -1,
gravity: 0
};
if (is.defined(image.blend)) {
if (is.string(blend[image.blend])) {
composite.blend = blend[image.blend];
} else {
throw is.invalidParameterError('blend', 'valid blend name', image.blend);
}
}
if (is.defined(image.tile)) {
if (is.bool(image.tile)) {
composite.tile = image.tile;
} else {
throw is.invalidParameterError('tile', 'boolean', image.tile);
}
}
if (is.defined(image.left)) {
if (is.integer(image.left) && image.left >= 0) {
composite.left = image.left;
} else {
throw is.invalidParameterError('left', 'positive integer', image.left);
}
}
if (is.defined(image.top)) {
if (is.integer(image.top) && image.top >= 0) {
composite.top = image.top;
} else {
throw is.invalidParameterError('top', 'positive integer', image.top);
}
}
if (composite.left !== composite.top && Math.min(composite.left, composite.top) === -1) {
throw new Error('Expected both left and top to be set');
}
if (is.defined(image.gravity)) {
if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
composite.gravity = image.gravity;
} else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
composite.gravity = this.constructor.gravity[image.gravity];
} else {
throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
}
}
return composite;
});
return this;
}
/**
* @deprecated
* @private
*/
function overlayWith (input, options) {
const blend = (is.object(options) && options.cutout) ? 'dest-in' : 'over';
return this.composite([Object.assign({ input, blend }, options)]);
}
/**
* Decorate the Sharp prototype with composite-related functions.
* @private
*/
module.exports = function (Sharp) {
Sharp.prototype.composite = composite;
Sharp.prototype.overlayWith = deprecate(overlayWith, 'overlayWith(input, options) is deprecated, use composite([{ input, ...options }]) instead');
Sharp.blend = blend;
};

241
lib/constructor.js Normal file
View File

@@ -0,0 +1,241 @@
'use strict';
const path = require('path');
const util = require('util');
const stream = require('stream');
const events = require('events');
const is = require('./is');
require('./libvips').hasVendoredLibvips();
const sharp = require('bindings')('sharp.node');
// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
/**
* @class Sharp
*
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
*
* JPEG, PNG, WebP or TIFF format image data can be streamed out from this object.
* When using Stream based output, derived attributes are available from the `info` event.
*
* Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
*
* @example
* sharp('input.jpg')
* .resize(300, 200)
* .toFile('output.jpg', function(err) {
* // output.jpg is a 300 pixels wide and 200 pixels high image
* // containing a scaled and cropped version of input.jpg
* });
*
* @example
* // Read image data from readableStream,
* // resize to 300 pixels wide,
* // emit an 'info' event with calculated dimensions
* // and finally write image data to writableStream
* var transformer = sharp()
* .resize(300)
* .on('info', function(info) {
* console.log('Image height is ' + info.height);
* });
* readableStream.pipe(transformer).pipe(writableStream);
*
* @example
* // Create a blank 300x200 PNG image of semi-transluent red pixels
* sharp({
* create: {
* width: 300,
* height: 200,
* channels: 4,
* background: { r: 255, g: 0, b: 0, alpha: 0.5 }
* }
* })
* .png()
* .toBuffer()
* .then( ... );
*
* @param {(Buffer|String)} [input] - if present, can be
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {Boolean} [options.failOnError=true] - by default halt processing and raise an error when loading invalid images.
* Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid.
* @param {Number} [options.density=72] - number representing the DPI for vector images.
* @param {Number} [options.pages=1] - number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages.
* @param {Number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {Number} [options.raw.width]
* @param {Number} [options.raw.height]
* @param {Number} [options.raw.channels] - 1-4
* @param {Object} [options.create] - describes a new image to be created.
* @param {Number} [options.create.width]
* @param {Number} [options.create.height]
* @param {Number} [options.create.channels] - 3-4
* @param {String|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
const Sharp = function (input, options) {
if (arguments.length === 1 && !is.defined(input)) {
throw new Error('Invalid input');
}
if (!(this instanceof Sharp)) {
return new Sharp(input, options);
}
stream.Duplex.call(this);
this.options = {
// input options
sequentialRead: false,
limitInputPixels: Math.pow(0x3FFF, 2),
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
widthPre: -1,
heightPre: -1,
topOffsetPost: -1,
leftOffsetPost: -1,
widthPost: -1,
heightPost: -1,
width: -1,
height: -1,
canvas: 'crop',
position: 0,
resizeBackground: [0, 0, 0, 255],
useExifOrientation: false,
angle: 0,
rotationAngle: 0,
rotationBackground: [0, 0, 0, 255],
rotateBeforePreExtract: false,
flip: false,
flop: false,
extendTop: 0,
extendBottom: 0,
extendLeft: 0,
extendRight: 0,
extendBackground: [0, 0, 0, 255],
withoutEnlargement: false,
kernel: 'lanczos3',
fastShrinkOnLoad: true,
// operations
tintA: 128,
tintB: 128,
flatten: false,
flattenBackground: [0, 0, 0],
negate: false,
medianSize: 0,
blurSigma: 0,
sharpenSigma: 0,
sharpenFlat: 1,
sharpenJagged: 2,
threshold: 0,
thresholdGrayscale: true,
trimThreshold: 0,
gamma: 0,
gammaOut: 0,
greyscale: false,
normalise: 0,
booleanBufferIn: null,
booleanFileIn: '',
joinChannelIn: [],
extractChannel: -1,
removeAlpha: false,
ensureAlpha: false,
colourspace: 'srgb',
composite: [],
// output
fileOut: '',
formatOut: 'input',
streamOut: false,
withMetadata: false,
withMetadataOrientation: -1,
resolveWithObject: false,
// output format
jpegQuality: 80,
jpegProgressive: false,
jpegChromaSubsampling: '4:2:0',
jpegTrellisQuantisation: false,
jpegOvershootDeringing: false,
jpegOptimiseScans: false,
jpegOptimiseCoding: true,
jpegQuantisationTable: 0,
pngProgressive: false,
pngCompressionLevel: 9,
pngAdaptiveFiltering: false,
pngPalette: false,
pngQuality: 100,
pngColours: 256,
pngDither: 1,
webpQuality: 80,
webpAlphaQuality: 100,
webpLossless: false,
webpNearLossless: false,
tiffQuality: 80,
tiffCompression: 'jpeg',
tiffPredictor: 'horizontal',
tiffPyramid: false,
tiffSquash: false,
tiffTile: false,
tiffTileHeight: 256,
tiffTileWidth: 256,
tiffXres: 1.0,
tiffYres: 1.0,
tileSize: 256,
tileOverlap: 0,
linearA: 1,
linearB: 0,
// Function to notify of libvips warnings
debuglog: debuglog,
// Function to notify of queue length changes
queueListener: function (queueLength) {
queue.emit('change', queueLength);
}
};
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
return this;
};
util.inherits(Sharp, stream.Duplex);
/**
* An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread
* - complete
* @member
* @example
* sharp.queue.on('change', function(queueLength) {
* console.log('Queue contains ' + queueLength + ' task(s)');
* });
*/
const queue = new events.EventEmitter();
Sharp.queue = queue;
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @example
* console.log(sharp.format);
* @returns {Object}
*/
Sharp.format = sharp.format();
/**
* An Object containing the version numbers of libvips and its dependencies.
* @member
* @example
* console.log(sharp.versions);
*/
Sharp.versions = {
vips: sharp.libvipsVersion()
};
try {
Sharp.versions = require('../vendor/versions.json');
} catch (err) {}
/**
* Export constructor.
* @private
*/
module.exports = Sharp;

BIN
lib/icc/cmyk.icm Normal file

Binary file not shown.

BIN
lib/icc/sRGB.icc Normal file

Binary file not shown.

13
lib/index.js Normal file
View File

@@ -0,0 +1,13 @@
'use strict';
const Sharp = require('./constructor');
require('./input')(Sharp);
require('./resize')(Sharp);
require('./composite')(Sharp);
require('./operation')(Sharp);
require('./colour')(Sharp);
require('./channel')(Sharp);
require('./output')(Sharp);
require('./utility')(Sharp);
module.exports = Sharp;

389
lib/input.js Normal file
View File

@@ -0,0 +1,389 @@
'use strict';
const color = require('color');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* Create Object containing input and input-related options.
* @private
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = { failOnError: true };
if (is.string(input)) {
// filesystem
inputDescriptor.file = input;
} else if (is.buffer(input)) {
// Buffer
inputDescriptor.buffer = input;
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
// Plain Object descriptor, e.g. create
inputOptions = input;
if (is.plainObject(inputOptions.raw)) {
// Raw Stream
inputDescriptor.buffer = [];
}
} else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream
inputDescriptor.buffer = [];
} else {
throw new Error('Unsupported input ' + typeof input);
}
if (is.object(inputOptions)) {
// Fail on error
if (is.defined(inputOptions.failOnError)) {
if (is.bool(inputOptions.failOnError)) {
inputDescriptor.failOnError = inputOptions.failOnError;
} else {
throw new Error('Invalid failOnError (boolean) ' + inputOptions.failOnError);
}
}
// Density
if (is.defined(inputOptions.density)) {
if (is.inRange(inputOptions.density, 1, 2400)) {
inputDescriptor.density = inputOptions.density;
} else {
throw new Error('Invalid density (1 to 2400) ' + inputOptions.density);
}
}
// Raw pixel input
if (is.defined(inputOptions.raw)) {
if (
is.object(inputOptions.raw) &&
is.integer(inputOptions.raw.width) && inputOptions.raw.width > 0 &&
is.integer(inputOptions.raw.height) && inputOptions.raw.height > 0 &&
is.integer(inputOptions.raw.channels) && is.inRange(inputOptions.raw.channels, 1, 4)
) {
inputDescriptor.rawWidth = inputOptions.raw.width;
inputDescriptor.rawHeight = inputOptions.raw.height;
inputDescriptor.rawChannels = inputOptions.raw.channels;
} else {
throw new Error('Expected width, height and channels for raw pixel input');
}
}
// Multi-page input (GIF, TIFF, PDF)
if (is.defined(inputOptions.pages)) {
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
inputDescriptor.pages = inputOptions.pages;
}
}
if (is.defined(inputOptions.page)) {
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
inputDescriptor.page = inputOptions.page;
}
}
// Create new image
if (is.defined(inputOptions.create)) {
if (
is.object(inputOptions.create) &&
is.integer(inputOptions.create.width) && inputOptions.create.width > 0 &&
is.integer(inputOptions.create.height) && inputOptions.create.height > 0 &&
is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) &&
is.defined(inputOptions.create.background)
) {
inputDescriptor.createWidth = inputOptions.create.width;
inputDescriptor.createHeight = inputOptions.create.height;
inputDescriptor.createChannels = inputOptions.create.channels;
const background = color(inputOptions.create.background);
inputDescriptor.createBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
delete inputDescriptor.buffer;
} else {
throw new Error('Expected width, height, channels and background to create a new input image');
}
}
} else if (is.defined(inputOptions)) {
throw new Error('Invalid input options ' + inputOptions);
}
return inputDescriptor;
}
/**
* Handle incoming Buffer chunk on Writable Stream.
* @private
* @param {Buffer} chunk
* @param {String} encoding - unused
* @param {Function} callback
*/
function _write (chunk, encoding, callback) {
/* istanbul ignore else */
if (Array.isArray(this.options.input.buffer)) {
/* istanbul ignore else */
if (is.buffer(chunk)) {
if (this.options.input.buffer.length === 0) {
const that = this;
this.on('finish', function () {
that.streamInFinished = true;
});
}
this.options.input.buffer.push(chunk);
callback();
} else {
callback(new Error('Non-Buffer data on Writable Stream'));
}
} else {
callback(new Error('Unexpected data on Writable Stream'));
}
}
/**
* Flattens the array of chunks accumulated in input.buffer.
* @private
*/
function _flattenBufferIn () {
if (this._isStreamInput()) {
this.options.input.buffer = Buffer.concat(this.options.input.buffer);
}
}
/**
* Are we expecting Stream-based input?
* @private
* @returns {Boolean}
*/
function _isStreamInput () {
return Array.isArray(this.options.input.buffer);
}
/**
* Take a "snapshot" of the Sharp instance, returning a new instance.
* Cloned instances inherit the input of their parent instance.
* This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
*
* @example
* const pipeline = sharp().rotate();
* pipeline.clone().resize(800, 600).pipe(firstWritableStream);
* pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
* readableStream.pipe(pipeline);
* // firstWritableStream receives auto-rotated, resized readableStream
* // secondWritableStream receives auto-rotated, extracted region of readableStream
*
* @returns {Sharp}
*/
function clone () {
const that = this;
// Clone existing options
const clone = this.constructor.call();
clone.options = Object.assign({}, this.options);
// Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) {
this.on('finish', function () {
// Clone inherits input data
that._flattenBufferIn();
clone.options.bufferIn = that.options.bufferIn;
clone.emit('finish');
});
}
return clone;
}
/**
* Fast access to (uncached) image metadata without decoding any compressed image data.
* A `Promise` is returned when `callback` is not provided.
*
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
* - `size`: Total size of image in bytes, for Stream and Buffer input only
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
* - `density`: Number of pixels per inch (DPI), if present
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, PDF, animated GIF and animated WebP
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
* - `orientation`: Number value of the EXIF Orientation header, if present
* - `exif`: Buffer containing raw EXIF data, if present
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
* - `iptc`: Buffer containing raw IPTC data, if present
* - `xmp`: Buffer containing raw XMP data, if present
*
* @example
* const image = sharp(inputJpg);
* image
* .metadata()
* .then(function(metadata) {
* return image
* .resize(Math.round(metadata.width / 2))
* .webp()
* .toBuffer();
* })
* .then(function(data) {
* // data contains a WebP image half the width and height of the original JPEG
* });
*
* @param {Function} [callback] - called with the arguments `(err, metadata)`
* @returns {Promise<Object>|Sharp}
*/
function metadata (callback) {
const that = this;
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', function () {
that._flattenBufferIn();
sharp.metadata(that.options, callback);
});
} else {
sharp.metadata(this.options, callback);
}
return this;
} else {
if (this._isStreamInput()) {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.metadata(that.options, function (err, metadata) {
if (err) {
reject(err);
} else {
resolve(metadata);
}
});
});
});
} else {
return new Promise(function (resolve, reject) {
sharp.metadata(that.options, function (err, metadata) {
if (err) {
reject(err);
} else {
resolve(metadata);
}
});
});
}
}
}
/**
* Access to pixel-derived image statistics for every channel in the image.
* A `Promise` is returned when `callback` is not provided.
*
* - `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
* - `min` (minimum value in the channel)
* - `max` (maximum value in the channel)
* - `sum` (sum of all values in a channel)
* - `squaresSum` (sum of squared values in a channel)
* - `mean` (mean of the values in a channel)
* - `stdev` (standard deviation for the values in a channel)
* - `minX` (x-coordinate of one of the pixel where the minimum lies)
* - `minY` (y-coordinate of one of the pixel where the minimum lies)
* - `maxX` (x-coordinate of one of the pixel where the maximum lies)
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
* - `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
*
* @example
* const image = sharp(inputJpg);
* image
* .stats()
* .then(function(stats) {
* // stats contains the channel-wise statistics array and the isOpaque value
* });
*
* @param {Function} [callback] - called with the arguments `(err, stats)`
* @returns {Promise<Object>}
*/
function stats (callback) {
const that = this;
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', function () {
that._flattenBufferIn();
sharp.stats(that.options, callback);
});
} else {
sharp.stats(this.options, callback);
}
return this;
} else {
if (this._isStreamInput()) {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.stats(that.options, function (err, stats) {
if (err) {
reject(err);
} else {
resolve(stats);
}
});
});
});
} else {
return new Promise(function (resolve, reject) {
sharp.stats(that.options, function (err, stats) {
if (err) {
reject(err);
} else {
resolve(stats);
}
});
});
}
}
}
/**
* Do not process input images where the number of pixels (width x height) exceeds this limit.
* Assumes image dimensions contained in the input metadata can be trusted.
* The default limit is 268402689 (0x3FFF x 0x3FFF) pixels.
* @param {(Number|Boolean)} limit - an integral Number of pixels, zero or false to remove limit, true to use default limit.
* @returns {Sharp}
* @throws {Error} Invalid limit
*/
function limitInputPixels (limit) {
// if we pass in false we represent the integer as 0 to disable
if (limit === false) {
limit = 0;
} else if (limit === true) {
limit = Math.pow(0x3FFF, 2);
}
if (is.integer(limit) && limit >= 0) {
this.options.limitInputPixels = limit;
} else {
throw is.invalidParameterError('limitInputPixels', 'integer', limit);
}
return this;
}
/**
* An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
* This will reduce memory usage and can improve performance on some systems.
*
* The default behaviour *before* function call is `false`, meaning the libvips access method is not sequential.
*
* @param {Boolean} [sequentialRead=true]
* @returns {Sharp}
*/
function sequentialRead (sequentialRead) {
this.options.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true;
return this;
}
/**
* Decorate the Sharp prototype with input-related functions.
* @private
*/
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Private
_createInputDescriptor,
_write,
_flattenBufferIn,
_isStreamInput,
// Public
clone,
metadata,
stats,
limitInputPixels,
sequentialRead
});
};

119
lib/is.js Normal file
View File

@@ -0,0 +1,119 @@
'use strict';
/**
* Is this value defined and not null?
* @private
*/
const defined = function (val) {
return typeof val !== 'undefined' && val !== null;
};
/**
* Is this value an object?
* @private
*/
const object = function (val) {
return typeof val === 'object';
};
/**
* Is this value a plain object?
* @private
*/
const plainObject = function (val) {
return object(val) && Object.prototype.toString.call(val) === '[object Object]';
};
/**
* Is this value a function?
* @private
*/
const fn = function (val) {
return typeof val === 'function';
};
/**
* Is this value a boolean?
* @private
*/
const bool = function (val) {
return typeof val === 'boolean';
};
/**
* Is this value a Buffer object?
* @private
*/
const buffer = function (val) {
return object(val) && val instanceof Buffer;
};
/**
* Is this value a non-empty string?
* @private
*/
const string = function (val) {
return typeof val === 'string' && val.length > 0;
};
/**
* Is this value a real number?
* @private
*/
const number = function (val) {
return typeof val === 'number' && !Number.isNaN(val);
};
/**
* Is this value an integer?
* @private
*/
const integer = function (val) {
return number(val) && val % 1 === 0;
};
/**
* Is this value within an inclusive given range?
* @private
*/
const inRange = function (val, min, max) {
return val >= min && val <= max;
};
/**
* Is this value within the elements of an array?
* @private
*/
const inArray = function (val, list) {
return list.indexOf(val) !== -1;
};
/**
* Create an Error with a message relating to an invalid parameter.
*
* @param {String} name - parameter name.
* @param {String} expected - description of the type/value/range expected.
* @param {*} actual - the value received.
* @returns {Error} Containing the formatted message.
* @private
*/
const invalidParameterError = function (name, expected, actual) {
return new Error(
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
);
};
module.exports = {
defined: defined,
object: object,
plainObject: plainObject,
fn: fn,
bool: bool,
buffer: buffer,
string: string,
number: number,
integer: integer,
inRange: inRange,
inArray: inArray,
invalidParameterError: invalidParameterError
};

95
lib/libvips.js Normal file
View File

@@ -0,0 +1,95 @@
'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const spawnSync = require('child_process').spawnSync;
const semver = require('semver');
const platform = require('./platform');
const env = process.env;
const minimumLibvipsVersion = env.npm_package_config_libvips || require('../package.json').config.libvips;
const spawnSyncOptions = {
encoding: 'utf8',
shell: true
};
const mkdirSync = function (dirPath) {
try {
fs.mkdirSync(dirPath);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
}
}
};
const cachePath = function () {
const npmCachePath = env.npm_config_cache || (env.APPDATA ? path.join(env.APPDATA, 'npm-cache') : path.join(os.homedir(), '.npm'));
mkdirSync(npmCachePath);
const libvipsCachePath = path.join(npmCachePath, '_libvips');
mkdirSync(libvipsCachePath);
return libvipsCachePath;
};
const globalLibvipsVersion = function () {
if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout || '';
return globalLibvipsVersion.trim();
} else {
return '';
}
};
const hasVendoredLibvips = function () {
const currentPlatformId = platform();
const vendorPath = path.join(__dirname, '..', 'vendor');
let vendorVersionId;
let vendorPlatformId;
try {
vendorVersionId = require(path.join(vendorPath, 'versions.json')).vips;
vendorPlatformId = require(path.join(vendorPath, 'platform.json'));
} catch (err) {}
if (vendorVersionId && vendorVersionId !== minimumLibvipsVersion) {
throw new Error(`Found vendored libvips v${vendorVersionId} but require v${minimumLibvipsVersion}. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
}
if (vendorPlatformId) {
if (currentPlatformId === vendorPlatformId) {
return true;
} else {
throw new Error(`'${vendorPlatformId}' binaries cannot be used on the '${currentPlatformId}' platform. Please remove the 'node_modules/sharp/vendor' directory and run 'npm install'.`);
}
}
return false;
};
const pkgConfigPath = function () {
if (process.platform !== 'win32') {
const brewPkgConfigPath = spawnSync('which brew >/dev/null 2>&1 && eval $(brew --env) && echo $PKG_CONFIG_LIBDIR', spawnSyncOptions).stdout || '';
return [brewPkgConfigPath.trim(), env.PKG_CONFIG_PATH, '/usr/local/lib/pkgconfig', '/usr/lib/pkgconfig']
.filter(function (p) { return !!p; })
.join(':');
} else {
return '';
}
};
const useGlobalLibvips = function () {
if (Boolean(env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
return false;
}
const globalVipsVersion = globalLibvipsVersion();
return !!globalVipsVersion && semver.gte(globalVipsVersion, minimumLibvipsVersion);
};
module.exports = {
minimumLibvipsVersion: minimumLibvipsVersion,
cachePath: cachePath,
globalLibvipsVersion: globalLibvipsVersion,
hasVendoredLibvips: hasVendoredLibvips,
pkgConfigPath: pkgConfigPath,
useGlobalLibvips: useGlobalLibvips,
mkdirSync: mkdirSync
};

441
lib/operation.js Normal file
View File

@@ -0,0 +1,441 @@
'use strict';
const color = require('color');
const is = require('./is');
/**
* Rotate the output image by either an explicit angle
* or auto-orient based on the EXIF `Orientation` tag.
*
* If an angle is provided, it is converted to a valid positive degree rotation.
* For example, `-450` will produce a 270deg rotation.
*
* When rotating by an angle other than a multiple of 90,
* the background colour can be provided with the `background` option.
*
* If no angle is provided, it is determined from the EXIF data.
* Mirroring is supported and may infer the use of a flip operation.
*
* The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
*
* Method order is important when both rotating and extracting regions,
* for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
*
* @example
* const pipeline = sharp()
* .rotate()
* .resize(null, 200)
* .toBuffer(function (err, outputBuffer, info) {
* // outputBuffer contains 200px high JPEG image data,
* // auto-rotated using EXIF Orientation tag
* // info.width and info.height contain the dimensions of the resized image
* });
* readableStream.pipe(pipeline);
*
* @param {Number} [angle=auto] angle of rotation.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {String|Object} [options.background="#000000"] parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function rotate (angle, options) {
if (!is.defined(angle)) {
this.options.useExifOrientation = true;
} else if (is.integer(angle) && !(angle % 90)) {
this.options.angle = angle;
} else if (is.number(angle)) {
this.options.rotationAngle = angle;
if (is.object(options) && options.background) {
const backgroundColour = color(options.background);
this.options.rotationBackground = [
backgroundColour.red(),
backgroundColour.green(),
backgroundColour.blue(),
Math.round(backgroundColour.alpha() * 255)
];
}
} else {
throw new Error('Unsupported angle: must be a number.');
}
return this;
}
/**
* Flip the image about the vertical Y axis. This always occurs after rotation, if any.
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
* @param {Boolean} [flip=true]
* @returns {Sharp}
*/
function flip (flip) {
this.options.flip = is.bool(flip) ? flip : true;
return this;
}
/**
* Flop the image about the horizontal X axis. This always occurs after rotation, if any.
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
* @param {Boolean} [flop=true]
* @returns {Sharp}
*/
function flop (flop) {
this.options.flop = is.bool(flop) ? flop : true;
return this;
}
/**
* Sharpen the image.
* When used without parameters, performs a fast, mild sharpen of the output image.
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
* Separate control over the level of sharpening in "flat" and "jagged" areas is available.
*
* @param {Number} [sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @param {Number} [flat=1.0] - the level of sharpening to apply to "flat" areas.
* @param {Number} [jagged=2.0] - the level of sharpening to apply to "jagged" areas.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function sharpen (sigma, flat, jagged) {
if (!is.defined(sigma)) {
// No arguments: default to mild sharpen
this.options.sharpenSigma = -1;
} else if (is.bool(sigma)) {
// Boolean argument: apply mild sharpen?
this.options.sharpenSigma = sigma ? -1 : 0;
} else if (is.number(sigma) && is.inRange(sigma, 0.01, 10000)) {
// Numeric argument: specific sigma
this.options.sharpenSigma = sigma;
// Control over flat areas
if (is.defined(flat)) {
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
this.options.sharpenFlat = flat;
} else {
throw new Error('Invalid sharpen level for flat areas (0.0 - 10000.0) ' + flat);
}
}
// Control over jagged areas
if (is.defined(jagged)) {
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
this.options.sharpenJagged = jagged;
} else {
throw new Error('Invalid sharpen level for jagged areas (0.0 - 10000.0) ' + jagged);
}
}
} else {
throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
}
return this;
}
/**
* Apply median filter.
* When used without parameters the default window is 3x3.
* @param {Number} [size=3] square mask size: size x size
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function median (size) {
if (!is.defined(size)) {
// No arguments: default to 3x3
this.options.medianSize = 3;
} else if (is.integer(size) && is.inRange(size, 1, 1000)) {
// Numeric argument: specific sigma
this.options.medianSize = size;
} else {
throw new Error('Invalid median size ' + size);
}
return this;
}
/**
* Blur the image.
* When used without parameters, performs a fast, mild blur of the output image.
* When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
* @param {Number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function blur (sigma) {
if (!is.defined(sigma)) {
// No arguments: default to mild blur
this.options.blurSigma = -1;
} else if (is.bool(sigma)) {
// Boolean argument: apply mild blur?
this.options.blurSigma = sigma ? -1 : 0;
} else if (is.number(sigma) && is.inRange(sigma, 0.3, 1000)) {
// Numeric argument: specific sigma
this.options.blurSigma = sigma;
} else {
throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
}
return this;
}
/**
* Merge alpha transparency channel, if any, with a background.
* @param {Object} [options]
* @param {String|Object} [options.background={r: 0, g: 0, b: 0}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black.
* @returns {Sharp}
*/
function flatten (options) {
this.options.flatten = is.bool(options) ? options : true;
if (is.object(options)) {
this._setColourOption('flattenBackground', options.background);
}
return this;
}
/**
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
* then increasing the encoding (brighten) post-resize at a factor of `gamma`.
* This can improve the perceived brightness of a resized image in non-linear colour spaces.
* JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
* when applying a gamma correction.
*
* Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
*
* @param {Number} [gamma=2.2] value between 1.0 and 3.0.
* @param {Number} [gammaOut] value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function gamma (gamma, gammaOut) {
if (!is.defined(gamma)) {
// Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2;
} else if (is.number(gamma) && is.inRange(gamma, 1, 3)) {
this.options.gamma = gamma;
} else {
throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
}
if (!is.defined(gammaOut)) {
// Default gamma correction for output is same as input
this.options.gammaOut = this.options.gamma;
} else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) {
this.options.gammaOut = gammaOut;
} else {
throw new Error('Invalid output gamma correction (1.0 to 3.0) ' + gammaOut);
}
return this;
}
/**
* Produce the "negative" of the image.
* @param {Boolean} [negate=true]
* @returns {Sharp}
*/
function negate (negate) {
this.options.negate = is.bool(negate) ? negate : true;
return this;
}
/**
* Enhance output image contrast by stretching its luminance to cover the full dynamic range.
* @param {Boolean} [normalise=true]
* @returns {Sharp}
*/
function normalise (normalise) {
this.options.normalise = is.bool(normalise) ? normalise : true;
return this;
}
/**
* Alternative spelling of normalise.
* @param {Boolean} [normalize=true]
* @returns {Sharp}
*/
function normalize (normalize) {
return this.normalise(normalize);
}
/**
* Convolve the image with the specified kernel.
*
* @example
* sharp(input)
* .convolve({
* width: 3,
* height: 3,
* kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1]
* })
* .raw()
* .toBuffer(function(err, data, info) {
* // data contains the raw pixel data representing the convolution
* // of the input image with the horizontal Sobel operator
* });
*
* @param {Object} kernel
* @param {Number} kernel.width - width of the kernel in pixels.
* @param {Number} kernel.height - width of the kernel in pixels.
* @param {Array<Number>} kernel.kernel - Array of length `width*height` containing the kernel values.
* @param {Number} [kernel.scale=sum] - the scale of the kernel in pixels.
* @param {Number} [kernel.offset=0] - the offset of the kernel in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function convolve (kernel) {
if (!is.object(kernel) || !Array.isArray(kernel.kernel) ||
!is.integer(kernel.width) || !is.integer(kernel.height) ||
!is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) ||
kernel.height * kernel.width !== kernel.kernel.length
) {
// must pass in a kernel
throw new Error('Invalid convolution kernel');
}
// Default scale is sum of kernel values
if (!is.integer(kernel.scale)) {
kernel.scale = kernel.kernel.reduce(function (a, b) {
return a + b;
}, 0);
}
// Clip scale to a minimum value of 1
if (kernel.scale < 1) {
kernel.scale = 1;
}
if (!is.integer(kernel.offset)) {
kernel.offset = 0;
}
this.options.convKernel = kernel;
return this;
}
/**
* Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
* @param {Number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
* @param {Object} [options]
* @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
* @param {Boolean} [options.grayscale=true] - alternative spelling for greyscale.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function threshold (threshold, options) {
if (!is.defined(threshold)) {
this.options.threshold = 128;
} else if (is.bool(threshold)) {
this.options.threshold = threshold ? 128 : 0;
} else if (is.integer(threshold) && is.inRange(threshold, 0, 255)) {
this.options.threshold = threshold;
} else {
throw new Error('Invalid threshold (0 to 255) ' + threshold);
}
if (!is.object(options) || options.greyscale === true || options.grayscale === true) {
this.options.thresholdGrayscale = true;
} else {
this.options.thresholdGrayscale = false;
}
return this;
}
/**
* Perform a bitwise boolean operation with operand image.
*
* This operation creates an output image where each pixel is the result of
* the selected bitwise boolean `operation` between the corresponding pixels of the input images.
*
* @param {Buffer|String} operand - Buffer containing image data or String containing the path to an image file.
* @param {String} operator - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @param {Object} [options]
* @param {Object} [options.raw] - describes operand when using raw pixel data.
* @param {Number} [options.raw.width]
* @param {Number} [options.raw.height]
* @param {Number} [options.raw.channels]
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function boolean (operand, operator, options) {
this.options.boolean = this._createInputDescriptor(operand, options);
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
this.options.booleanOp = operator;
} else {
throw new Error('Invalid boolean operator ' + operator);
}
return this;
}
/**
* Apply the linear formula a * input + b to the image (levels adjustment)
* @param {Number} [a=1.0] multiplier
* @param {Number} [b=0.0] offset
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function linear (a, b) {
if (!is.defined(a)) {
this.options.linearA = 1.0;
} else if (is.number(a)) {
this.options.linearA = a;
} else {
throw new Error('Invalid linear transform multiplier ' + a);
}
if (!is.defined(b)) {
this.options.linearB = 0.0;
} else if (is.number(b)) {
this.options.linearB = b;
} else {
throw new Error('Invalid linear transform offset ' + b);
}
return this;
}
/**
* Recomb the image with the specified matrix.
*
* @example
* sharp(input)
* .recomb([
* [0.3588, 0.7044, 0.1368],
* [0.2990, 0.5870, 0.1140],
* [0.2392, 0.4696, 0.0912],
* ])
* .raw()
* .toBuffer(function(err, data, info) {
* // data contains the raw pixel data after applying the recomb
* // With this example input, a sepia filter has been applied
* });
*
* @param {Array<Array<Number>>} 3x3 Recombination matrix
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function recomb (inputMatrix) {
if (!Array.isArray(inputMatrix) || inputMatrix.length !== 3 ||
inputMatrix[0].length !== 3 ||
inputMatrix[1].length !== 3 ||
inputMatrix[2].length !== 3
) {
// must pass in a kernel
throw new Error('Invalid Recomb Matrix');
}
this.options.recombMatrix = [
inputMatrix[0][0], inputMatrix[0][1], inputMatrix[0][2],
inputMatrix[1][0], inputMatrix[1][1], inputMatrix[1][2],
inputMatrix[2][0], inputMatrix[2][1], inputMatrix[2][2]
].map(Number);
return this;
}
/**
* Decorate the Sharp prototype with operation-related functions.
* @private
*/
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
rotate,
flip,
flop,
sharpen,
median,
blur,
flatten,
gamma,
negate,
normalise,
normalize,
convolve,
threshold,
boolean,
linear,
recomb
});
};

732
lib/output.js Normal file
View File

@@ -0,0 +1,732 @@
'use strict';
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* Write output image data to a file.
*
* If an explicit output format is not selected, it will be inferred from the extension,
* with JPEG, PNG, WebP, TIFF, DZI, and libvips' V format supported.
* Note that raw pixel data is only supported for buffer output.
*
* A `Promise` is returned when `callback` is not provided.
*
* @example
* sharp(input)
* .toFile('output.png', (err, info) => { ... });
*
* @example
* sharp(input)
* .toFile('output.png')
* .then(info => { ... })
* .catch(err => { ... });
*
* @param {String} fileOut - the path to write the image data to.
* @param {Function} [callback] - called on completion with two arguments `(err, info)`.
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
* @returns {Promise<Object>} - when no callback is provided
* @throws {Error} Invalid parameters
*/
function toFile (fileOut, callback) {
if (!fileOut || fileOut.length === 0) {
const errOutputInvalid = new Error('Missing output file path');
if (is.fn(callback)) {
callback(errOutputInvalid);
} else {
return Promise.reject(errOutputInvalid);
}
} else {
if (this.options.input.file === fileOut) {
const errOutputIsInput = new Error('Cannot use same file for input and output');
if (is.fn(callback)) {
callback(errOutputIsInput);
} else {
return Promise.reject(errOutputIsInput);
}
} else {
this.options.fileOut = fileOut;
return this._pipeline(callback);
}
}
return this;
}
/**
* Write output to a Buffer.
* JPEG, PNG, WebP, TIFF and RAW output are supported.
* By default, the format will match the input image, except GIF and SVG input which become PNG output.
*
* `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any.
* - `data` is the output image data.
* - `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
*
* A `Promise` is returned when `callback` is not provided.
*
* @example
* sharp(input)
* .toBuffer((err, data, info) => { ... });
*
* @example
* sharp(input)
* .toBuffer()
* .then(data => { ... })
* .catch(err => { ... });
*
* @example
* sharp(input)
* .toBuffer({ resolveWithObject: true })
* .then(({ data, info }) => { ... })
* .catch(err => { ... });
*
* @param {Object} [options]
* @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {Function} [callback]
* @returns {Promise<Buffer>} - when no callback is provided
*/
function toBuffer (options, callback) {
if (is.object(options)) {
if (is.bool(options.resolveWithObject)) {
this.options.resolveWithObject = options.resolveWithObject;
}
}
return this._pipeline(is.fn(options) ? options : callback);
}
/**
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
* The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
* This will also convert to and add a web-friendly sRGB ICC profile.
*
* @example
* sharp('input.jpg')
* .withMetadata()
* .toFile('output-with-metadata.jpg')
* .then(info => { ... });
*
* @param {Object} [withMetadata]
* @param {Number} [withMetadata.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withMetadata (withMetadata) {
this.options.withMetadata = is.bool(withMetadata) ? withMetadata : true;
if (is.object(withMetadata)) {
if (is.defined(withMetadata.orientation)) {
if (is.integer(withMetadata.orientation) && is.inRange(withMetadata.orientation, 1, 8)) {
this.options.withMetadataOrientation = withMetadata.orientation;
} else {
throw new Error('Invalid orientation (1 to 8) ' + withMetadata.orientation);
}
}
}
return this;
}
/**
* Use these JPEG options for output image.
*
* @example
* // Convert any input to very high quality JPEG output
* const data = await sharp(input)
* .jpeg({
* quality: 100,
* chromaSubsampling: '4:4:4'
* })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {String} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling when quality <= 90
* @param {Boolean} [options.trellisQuantisation=false] - apply trellis quantisation, requires mozjpeg
* @param {Boolean} [options.overshootDeringing=false] - apply overshoot deringing, requires mozjpeg
* @param {Boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive, requires mozjpeg
* @param {Boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
* @param {Boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
* @param {Boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
* @param {Number} [options.quantisationTable=0] - quantization table to use, integer 0-8, requires mozjpeg
* @param {Number} [options.quantizationTable=0] - alternative spelling of quantisationTable
* @param {Boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function jpeg (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.jpegQuality = options.quality;
} else {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
}
}
if (is.defined(options.progressive)) {
this._setBooleanOption('jpegProgressive', options.progressive);
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.jpegChromaSubsampling = options.chromaSubsampling;
} else {
throw new Error('Invalid chromaSubsampling (4:2:0, 4:4:4) ' + options.chromaSubsampling);
}
}
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
if (is.defined(trellisQuantisation)) {
this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
}
if (is.defined(options.overshootDeringing)) {
this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
}
const optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
if (is.defined(optimiseScans)) {
this._setBooleanOption('jpegOptimiseScans', optimiseScans);
if (optimiseScans) {
this.options.jpegProgressive = true;
}
}
const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
if (is.defined(optimiseCoding)) {
this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
}
const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
if (is.defined(quantisationTable)) {
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
this.options.jpegQuantisationTable = quantisationTable;
} else {
throw new Error('Invalid quantisation table (integer, 0-8) ' + quantisationTable);
}
}
}
return this._updateFormatOut('jpeg', options);
}
/**
* Use these PNG options for output image.
*
* PNG output is always full colour at 8 or 16 bits per pixel.
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .png()
* .toBuffer();
*
* @param {Object} [options]
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
* @param {Boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libimagequant
* @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libimagequant
* @param {Number} [options.colours=256] - maximum number of palette entries, requires libimagequant
* @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libimagequant
* @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libimagequant
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function png (options) {
if (is.object(options)) {
if (is.defined(options.progressive)) {
this._setBooleanOption('pngProgressive', options.progressive);
}
if (is.defined(options.compressionLevel)) {
if (is.integer(options.compressionLevel) && is.inRange(options.compressionLevel, 0, 9)) {
this.options.pngCompressionLevel = options.compressionLevel;
} else {
throw new Error('Invalid compressionLevel (integer, 0-9) ' + options.compressionLevel);
}
}
if (is.defined(options.adaptiveFiltering)) {
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
}
if (is.defined(options.palette)) {
this._setBooleanOption('pngPalette', options.palette);
if (this.options.pngPalette) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
this.options.pngQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
}
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
this.options.pngColours = colours;
} else {
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
}
}
if (is.defined(options.dither)) {
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
this.options.pngDither = options.dither;
} else {
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
}
}
}
}
}
return this._updateFormatOut('png', options);
}
/**
* Use these WebP options for output image.
*
* @example
* // Convert any input to lossless WebP output
* const data = await sharp(input)
* .webp({ lossless: true })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
* @param {Boolean} [options.lossless=false] - use lossless compression mode
* @param {Boolean} [options.nearLossless=false] - use near_lossless compression mode
* @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function webp (options) {
if (is.object(options) && is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.webpQuality = options.quality;
} else {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
}
}
if (is.object(options) && is.defined(options.alphaQuality)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
this.options.webpAlphaQuality = options.alphaQuality;
} else {
throw new Error('Invalid webp alpha quality (integer, 0-100) ' + options.alphaQuality);
}
}
if (is.object(options) && is.defined(options.lossless)) {
this._setBooleanOption('webpLossless', options.lossless);
}
if (is.object(options) && is.defined(options.nearLossless)) {
this._setBooleanOption('webpNearLossless', options.nearLossless);
}
return this._updateFormatOut('webp', options);
}
/**
* Use these TIFF options for output image.
*
* @example
* // Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
* sharp('input.svg')
* .tiff({
* compression: 'lzw',
* squash: true
* })
* .toFile('1-bpp-output.tiff')
* .then(info => { ... });
*
* @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
* @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {Boolean} [options.pyramid=false] - write an image pyramid
* @param {Boolean} [options.tile=false] - write a tiled tiff
* @param {Boolean} [options.tileWidth=256] - horizontal tile size
* @param {Boolean} [options.tileHeight=256] - vertical tile size
* @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
* @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function tiff (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.tiffQuality = options.quality;
} else {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
}
}
if (is.defined(options.squash)) {
if (is.bool(options.squash)) {
this.options.tiffSquash = options.squash;
} else {
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
}
}
// tiling
if (is.defined(options.tile)) {
if (is.bool(options.tile)) {
this.options.tiffTile = options.tile;
} else {
throw new Error('Invalid Value for tile ' + options.tile + ' Only Boolean values allowed for options.tile');
}
}
if (is.defined(options.tileWidth)) {
if (is.number(options.tileWidth) && options.tileWidth > 0) {
this.options.tiffTileWidth = options.tileWidth;
} else {
throw new Error('Invalid Value for tileWidth ' + options.tileWidth + ' Only positive numeric values allowed for options.tileWidth');
}
}
if (is.defined(options.tileHeight)) {
if (is.number(options.tileHeight) && options.tileHeight > 0) {
this.options.tiffTileHeight = options.tileHeight;
} else {
throw new Error('Invalid Value for tileHeight ' + options.tileHeight + ' Only positive numeric values allowed for options.tileHeight');
}
}
// pyramid
if (is.defined(options.pyramid)) {
if (is.bool(options.pyramid)) {
this.options.tiffPyramid = options.pyramid;
} else {
throw new Error('Invalid Value for pyramid ' + options.pyramid + ' Only Boolean values allowed for options.pyramid');
}
}
// resolution
if (is.defined(options.xres)) {
if (is.number(options.xres)) {
this.options.tiffXres = options.xres;
} else {
throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
}
}
if (is.defined(options.yres)) {
if (is.number(options.yres)) {
this.options.tiffYres = options.yres;
} else {
throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
}
}
// compression
if (is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
this.options.tiffCompression = options.compression;
} else {
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
throw new Error(message);
}
}
// predictor
if (is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
this.options.tiffPredictor = options.predictor;
} else {
const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
throw new Error(message);
}
}
}
return this._updateFormatOut('tiff', options);
}
/**
* Force output to be raw, uncompressed uint8 pixel data.
*
* @example
* // Extract raw RGB pixel data from JPEG input
* const { data, info } = await sharp('input.jpg')
* .raw()
* .toBuffer({ resolveWithObject: true });
*
* @returns {Sharp}
*/
function raw () {
return this._updateFormatOut('raw');
}
/**
* Force output to a given format.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .toFormat('png')
* .toBuffer();
*
* @param {(String|Object)} format - as a String or an Object with an 'id' attribute
* @param {Object} options - output options
* @returns {Sharp}
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
if (is.object(format) && is.string(format.id)) {
format = format.id;
}
if (format === 'jpg') format = 'jpeg';
if (!is.inArray(format, ['jpeg', 'png', 'webp', 'tiff', 'raw'])) {
throw new Error('Unsupported output format ' + format);
}
return this[format](options);
}
/**
* Use tile-based deep zoom (image pyramid) output.
* Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
* Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
*
* Warning: multiple sharp instances concurrently producing tile output can expose a possible race condition in some versions of libgsf.
*
* @example
* sharp('input.tiff')
* .png()
* .tile({
* size: 512
* })
* .toFile('output.dz', function(err, info) {
* // output.dzi is the Deep Zoom XML definition
* // output_files contains 512x512 tiles grouped by zoom level
* });
*
* @param {Object} [tile]
* @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192.
* @param {Number} [tile.overlap=0] tile overlap in pixels, a value between 0 and 8192.
* @param {Number} [tile.angle=0] tile angle of rotation, must be a multiple of 90.
* @param {String} [tile.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {String} [tile.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function tile (tile) {
if (is.object(tile)) {
// Size of square tiles, in pixels
if (is.defined(tile.size)) {
if (is.integer(tile.size) && is.inRange(tile.size, 1, 8192)) {
this.options.tileSize = tile.size;
} else {
throw new Error('Invalid tile size (1 to 8192) ' + tile.size);
}
}
// Overlap of tiles, in pixels
if (is.defined(tile.overlap)) {
if (is.integer(tile.overlap) && is.inRange(tile.overlap, 0, 8192)) {
if (tile.overlap > this.options.tileSize) {
throw new Error('Tile overlap ' + tile.overlap + ' cannot be larger than tile size ' + this.options.tileSize);
}
this.options.tileOverlap = tile.overlap;
} else {
throw new Error('Invalid tile overlap (0 to 8192) ' + tile.overlap);
}
}
// Container
if (is.defined(tile.container)) {
if (is.string(tile.container) && is.inArray(tile.container, ['fs', 'zip'])) {
this.options.tileContainer = tile.container;
} else {
throw new Error('Invalid tile container ' + tile.container);
}
}
// Layout
if (is.defined(tile.layout)) {
if (is.string(tile.layout) && is.inArray(tile.layout, ['dz', 'google', 'zoomify'])) {
this.options.tileLayout = tile.layout;
} else {
throw new Error('Invalid tile layout ' + tile.layout);
}
}
// Angle of rotation,
if (is.defined(tile.angle)) {
if (is.integer(tile.angle) && !(tile.angle % 90)) {
this.options.tileAngle = tile.angle;
} else {
throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + tile.angle);
}
}
// Depth of tiles
if (is.defined(tile.depth)) {
if (is.string(tile.depth) && is.inArray(tile.depth, ['onepixel', 'onetile', 'one'])) {
this.options.tileDepth = tile.depth;
} else {
throw new Error("Invalid tile depth '" + tile.depth + "', should be one of 'onepixel', 'onetile' or 'one'");
}
}
}
// Format
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
this.options.tileFormat = this.options.formatOut;
} else if (this.options.formatOut !== 'input') {
throw new Error('Invalid tile format ' + this.options.formatOut);
}
return this._updateFormatOut('dz');
}
/**
* Update the output format unless options.force is false,
* in which case revert to input format.
* @private
* @param {String} formatOut
* @param {Object} [options]
* @param {Boolean} [options.force=true] - force output format, otherwise attempt to use input format
* @returns {Sharp}
*/
function _updateFormatOut (formatOut, options) {
if (!(is.object(options) && options.force === false)) {
this.options.formatOut = formatOut;
}
return this;
}
/**
* Update a Boolean attribute of the this.options Object.
* @private
* @param {String} key
* @param {Boolean} val
* @throws {Error} Invalid key
*/
function _setBooleanOption (key, val) {
if (is.bool(val)) {
this.options[key] = val;
} else {
throw new Error('Invalid ' + key + ' (boolean) ' + val);
}
}
/**
* Called by a WriteableStream to notify us it is ready for data.
* @private
*/
function _read () {
if (!this.options.streamOut) {
this.options.streamOut = true;
this._pipeline();
}
}
/**
* Invoke the C++ image processing pipeline
* Supports callback, stream and promise variants
* @private
*/
function _pipeline (callback) {
const that = this;
if (typeof callback === 'function') {
// output=file/buffer
if (this._isStreamInput()) {
// output=file/buffer, input=stream
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, callback);
});
} else {
// output=file/buffer, input=file/buffer
sharp.pipeline(this.options, callback);
}
return this;
} else if (this.options.streamOut) {
// output=stream
if (this._isStreamInput()) {
// output=stream, input=stream
if (this.streamInFinished) {
this._flattenBufferIn();
sharp.pipeline(this.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
}
that.push(null);
});
} else {
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
}
that.push(null);
});
});
}
} else {
// output=stream, input=file/buffer
sharp.pipeline(this.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
}
that.push(null);
});
}
return this;
} else {
// output=promise
if (this._isStreamInput()) {
// output=promise, input=stream
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
reject(err);
} else {
if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
}
}
});
});
});
} else {
// output=promise, input=file/buffer
return new Promise(function (resolve, reject) {
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
reject(err);
} else {
if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
}
}
});
});
}
}
}
/**
* Decorate the Sharp prototype with output-related functions.
* @private
*/
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
// Public
toFile,
toBuffer,
withMetadata,
jpeg,
png,
webp,
tiff,
raw,
toFormat,
tile,
// Private
_updateFormatOut,
_setBooleanOption,
_read,
_pipeline
});
};

23
lib/platform.js Normal file
View File

@@ -0,0 +1,23 @@
'use strict';
const detectLibc = require('detect-libc');
const env = process.env;
module.exports = function () {
const arch = env.npm_config_arch || process.arch;
const platform = env.npm_config_platform || process.platform;
const libc = (platform === 'linux' && detectLibc.isNonGlibcLinux) ? detectLibc.family : '';
const platformId = [`${platform}${libc}`];
if (arch === 'arm') {
platformId.push(`armv${env.npm_config_arm_version || process.config.variables.arm_version || '6'}`);
} else if (arch === 'arm64') {
platformId.push(`arm64v${env.npm_config_arm_version || '8'}`);
} else {
platformId.push(arch);
}
return platformId.join('-');
};

397
lib/resize.js Normal file
View File

@@ -0,0 +1,397 @@
'use strict';
const is = require('./is');
/**
* Weighting to apply when using contain/cover fit.
* @member
* @private
*/
const gravity = {
center: 0,
centre: 0,
north: 1,
east: 2,
south: 3,
west: 4,
northeast: 5,
southeast: 6,
southwest: 7,
northwest: 8
};
/**
* Position to apply when using contain/cover fit.
* @member
* @private
*/
const position = {
top: 1,
right: 2,
bottom: 3,
left: 4,
'right top': 5,
'right bottom': 6,
'left bottom': 7,
'left top': 8
};
/**
* Strategies for automagic cover behaviour.
* @member
* @private
*/
const strategy = {
entropy: 16,
attention: 17
};
/**
* Reduction kernels.
* @member
* @private
*/
const kernel = {
nearest: 'nearest',
cubic: 'cubic',
mitchell: 'mitchell',
lanczos2: 'lanczos2',
lanczos3: 'lanczos3'
};
/**
* Methods by which an image can be resized to fit the provided dimensions.
* @member
* @private
*/
const fit = {
contain: 'contain',
cover: 'cover',
fill: 'fill',
inside: 'inside',
outside: 'outside'
};
/**
* Map external fit property to internal canvas property.
* @member
* @private
*/
const mapFitToCanvas = {
contain: 'embed',
cover: 'crop',
fill: 'ignore_aspect',
inside: 'max',
outside: 'min'
};
/**
* Resize image to `width`, `height` or `width x height`.
*
* When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
* - `cover`: Crop to cover both provided dimensions (the default).
* - `contain`: Embed within both provided dimensions.
* - `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
* - `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
* - `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
*
* When using a `fit` of `cover` or `contain`, the default **position** is `centre`. Other options are:
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
* - `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
* - `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
* Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
*
* The experimental strategy-based approach resizes so one dimension is at its target length
* then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
* - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
* - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
*
* Possible interpolation kernels are:
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
* - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
*
* @example
* sharp(input)
* .resize({ width: 100 })
* .toBuffer()
* .then(data => {
* // 100 pixels wide, auto-scaled height
* });
*
* @example
* sharp(input)
* .resize({ height: 100 })
* .toBuffer()
* .then(data => {
* // 100 pixels high, auto-scaled width
* });
*
* @example
* sharp(input)
* .resize(200, 300, {
* kernel: sharp.kernel.nearest,
* fit: 'contain',
* position: 'right top',
* background: { r: 255, g: 255, b: 255, alpha: 0.5 }
* })
* .toFile('output.png')
* .then(() => {
* // output.png is a 200 pixels wide and 300 pixels high image
* // containing a nearest-neighbour scaled version
* // contained within the north-east corner of a semi-transparent white canvas
* });
*
* @example
* const transformer = sharp()
* .resize({
* width: 200,
* height: 200,
* fit: sharp.fit.cover,
* position: sharp.strategy.entropy
* });
* // Read image data from readableStream
* // Write 200px square auto-cropped image data to writableStream
* readableStream
* .pipe(transformer)
* .pipe(writableStream);
*
* @example
* sharp(input)
* .resize(200, 200, {
* fit: sharp.fit.inside,
* withoutEnlargement: true
* })
* .toFormat('jpeg')
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains JPEG image data
* // no wider and no higher than 200 pixels
* // and no larger than the input image
* });
*
* @param {Number} [width] - pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* @param {Number} [height] - pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {Object} [options]
* @param {String} [options.width] - alternative means of specifying `width`. If both are present this take priority.
* @param {String} [options.height] - alternative means of specifying `height`. If both are present this take priority.
* @param {String} [options.fit='cover'] - how the image should be resized to fit both provided dimensions, one of `cover`, `contain`, `fill`, `inside` or `outside`.
* @param {String} [options.position='centre'] - position, gravity or strategy to use when `fit` is `cover` or `contain`.
* @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when using a `fit` of `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
* @param {Boolean} [options.withoutEnlargement=false] - do not enlarge if the width *or* height are already less than the specified dimensions, equivalent to GraphicsMagick's `>` geometry option.
* @param {Boolean} [options.fastShrinkOnLoad=true] - take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern on some images.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function resize (width, height, options) {
if (is.defined(width)) {
if (is.object(width) && !is.defined(options)) {
options = width;
} else if (is.integer(width) && width > 0) {
this.options.width = width;
} else {
throw is.invalidParameterError('width', 'positive integer', width);
}
} else {
this.options.width = -1;
}
if (is.defined(height)) {
if (is.integer(height) && height > 0) {
this.options.height = height;
} else {
throw is.invalidParameterError('height', 'positive integer', height);
}
} else {
this.options.height = -1;
}
if (is.object(options)) {
// Width
if (is.integer(options.width) && options.width > 0) {
this.options.width = options.width;
}
// Height
if (is.integer(options.height) && options.height > 0) {
this.options.height = options.height;
}
// Fit
if (is.defined(options.fit)) {
const canvas = mapFitToCanvas[options.fit];
if (is.string(canvas)) {
this.options.canvas = canvas;
} else {
throw is.invalidParameterError('fit', 'valid fit', options.fit);
}
}
// Position
if (is.defined(options.position)) {
const pos = is.integer(options.position)
? options.position
: strategy[options.position] || position[options.position] || gravity[options.position];
if (is.integer(pos) && (is.inRange(pos, 0, 8) || is.inRange(pos, 16, 17))) {
this.options.position = pos;
} else {
throw is.invalidParameterError('position', 'valid position/gravity/strategy', options.position);
}
}
// Background
if (is.defined(options.background)) {
this._setColourOption('resizeBackground', options.background);
}
// Kernel
if (is.defined(options.kernel)) {
if (is.string(kernel[options.kernel])) {
this.options.kernel = kernel[options.kernel];
} else {
throw is.invalidParameterError('kernel', 'valid kernel name', options.kernel);
}
}
// Without enlargement
if (is.defined(options.withoutEnlargement)) {
this._setBooleanOption('withoutEnlargement', options.withoutEnlargement);
}
// Shrink on load
if (is.defined(options.fastShrinkOnLoad)) {
this._setBooleanOption('fastShrinkOnLoad', options.fastShrinkOnLoad);
}
}
return this;
}
/**
* Extends/pads the edges of the image with the provided background colour.
* This operation will always occur after resizing and extraction, if any.
*
* @example
* // Resize to 140 pixels wide, then add 10 transparent pixels
* // to the top, left and right edges and 20 to the bottom edge
* sharp(input)
* .resize(140)
* .extend({
* top: 10,
* bottom: 20,
* left: 10,
* right: 10,
* background: { r: 0, g: 0, b: 0, alpha: 0 }
* })
* ...
*
* @param {(Number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
* @param {Number} [extend.top]
* @param {Number} [extend.left]
* @param {Number} [extend.bottom]
* @param {Number} [extend.right]
* @param {String|Object} [extend.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extend (extend) {
if (is.integer(extend) && extend > 0) {
this.options.extendTop = extend;
this.options.extendBottom = extend;
this.options.extendLeft = extend;
this.options.extendRight = extend;
} else if (
is.object(extend) &&
is.integer(extend.top) && extend.top >= 0 &&
is.integer(extend.bottom) && extend.bottom >= 0 &&
is.integer(extend.left) && extend.left >= 0 &&
is.integer(extend.right) && extend.right >= 0
) {
this.options.extendTop = extend.top;
this.options.extendBottom = extend.bottom;
this.options.extendLeft = extend.left;
this.options.extendRight = extend.right;
this._setColourOption('extendBackground', extend.background);
} else {
throw new Error('Invalid edge extension ' + extend);
}
return this;
}
/**
* Extract a region of the image.
*
* - Use `extract` before `resize` for pre-resize extraction.
* - Use `extract` after `resize` for post-resize extraction.
* - Use `extract` before and after for both.
*
* @example
* sharp(input)
* .extract({ left: left, top: top, width: width, height: height })
* .toFile(output, function(err) {
* // Extract a region of the input image, saving in the same format.
* });
* @example
* sharp(input)
* .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
* .resize(width, height)
* .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
* .toFile(output, function(err) {
* // Extract a region, resize, then extract from the resized image
* });
*
* @param {Object} options
* @param {Number} options.left - zero-indexed offset from left edge
* @param {Number} options.top - zero-indexed offset from top edge
* @param {Number} options.width - dimension of extracted image
* @param {Number} options.height - dimension of extracted image
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extract (options) {
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
['left', 'top', 'width', 'height'].forEach(function (name) {
const value = options[name];
if (is.integer(value) && value >= 0) {
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else {
throw new Error('Non-integer value for ' + name + ' of ' + value);
}
}, this);
// Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
this.options.rotateBeforePreExtract = true;
}
return this;
}
/**
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
* @param {Number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function trim (threshold) {
if (!is.defined(threshold)) {
this.options.trimThreshold = 10;
} else if (is.number(threshold) && threshold > 0) {
this.options.trimThreshold = threshold;
} else {
throw is.invalidParameterError('threshold', 'number greater than zero', threshold);
}
return this;
}
/**
* Decorate the Sharp prototype with resize-related functions.
* @private
*/
module.exports = function (Sharp) {
Object.assign(Sharp.prototype, {
resize,
extend,
extract,
trim
});
// Class attributes
Sharp.gravity = gravity;
Sharp.strategy = strategy;
Sharp.kernel = kernel;
Sharp.fit = fit;
Sharp.position = position;
};

113
lib/utility.js Normal file
View File

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

View File

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

View File

@@ -1,7 +1,9 @@
{
"name": "sharp",
"version": "0.14.1",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.22.0",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
"Jonathan Ong <jonathanrichardong@gmail.com>",
@@ -22,18 +24,54 @@
"John Tobin <john@limelightmobileinc.com>",
"Kenton Gray <kentongray@gmail.com>",
"Felix Bünemann <Felix.Buenemann@gmail.com>",
"Samy Al Zahrani <samyalzahrany@gmail.com>"
"Samy Al Zahrani <samyalzahrany@gmail.com>",
"Chintan Thakkar <lemnisk8@gmail.com>",
"F. Orlando Galashan <frulo@gmx.de>",
"Kleis Auke Wolthuizen <info@kleisauke.nl>",
"Matt Hirsch <mhirsch@media.mit.edu>",
"Matthias Thoemmes <thoemmes@gmail.com>",
"Patrick Paskaris <patrick@paskaris.gr>",
"Jérémy Lal <kapouer@melix.org>",
"Rahul Nanwani <r.nanwani@gmail.com>",
"Alice Monday <alice0meta@gmail.com>",
"Kristo Jorgenson <kristo.jorgenson@gmail.com>",
"YvesBos <yves_bos@outlook.com>",
"Guy Maliar <guy@tailorbrands.com>",
"Nicolas Coden <nicolas@ncoden.fr>",
"Matt Parrish <matt.r.parrish@gmail.com>",
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
"Matthew McEachen <matthew+github@mceachen.org>",
"Jarda Kotěšovec <jarda.kotesovec@gmail.com>",
"Kenric D'Souza <kenric.dsouza@gmail.com>",
"Oleh Aleinyk <oleg.aleynik@gmail.com>",
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
"Andrea Bianco <andrea.bianco@unibas.ch>",
"Rik Heywood <rik@rik.org>",
"Thomas Parisot <hi@oncletom.io>",
"Nathan Graves <nathanrgraves+github@gmail.com>",
"Tom Lokhorst <tom@lokhorst.eu>",
"Espen Hovlandsdal <espen@hovlandsdal.com>",
"Sylvain Dumont <sylvain.dumont35@gmail.com>",
"Alun Davies <alun.owain.davies@googlemail.com>",
"Aidan Hoolachan <ajhoolachan21@gmail.com>",
"Axel Eirola <axel.eirola@iki.fi>",
"Freezy <freezy@xbmc.org>",
"Daiz <taneli.vatanen@gmail.com>",
"Julian Aubourg <j@ubourg.net>",
"Keith Belovay <keith@picthrive.com>",
"Michael B. Klein <mbklein@gmail.com>"
],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
"scripts": {
"clean": "rm -rf node_modules/ build/ include/ lib/ coverage/ test/fixtures/output.*",
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=30000 ./test/unit/*.js",
"test-win": "node ./node_modules/mocha/bin/mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
"test": "semistandard && cc && npm run test-unit && npm run test-licensing && prebuild-ci",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
"test-coverage": "./test/coverage/report.sh",
"test-leak": "./test/leak/leak.sh",
"test-packaging": "./packaging/test.sh",
"test-clean": "rm -rf coverage/ test/fixtures/output.* && npm install && npm test"
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
},
"main": "index.js",
"main": "lib/index.js",
"repository": {
"type": "git",
"url": "git://github.com/lovell/sharp"
@@ -43,39 +81,65 @@
"png",
"webp",
"tiff",
"gif",
"svg",
"dzi",
"image",
"resize",
"thumbnail",
"crop",
"embed",
"libvips",
"vips"
],
"dependencies": {
"bluebird": "^3.3.5",
"color": "^0.11.1",
"nan": "^2.2.1",
"semver": "^5.1.0",
"request": "^2.71.0",
"tar": "^2.2.1"
"bindings": "^1.5.0",
"color": "^3.1.0",
"detect-libc": "^1.0.3",
"fs-copy-file-sync": "^1.1.1",
"nan": "^2.13.1",
"npmlog": "^4.1.2",
"prebuild-install": "^5.2.5",
"semver": "^5.6.0",
"simple-get": "^3.0.3",
"tar": "^4.4.8",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
"async": "^1.5.2",
"bufferutil": "^1.2.1",
"coveralls": "^2.11.9",
"exif-reader": "^1.0.0",
"icc": "^0.0.2",
"istanbul": "^0.4.3",
"mocha": "^2.4.5",
"mocha-jshint": "^2.3.1",
"node-cpplint": "^0.4.0",
"rimraf": "^2.5.2",
"unzip": "^0.1.11"
"async": "^2.6.2",
"cc": "^1.0.2",
"decompress-zip": "^0.3.2",
"documentation": "^9.3.1",
"exif-reader": "^1.0.2",
"icc": "^1.0.0",
"license-checker": "^25.0.1",
"mocha": "^6.0.2",
"mock-fs": "^4.8.0",
"nyc": "^13.3.0",
"prebuild": "8.1.0",
"prebuild-ci": "^2.3.0",
"rimraf": "^2.6.3",
"semistandard": "^13.0.1"
},
"license": "Apache-2.0",
"config": {
"libvips": "8.2.3"
"libvips": "8.7.4"
},
"engines": {
"node": ">=0.10"
"node": ">=6"
},
"semistandard": {
"env": [
"mocha"
]
},
"cc": {
"linelength": "120",
"filter": [
"build/c++11",
"build/include",
"runtime/indentation_namespace",
"runtime/references"
]
}
}

View File

@@ -1,34 +0,0 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 IP"
echo "Build libvips for ARM using Docker, where IP is"
echo "the address of a Raspberry Pi running HypriotOS"
exit 1
fi
IP="$1"
echo "Verifying connectivity to $IP"
if ! ping -c 1 $IP; then
echo "Could not connect to $IP"
exit 1
fi
if ! type sshpass >/dev/null; then
echo "Please install sshpass"
exit 1
fi
export SSHPASS=hypriot
echo "Copying arm/Dockerfile and arm/build.sh to device"
sshpass -e scp -o PreferredAuthentications=password -r arm root@${IP}:/root
echo "Building Raspbian-based container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker build -t vips-dev-arm arm"
echo "Running arm/build.sh within container"
sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker run -i -t --rm -v \${PWD}/arm:/arm vips-dev-arm sh -c 'cd /arm && ./build.sh' | tee arm/build.log"
echo "Copying resultant tar.gz file from device"
sshpass -e scp -o PreferredAuthentications=password root@${IP}:/root/arm/*.tar.gz .

View File

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

View File

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

View File

@@ -1,137 +0,0 @@
#!/bin/sh
# To be run inside a Raspbian container
# Working directories
DEPS=/deps
TARGET=/target
mkdir ${DEPS}
mkdir ${TARGET}
# Common build paths and flags
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig"
export PATH="${PATH}:${TARGET}/bin"
export CPPFLAGS="-I${TARGET}/include"
export LDFLAGS="-L${TARGET}/lib"
export CFLAGS="-O3"
export CXXFLAGS="-O3"
# Dependency version numbers
VERSION_ZLIB=1.2.8
VERSION_FFI=3.2.1
VERSION_GLIB=2.47.6
VERSION_XML2=2.9.3
VERSION_GSF=1.14.34
VERSION_EXIF=0.6.21
VERSION_LCMS2=2.7
VERSION_GM=1.3.23
VERSION_JPEG=1.4.2
VERSION_PNG16=1.6.21
VERSION_WEBP=0.5.0
VERSION_TIFF=4.0.6
VERSION_ORC=0.4.25
VERSION_VIPS=8.2.3
mkdir ${DEPS}/zlib
curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1
cd ${DEPS}/zlib
./configure --prefix=${TARGET} && make install
rm ${TARGET}/lib/libz.a
mkdir ${DEPS}/ffi
curl -Ls ftp://sourceware.org/pub/libffi/libffi-${VERSION_FFI}.tar.gz | tar xzC ${DEPS}/ffi --strip-components=1
cd ${DEPS}/ffi
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip
mkdir ${DEPS}/glib
curl -Ls https://download.gnome.org/sources/glib/2.47/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
cd ${DEPS}/glib
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal && make install-strip
mkdir ${DEPS}/xml2
curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1
cd ${DEPS}/xml2
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip
mkdir ${DEPS}/gsf
curl -Ls https://download.gnome.org/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1
cd ${DEPS}/gsf
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/exif
curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1
cd ${DEPS}/exif
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/lcms2
curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
cd ${DEPS}/lcms2
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/gm
curl -Ls http://heanet.dl.sourceforge.net/project/graphicsmagick/graphicsmagick/${VERSION_GM}/GraphicsMagick-${VERSION_GM}.tar.xz | tar xJC ${DEPS}/gm --strip-components=1
cd ${DEPS}/gm
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip
mkdir ${DEPS}/jpeg
curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1
cd ${DEPS}/jpeg
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip
mkdir ${DEPS}/png16
curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1
cd ${DEPS}/png16
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/webp
curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
cd ${DEPS}/webp
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/tiff
curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
cd ${DEPS}/tiff
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
rm ${TARGET}/lib/libtiffxx*
mkdir ${DEPS}/orc
curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1
cd ${DEPS}/orc
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip
mkdir ${DEPS}/vips
curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
cd ${DEPS}/vips
./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw --with-magickpackage=GraphicsMagick \
--with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \
--with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \
&& make install-strip
# Remove the old C++ bindings
cd ${TARGET}/include
rm -rf vips/vipsc++.h vips/vipscpp.h
cd ${TARGET}/lib
rm -rf pkgconfig .libs *.la libvipsCC*
# Create JSON file of version numbers
cd ${TARGET}
echo "{\n\
\"exif\": \"${VERSION_EXIF}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\
\"glib\": \"${VERSION_GLIB}\",\n\
\"gsf\": \"${VERSION_GSF}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\
\"gm\": \"${VERSION_GM}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\
\"png\": \"${VERSION_PNG16}\",\n\
\"tiff\": \"${VERSION_TIFF}\",\n\
\"vips\": \"${VERSION_VIPS}\"\n\
\"webp\": \"${VERSION_WEBP}\",\n\
\"xml\": \"${VERSION_XML2}\",\n\
\"zlib\": \"${VERSION_ZLIB}\",\n\
}" >lib/versions.json
# Create .tar.gz
GZIP=-9 tar czf /arm/libvips-${VERSION_VIPS}-arm.tar.gz include lib

View File

@@ -1,30 +0,0 @@
#!/bin/sh
VERSION_VIPS=8.2.3
# Is docker available?
if ! type docker >/dev/null; then
echo "Please install docker"
exit 1
fi
# TODO: docker v1.9.0 will allow build-time args - https://github.com/docker/docker/pull/15182
# Windows
docker build -t vips-dev-win win
WIN_CONTAINER_ID=$(docker run -d vips-dev-win)
docker cp "${WIN_CONTAINER_ID}:/libvips-${VERSION_VIPS}-win.tar.gz" .
docker rm "${WIN_CONTAINER_ID}"
# Linux
docker build -t vips-dev-lin lin
LIN_CONTAINER_ID=$(docker run -d vips-dev-lin)
docker cp "${LIN_CONTAINER_ID}:/libvips-${VERSION_VIPS}-lin.tar.gz" .
docker rm "${LIN_CONTAINER_ID}"
# Checksums
sha256sum *.tar.gz

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
#!/bin/sh
# Install build dependencies
apk add --update make gcc g++ python nodejs
# Install libvips with build headers and dependencies
apk add libvips-dev --update --repository https://s3.amazonaws.com/wjordan-apk --allow-untrusted

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,322 +0,0 @@
#!/bin/sh
# This script is no longer required on most
# 64-bit Linux systems when using sharp v0.12.0+
# See http://sharp.dimens.io/page/install#linux
# If you really need this script, it will attempt to
# globally install libvips if not already available.
# Supports:
# * Debian Linux
# * Debian 7, 8
# * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10
# * Mint 13, 17
# * Elementary 0.3
# * Red Hat Linux
# * RHEL/Centos/Scientific 6, 7
# * Fedora 21, 22, 23
# * Amazon Linux 2015.03, 2015.09
# * OpenSuse 13
vips_version_minimum=8.2.3
vips_version_latest_major_minor=8.2
vips_version_latest_patch=3
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 $(PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig pkg-config --modversion vips)"
}
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
# Deprecation warning
if [ "$(arch)" == "x86_64" ]; then
echo "This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
fi
# OS-specific installations of libopenslide follows
# Either openslide does not exist, or vips is installed without openslide support
if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then
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|trusty|utopic|vivid|wily|xenial|qiana|rebecca|rafaela|freya)
# Debian 8, Ubuntu 14.04+, 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 tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
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 tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel
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"*)
# Fedora 21, 22, 23
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
install_libvips_from_source "--prefix=/usr"
;;
*)
# 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

View File

@@ -1,32 +1,93 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib>
#include <string>
#include <string.h>
#include <vector>
#include <queue>
#include <mutex>
#include <node.h>
#include <node_buffer.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 2))
#error libvips version 8.2.0+ required - see sharp.dimens.io/page/install
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
#error GCC version 4.6+ is required for C++11 features - see sharp.dimens.io/page/install#prerequisites
#endif
#if (defined(__clang__) && defined(__has_feature))
#if (!__has_feature(cxx_range_for))
#error clang version 3.0+ is required for C++11 features - see sharp.dimens.io/page/install#prerequisites
#endif
#endif
#define EXIF_IFD0_ORIENTATION "exif-ifd0-Orientation"
using vips::VImage;
namespace sharp {
// Convenience methods to access the attributes of a v8::Object
bool HasAttr(v8::Handle<v8::Object> obj, std::string attr) {
return Nan::Has(obj, Nan::New(attr).ToLocalChecked()).FromJust();
}
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr) {
return *Nan::Utf8String(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked());
}
std::vector<double> AttrAsRgba(v8::Handle<v8::Object> obj, std::string attr) {
v8::Local<v8::Object> background = AttrAs<v8::Object>(obj, attr);
std::vector<double> rgba(4);
for (unsigned int i = 0; i < 4; i++) {
rgba[i] = AttrTo<double>(background, i);
}
return rgba;
}
// Create an InputDescriptor instance from a v8::Object describing an input image
InputDescriptor* CreateInputDescriptor(
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist
) {
Nan::HandleScope();
InputDescriptor *descriptor = new InputDescriptor;
if (HasAttr(input, "file")) {
descriptor->file = AttrAsStr(input, "file");
} else if (HasAttr(input, "buffer")) {
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
descriptor->bufferLength = node::Buffer::Length(buffer);
descriptor->buffer = node::Buffer::Data(buffer);
buffersToPersist.push_back(buffer);
}
descriptor->failOnError = AttrTo<bool>(input, "failOnError");
// Density for vector-based input
if (HasAttr(input, "density")) {
descriptor->density = AttrTo<double>(input, "density");
}
// Raw pixel input
if (HasAttr(input, "rawChannels")) {
descriptor->rawChannels = AttrTo<uint32_t>(input, "rawChannels");
descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth");
descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight");
}
// Multi-page input (GIF, TIFF, PDF)
if (HasAttr(input, "pages")) {
descriptor->pages = AttrTo<int32_t>(input, "pages");
}
if (HasAttr(input, "page")) {
descriptor->page = AttrTo<uint32_t>(input, "page");
}
// Create new image
if (HasAttr(input, "createChannels")) {
descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels");
descriptor->createWidth = AttrTo<uint32_t>(input, "createWidth");
descriptor->createHeight = AttrTo<uint32_t>(input, "createHeight");
descriptor->createBackground = AttrAsRgba(input, "createBackground");
}
return descriptor;
}
// How many tasks are in the queue?
volatile int counterQueue = 0;
@@ -55,6 +116,9 @@ namespace sharp {
bool IsDzZip(std::string const &str) {
return EndsWith(str, ".zip") || EndsWith(str, ".ZIP") || EndsWith(str, ".szi") || EndsWith(str, ".SZI");
}
bool IsV(std::string const &str) {
return EndsWith(str, ".v") || EndsWith(str, ".V") || EndsWith(str, ".vips") || EndsWith(str, ".VIPS");
}
/*
Provide a string identifier for the given image type.
@@ -66,12 +130,17 @@ namespace sharp {
case ImageType::PNG: id = "png"; break;
case ImageType::WEBP: id = "webp"; break;
case ImageType::TIFF: id = "tiff"; break;
case ImageType::GIF: id = "gif"; break;
case ImageType::SVG: id = "svg"; break;
case ImageType::PDF: id = "pdf"; break;
case ImageType::MAGICK: id = "magick"; break;
case ImageType::OPENSLIDE: id = "openslide"; break;
case ImageType::PPM: id = "ppm"; break;
case ImageType::FITS: id = "fits"; break;
case ImageType::VIPS: id = "v"; break;
case ImageType::RAW: id = "raw"; break;
case ImageType::UNKNOWN: id = "unknown"; break;
case ImageType::MISSING: id = "missing"; break;
}
return id;
}
@@ -92,6 +161,12 @@ namespace sharp {
imageType = ImageType::WEBP;
} else if (EndsWith(loader, "TiffBuffer")) {
imageType = ImageType::TIFF;
} else if (EndsWith(loader, "GifBuffer")) {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgBuffer")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "PdfBuffer")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "MagickBuffer")) {
imageType = ImageType::MAGICK;
}
@@ -117,17 +192,135 @@ namespace sharp {
imageType = ImageType::OPENSLIDE;
} else if (EndsWith(loader, "TiffFile")) {
imageType = ImageType::TIFF;
} else if (EndsWith(loader, "GifFile")) {
imageType = ImageType::GIF;
} else if (EndsWith(loader, "SvgFile")) {
imageType = ImageType::SVG;
} else if (EndsWith(loader, "PdfFile")) {
imageType = ImageType::PDF;
} else if (EndsWith(loader, "Ppm")) {
imageType = ImageType::PPM;
} else if (EndsWith(loader, "Fits")) {
imageType = ImageType::FITS;
} else if (EndsWith(loader, "Vips")) {
imageType = ImageType::VIPS;
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
imageType = ImageType::MAGICK;
}
} else {
if (EndsWith(vips::VError().what(), " not found\n")) {
imageType = ImageType::MISSING;
}
}
return imageType;
}
/*
Does this image type support multiple pages?
*/
bool ImageTypeSupportsPage(ImageType imageType) {
return
imageType == ImageType::GIF ||
imageType == ImageType::TIFF ||
imageType == ImageType::PDF;
}
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
VImage image;
ImageType imageType;
if (descriptor->buffer != nullptr) {
if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, VIPS_FORMAT_UCHAR);
if (descriptor->rawChannels < 3) {
image.get_image()->Type = VIPS_INTERPRETATION_B_W;
} else {
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
}
imageType = ImageType::RAW;
} else {
// Compressed data
imageType = DetermineImageType(descriptor->buffer, descriptor->bufferLength);
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", accessMethod)
->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", descriptor->density);
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
if (ImageTypeSupportsPage(imageType)) {
option->set("n", descriptor->pages);
option->set("page", descriptor->page);
}
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
}
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
}
} else {
throw vips::VError("Input buffer contains unsupported image format");
}
}
} else {
if (descriptor->createChannels > 0) {
// Create new image
std::vector<double> background = {
descriptor->createBackground[0],
descriptor->createBackground[1],
descriptor->createBackground[2]
};
if (descriptor->createChannels == 4) {
background.push_back(descriptor->createBackground[3]);
}
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
imageType = ImageType::RAW;
} else {
// From filesystem
imageType = DetermineImageType(descriptor->file.data());
if (imageType == ImageType::MISSING) {
throw vips::VError("Input file is missing");
}
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", accessMethod)
->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", descriptor->density);
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
if (ImageTypeSupportsPage(imageType)) {
option->set("n", descriptor->pages);
option->set("page", descriptor->page);
}
image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
}
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
}
} else {
throw vips::VError("Input file contains unsupported image format");
}
}
}
return std::make_tuple(image, imageType);
}
/*
Does this image have an embedded profile?
*/
@@ -145,8 +338,7 @@ namespace sharp {
return (
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK)
);
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK));
}
/*
@@ -154,11 +346,8 @@ namespace sharp {
*/
int ExifOrientation(VImage image) {
int orientation = 0;
if (image.get_typeof(EXIF_IFD0_ORIENTATION) != 0) {
char const *exif = image.get_string(EXIF_IFD0_ORIENTATION);
if (exif != nullptr) {
orientation = atoi(&exif[0]);
}
if (image.get_typeof(VIPS_META_ORIENTATION) != 0) {
orientation = image.get_int(VIPS_META_ORIENTATION);
}
return orientation;
}
@@ -167,16 +356,14 @@ namespace sharp {
Set EXIF Orientation of image.
*/
void SetExifOrientation(VImage image, int const orientation) {
char exif[3];
g_snprintf(exif, sizeof(exif), "%d", orientation);
image.set(EXIF_IFD0_ORIENTATION, exif);
image.set(VIPS_META_ORIENTATION, orientation);
}
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image) {
SetExifOrientation(image, 0);
vips_image_remove(image.get_image(), VIPS_META_ORIENTATION);
}
/*
@@ -196,13 +383,28 @@ namespace sharp {
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const int density) {
const double pixelsPerMm = static_cast<double>(density) / 25.4;
void SetDensity(VImage image, const double density) {
const double pixelsPerMm = density / 25.4;
image.set("Xres", pixelsPerMm);
image.set("Yres", pixelsPerMm);
image.set(VIPS_META_RESOLUTION_UNIT, "in");
}
/*
Check the proposed format supports the current dimensions.
*/
void AssertImageTypeDimensions(VImage image, ImageType const imageType) {
if (imageType == ImageType::JPEG) {
if (image.width() > 65535 || image.height() > 65535) {
throw vips::VError("Processed image is too large for the JPEG format");
}
} else if (imageType == ImageType::WEBP) {
if (image.width() > 16383 || image.height() > 16383) {
throw vips::VError("Processed image is too large for the WebP format");
}
}
}
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
@@ -212,9 +414,91 @@ namespace sharp {
}
}
/*
Temporary buffer of warnings
*/
std::queue<std::string> vipsWarnings;
std::mutex vipsWarningsMutex;
/*
Called with warnings from the glib-registered "VIPS" domain
*/
void VipsWarningCallback(char const* log_domain, GLogLevelFlags log_level, char const* message, void* ignore) {
std::lock_guard<std::mutex> lock(vipsWarningsMutex);
vipsWarnings.emplace(message);
}
/*
Pop the oldest warning message from the queue
*/
std::string VipsWarningPop() {
std::string warning;
std::lock_guard<std::mutex> lock(vipsWarningsMutex);
if (!vipsWarnings.empty()) {
warning = vipsWarnings.front();
vipsWarnings.pop();
}
return warning;
}
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity.
within the input image, applying the given gravity during an embed.
@Azurebyte: We are basically swapping the inWidth and outWidth, inHeight and outHeight from the CalculateCrop function.
*/
std::tuple<int, int> CalculateEmbedPosition(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const gravity) {
int left = 0;
int top = 0;
switch (gravity) {
case 1:
// North
left = (outWidth - inWidth) / 2;
break;
case 2:
// East
left = outWidth - inWidth;
top = (outHeight - inHeight) / 2;
break;
case 3:
// South
left = (outWidth - inWidth) / 2;
top = outHeight - inHeight;
break;
case 4:
// West
top = (outHeight - inHeight) / 2;
break;
case 5:
// Northeast
left = outWidth - inWidth;
break;
case 6:
// Southeast
left = outWidth - inWidth;
top = outHeight - inHeight;
break;
case 7:
// Southwest
top = outHeight - inHeight;
break;
case 8:
// Northwest
// Which is the default is 0,0 so we do not assign anything here.
break;
default:
// Centre
left = (outWidth - inWidth) / 2;
top = (outHeight - inHeight) / 2;
}
return std::make_tuple(left, top);
}
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity during a crop.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const gravity) {
@@ -248,9 +532,11 @@ namespace sharp {
// Southeast
left = inWidth - outWidth;
top = inHeight - outHeight;
break;
case 7:
// Southwest
top = inHeight - outHeight;
break;
case 8:
// Northwest
break;
@@ -262,4 +548,122 @@ namespace sharp {
return std::make_tuple(left, top);
}
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given x and y offsets.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const x, int const y) {
// default values
int left = 0;
int top = 0;
// assign only if valid
if (x >= 0 && x < (inWidth - outWidth)) {
left = x;
} else if (x >= (inWidth - outWidth)) {
left = inWidth - outWidth;
}
if (y >= 0 && y < (inHeight - outHeight)) {
top = y;
} else if (y >= (inHeight - outHeight)) {
top = inHeight - outHeight;
}
// the resulting left and top could have been outside the image after calculation from bottom/right edges
if (left < 0) {
left = 0;
}
if (top < 0) {
top = 0;
}
return std::make_tuple(left, top);
}
/*
Are pixel values in this image 16-bit integer?
*/
bool Is16Bit(VipsInterpretation const interpretation) {
return interpretation == VIPS_INTERPRETATION_RGB16 || interpretation == VIPS_INTERPRETATION_GREY16;
}
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation) {
return Is16Bit(interpretation) ? 65535.0 : 255.0;
}
/*
Get boolean operation type from string
*/
VipsOperationBoolean GetBooleanOperation(std::string const opStr) {
return static_cast<VipsOperationBoolean>(
vips_enum_from_nick(nullptr, VIPS_TYPE_OPERATION_BOOLEAN, opStr.data()));
}
/*
Get interpretation type from string
*/
VipsInterpretation GetInterpretation(std::string const typeStr) {
return static_cast<VipsInterpretation>(
vips_enum_from_nick(nullptr, VIPS_TYPE_INTERPRETATION, typeStr.data()));
}
/*
Convert RGBA value to another colourspace
*/
std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba, VipsInterpretation const interpretation) {
int const bands = static_cast<int>(rgba.size());
if (bands < 3 || interpretation == VIPS_INTERPRETATION_sRGB || interpretation == VIPS_INTERPRETATION_RGB) {
return rgba;
} else {
VImage pixel = VImage::new_matrix(1, 1);
pixel.set("bands", bands);
pixel = pixel.new_from_image(rgba);
pixel = pixel.colourspace(interpretation, VImage::option()->set("source_space", VIPS_INTERPRETATION_sRGB));
return pixel(0, 0);
}
}
/*
Apply the alpha channel to a given colour
*/
std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour) {
// Scale up 8-bit values to match 16-bit input image
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
// Create alphaColour colour
std::vector<double> alphaColour;
if (image.bands() > 2) {
alphaColour = {
multiplier * colour[0],
multiplier * colour[1],
multiplier * colour[2]
};
} else {
// Convert sRGB to greyscale
alphaColour = { multiplier * (
0.2126 * colour[0] +
0.7152 * colour[1] +
0.0722 * colour[2])
};
}
// Add alpha channel to alphaColour colour
if (colour[3] < 255.0 || HasAlpha(image)) {
alphaColour.push_back(colour[3] * multiplier);
}
// Ensure alphaColour colour uses correct colourspace
alphaColour = sharp::GetRgbaAsColourspace(alphaColour, image.interpretation());
// Add non-transparent alpha channel, if required
if (colour[3] < 255.0 && !HasAlpha(image)) {
image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
}
return std::make_tuple(image, alphaColour);
}
} // namespace sharp

View File

@@ -1,26 +1,115 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_COMMON_H_
#define SRC_COMMON_H_
#include <string>
#include <tuple>
#include <vector>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 7))
#error libvips version 8.7.0+ is required - see sharp.pixelplumbing.com/page/install
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
#error GCC version 4.6+ is required for C++11 features - see sharp.pixelplumbing.com/page/install#prerequisites
#endif
#if (defined(__clang__) && defined(__has_feature))
#if (!__has_feature(cxx_range_for))
#error clang version 3.0+ is required for C++11 features - see sharp.pixelplumbing.com/page/install#prerequisites
#endif
#endif
using vips::VImage;
namespace sharp {
struct InputDescriptor {
std::string name;
std::string file;
char *buffer;
bool failOnError;
size_t bufferLength;
double density;
int rawChannels;
int rawWidth;
int rawHeight;
int pages;
int page;
int createChannels;
int createWidth;
int createHeight;
std::vector<double> createBackground;
InputDescriptor():
buffer(nullptr),
failOnError(TRUE),
bufferLength(0),
density(72.0),
rawChannels(0),
rawWidth(0),
rawHeight(0),
pages(1),
page(0),
createChannels(0),
createWidth(0),
createHeight(0),
createBackground{ 0.0, 0.0, 0.0, 255.0 } {}
};
// Convenience methods to access the attributes of a v8::Object
bool HasAttr(v8::Handle<v8::Object> obj, std::string attr);
std::string AttrAsStr(v8::Handle<v8::Object> obj, std::string attr);
std::vector<double> AttrAsRgba(v8::Handle<v8::Object> obj, std::string attr);
template<typename T> v8::Local<T> AttrAs(v8::Handle<v8::Object> obj, std::string attr) {
return Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked().As<T>();
}
template<typename T> T AttrTo(v8::Handle<v8::Object> obj, std::string attr) {
return Nan::To<T>(Nan::Get(obj, Nan::New(attr).ToLocalChecked()).ToLocalChecked()).FromJust();
}
template<typename T> T AttrTo(v8::Handle<v8::Object> obj, int attr) {
return Nan::To<T>(Nan::Get(obj, attr).ToLocalChecked()).FromJust();
}
// Create an InputDescriptor instance from a v8::Object describing an input image
InputDescriptor* CreateInputDescriptor(
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist);
enum class ImageType {
UNKNOWN,
JPEG,
PNG,
WEBP,
TIFF,
GIF,
SVG,
PDF,
MAGICK,
OPENSLIDE,
PPM,
FITS,
RAW
VIPS,
RAW,
UNKNOWN,
MISSING
};
// How many tasks are in the queue?
@@ -36,6 +125,7 @@ namespace sharp {
bool IsTiff(std::string const &str);
bool IsDz(std::string const &str);
bool IsDzZip(std::string const &str);
bool IsV(std::string const &str);
/*
Provide a string identifier for the given image type.
@@ -52,6 +142,16 @@ namespace sharp {
*/
ImageType DetermineImageType(char const *file);
/*
Does this image type support multiple pages?
*/
bool ImageTypeSupportsPage(ImageType imageType);
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod);
/*
Does this image have an embedded profile?
*/
@@ -91,13 +191,35 @@ namespace sharp {
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const int density);
void SetDensity(VImage image, const double density);
/*
Check the proposed format supports the current dimensions.
*/
void AssertImageTypeDimensions(VImage image, ImageType const imageType);
/*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/
void FreeCallback(char* data, void* hint);
/*
Called with warnings from the glib-registered "VIPS" domain
*/
void VipsWarningCallback(char const* log_domain, GLogLevelFlags log_level, char const* message, void* ignore);
/*
Pop the oldest warning message from the queue
*/
std::string VipsWarningPop();
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity during an embed.
*/
std::tuple<int, int> CalculateEmbedPosition(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const gravity);
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity.
@@ -105,6 +227,44 @@ namespace sharp {
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const gravity);
/*
Calculate the (left, top) coordinates of the output image
within the input image, applying the given x and y offsets of the output image.
*/
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const x, int const y);
/*
Are pixel values in this image 16-bit integer?
*/
bool Is16Bit(VipsInterpretation const interpretation);
/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
double MaximumImageAlpha(VipsInterpretation const interpretation);
/*
Get boolean operation type from string
*/
VipsOperationBoolean GetBooleanOperation(std::string const opStr);
/*
Get interpretation type from string
*/
VipsInterpretation GetInterpretation(std::string const typeStr);
/*
Convert RGBA value to another colourspace
*/
std::vector<double> GetRgbaAsColourspace(std::vector<double> const rgba, VipsInterpretation const interpretation);
/*
Apply the alpha channel to a given colour
*/
std::tuple<VImage, std::vector<double>> ApplyAlpha(VImage image, std::vector<double> colour);
} // namespace sharp
#endif // SRC_COMMON_H_

View File

@@ -2,6 +2,10 @@
*
* 30/12/14
* - allow set enum value from string
* 10/6/16
* - missing implementation of VImage::write()
* 11/6/16
* - added arithmetic assignment overloads, += etc.
*/
/*
@@ -201,6 +205,30 @@ VOption::set( const char *name, std::vector<double> value )
return( this );
}
// input int array
VOption *
VOption::set( const char *name, std::vector<int> value )
{
Pair *pair = new Pair( name );
int *array;
unsigned int i;
pair->input = true;
g_value_init( &pair->value, VIPS_TYPE_ARRAY_INT );
vips_value_set_array_int( &pair->value, NULL,
static_cast< int >( value.size() ) );
array = vips_value_get_array_int( &pair->value, NULL );
for( i = 0; i < value.size(); i++ )
array[i] = value[i];
options.push_back( pair );
return( this );
}
// input image array
VOption *
VOption::set( const char *name, std::vector<VImage> value )
@@ -346,7 +374,7 @@ set_property( VipsObject *object, const char *name, const GValue *value )
if( vips_object_get_argument( object, name,
&pspec, &argument_class, &argument_instance ) ) {
vips_warn( NULL, "%s", vips_error_buffer() );
g_warning( "%s", vips_error_buffer() );
vips_error_clear();
return;
}
@@ -360,7 +388,7 @@ set_property( VipsObject *object, const char *name, const GValue *value )
if( (enum_value = vips_enum_from_nick( object_class->nickname,
pspec_type, g_value_get_string( value ) )) < 0 ) {
vips_warn( NULL, "%s", vips_error_buffer() );
g_warning( "%s", vips_error_buffer() );
vips_error_clear();
return;
}
@@ -457,12 +485,11 @@ VImage::call_option_string( const char *operation_name,
{
VipsOperation *operation;
VIPS_DEBUG_MSG( "vips_call_by_name: starting for %s ...\n",
VIPS_DEBUG_MSG( "call_option_string: starting for %s ...\n",
operation_name );
if( !(operation = vips_operation_new( operation_name )) ) {
if( options )
delete options;
delete options;
throw( VError() );
}
@@ -485,6 +512,7 @@ VImage::call_option_string( const char *operation_name,
*/
if( vips_cache_operation_buildp( &operation ) ) {
vips_object_unref_outputs( VIPS_OBJECT( operation ) );
g_object_unref( operation );
delete options;
throw( VError() );
}
@@ -560,34 +588,6 @@ VImage::new_from_buffer( void *buf, size_t len, const char *option_string,
return( out );
}
VImage
VImage::new_from_image( std::vector<double> pixel )
{
VImage onepx = VImage::black( 1, 1,
VImage::option()->set( "bands", bands() ) );
onepx = onepx.linear( to_vectorv( 1, 1.0 ), pixel ).cast( format() );
VImage big = onepx.embed( 0, 0, width(), height(),
VImage::option()->set( "extend", VIPS_EXTEND_COPY ) );
big = big.copy(
VImage::option()->
set( "interpretation", interpretation() )->
set( "xres", xres() )->
set( "yres", yres() )->
set( "xoffset", xres() )->
set( "yoffset", yres() ) );
return( big );
}
VImage
VImage::new_from_image( double pixel )
{
return( new_from_image( to_vectorv( 1, pixel ) ) );
}
VImage
VImage::new_matrix( int width, int height )
{
@@ -612,8 +612,17 @@ VImage::new_matrixv( int width, int height, ... )
return( matrix );
}
VImage
VImage::write( VImage out ) const
{
if( vips_image_write( this->get_image(), out.get_image() ) )
throw VError();
return( out );
}
void
VImage::write_to_file( const char *name, VOption *options )
VImage::write_to_file( const char *name, VOption *options ) const
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
@@ -633,7 +642,7 @@ VImage::write_to_file( const char *name, VOption *options )
void
VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
VOption *options )
VOption *options ) const
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
@@ -666,7 +675,7 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
#include "vips-operators.cpp"
std::vector<VImage>
VImage::bandsplit( VOption *options )
VImage::bandsplit( VOption *options ) const
{
std::vector<VImage> b;
@@ -677,16 +686,27 @@ VImage::bandsplit( VOption *options )
}
VImage
VImage::bandjoin( VImage other, VOption *options )
VImage::bandjoin( VImage other, VOption *options ) const
{
VImage v[2] = { *this, other };
std::vector<VImage> vec( v, v + VIPS_NUMBER( v ) );
VImage v[2] = { *this, other };
std::vector<VImage> vec( v, v + VIPS_NUMBER( v ) );
return( bandjoin( vec, options ) );
return( bandjoin( vec, options ) );
}
VImage
VImage::composite( VImage other, VipsBlendMode mode, VOption *options ) const
{
VImage v[2] = { *this, other };
std::vector<VImage> ivec( v, v + VIPS_NUMBER( v ) );
int m[1] = { static_cast<int>( mode ) };
std::vector<int> mvec( m, m + VIPS_NUMBER( m ) );
return( composite( ivec, mvec, options ) );
}
std::complex<double>
VImage::minpos( VOption *options )
VImage::minpos( VOption *options ) const
{
double x, y;
@@ -699,7 +719,7 @@ VImage::minpos( VOption *options )
}
std::complex<double>
VImage::maxpos( VOption *options )
VImage::maxpos( VOption *options ) const
{
double x, y;
@@ -711,4 +731,686 @@ VImage::maxpos( VOption *options )
return( std::complex<double>( x, y ) );
}
// Operator overloads
VImage
VImage::operator[]( int index ) const
{
return( this->extract_band( index ) );
}
std::vector<double>
VImage::operator()( int x, int y ) const
{
return( this->getpoint( x, y ) );
}
VImage
operator+( const VImage a, const VImage b )
{
return( a.add( b ) );
}
VImage
operator+( double a, const VImage b )
{
return( b.linear( 1.0, a ) );
}
VImage
operator+( const VImage a, double b )
{
return( a.linear( 1.0, b ) );
}
VImage
operator+( const std::vector<double> a, const VImage b )
{
return( b.linear( 1.0, a ) );
}
VImage
operator+( const VImage a, const std::vector<double> b )
{
return( a.linear( 1.0, b ) );
}
VImage &
operator+=( VImage &a, const VImage b )
{
return( a = a + b );
}
VImage &
operator+=( VImage &a, const double b )
{
return( a = a + b );
}
VImage &
operator+=( VImage &a, const std::vector<double> b )
{
return( a = a + b );
}
VImage
operator-( const VImage a, const VImage b )
{
return( a.subtract( b ) );
}
VImage
operator-( double a, const VImage b )
{
return( b.linear( -1.0, a ) );
}
VImage
operator-( const VImage a, double b )
{
return( a.linear( 1.0, -b ) );
}
VImage
operator-( const std::vector<double> a, const VImage b )
{
return( b.linear( -1.0, a ) );
}
VImage
operator-( const VImage a, const std::vector<double> b )
{
return( a.linear( 1.0, vips::negate( b ) ) );
}
VImage &
operator-=( VImage &a, const VImage b )
{
return( a = a - b );
}
VImage &
operator-=( VImage &a, const double b )
{
return( a = a - b );
}
VImage &
operator-=( VImage &a, const std::vector<double> b )
{
return( a = a - b );
}
VImage
operator-( const VImage a )
{
return( a * -1 );
}
VImage
operator*( const VImage a, const VImage b )
{
return( a.multiply( b ) );
}
VImage
operator*( double a, const VImage b )
{
return( b.linear( a, 0.0 ) );
}
VImage
operator*( const VImage a, double b )
{
return( a.linear( b, 0.0 ) );
}
VImage
operator*( const std::vector<double> a, const VImage b )
{
return( b.linear( a, 0.0 ) );
}
VImage
operator*( const VImage a, const std::vector<double> b )
{
return( a.linear( b, 0.0 ) );
}
VImage &
operator*=( VImage &a, const VImage b )
{
return( a = a * b );
}
VImage &
operator*=( VImage &a, const double b )
{
return( a = a * b );
}
VImage &
operator*=( VImage &a, const std::vector<double> b )
{
return( a = a * b );
}
VImage
operator/( const VImage a, const VImage b )
{
return( a.divide( b ) );
}
VImage
operator/( double a, const VImage b )
{
return( b.pow( -1.0 ).linear( a, 0.0 ) );
}
VImage
operator/( const VImage a, double b )
{
return( a.linear( 1.0 / b, 0.0 ) );
}
VImage
operator/( const std::vector<double> a, const VImage b )
{
return( b.pow( -1.0 ).linear( a, 0.0 ) );
}
VImage
operator/( const VImage a, const std::vector<double> b )
{
return( a.linear( vips::invert( b ), 0.0 ) );
}
VImage &
operator/=( VImage &a, const VImage b )
{
return( a = a / b );
}
VImage &
operator/=( VImage &a, const double b )
{
return( a = a / b );
}
VImage &
operator/=( VImage &a, const std::vector<double> b )
{
return( a = a / b );
}
VImage
operator%( const VImage a, const VImage b )
{
return( a.remainder( b ) );
}
VImage
operator%( const VImage a, const double b )
{
return( a.remainder_const( to_vector( b ) ) );
}
VImage
operator%( const VImage a, const std::vector<double> b )
{
return( a.remainder_const( b ) );
}
VImage &
operator%=( VImage &a, const VImage b )
{
return( a = a % b );
}
VImage &
operator%=( VImage &a, const double b )
{
return( a = a % b );
}
VImage &
operator%=( VImage &a, const std::vector<double> b )
{
return( a = a % b );
}
VImage
operator<( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_LESS ) );
}
VImage
operator<( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
to_vector( a ) ) );
}
VImage
operator<( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
to_vector( b ) ) );
}
VImage
operator<( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
a ) );
}
VImage
operator<( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
b ) );
}
VImage
operator<=( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_LESSEQ ) );
}
VImage
operator<=( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
to_vector( a ) ) );
}
VImage
operator<=( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
to_vector( b ) ) );
}
VImage
operator<=( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
a ) );
}
VImage
operator<=( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
b ) );
}
VImage
operator>( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_MORE ) );
}
VImage
operator>( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
to_vector( a ) ) );
}
VImage
operator>( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
to_vector( b ) ) );
}
VImage
operator>( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
a ) );
}
VImage
operator>( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
b ) );
}
VImage
operator>=( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_MOREEQ ) );
}
VImage
operator>=( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
to_vector( a ) ) );
}
VImage
operator>=( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
to_vector( b ) ) );
}
VImage
operator>=( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
a ) );
}
VImage
operator>=( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
b ) );
}
VImage
operator==( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_EQUAL ) );
}
VImage
operator==( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
to_vector( a ) ) );
}
VImage
operator==( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
to_vector( b ) ) );
}
VImage
operator==( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
a ) );
}
VImage
operator==( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
b ) );
}
VImage
operator!=( const VImage a, const VImage b )
{
return( a.relational( b, VIPS_OPERATION_RELATIONAL_NOTEQ ) );
}
VImage
operator!=( const double a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
to_vector( a ) ) );
}
VImage
operator!=( const VImage a, const double b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
to_vector( b ) ) );
}
VImage
operator!=( const std::vector<double> a, const VImage b )
{
return( b.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
a ) );
}
VImage
operator!=( const VImage a, const std::vector<double> b )
{
return( a.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
b ) );
}
VImage
operator&( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_AND ) );
}
VImage
operator&( const double a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_AND,
to_vector( a ) ) );
}
VImage
operator&( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_AND,
to_vector( b ) ) );
}
VImage
operator&( const std::vector<double> a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_AND, a ) );
}
VImage
operator&( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_AND, b ) );
}
VImage &
operator&=( VImage &a, const VImage b )
{
return( a = a & b );
}
VImage &
operator&=( VImage &a, const double b )
{
return( a = a & b );
}
VImage &
operator&=( VImage &a, const std::vector<double> b )
{
return( a = a & b );
}
VImage
operator|( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_OR ) );
}
VImage
operator|( const double a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
to_vector( a ) ) );
}
VImage
operator|( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
to_vector( b ) ) );
}
VImage
operator|( const std::vector<double> a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
a ) );
}
VImage
operator|( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
b ) );
}
VImage &
operator|=( VImage &a, const VImage b )
{
return( a = a | b );
}
VImage &
operator|=( VImage &a, const double b )
{
return( a = a | b );
}
VImage &
operator|=( VImage &a, const std::vector<double> b )
{
return( a = a | b );
}
VImage
operator^( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_EOR ) );
}
VImage
operator^( const double a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
to_vector( a ) ) );
}
VImage
operator^( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
to_vector( b ) ) );
}
VImage
operator^( const std::vector<double> a, const VImage b )
{
return( b.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
a ) );
}
VImage
operator^( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
b ) );
}
VImage &
operator^=( VImage &a, const VImage b )
{
return( a = a ^ b );
}
VImage &
operator^=( VImage &a, const double b )
{
return( a = a ^ b );
}
VImage &
operator^=( VImage &a, const std::vector<double> b )
{
return( a = a ^ b );
}
VImage
operator<<( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_LSHIFT ) );
}
VImage
operator<<( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_LSHIFT,
to_vector( b ) ) );
}
VImage
operator<<( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_LSHIFT,
b ) );
}
VImage &
operator<<=( VImage &a, const VImage b )
{
return( a = a << b );
}
VImage &
operator<<=( VImage &a, const double b )
{
return( a = a << b );
}
VImage &
operator<<=( VImage &a, const std::vector<double> b )
{
return( a = a << b );
}
VImage
operator>>( const VImage a, const VImage b )
{
return( a.boolean( b, VIPS_OPERATION_BOOLEAN_RSHIFT ) );
}
VImage
operator>>( const VImage a, const double b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_RSHIFT,
to_vector( b ) ) );
}
VImage
operator>>( const VImage a, const std::vector<double> b )
{
return( a.boolean_const( VIPS_OPERATION_BOOLEAN_RSHIFT,
b ) );
}
VImage &
operator>>=( VImage &a, const VImage b )
{
return( a = a << b );
}
VImage &
operator>>=( VImage &a, const double b )
{
return( a = a << b );
}
VImage &
operator>>=( VImage &a, const std::vector<double> b )
{
return( a = a << b );
}
VIPS_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -1,135 +1,86 @@
#include <node.h>
#include <vips/vips8>
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#include <numeric>
#include <vector>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
#include "metadata.h"
using 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 vips::VImage;
using vips::VError;
using sharp::ImageType;
using sharp::ImageTypeId;
using sharp::DetermineImageType;
using sharp::HasProfile;
using sharp::HasAlpha;
using sharp::ExifOrientation;
using sharp::HasDensity;
using sharp::GetDensity;
using sharp::FreeCallback;
using sharp::counterQueue;
struct MetadataBaton {
// Input
std::string fileIn;
char *bufferIn;
size_t bufferInLength;
// Output
std::string format;
int width;
int height;
std::string space;
int channels;
int density;
bool hasProfile;
bool hasAlpha;
int orientation;
char *exif;
size_t exifLength;
char *icc;
size_t iccLength;
std::string err;
MetadataBaton():
bufferInLength(0),
density(0),
orientation(0),
exifLength(0),
iccLength(0) {}
};
class MetadataWorker : public AsyncWorker {
class MetadataWorker : public Nan::AsyncWorker {
public:
MetadataWorker(Callback *callback, MetadataBaton *baton, const Local<Object> &bufferIn) :
AsyncWorker(callback), baton(baton) {
if (baton->bufferInLength > 0) {
SaveToPersistent("bufferIn", bufferIn);
}
}
MetadataWorker(
Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *debuglog,
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
Nan::AsyncWorker(callback, "sharp:MetadataWorker"),
baton(baton), debuglog(debuglog),
buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer);
return index + 1;
});
}
~MetadataWorker() {}
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&counterQueue);
g_atomic_int_dec_and_test(&sharp::counterQueue);
ImageType imageType = ImageType::UNKNOWN;
VImage image;
if (baton->bufferInLength > 0) {
// From buffer
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
if (imageType != ImageType::UNKNOWN) {
try {
image = VImage::new_from_buffer(baton->bufferIn, baton->bufferInLength, nullptr);
} catch (...) {
(baton->err).append("Input buffer has corrupt header");
imageType = ImageType::UNKNOWN;
}
} else {
(baton->err).append("Input buffer contains unsupported image format");
}
} else {
// From file
imageType = DetermineImageType(baton->fileIn.data());
if (imageType != ImageType::UNKNOWN) {
try {
image = VImage::new_from_file(baton->fileIn.data());
} catch (...) {
(baton->err).append("Input file has corrupt header");
imageType = ImageType::UNKNOWN;
}
} else {
(baton->err).append("Input file is missing or of an unsupported image format");
}
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, VIPS_ACCESS_SEQUENTIAL);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
if (imageType != ImageType::UNKNOWN) {
if (imageType != sharp::ImageType::UNKNOWN) {
// Image type
baton->format = ImageTypeId(imageType);
baton->format = sharp::ImageTypeId(imageType);
// VipsImage attributes
baton->width = image.width();
baton->height = image.height();
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
baton->channels = image.bands();
if (HasDensity(image)) {
baton->density = GetDensity(image);
baton->depth = vips_enum_nick(VIPS_TYPE_BAND_FORMAT, image.format());
if (sharp::HasDensity(image)) {
baton->density = sharp::GetDensity(image);
}
baton->hasProfile = HasProfile(image);
if (image.get_typeof("jpeg-chroma-subsample") == VIPS_TYPE_REF_STRING) {
baton->chromaSubsampling = image.get_string("jpeg-chroma-subsample");
}
if (image.get_typeof("interlaced") == G_TYPE_INT) {
baton->isProgressive = image.get_int("interlaced") == 1;
}
if (image.get_typeof("palette-bit-depth") == G_TYPE_INT) {
baton->paletteBitDepth = image.get_int("palette-bit-depth");
}
if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT) {
baton->pages = image.get_int(VIPS_META_N_PAGES);
}
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
}
baton->hasProfile = sharp::HasProfile(image);
// Derived attributes
baton->hasAlpha = HasAlpha(image);
baton->orientation = ExifOrientation(image);
baton->hasAlpha = sharp::HasAlpha(image);
baton->orientation = sharp::ExifOrientation(image);
// EXIF
if (image.get_typeof(VIPS_META_EXIF_NAME) == VIPS_TYPE_BLOB) {
size_t exifLength;
@@ -146,88 +97,143 @@ class MetadataWorker : public AsyncWorker {
memcpy(baton->icc, icc, iccLength);
baton->iccLength = iccLength;
}
// IPTC
if (image.get_typeof(VIPS_META_IPTC_NAME) == VIPS_TYPE_BLOB) {
size_t iptcLength;
void const *iptc = image.get_blob(VIPS_META_IPTC_NAME, &iptcLength);
baton->iptc = static_cast<char *>(g_malloc(iptcLength));
memcpy(baton->iptc, iptc, iptcLength);
baton->iptcLength = iptcLength;
}
// XMP
if (image.get_typeof(VIPS_META_XMP_NAME) == VIPS_TYPE_BLOB) {
size_t xmpLength;
void const *xmp = image.get_blob(VIPS_META_XMP_NAME, &xmpLength);
baton->xmp = static_cast<char *>(g_malloc(xmpLength));
memcpy(baton->xmp, xmp, xmpLength);
baton->xmpLength = xmpLength;
}
}
// Clean up
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback () {
HandleScope();
void HandleOKCallback() {
using Nan::New;
using Nan::Set;
Nan::HandleScope();
Local<Value> argv[2] = { Null(), Null() };
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
// Error
argv[0] = Error(baton->err.data());
argv[0] = Nan::Error(baton->err.data());
} else {
// Metadata Object
Local<Object> info = New<Object>();
Set(info, New("format").ToLocalChecked(), New<String>(baton->format).ToLocalChecked());
Set(info, New("width").ToLocalChecked(), New<Number>(baton->width));
Set(info, New("height").ToLocalChecked(), New<Number>(baton->height));
Set(info, New("space").ToLocalChecked(), New<String>(baton->space).ToLocalChecked());
Set(info, New("channels").ToLocalChecked(), New<Number>(baton->channels));
if (baton->density > 0) {
Set(info, New("density").ToLocalChecked(), New<Number>(baton->density));
v8::Local<v8::Object> info = New<v8::Object>();
Set(info, New("format").ToLocalChecked(), New<v8::String>(baton->format).ToLocalChecked());
if (baton->input->bufferLength > 0) {
Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->input->bufferLength)));
}
Set(info, New("hasProfile").ToLocalChecked(), New<Boolean>(baton->hasProfile));
Set(info, New("hasAlpha").ToLocalChecked(), New<Boolean>(baton->hasAlpha));
Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(baton->width));
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(baton->height));
Set(info, New("space").ToLocalChecked(), New<v8::String>(baton->space).ToLocalChecked());
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(baton->channels));
Set(info, New("depth").ToLocalChecked(), New<v8::String>(baton->depth).ToLocalChecked());
if (baton->density > 0) {
Set(info, New("density").ToLocalChecked(), New<v8::Uint32>(baton->density));
}
if (!baton->chromaSubsampling.empty()) {
Set(info,
New("chromaSubsampling").ToLocalChecked(),
New<v8::String>(baton->chromaSubsampling).ToLocalChecked());
}
Set(info, New("isProgressive").ToLocalChecked(), New<v8::Boolean>(baton->isProgressive));
if (baton->paletteBitDepth > 0) {
Set(info, New("paletteBitDepth").ToLocalChecked(), New<v8::Uint32>(baton->paletteBitDepth));
}
if (baton->pages > 0) {
Set(info, New("pages").ToLocalChecked(), New<v8::Uint32>(baton->pages));
}
if (baton->pageHeight > 0) {
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
}
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
if (baton->orientation > 0) {
Set(info, New("orientation").ToLocalChecked(), New<Number>(baton->orientation));
Set(info, New("orientation").ToLocalChecked(), New<v8::Uint32>(baton->orientation));
}
if (baton->exifLength > 0) {
Set(info,
New("exif").ToLocalChecked(),
NewBuffer(baton->exif, baton->exifLength, FreeCallback, nullptr).ToLocalChecked()
);
Nan::NewBuffer(baton->exif, baton->exifLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->iccLength > 0) {
Set(info,
New("icc").ToLocalChecked(),
NewBuffer(baton->icc, baton->iccLength, FreeCallback, nullptr).ToLocalChecked()
);
Nan::NewBuffer(baton->icc, baton->iccLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->iptcLength > 0) {
Set(info,
New("iptc").ToLocalChecked(),
Nan::NewBuffer(baton->iptc, baton->iptcLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->xmpLength > 0) {
Set(info,
New("xmp").ToLocalChecked(),
Nan::NewBuffer(baton->xmp, baton->xmpLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
argv[1] = info;
}
// Dispose of Persistent wrapper around input Buffer so it can be garbage collected
if (baton->bufferInLength > 0) {
GetFromPersistent("bufferIn");
}
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index);
return index + 1;
});
delete baton->input;
delete baton;
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message, async_resource);
warning = sharp::VipsWarningPop();
}
// Return to JavaScript
callback->Call(2, argv);
callback->Call(2, argv, async_resource);
}
private:
MetadataBaton* baton;
Nan::Callback *debuglog;
std::vector<v8::Local<v8::Object>> buffersToPersist;
};
/*
metadata(options, callback)
*/
NAN_METHOD(metadata) {
HandleScope();
// Input Buffers must not undergo GC compaction during processing
std::vector<v8::Local<v8::Object>> buffersToPersist;
// V8 objects are converted to non-V8 types held in the baton struct
MetadataBaton *baton = new MetadataBaton;
Local<Object> options = info[0].As<Object>();
v8::Local<v8::Object> options = info[0].As<v8::Object>();
// Input filename
baton->fileIn = *Utf8String(Get(options, New("fileIn").ToLocalChecked()).ToLocalChecked());
// Input Buffer object
Local<Object> bufferIn;
if (node::Buffer::HasInstance(Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked())) {
bufferIn = Get(options, New("bufferIn").ToLocalChecked()).ToLocalChecked().As<Object>();
baton->bufferInLength = node::Buffer::Length(bufferIn);
baton->bufferIn = node::Buffer::Data(bufferIn);
}
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
// Join queue for worker thread
Callback *callback = new Callback(info[1].As<Function>());
AsyncQueueWorker(new MetadataWorker(callback, baton, bufferIn));
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, debuglog, buffersToPersist));
// Increment queued task counter
g_atomic_int_inc(&counterQueue);
g_atomic_int_inc(&sharp::counterQueue);
}

View File

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

View File

@@ -1,5 +1,23 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vector>
#include <vips/vips8>
#include "common.h"
@@ -11,80 +29,58 @@ using vips::VError;
namespace sharp {
/*
Alpha composite src over dst with given gravity.
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
Removes alpha channel, if any.
*/
VImage RemoveAlpha(VImage image) {
if (HasAlpha(image)) {
image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
}
return image;
}
/*
Ensures alpha channel, if missing.
*/
VImage EnsureAlpha(VImage image) {
if (!HasAlpha(image)) {
std::vector<double> alpha;
alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
image = image.bandjoin_const(alpha);
}
return image;
}
/*
* Tint an image using the specified chroma, preserving the original image luminance
*/
VImage Composite(VImage src, VImage dst, const int gravity) {
using sharp::CalculateCrop;
using sharp::HasAlpha;
if (!HasAlpha(src)) {
throw VError("Overlay image must have an alpha channel");
VImage Tint(VImage image, double const a, double const b) {
// Get original colourspace
VipsInterpretation typeBeforeTint = image.interpretation();
if (typeBeforeTint == VIPS_INTERPRETATION_RGB) {
typeBeforeTint = VIPS_INTERPRETATION_sRGB;
}
if (!HasAlpha(dst)) {
throw VError("Image to be overlaid must have an alpha channel");
// Extract luminance
VImage luminance = image.colourspace(VIPS_INTERPRETATION_LAB)[0];
// Create the tinted version by combining the L from the original and the chroma from the tint
std::vector<double> chroma {a, b};
VImage tinted = luminance
.bandjoin(chroma)
.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_LAB))
.colourspace(typeBeforeTint);
// Attach original alpha channel, if any
if (HasAlpha(image)) {
// Extract original alpha channel
VImage alpha = image[image.bands() - 1];
// Join alpha channel to normalised image
tinted = tinted.bandjoin(alpha);
}
if (src.width() > dst.width() || src.height() > dst.height()) {
throw VError("Overlay image must have same dimensions or smaller");
}
// Enlarge overlay src, if required
if (src.width() < dst.width() || src.height() < dst.height()) {
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
int left;
int top;
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), gravity);
// Embed onto transparent background
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background)
);
}
// Split src into non-alpha and alpha channels
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
// Split dst into non-alpha and alpha channels
VImage dstWithoutAlpha = dst.extract_band(0, VImage::option()->set("n", dst.bands() - 1));
VImage dstAlpha = dst[dst.bands() - 1] * (1.0 / 255.0);
//
// Compute normalized output alpha channel:
//
// References:
// - http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
// - https://github.com/jcupitt/ruby-vips/issues/28#issuecomment-9014826
//
// out_a = src_a + dst_a * (1 - src_a)
// ^^^^^^^^^^^
// t0
VImage t0 = srcAlpha.linear(-1.0, 1.0);
VImage outAlphaNormalized = srcAlpha + dstAlpha * t0;
//
// Compute output RGB channels:
//
// Wikipedia:
// out_rgb = (src_rgb * src_a + dst_rgb * dst_a * (1 - src_a)) / out_a
// ^^^^^^^^^^^
// t0
//
// Omit division by `out_a` since `Compose` is supposed to output a
// premultiplied RGBA image as reversal of premultiplication is handled
// externally.
//
VImage outRGBPremultiplied = srcWithoutAlpha + dstWithoutAlpha * t0;
// Combine RGB and alpha channel into output image:
return outRGBPremultiplied.bandjoin(outAlphaNormalized * 255.0);
return tinted;
}
/*
* Stretch luminance to cover full dynamic range.
*/
VImage Normalize(VImage image) {
VImage Normalise(VImage image) {
// Get original colourspace
VipsInterpretation typeBeforeNormalize = image.interpretation();
if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) {
@@ -125,20 +121,18 @@ namespace sharp {
VImage Gamma(VImage image, double const exponent) {
if (HasAlpha(image)) {
// Separate alpha channel
VImage imageWithoutAlpha = image.extract_band(0,
VImage::option()->set("n", image.bands() - 1));
VImage alpha = image[image.bands() - 1];
return imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
} else {
return image.gamma(VImage::option()->set("exponent", exponent));
}
}
/*
* Gaussian blur (use sigma <0 for fast blur)
* Gaussian blur. Use sigma of -1.0 for fast blur.
*/
VImage Blur(VImage image, double const sigma) {
if (sigma < 0.0) {
if (sigma == -1.0) {
// Fast, mild blur - averages neighbouring pixels
VImage blur = VImage::new_matrixv(3, 3,
1.0, 1.0, 1.0,
@@ -153,10 +147,49 @@ namespace sharp {
}
/*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
* Convolution with a kernel.
*/
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged) {
if (radius == -1) {
VImage Convolve(VImage image, int const width, int const height,
double const scale, double const offset,
std::unique_ptr<double[]> const &kernel_v
) {
VImage kernel = VImage::new_from_memory(
kernel_v.get(),
width * height * sizeof(double),
width,
height,
1,
VIPS_FORMAT_DOUBLE);
kernel.set("scale", scale);
kernel.set("offset", offset);
return image.conv(kernel);
}
/*
* Recomb with a Matrix of the given bands/channel size.
* Eg. RGB will be a 3x3 matrix.
*/
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix) {
double *m = matrix.get();
return image
.colourspace(VIPS_INTERPRETATION_sRGB)
.recomb(image.bands() == 3
? VImage::new_from_memory(
m, 9 * sizeof(double), 3, 3, 1, VIPS_FORMAT_DOUBLE
)
: VImage::new_matrixv(4, 4,
m[0], m[1], m[2], 0.0,
m[3], m[4], m[5], 0.0,
m[6], m[7], m[8], 0.0,
0.0, 0.0, 0.0, 1.0));
}
/*
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
if (sigma == -1.0) {
// Fast, mild sharpen
VImage sharpen = VImage::new_matrixv(3, 3,
-1.0, -1.0, -1.0,
@@ -166,88 +199,67 @@ namespace sharp {
return image.conv(sharpen);
} else {
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
VipsInterpretation colourspaceBeforeSharpen = image.interpretation();
if (colourspaceBeforeSharpen == VIPS_INTERPRETATION_RGB) {
colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
}
return image.sharpen(
VImage::option()->set("radius", radius)->set("m1", flat)->set("m2", jagged)
);
VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged))
.colourspace(colourspaceBeforeSharpen);
}
}
VImage Threshold(VImage image, double const threshold, bool const thresholdGrayscale) {
if (!thresholdGrayscale) {
return image >= threshold;
}
return image.colourspace(VIPS_INTERPRETATION_B_W) >= threshold;
}
/*
Calculate crop area based on image entropy
Perform boolean/bitwise operation on image color channels - results in one channel image
*/
std::tuple<int, int> EntropyCrop(VImage image, int const outWidth, int const outHeight) {
int left = 0;
int top = 0;
int const inWidth = image.width();
int const inHeight = image.height();
if (inWidth > outWidth) {
// Reduce width by repeated removing slices from edge with lowest entropy
int width = inWidth;
double leftEntropy = 0.0;
double rightEntropy = 0.0;
// Max width of each slice
int const maxSliceWidth = static_cast<int>(ceil((inWidth - outWidth) / 8.0));
while (width > outWidth) {
// Width of current slice
int const slice = std::min(width - outWidth, maxSliceWidth);
if (leftEntropy == 0.0) {
// Update entropy of left slice
leftEntropy = Entropy(image.extract_area(left, 0, slice, inHeight));
}
if (rightEntropy == 0.0) {
// Update entropy of right slice
rightEntropy = Entropy(image.extract_area(width - slice - 1, 0, slice, inHeight));
}
// Keep slice with highest entropy
if (leftEntropy >= rightEntropy) {
// Discard right slice
rightEntropy = 0.0;
} else {
// Discard left slice
leftEntropy = 0.0;
left = left + slice;
}
width = width - slice;
}
}
if (inHeight > outHeight) {
// Reduce height by repeated removing slices from edge with lowest entropy
int height = inHeight;
double topEntropy = 0.0;
double bottomEntropy = 0.0;
// Max height of each slice
int const maxSliceHeight = static_cast<int>(ceil((inHeight - outHeight) / 8.0));
while (height > outHeight) {
// Height of current slice
int const slice = std::min(height - outHeight, maxSliceHeight);
if (topEntropy == 0.0) {
// Update entropy of top slice
topEntropy = Entropy(image.extract_area(0, top, inWidth, slice));
}
if (bottomEntropy == 0.0) {
// Update entropy of bottom slice
bottomEntropy = Entropy(image.extract_area(0, height - slice - 1, inWidth, slice));
}
// Keep slice with highest entropy
if (topEntropy >= bottomEntropy) {
// Discard bottom slice
bottomEntropy = 0.0;
} else {
// Discard top slice
topEntropy = 0.0;
top = top + slice;
}
height = height - slice;
}
}
return std::make_tuple(left, top);
VImage Bandbool(VImage image, VipsOperationBoolean const boolean) {
image = image.bandbool(boolean);
return image.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));
}
/*
Calculate the Shannon entropy for an image
Perform bitwise boolean operation between images
*/
double Entropy(VImage image) {
return image.hist_find().hist_entropy();
VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean) {
return image.boolean(imageR, boolean);
}
/*
Trim an image
*/
VImage Trim(VImage image, double const threshold) {
// Top-left pixel provides the background colour
VImage background = image.extract_area(0, 0, 1, 1);
if (HasAlpha(background)) {
background = background.flatten();
}
int top, width, height;
int const left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background(0, 0))
->set("threshold", threshold));
if (width == 0 || height == 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance");
}
return image.extract_area(left, top, width, height);
}
/*
* Calculate (a * in + b)
*/
VImage Linear(VImage image, double const a, double const b) {
if (HasAlpha(image)) {
// Separate alpha channel
VImage alpha = image[image.bands() - 1];
return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
} else {
return image.linear(a, b);
}
}
} // namespace sharp

View File

@@ -1,6 +1,23 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_
#include <algorithm>
#include <functional>
#include <memory>
#include <tuple>
#include <vips/vips8>
@@ -9,15 +26,24 @@ using vips::VImage;
namespace sharp {
/*
Alpha composite src over dst with given gravity.
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
Removes alpha channel, if any.
*/
VImage RemoveAlpha(VImage image);
/*
Ensures alpha channel, if missing.
*/
VImage EnsureAlpha(VImage image);
/*
* Tint an image using the specified chroma, preserving the original image luminance
*/
VImage Composite(VImage src, VImage dst, const int gravity);
VImage Tint(VImage image, double const a, double const b);
/*
* Stretch luminance to cover full dynamic range.
*/
VImage Normalize(VImage image);
VImage Normalise(VImage image);
/*
* Gamma encoding/decoding
@@ -25,24 +51,51 @@ namespace sharp {
VImage Gamma(VImage image, double const exponent);
/*
* Gaussian blur. Use sigma of -1 for fast blur.
* Gaussian blur. Use sigma of -1.0 for fast blur.
*/
VImage Blur(VImage image, double const sigma);
/*
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
* Convolution with a kernel.
*/
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged);
VImage Convolve(VImage image, int const width, int const height,
double const scale, double const offset, std::unique_ptr<double[]> const &kernel_v);
/*
Calculate crop area based on image entropy
*/
std::tuple<int, int> EntropyCrop(VImage image, int const outWidth, int const outHeight);
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
*/
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
/*
Calculate the Shannon entropy for an image
Threshold an image
*/
double Entropy(VImage image);
VImage Threshold(VImage image, double const threshold, bool const thresholdColor);
/*
Perform boolean/bitwise operation on image color channels - results in one channel image
*/
VImage Bandbool(VImage image, VipsOperationBoolean const boolean);
/*
Perform bitwise boolean operation between images
*/
VImage Boolean(VImage image, VImage imageR, VipsOperationBoolean const boolean);
/*
Trim an image
*/
VImage Trim(VImage image, double const threshold);
/*
* Linear adjustment (a * in + b)
*/
VImage Linear(VImage image, double const a, double const b);
/*
* Recomb with a Matrix of the given bands/channel size.
* Eg. RGB will be a 3x3 matrix.
*/
VImage Recomb(VImage image, std::unique_ptr<double[]> const &matrix);
} // namespace sharp

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,28 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_PIPELINE_H_
#define SRC_PIPELINE_H_
#include <memory>
#include <string>
#include <vector>
#include <nan.h>
#include <vips/vips8>
#include "nan.h"
#include "./common.h"
NAN_METHOD(pipeline);
@@ -15,24 +34,33 @@ enum class Canvas {
IGNORE_ASPECT
};
struct Composite {
sharp::InputDescriptor *input;
VipsBlendMode mode;
int gravity;
int left;
int top;
bool tile;
Composite():
input(nullptr),
mode(VIPS_BLEND_MODE_OVER),
gravity(0),
left(-1),
top(-1),
tile(false) {}
};
struct PipelineBaton {
std::string fileIn;
char *bufferIn;
size_t bufferInLength;
sharp::InputDescriptor *input;
std::string iccProfilePath;
int limitInputPixels;
int density;
int rawWidth;
int rawHeight;
int rawChannels;
std::string formatOut;
std::string fileOut;
void *bufferOut;
size_t bufferOutLength;
std::string overlayFileIn;
char *overlayBufferIn;
size_t overlayBufferInLength;
int overlayGravity;
std::vector<Composite *> composite;
std::vector<sharp::InputDescriptor *> joinChannelIn;
int topOffsetPre;
int leftOffsetPre;
int widthPre;
@@ -45,20 +73,39 @@ struct PipelineBaton {
int height;
int channels;
Canvas canvas;
int crop;
std::string interpolator;
double background[4];
int position;
std::vector<double> resizeBackground;
bool hasCropOffset;
int cropOffsetLeft;
int cropOffsetTop;
bool premultiplied;
std::string kernel;
bool fastShrinkOnLoad;
double tintA;
double tintB;
bool flatten;
std::vector<double> flattenBackground;
bool negate;
double blurSigma;
int sharpenRadius;
int medianSize;
double sharpenSigma;
double sharpenFlat;
double sharpenJagged;
int threshold;
bool thresholdGrayscale;
double trimThreshold;
int trimOffsetLeft;
int trimOffsetTop;
double linearA;
double linearB;
double gamma;
double gammaOut;
bool greyscale;
bool normalize;
bool normalise;
bool useExifOrientation;
int angle;
double rotationAngle;
std::vector<double> rotationBackground;
bool rotateBeforePreExtract;
bool flip;
bool flop;
@@ -66,78 +113,153 @@ struct PipelineBaton {
int extendBottom;
int extendLeft;
int extendRight;
bool progressive;
std::vector<double> extendBackground;
bool withoutEnlargement;
VipsAccess accessMethod;
int quality;
int compressionLevel;
bool withoutAdaptiveFiltering;
bool withoutChromaSubsampling;
bool trellisQuantisation;
bool overshootDeringing;
bool optimiseScans;
int jpegQuality;
bool jpegProgressive;
std::string jpegChromaSubsampling;
bool jpegTrellisQuantisation;
int jpegQuantisationTable;
bool jpegOvershootDeringing;
bool jpegOptimiseScans;
bool jpegOptimiseCoding;
bool pngProgressive;
int pngCompressionLevel;
bool pngAdaptiveFiltering;
bool pngPalette;
int pngQuality;
int pngColours;
double pngDither;
int webpQuality;
int webpAlphaQuality;
bool webpNearLossless;
bool webpLossless;
int tiffQuality;
VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor;
bool tiffPyramid;
bool tiffSquash;
bool tiffTile;
int tiffTileHeight;
int tiffTileWidth;
double tiffXres;
double tiffYres;
std::string err;
bool withMetadata;
int withMetadataOrientation;
std::unique_ptr<double[]> convKernel;
int convKernelWidth;
int convKernelHeight;
double convKernelScale;
double convKernelOffset;
sharp::InputDescriptor *boolean;
VipsOperationBoolean booleanOp;
VipsOperationBoolean bandBoolOp;
int extractChannel;
bool removeAlpha;
bool ensureAlpha;
VipsInterpretation colourspace;
int tileSize;
int tileOverlap;
VipsForeignDzContainer tileContainer;
VipsForeignDzLayout tileLayout;
std::string tileFormat;
int tileAngle;
VipsForeignDzDepth tileDepth;
std::unique_ptr<double[]> recombMatrix;
PipelineBaton():
bufferInLength(0),
input(nullptr),
limitInputPixels(0),
density(72),
rawWidth(0),
rawHeight(0),
rawChannels(0),
formatOut(""),
fileOut(""),
bufferOutLength(0),
overlayBufferInLength(0),
overlayGravity(0),
topOffsetPre(-1),
topOffsetPost(-1),
channels(0),
canvas(Canvas::CROP),
crop(0),
position(0),
resizeBackground{ 0.0, 0.0, 0.0, 255.0 },
hasCropOffset(false),
cropOffsetLeft(0),
cropOffsetTop(0),
premultiplied(false),
tintA(128.0),
tintB(128.0),
flatten(false),
flattenBackground{ 0.0, 0.0, 0.0 },
negate(false),
blurSigma(0.0),
sharpenRadius(0),
medianSize(0),
sharpenSigma(0.0),
sharpenFlat(1.0),
sharpenJagged(2.0),
threshold(0),
thresholdGrayscale(true),
trimThreshold(0.0),
trimOffsetLeft(0),
trimOffsetTop(0),
linearA(1.0),
linearB(0.0),
gamma(0.0),
greyscale(false),
normalize(false),
normalise(false),
useExifOrientation(false),
angle(0),
rotationAngle(0.0),
rotationBackground{ 0.0, 0.0, 0.0, 255.0 },
flip(false),
flop(false),
extendTop(0),
extendBottom(0),
extendLeft(0),
extendRight(0),
progressive(false),
extendBackground{ 0.0, 0.0, 0.0, 255.0 },
withoutEnlargement(false),
quality(80),
compressionLevel(6),
withoutAdaptiveFiltering(false),
withoutChromaSubsampling(false),
trellisQuantisation(false),
overshootDeringing(false),
optimiseScans(false),
jpegQuality(80),
jpegProgressive(false),
jpegChromaSubsampling("4:2:0"),
jpegTrellisQuantisation(false),
jpegQuantisationTable(0),
jpegOvershootDeringing(false),
jpegOptimiseScans(false),
jpegOptimiseCoding(true),
pngProgressive(false),
pngCompressionLevel(9),
pngAdaptiveFiltering(false),
pngPalette(false),
pngQuality(100),
pngColours(256),
pngDither(1.0),
webpQuality(80),
tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
tiffPyramid(false),
tiffSquash(false),
tiffTile(false),
tiffTileHeight(256),
tiffTileWidth(256),
tiffXres(1.0),
tiffYres(1.0),
withMetadata(false),
withMetadataOrientation(-1),
convKernelWidth(0),
convKernelHeight(0),
convKernelScale(0.0),
convKernelOffset(0.0),
boolean(nullptr),
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
extractChannel(-1),
removeAlpha(false),
ensureAlpha(false),
colourspace(VIPS_INTERPRETATION_LAST),
tileSize(256),
tileOverlap(0),
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ) {
background[0] = 0.0;
background[1] = 0.0;
background[2] = 0.0;
background[3] = 255.0;
}
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
tileAngle(0),
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST) {}
};
#endif // SRC_PIPELINE_H_

View File

@@ -1,16 +1,33 @@
#include <node.h>
#include <vips/vips8>
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h"
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
#include "metadata.h"
#include "pipeline.h"
#include "utilities.h"
#include "stats.h"
NAN_MODULE_INIT(init) {
vips_init("sharp");
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
// Methods available to JavaScript
Nan::Set(target, Nan::New("metadata").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked());
@@ -30,6 +47,8 @@ NAN_MODULE_INIT(init) {
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(format)).ToLocalChecked());
Nan::Set(target, Nan::New("_maxColourDistance").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(_maxColourDistance)).ToLocalChecked());
Nan::Set(target, Nan::New("stats").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(stats)).ToLocalChecked());
}
NODE_MODULE(sharp, init)

192
src/stats.cc Normal file
View File

@@ -0,0 +1,192 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <numeric>
#include <vector>
#include <iostream>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
#include "stats.h"
class StatsWorker : public Nan::AsyncWorker {
public:
StatsWorker(
Nan::Callback *callback, StatsBaton *baton, Nan::Callback *debuglog,
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
Nan::AsyncWorker(callback, "sharp:StatsWorker"),
baton(baton), debuglog(debuglog),
buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer);
return index + 1;
});
}
~StatsWorker() {}
const int STAT_MIN_INDEX = 0;
const int STAT_MAX_INDEX = 1;
const int STAT_SUM_INDEX = 2;
const int STAT_SQ_SUM_INDEX = 3;
const int STAT_MEAN_INDEX = 4;
const int STAT_STDEV_INDEX = 5;
const int STAT_MINX_INDEX = 6;
const int STAT_MINY_INDEX = 7;
const int STAT_MAXX_INDEX = 8;
const int STAT_MAXY_INDEX = 9;
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
using Nan::New;
using Nan::Set;
using sharp::MaximumImageAlpha;
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, baton->accessMethod);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
if (imageType != sharp::ImageType::UNKNOWN) {
try {
vips::VImage stats = image.stats();
int const bands = image.bands();
for (int b = 1; b <= bands; b++) {
ChannelStats cStats(static_cast<int>(stats.getpoint(STAT_MIN_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAX_INDEX, b).front()),
stats.getpoint(STAT_SUM_INDEX, b).front(), stats.getpoint(STAT_SQ_SUM_INDEX, b).front(),
stats.getpoint(STAT_MEAN_INDEX, b).front(), stats.getpoint(STAT_STDEV_INDEX, b).front(),
static_cast<int>(stats.getpoint(STAT_MINX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MINY_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXY_INDEX, b).front()));
baton->channelStats.push_back(cStats);
}
// Image is not opaque when alpha layer is present and contains a non-mamixa value
if (sharp::HasAlpha(image)) {
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
if (minAlpha != MaximumImageAlpha(image.interpretation())) {
baton->isOpaque = false;
}
}
// Estimate entropy via histogram of greyscale value frequency
baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
}
// Clean up
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback() {
using Nan::New;
using Nan::Set;
Nan::HandleScope();
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
argv[0] = Nan::Error(baton->err.data());
} else {
// Stats Object
v8::Local<v8::Object> info = New<v8::Object>();
v8::Local<v8::Array> channels = New<v8::Array>();
std::vector<ChannelStats>::iterator it;
int i = 0;
for (it=baton->channelStats.begin() ; it < baton->channelStats.end(); it++, i++) {
v8::Local<v8::Object> channelStat = New<v8::Object>();
Set(channelStat, New("min").ToLocalChecked(), New<v8::Number>(it->min));
Set(channelStat, New("max").ToLocalChecked(), New<v8::Number>(it->max));
Set(channelStat, New("sum").ToLocalChecked(), New<v8::Number>(it->sum));
Set(channelStat, New("squaresSum").ToLocalChecked(), New<v8::Number>(it->squaresSum));
Set(channelStat, New("mean").ToLocalChecked(), New<v8::Number>(it->mean));
Set(channelStat, New("stdev").ToLocalChecked(), New<v8::Number>(it->stdev));
Set(channelStat, New("minX").ToLocalChecked(), New<v8::Number>(it->minX));
Set(channelStat, New("minY").ToLocalChecked(), New<v8::Number>(it->minY));
Set(channelStat, New("maxX").ToLocalChecked(), New<v8::Number>(it->maxX));
Set(channelStat, New("maxY").ToLocalChecked(), New<v8::Number>(it->maxY));
channels->Set(i, channelStat);
}
Set(info, New("channels").ToLocalChecked(), channels);
Set(info, New("isOpaque").ToLocalChecked(), New<v8::Boolean>(baton->isOpaque));
Set(info, New("entropy").ToLocalChecked(), New<v8::Number>(baton->entropy));
argv[1] = info;
}
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index);
return index + 1;
});
delete baton->input;
delete baton;
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message, async_resource);
warning = sharp::VipsWarningPop();
}
// Return to JavaScript
callback->Call(2, argv, async_resource);
}
private:
StatsBaton* baton;
Nan::Callback *debuglog;
std::vector<v8::Local<v8::Object>> buffersToPersist;
};
/*
stats(options, callback)
*/
NAN_METHOD(stats) {
using sharp::AttrTo;
// Input Buffers must not undergo GC compaction during processing
std::vector<v8::Local<v8::Object>> buffersToPersist;
// V8 objects are converted to non-V8 types held in the baton struct
StatsBaton *baton = new StatsBaton;
v8::Local<v8::Object> options = info[0].As<v8::Object>();
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
// Join queue for worker thread
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new StatsWorker(callback, baton, debuglog, buffersToPersist));
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
}

67
src/stats.h Normal file
View File

@@ -0,0 +1,67 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_STATS_H_
#define SRC_STATS_H_
#include <string>
#include <nan.h>
#include "./common.h"
struct ChannelStats {
// stats per channel
int min;
int max;
double sum;
double squaresSum;
double mean;
double stdev;
int minX;
int minY;
int maxX;
int maxY;
ChannelStats():
min(0), max(0), sum(0), squaresSum(0), mean(0), stdev(0)
, minX(0), minY(0), maxX(0), maxY(0) {}
ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal,
double meanVal, double stdevVal, int minXVal, int minYVal, int maxXVal, int maxYVal):
min(minVal), max(maxVal), sum(sumVal), squaresSum(squaresSumVal),
mean(meanVal), stdev(stdevVal), minX(minXVal), minY(minYVal), maxX(maxXVal), maxY(maxYVal) {}
};
struct StatsBaton {
// Input
sharp::InputDescriptor *input;
VipsAccess accessMethod;
// Output
std::vector<ChannelStats> channelStats;
bool isOpaque;
double entropy;
std::string err;
StatsBaton():
input(nullptr),
isOpaque(true),
entropy(0.0)
{}
};
NAN_METHOD(stats);
#endif // SRC_STATS_H_

View File

@@ -1,10 +1,25 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cmath>
#include <string>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include <vips/vector.h>
#include "nan.h"
#include "common.h"
#include "operations.h"
#include "utilities.h"
@@ -45,14 +60,11 @@ NAN_METHOD(cache) {
// Get memory stats
Local<Object> memory = New<Object>();
Set(memory, New("current").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576)))
);
New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576))));
Set(memory, New("high").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576)))
);
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576))));
Set(memory, New("max").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576)))
);
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576))));
// Get file stats
Local<Object> files = New<Object>();
Set(files, New("current").ToLocalChecked(), New<Integer>(vips_tracked_get_files()));
@@ -138,7 +150,9 @@ NAN_METHOD(format) {
// Which load/save operations are available for each compressed format?
Local<Object> format = New<Object>();
for (std::string f : {"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz"}) {
for (std::string f : {
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "pdf", "v"
}) {
// Input
Local<Boolean> hasInputFile =
New<Boolean>(vips_type_find("VipsOperation", (f + "load").c_str()));
@@ -245,7 +259,7 @@ NAN_METHOD(_maxColourDistance) {
}
// Calculate colour distance
maxColourDistance = image1.dE00(image2).max();
} catch (VError err) {
} catch (VError const &err) {
return ThrowError(err.what());
}

View File

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

View File

@@ -5,20 +5,20 @@
"author": "Lovell Fuller <npm@lovell.info>",
"description": "Benchmark and performance tests for sharp",
"scripts": {
"test": "VIPS_WARNING=0 node perf && node random && node parallel"
"test": "node perf && node random && node parallel"
},
"devDependencies": {
"async": "^1.5.2",
"benchmark": "^2.1.0",
"gm": "^1.21.0",
"async": "^2.6.1",
"benchmark": "^2.1.4",
"gm": "^1.23.1",
"imagemagick": "^0.1.3",
"imagemagick-native": "^1.9.2",
"jimp": "^0.2.20",
"lwip": "^0.0.8",
"semver": "^5.1.0"
"imagemagick-native": "^1.9.3",
"jimp": "^0.5.3",
"mapnik": "^4.0.1",
"semver": "^5.5.1"
},
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
"node": ">=6"
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

6
test/coverage/report.sh Executable file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 MiB

BIN
test/fixtures/320x240.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
test/fixtures/8bit_depth.tiff vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
test/fixtures/G31D_MULTI.TIF vendored Normal file

Binary file not shown.

BIN
test/fixtures/Landscape_9.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
test/fixtures/bandbool.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
test/fixtures/booleanTest.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
test/fixtures/centered_image.jpeg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
test/fixtures/cielab-dagams.tiff vendored Normal file

Binary file not shown.

BIN
test/fixtures/embedgravitybird.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

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