Compare commits

..

61 Commits

Author SHA1 Message Date
Lovell Fuller
d5ecc537af Release v0.24.1 2020-02-15 13:48:46 +00:00
Lovell Fuller
c7a49054fd Docs: update libvips URLs for band format/interpretation 2020-02-15 13:43:47 +00:00
Lovell Fuller
a2314c4aa0 Ensure RGBA LZW TIFF info.channel count #2064 2020-02-15 11:46:13 +00:00
Lovell Fuller
1717173f17 Tests: tighten composite offset thresholds 2020-02-15 10:53:30 +00:00
Lovell Fuller
e44c12f029 Bump dependencies, tar is now node>=10 2020-02-15 10:53:00 +00:00
Lovell Fuller
1a98c390fc Prevent sequentialRead for EXIF-based rotate op #2042 2020-01-20 21:50:43 +00:00
Lovell Fuller
91902740e4 Attempt to detect out-of-date homebrew-managed vips 2020-01-19 19:58:24 +00:00
Lovell Fuller
6aa6a93b44 Docs: add details of ignore-scripts to installation guide 2020-01-19 19:27:50 +00:00
Lovell Fuller
b4135ac9b3 Docs: fix any remaining redirects 2020-01-16 20:57:02 +00:00
Lovell Fuller
78906e6551 Update any remaining documentation links 2020-01-16 20:52:19 +00:00
Lovell Fuller
ba29ba1ab7 Release v0.24.0 2020-01-16 19:11:34 +00:00
Lovell Fuller
e0fa15f5cb Update performance test results 2020-01-16 18:56:25 +00:00
Lovell Fuller
82a1ff359d Update dev and bench dependencies 2020-01-15 21:17:25 +00:00
Lovell Fuller
18e1f10a94 Add support for input with 16-bit RGB profile #2037 2020-01-15 21:16:11 +00:00
Lovell Fuller
4828a17643 Tests: add fontconfig static data leak suppression 2020-01-15 21:08:45 +00:00
Lovell Fuller
3b4f95597a Prevent use of sequentialRead for rotate ops #2016 2020-01-14 08:34:54 +00:00
Lovell Fuller
bd52e93fca Deprecate limitInputPixels and sequentialRead, move to input options 2020-01-12 19:59:39 +00:00
Lovell Fuller
6fdc79d569 Docs: ARM64 has prebuilt libvips, not sharp 2020-01-12 10:50:14 +00:00
Lovell Fuller
7dbad7206e Drop support for undef input where opts also provided #1768 2020-01-11 12:02:31 +00:00
Lovell Fuller
a8a0c1e935 Update doc links contained within code
Throw rather than exit if require fails
2020-01-10 16:29:40 +00:00
Lovell Fuller
00bcf60566 Docs: metadata delay/loop properties 2020-01-09 23:04:06 +00:00
Lovell Fuller
4a745f2d2e Expose delay/loop metadata for animated images #1905 2020-01-09 22:51:08 +00:00
Lovell Fuller
057074b238 Docs: improve header layout on narrow screen devices 2020-01-08 23:13:33 +00:00
Lovell Fuller
4b8cc13a05 Add 2020 to list of copyright years 2020-01-08 21:20:53 +00:00
Lovell Fuller
a4e8586b59 Docs: rewrite installation guide 2020-01-08 21:04:16 +00:00
Lovell Fuller
095b37db6a Rename OS X as macOS
Bump CI to latest xcode available on maxOS 10.13
2020-01-08 21:03:50 +00:00
Lovell Fuller
efff650314 Upgrade to libvips v8.9.0 2020-01-08 16:18:43 +00:00
Lovell Fuller
0764ca8fde Changelog entry and credit for #2024 2020-01-08 16:04:42 +00:00
Brychan
403160434b Correctly bind max width and height values when using withoutEnlargement (#2024) 2020-01-07 12:10:35 +00:00
Lovell Fuller
730ab14faa Docs: correct SVG logo URL 2020-01-06 17:51:11 +00:00
Lovell Fuller
51d70c4574 Docs: switch hosting from Readthedocs to Firebase 2020-01-06 14:48:35 +00:00
Lovell Fuller
765ccd2aaa Docs: add icons 2020-01-06 14:44:07 +00:00
Lovell Fuller
5399332f81 Docs: improve section nesting levels 2020-01-06 14:26:54 +00:00
Lovell Fuller
43b7f9fc0e Docs: inject API section name as title 2020-01-04 11:11:27 +00:00
Sebin Benjamin
3925689435 Docs: initial setup for docute documentation site generator 2020-01-04 10:59:08 +00:00
Lovell Fuller
96a994a4c0 Move functions to improve logical ordering of docs 2020-01-03 20:58:57 +00:00
Lovell Fuller
84c20373ec Update leak test suppressions 2020-01-03 20:47:01 +00:00
Lovell Fuller
a216d2945b Upgrade to libvips v8.9.0-rc4, drop support for Node.js 8 2020-01-03 20:26:55 +00:00
Lovell Fuller
755a0caf3d Fix 16-bit, 2-channel PNG w/ ICC profile support #2013 2019-12-20 17:19:33 +00:00
Lovell Fuller
703d90e663 Docs: add example of using metadata and resize to scale 2019-12-16 21:15:49 +00:00
Lovell Fuller
e230ce41d7 Docs: remove references to unmaintained Docker images 2019-12-16 21:07:31 +00:00
Lovell Fuller
596b38a3bb Release v0.23.4 2019-12-05 10:00:29 +00:00
Lovell Fuller
d31a91a599 Expose raw TIFFTAG_PHOTOSHOP metadata #1600 2019-11-29 13:05:07 +00:00
Lovell Fuller
400ef71b6f Handle zero-length Buffers in Node.js v13.2.0+
These now use nullptr internally - see
https://github.com/nodejs/node/pull/30339
2019-11-28 21:20:32 +00:00
Lovell Fuller
bb15cd9067 Improve thread safety with copy-on-write for metadata #1986 2019-11-27 23:15:56 +00:00
Lovell Fuller
6ee6a226e1 Docs: add FreeBSD build status 2019-11-19 11:16:48 +00:00
Lovell Fuller
94d51a94c8 Docs: recommend jemalloc buildpack for Heroku 2019-11-19 11:13:25 +00:00
Lovell Fuller
d0feb4156c Release v0.23.3 2019-11-17 15:42:07 +00:00
Lovell Fuller
0d1278dade Improve C++ linting, move exceptions inline 2019-11-14 22:06:38 +00:00
Lovell Fuller
1b401b1195 Add FreeBSD to CI via Cirrus #1953 2019-11-14 21:55:20 +00:00
Lovell Fuller
11daa3b4d1 Tests: flatten to mid-grey before generating fingerprint 2019-11-14 13:18:14 +00:00
Lovell Fuller
88a3919ce0 Docs refresh 2019-11-14 11:32:49 +00:00
Lovell Fuller
c41b87303d Ensure trim op supports image-in-alpha #1597 2019-11-14 11:29:45 +00:00
Lovell Fuller
833aaead56 Ensure modulate can co-exist with other colour ops #1958 2019-11-11 22:16:28 +00:00
Lovell Fuller
ff98d2e44a Docs: using custom binaries with prebuild-install 2019-11-07 20:00:03 +00:00
Lovell Fuller
fcf05f608a Changelog entry for #1952 2019-11-07 19:59:18 +00:00
Pouya Eghbali
9baf38db44 Allow compilation of v0.23.x on FreeBSD and variants (#1952) 2019-11-06 23:11:44 +00:00
Lovell Fuller
69050ef1c8 Add funding link to libvips' Open Collective 2019-11-05 21:51:14 +00:00
Lovell Fuller
b35b9f7850 Ensure Travis does not cache builds #1948 2019-11-01 16:17:36 +00:00
Lovell Fuller
500ae97cac Changelog entry for #1921 2019-11-01 16:15:02 +00:00
Brendan Kennedy
d5b7040557 Ensure tile overlap option works as expected (#1921) 2019-10-30 20:02:07 +00:00
77 changed files with 1885 additions and 861 deletions

13
.cirrus.yml Normal file
View File

@@ -0,0 +1,13 @@
freebsd_instance:
image_family: freebsd-12-0
task:
prerequisites_script:
- sed -i '' 's/quarterly/latest/g' /etc/pkg/FreeBSD.conf
- pkg update -f
- pkg upgrade -y
- pkg install -y pkgconf vips libnghttp2 node npm
install_script:
- npm install --unsafe-perm
test_script:
- npm test

View File

@@ -71,7 +71,7 @@ The public API is documented with [JSDoc](http://usejsdoc.org/) annotated commen
These can be converted to Markdown by running:
```sh
npm run docs
npm run docs-build
```
Please include documentation updates in any Pull Request that modifies the public API.

View File

@@ -7,12 +7,14 @@ assignees: ''
---
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/en/stable/install/)?
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)?
Have you ensured the platform and version of Node.js used for `npm install` is the same as the platform and version of Node.js used at runtime?
If you are installing as a `root` or `sudo` user, have you tried with the `npm install --unsafe-perm` flag?
If you are using the `ignore-scripts` feature of `npm`, have you tried with the `npm install --ignore-scripts=false` flag?
What is the complete output of running `npm install --verbose sharp`? Have you checked this output for useful error messages?
What is the output of running `npx envinfo --binaries --languages --system --utilities`?

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ vendor
.vscode/
package-lock.json
.idea
.firebase

View File

@@ -1,89 +1,75 @@
matrix:
include:
- name: "Linux (glibc) - Node 8"
- name: "Linux (glibc 2.17+) - Node.js 10"
os: linux
dist: trusty
sudo: false
language: node_js
node_js: "8"
- name: "Linux (glibc) - Node 10"
os: linux
dist: trusty
sudo: false
dist: xenial
language: node_js
node_js: "10"
- name: "Linux (glibc) - Node 12"
- name: "Linux (glibc 2.17+) - Node.js 12"
os: linux
dist: trusty
sudo: false
dist: xenial
language: node_js
node_js: "12"
- name: "Linux (glibc) - Node 13"
- name: "Linux (glibc 2.17+) - Node.js 13"
os: linux
dist: trusty
sudo: false
dist: xenial
language: node_js
node_js: "13"
after_success:
- npm install coveralls
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
- name: "Linux (musl) - Node 8"
- name: "Linux (musl 1.1.20+) - Node.js 10"
os: linux
dist: trusty
sudo: true
dist: bionic
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 run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10.17.0-alpine3.9 # https://github.com/nodejs/docker-node/issues/1158
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux (musl) - Node 10"
- name: "Linux (musl 1.1.20+) - Node.js 12"
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 12"
os: linux
dist: trusty
sudo: true
dist: bionic
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12.0-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux (musl) - Node 13"
- name: "Linux (musl 1.1.20+) - Node.js 13"
os: linux
dist: trusty
sudo: true
dist: bionic
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:13.0-alpine
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "OS X - Node 8"
- name: "Linux ARM64v8 (glibc 2.29+) - Node.js 10"
arch: arm64
os: linux
dist: bionic
language: minimal
before_install:
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
- sudo docker exec sharp apt-get update
- sudo docker exec sharp apt-get install -y build-essential git python2 nodejs npm
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "macOS (10.13+) - Node.js 10"
os: osx
osx_image: xcode9.2
language: node_js
node_js: "8"
- name: "OS X - Node 10"
os: osx
osx_image: xcode9.2
osx_image: xcode10.1
language: node_js
node_js: "10"
- name: "OS X - Node 12"
- name: "macOS (10.13+) - Node.js 12"
os: osx
osx_image: xcode9.2
osx_image: xcode10.1
language: node_js
node_js: "12"
- name: "OS X - Node 13"
- name: "macOS (10.13+) - Node.js 13"
os: osx
osx_image: xcode10
osx_image: xcode10.1
language: node_js
node_js: "13"
cache:
npm: false

View File

@@ -20,8 +20,8 @@ Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most modern 64-bit OS X, Windows and Linux systems running
Node versions 8, 10, 12 and 13
Most modern 64-bit macOS, Windows and Linux systems running
Node versions 10, 12 and 13
do not require any additional install or runtime dependencies.
## Examples
@@ -90,10 +90,10 @@ readableStream
### Documentation
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).
[installation instructions](https://sharp.pixelplumbing.com/install),
[API documentation](https://sharp.pixelplumbing.com/api-constructor),
[benchmark tests](https://sharp.pixelplumbing.com/performance) and
[changelog](https://sharp.pixelplumbing.com/changelog).
### Contributing
@@ -102,7 +102,7 @@ covers reporting bugs, requesting features and submitting code changes.
### Licensing
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -1,10 +1,9 @@
os: Visual Studio 2015
os: Visual Studio 2017
version: "{build}"
build: off
platform: x64
environment:
matrix:
- nodejs_version: "8"
- nodejs_version: "10"
- nodejs_version: "12"
- nodejs_version: "13"

View File

@@ -11,6 +11,7 @@
],
'sources': [
'src/libvips/cplusplus/VError.cpp',
'src/libvips/cplusplus/VConnection.cpp',
'src/libvips/cplusplus/VInterpolate.cpp',
'src/libvips/cplusplus/VImage.cpp'
],
@@ -127,7 +128,6 @@
'../vendor/lib/libgobject-2.0.so',
# Dependencies of dependencies, included for openSUSE support
'../vendor/lib/libcairo.so',
'../vendor/lib/libcroco-0.6.so',
'../vendor/lib/libexif.so',
'../vendor/lib/libexpat.so',
'../vendor/lib/libffi.so',
@@ -183,16 +183,22 @@
},
'configurations': {
'Release': {
'cflags_cc': [
'-Wno-cast-function-type'
],
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'msvs_disabled_warnings': [
4275
'conditions': [
['OS == "linux"', {
'cflags_cc': [
'-Wno-cast-function-type'
]
}],
['OS == "win"', {
'msvs_settings': {
'VCCLCompilerTool': {
'ExceptionHandling': 1
}
},
'msvs_disabled_warnings': [
4275
]
}]
]
}
},

View File

@@ -1,6 +1,6 @@
# sharp
<img src="image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
<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">
The typical use case for this high speed Node.js module
is to convert large images in common formats to
@@ -16,8 +16,8 @@ Lanczos resampling ensures quality is not sacrificed for speed.
As well as image resizing, operations such as
rotation, extraction, compositing and gamma correction are available.
Most modern 64-bit OS X, Windows and Linux systems running
Node versions 8, 10, 12 and 13
Most modern 64-bit macOS, Windows and Linux systems running
Node versions 10, 12 and 13
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)
@@ -34,15 +34,14 @@ A single input Stream can be split into multiple processing pipelines and output
Deep Zoom image pyramids can be generated,
suitable for use with "slippy map" tile viewers like
[OpenSeadragon](https://github.com/openseadragon/openseadragon)
and [Leaflet](https://github.com/turban/Leaflet.Zoomify).
[OpenSeadragon](https://github.com/openseadragon/openseadragon).
### Fast
This module is powered by the blazingly fast
[libvips](https://github.com/libvips/libvips) image processing library,
originally created in 1989 at Birkbeck College
and currently maintained by
and currently maintained by a small team led by
[John Cupitt](https://github.com/jcupitt).
Only small regions of uncompressed image data
@@ -128,12 +127,16 @@ the help and code contributions of the following people:
* [Jakub Michálek](https://github.com/Goues)
* [Ilya Ovdin](https://github.com/iovdin)
* [Andargor](https://github.com/Andargor)
* [Nicolas Stepien](https://github.com/MayhemYDG)
* [Paul Neave](https://github.com/neave)
* [Brendan Kennedy](https://github.com/rustyguts)
* [Brychan Bennett-Odlum](https://github.com/BrychanOdlum)
Thank you!
### Licensing
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -32,6 +32,10 @@ sharp('rgb.jpg')
Returns **Sharp**
**Meta**
- **since**: 0.21.2
## extractChannel
Extract a single channel from a multi-channel image.

View File

@@ -61,6 +61,10 @@ sharp('input.png')
Returns **Sharp**
**Meta**
- **since**: 0.22.0
[1]: https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
[2]: https://www.cairographics.org/operators/

View File

@@ -11,6 +11,11 @@
- `options` **[Object][3]?** if present, is an Object with optional attributes.
- `options.failOnError` **[Boolean][4]** by default halt processing and raise an error when loading invalid images.
Set this flag to `false` if you'd rather apply a "best effort" to decode images, even if the data is corrupt or invalid. (optional, default `true`)
- `options.limitInputPixels` **([Number][5] \| [Boolean][4])** Do not process input images where the number of pixels
(width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
- `options.sequentialRead` **[Boolean][4]** Set this to `true` to use sequential rather than random access where possible.
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
- `options.density` **[Number][5]** number representing the DPI for vector images. (optional, default `72`)
- `options.pages` **[Number][5]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
- `options.page` **[Number][5]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
@@ -67,43 +72,25 @@ sharp({
Returns **[Sharp][8]**
### format
## clone
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
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
sharp.queue.on('change', function(queueLength) {
console.log('Queue contains ' + queueLength + ' task(s)');
});
const pipeline = sharp().rotate();
pipeline.clone().resize(800, 600).pipe(firstWritableStream);
pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
readableStream.pipe(pipeline);
// firstWritableStream receives auto-rotated, resized readableStream
// secondWritableStream receives auto-rotated, extracted region of readableStream
```
Returns **[Sharp][8]**
[1]: https://nodejs.org/api/buffer.html
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String

View File

@@ -1,24 +1,5 @@
<!-- 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.
@@ -35,7 +16,9 @@ A `Promise` is returned when `callback` is not provided.
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
- `pageHeight`: Number of pixels high each page in this PDF image will be.
- `pageHeight`: Number of pixels high each page in a multi-page image will be.
- `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
- `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
- `pagePrimary`: Number of the primary page in a HEIF image
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
@@ -44,6 +27,7 @@ A `Promise` is returned when `callback` is not provided.
- `icc`: Buffer containing raw [ICC][3] profile data, if present
- `iptc`: Buffer containing raw IPTC data, if present
- `xmp`: Buffer containing raw XMP data, if present
- `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
### Parameters
@@ -104,37 +88,9 @@ image
Returns **[Promise][5]&lt;[Object][6]>**
## limitInputPixels
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
Do not process input images where the number of pixels (width 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
[2]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat
[3]: https://www.npmjs.com/package/icc
@@ -143,9 +99,3 @@ Returns **Sharp**
[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

View File

@@ -287,6 +287,10 @@ sharp(input)
Returns **Sharp**
**Meta**
- **since**: 0.21.1
## modulate
Transforms the image using brightness, saturation and hue rotation.
@@ -322,6 +326,10 @@ sharp(input)
Returns **Sharp**
**Meta**
- **since**: 0.22.1
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object

View File

@@ -112,6 +112,28 @@ sharp('input.jpg')
Returns **Sharp**
## toFormat
Force output to a given format.
### Parameters
- `format` **([String][2] \| [Object][6])** as a String or an Object with an 'id' attribute
- `options` **[Object][6]** output options
### Examples
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.toFormat('png')
.toBuffer();
```
- Throws **[Error][4]** unsupported format or options
Returns **Sharp**
## jpeg
Use these JPEG options for output image.
@@ -268,6 +290,10 @@ Most versions of libheif support only the patent-encumbered HEVC compression for
Returns **Sharp**
**Meta**
- **since**: 0.23.0
## raw
Force output to be raw, uncompressed uint8 pixel data.
@@ -283,28 +309,6 @@ const { data, info } = await sharp('input.jpg')
Returns **Sharp**
## toFormat
Force output to a given format.
### Parameters
- `format` **([String][2] \| [Object][6])** as a String or an Object with an 'id' attribute
- `options` **[Object][6]** output options
### Examples
```javascript
// Convert any input to PNG output
const data = await sharp(input)
.toFormat('png')
.toBuffer();
```
- Throws **[Error][4]** unsupported format or options
Returns **Sharp**
## tile
Use tile-based deep zoom (image pyramid) output.
@@ -315,11 +319,11 @@ Warning: multiple sharp instances concurrently producing tile output can expose
### Parameters
- `options` **[Object][6]?**
- `options` **[Object][6]?**
- `options.size` **[Number][9]** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `options.overlap` **[Number][9]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `options.angle` **[Number][9]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
- `options.background` **([String][2] \| [Object][6])** background colour, parsed by the [color][10] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
- `options.background` **([String][2] \| [Object][6])** background colour, parsed by the [color][10] module, defaults to white without transparency. (optional, default `{r:255,g:255,b:255,alpha:1}`)
- `options.depth` **[String][2]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
- `options.skipBlanks` **[Number][9]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
- `options.container` **[String][2]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)

View File

@@ -114,6 +114,15 @@ sharp(input)
});
```
```javascript
const scaleByHalf = await sharp(input)
.metadata()
.then(({ width }) => sharp(input)
.resize(Math.round(width * 0.5))
.toBuffer()
);
```
- Throws **[Error][13]** Invalid parameters
Returns **Sharp**
@@ -196,6 +205,8 @@ Returns **Sharp**
## trim
Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
### Parameters

View File

@@ -1,5 +1,27 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
## format
An Object containing nested boolean values representing the available input and output formats/methods.
### Examples
```javascript
console.log(sharp.format);
```
Returns **[Object][1]**
## versions
An Object containing the version numbers of libvips and its dependencies.
### Examples
```javascript
console.log(sharp.versions);
```
## cache
Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
@@ -54,6 +76,21 @@ sharp.concurrency(0); // 4
Returns **[Number][3]** concurrency
## queue
An EventEmitter that emits a `change` event when a task is either:
- queued, waiting for _libuv_ to provide a worker thread
- complete
### Examples
```javascript
sharp.queue.on('change', function(queueLength) {
console.log('Queue contains ' + queueLength + ' task(s)');
});
```
## counters
Provides access to internal task counters.

View File

@@ -1,10 +1,74 @@
# Changelog
### v0.23 - "*vision*"
## v0.24 - "*wit*"
Requires libvips v8.9.0.
### v0.24.1 - 15<sup>th</sup> February 2020
* Prevent use of sequentialRead for EXIF-based rotate operation.
[#2042](https://github.com/lovell/sharp/issues/2042)
* Ensure RGBA LZW TIFF returns correct channel count.
[#2064](https://github.com/lovell/sharp/issues/2064)
### v0.24.0 - 16<sup>th</sup> January 2020
* Drop support for Node.js 8.
[#1910](https://github.com/lovell/sharp/issues/1910)
* Drop support for undefined input where options also provided.
[#1768](https://github.com/lovell/sharp/issues/1768)
* Move `limitInputPixels` and `sequentialRead` to input options, deprecating functions of the same name.
* Expose `delay` and `loop` metadata for animated images.
[#1905](https://github.com/lovell/sharp/issues/1905)
* Ensure correct colour output for 16-bit, 2-channel PNG input with ICC profile.
[#2013](https://github.com/lovell/sharp/issues/2013)
* Prevent use of sequentialRead for rotate operations.
[#2016](https://github.com/lovell/sharp/issues/2016)
* Correctly bind max width and height values when using withoutEnlargement.
[#2024](https://github.com/lovell/sharp/pull/2024)
[@BrychanOdlum](https://github.com/BrychanOdlum)
* Add support for input with 16-bit RGB profile.
[#2037](https://github.com/lovell/sharp/issues/2037)
## v0.23 - "*vision*"
Requires libvips v8.8.1.
#### v0.23.2 - 28<sup>th</sup> October 2019
### v0.23.4 - 5<sup>th</sup> December 2019
* Handle zero-length Buffer objects when using Node.js v13.2.0+.
* Expose raw TIFFTAG_PHOTOSHOP metadata.
[#1600](https://github.com/lovell/sharp/issues/1600)
* Improve thread safety by using copy-on-write when updating metadata.
[#1986](https://github.com/lovell/sharp/issues/1986)
### v0.23.3 - 17<sup>th</sup> November 2019
* Ensure `trim` operation supports images contained in the alpha channel.
[#1597](https://github.com/lovell/sharp/issues/1597)
* Ensure tile `overlap` option works as expected.
[#1921](https://github.com/lovell/sharp/pull/1921)
[@rustyguts](https://github.com/rustyguts)
* Allow compilation on FreeBSD and variants (broken since v0.23.0)
[#1952](https://github.com/lovell/sharp/pull/1952)
[@pouya-eghbali](https://github.com/pouya-eghbali)
* Ensure `modulate` and other colour-based operations can co-exist.
[#1958](https://github.com/lovell/sharp/issues/1958)
### v0.23.2 - 28<sup>th</sup> October 2019
* Add `background` option to tile output operation.
[#1924](https://github.com/lovell/sharp/pull/1924)
@@ -14,7 +78,7 @@ Requires libvips v8.8.1.
[#1932](https://github.com/lovell/sharp/pull/1932)
[@MayhemYDG](https://github.com/MayhemYDG)
#### v0.23.1 - 26<sup>th</sup> September 2019
### v0.23.1 - 26<sup>th</sup> September 2019
* Ensure `sharp.format.vips` is present and correct (filesystem only).
[#1813](https://github.com/lovell/sharp/issues/1813)
@@ -36,7 +100,7 @@ Requires libvips v8.8.1.
* Ensure image is at least 3x3 pixels before attempting trim operation.
#### v0.23.0 - 29<sup>th</sup> July 2019
### v0.23.0 - 29<sup>th</sup> July 2019
* Remove `overlayWith` previously deprecated in v0.22.0.
@@ -66,11 +130,11 @@ Requires libvips v8.8.1.
[#1755](https://github.com/lovell/sharp/pull/1755)
[@iovdin](https://github.com/iovdin)
### v0.22 - "*uptake*"
## v0.22 - "*uptake*"
Requires libvips v8.7.4.
#### v0.22.1 - 25<sup>th</sup> April 2019
### v0.22.1 - 25<sup>th</sup> April 2019
* Add `modulate` operation for brightness, saturation and hue.
[#1601](https://github.com/lovell/sharp/pull/1601)
@@ -83,7 +147,7 @@ Requires libvips v8.7.4.
* Add support for Node 12.
[#1668](https://github.com/lovell/sharp/issues/1668)
#### v0.22.0 - 18<sup>th</sup> March 2019
### 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`.
@@ -101,18 +165,18 @@ Requires libvips v8.7.4.
[#1595](https://github.com/lovell/sharp/pull/1595)
[@ramiel](https://github.com/ramiel)
### v0.21 - "*teeth*"
## v0.21 - "*teeth*"
Requires libvips v8.7.0.
#### v0.21.3 - 19<sup>th</sup> January 2019
### 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
### v0.21.2 - 13<sup>th</sup> January 2019
* Ensure all metadata is removed from PNG output unless `withMetadata` used.
@@ -137,7 +201,7 @@ Requires libvips v8.7.0.
* 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
### 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)
@@ -166,7 +230,7 @@ Requires libvips v8.7.0.
[#1483](https://github.com/lovell/sharp/pull/1483)
[@mbklein](https://github.com/mbklein)
#### v0.21.0 - 4<sup>th</sup> October 2018
### v0.21.0 - 4<sup>th</sup> October 2018
* Deprecate the following resize-related functions:
`crop`, `embed`, `ignoreAspectRatio`, `max`, `min` and `withoutEnlargement`.
@@ -203,11 +267,11 @@ Requires libvips v8.7.0.
[#1385](https://github.com/lovell/sharp/pull/1385)
[@freezy](https://github.com/freezy)
### v0.20 - "*prebuild*"
## v0.20 - "*prebuild*"
Requires libvips v8.6.1.
#### v0.20.8 - 5<sup>th</sup> September 2018
### v0.20.8 - 5<sup>th</sup> September 2018
* Avoid race conditions when creating directories during installation.
[#1358](https://github.com/lovell/sharp/pull/1358)
@@ -217,12 +281,12 @@ Requires libvips v8.6.1.
[#1362](https://github.com/lovell/sharp/pull/1362)
[@aeirola](https://github.com/aeirola)
#### v0.20.7 - 21<sup>st</sup> August 2018
### 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
### 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)
@@ -253,13 +317,13 @@ Requires libvips v8.6.1.
* Add experimental entropy field to stats response.
#### v0.20.5 - 27<sup>th</sup> June 2018
### 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
### 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)
@@ -269,13 +333,13 @@ Requires libvips v8.6.1.
[#1257](https://github.com/lovell/sharp/issues/1257)
[@jeremychone](https://github.com/jeremychone)
#### v0.20.3 - 29<sup>th</sup> May 2018
### 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
### v0.20.2 - 28<sup>th</sup> April 2018
* Add tint operation to set image chroma.
[#825](https://github.com/lovell/sharp/pull/825)
@@ -293,7 +357,7 @@ Requires libvips v8.6.1.
[#1208](https://github.com/lovell/sharp/pull/1208)
[@woolite64](https://github.com/woolite64)
#### v0.20.1 - 17<sup>th</sup> March 2018
### 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)
@@ -306,16 +370,16 @@ Requires libvips v8.6.1.
[#1161](https://github.com/lovell/sharp/pull/1161)
[@BiancoA](https://github.com/BiancoA)
#### v0.20.0 - 5<sup>th</sup> March 2018
### 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*"
## v0.19 - "*suit*"
Requires libvips v8.6.1.
#### v0.19.1 - 24<sup>th</sup> February 2018
### v0.19.1 - 24<sup>th</sup> February 2018
* Expose libvips' linear transform feature.
[#1024](https://github.com/lovell/sharp/pull/1024)
@@ -329,7 +393,7 @@ Requires libvips v8.6.1.
[#1134](https://github.com/lovell/sharp/issues/1134)
[@pieh](https://github.com/pieh)
#### v0.19.0 - 11<sup>th</sup> January 2018
### v0.19.0 - 11<sup>th</sup> January 2018
* Expose offset coordinates of strategy-based crop.
[#868](https://github.com/lovell/sharp/issues/868)
@@ -373,17 +437,17 @@ Requires libvips v8.6.1.
* TIFF output: switch default predictor from 'none' to 'horizontal' to match libvips' behaviour.
### v0.18 - "*ridge*"
## v0.18 - "*ridge*"
Requires libvips v8.5.5.
#### v0.18.4 - 18<sup>th</sup> September 2017
### v0.18.4 - 18<sup>th</sup> September 2017
* Ensure input Buffer really is marked as Persistent, prevents mark-sweep GC.
[#950](https://github.com/lovell/sharp/issues/950)
[@lfdoherty](https://github.com/lfdoherty)
#### v0.18.3 - 13<sup>th</sup> September 2017
### v0.18.3 - 13<sup>th</sup> September 2017
* Skip shrink-on-load when trimming.
[#888](https://github.com/lovell/sharp/pull/888)
@@ -393,7 +457,7 @@ Requires libvips v8.5.5.
[#945](https://github.com/lovell/sharp/pull/945)
[@pbomb](https://github.com/pbomb)
#### v0.18.2 - 1<sup>st</sup> July 2017
### v0.18.2 - 1<sup>st</sup> July 2017
* Expose libvips' xres and yres properties for TIFF output.
[#828](https://github.com/lovell/sharp/pull/828)
@@ -410,13 +474,13 @@ Requires libvips v8.5.5.
[#857](https://github.com/lovell/sharp/pull/857)
[@ekremkaraca](https://github.com/ekremkaraca)
#### v0.18.1 - 30<sup>th</sup> May 2017
### v0.18.1 - 30<sup>th</sup> May 2017
* Remove regression from #781 that could cause incorrect shrink calculation.
[#831](https://github.com/lovell/sharp/issues/831)
[@suprMax](https://github.com/suprMax)
#### v0.18.0 - 30<sup>th</sup> May 2017
### v0.18.0 - 30<sup>th</sup> May 2017
* Remove the previously-deprecated output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
@@ -474,11 +538,11 @@ Requires libvips v8.5.5.
[#814](https://github.com/lovell/sharp/pull/814)
[@jingsam](https://github.com/jingsam)
### v0.17 - "*quill*"
## v0.17 - "*quill*"
Requires libvips v8.4.2.
#### v0.17.3 - 1<sup>st</sup> April 2017
### v0.17.3 - 1<sup>st</sup> April 2017
* Allow toBuffer to optionally resolve a Promise with both info and data.
[#143](https://github.com/lovell/sharp/issues/143)
@@ -496,7 +560,7 @@ Requires libvips v8.4.2.
[#738](https://github.com/lovell/sharp/pull/738)
[@kristojorg](https://github.com/kristojorg)
#### v0.17.2 - 11<sup>th</sup> February 2017
### v0.17.2 - 11<sup>th</sup> February 2017
* Ensure Readable side of Stream can start flowing after Writable side has finished.
[#671](https://github.com/lovell/sharp/issues/671)
@@ -506,7 +570,7 @@ Requires libvips v8.4.2.
[#685](https://github.com/lovell/sharp/pull/685)
[@rnanwani](https://github.com/rnanwani)
#### v0.17.1 - 15<sup>th</sup> January 2017
### v0.17.1 - 15<sup>th</sup> January 2017
* Improve error messages for invalid parameters.
[@spikeon](https://github.com/spikeon)
@@ -519,7 +583,7 @@ Requires libvips v8.4.2.
[@wangzhiwei1888](https://github.com/wangzhiwei1888)
[#679](https://github.com/lovell/sharp/issues/679)
#### v0.17.0 - 11<sup>th</sup> December 2016
### v0.17.0 - 11<sup>th</sup> December 2016
* Drop support for versions of Node prior to v4.
@@ -556,17 +620,17 @@ Requires libvips v8.4.2.
[#646](https://github.com/lovell/sharp/issues/646)
[@DaGaMs](https://github.com/DaGaMs)
### v0.16 - "*pencil*"
## v0.16 - "*pencil*"
Requires libvips v8.3.3
#### v0.16.2 - 22<sup>nd</sup> October 2016
### v0.16.2 - 22<sup>nd</sup> October 2016
* Restrict readelf usage to Linux only when detecting global libvips version.
[#602](https://github.com/lovell/sharp/issues/602)
[@caoko](https://github.com/caoko)
#### v0.16.1 - 13<sup>th</sup> October 2016
### v0.16.1 - 13<sup>th</sup> October 2016
* C++11 ABI version is now auto-detected, remove sharp-cxx11 installation flag.
@@ -585,7 +649,7 @@ Requires libvips v8.3.3
[#566](https://github.com/lovell/sharp/issues/566)
[@Nateowami](https://github.com/Nateowami)
#### v0.16.0 - 18<sup>th</sup> August 2016
### v0.16.0 - 18<sup>th</sup> August 2016
* Add pre-compiled libvips for OS X, ARMv7 and ARMv8.
[#312](https://github.com/lovell/sharp/issues/312)
@@ -625,11 +689,11 @@ Requires libvips v8.3.3
* Remove deprecated interpolateWith method - use resize(w, h, { interpolator: ... })
[#310](https://github.com/lovell/sharp/issues/310)
### v0.15 - "*outfit*"
## v0.15 - "*outfit*"
Requires libvips v8.3.1
#### v0.15.1 - 12<sup>th</sup> July 2016
### v0.15.1 - 12<sup>th</sup> July 2016
* Concat Stream-based input in single operation for ~+3% perf and less GC.
[#429](https://github.com/lovell/sharp/issues/429)
@@ -695,7 +759,7 @@ Requires libvips v8.3.1
[#501](https://github.com/lovell/sharp/pull/501)
[@mhirsch](https://github.com/mhirsch)
#### v0.15.0 - 21<sup>st</sup> May 2016
### v0.15.0 - 21<sup>st</sup> May 2016
* Use libvips' new Lanczos 3 kernel as default for image reduction.
Deprecate interpolateWith method, now provided as a resize option.
@@ -713,11 +777,11 @@ Requires libvips v8.3.1
[#413](https://github.com/lovell/sharp/issues/413)
[@jardakotesovec](https://github.com/jardakotesovec)
### v0.14 - "*needle*"
## v0.14 - "*needle*"
Requires libvips v8.2.3
#### v0.14.1 - 16<sup>th</sup> April 2016
### v0.14.1 - 16<sup>th</sup> April 2016
* Allow removal of limitation on input pixel count via limitInputPixels. Use with care.
[#250](https://github.com/lovell/sharp/issues/250)
@@ -741,7 +805,7 @@ Requires libvips v8.2.3
[#412](https://github.com/lovell/sharp/issues/412)
[@nouh](https://github.com/nouh)
#### v0.14.0 - 2<sup>nd</sup> April 2016
### v0.14.0 - 2<sup>nd</sup> April 2016
* Add ability to extend (pad) the edges of an image.
[#128](https://github.com/lovell/sharp/issues/128)
@@ -778,17 +842,17 @@ Requires libvips v8.2.3
* Remove deprecated style of calling extract API. Breaks calls using positional arguments.
[#276](https://github.com/lovell/sharp/issues/276)
### v0.13 - "*mind*"
## v0.13 - "*mind*"
Requires libvips v8.2.2
#### v0.13.1 - 27<sup>th</sup> February 2016
### v0.13.1 - 27<sup>th</sup> February 2016
* Fix embedding onto transparent backgrounds; regression introduced in v0.13.0.
[#366](https://github.com/lovell/sharp/issues/366)
[@diegocsandrim](https://github.com/diegocsandrim)
#### v0.13.0 - 15<sup>th</sup> February 2016
### v0.13.0 - 15<sup>th</sup> February 2016
* Improve vector image support by allowing control of density/DPI.
Switch pre-built libs from Imagemagick to Graphicsmagick.
@@ -833,11 +897,11 @@ Requires libvips v8.2.2
* Add support for gamma correction of images with an alpha channel.
### v0.12 - "*look*"
## v0.12 - "*look*"
Requires libvips v8.2.0
#### v0.12.2 - 16<sup>th</sup> January 2016
### v0.12.2 - 16<sup>th</sup> January 2016
* Upgrade libvips to v8.2.0 for improved vips_shrink.
@@ -855,7 +919,7 @@ Requires libvips v8.2.0
[#331](https://github.com/lovell/sharp/issues/331)
[@dtoubelis](https://github.com/dtoubelis)
#### v0.12.1 - 12<sup>th</sup> December 2015
### v0.12.1 - 12<sup>th</sup> December 2015
* Allow use of SIMD vector instructions (via liborc) to be toggled on/off.
[#172](https://github.com/lovell/sharp/issues/172)
@@ -868,7 +932,7 @@ Requires libvips v8.2.0
* Use the NPM-configured HTTPS proxy, if any, for binary downloads.
#### v0.12.0 - 23<sup>rd</sup> November 2015
### v0.12.0 - 23<sup>rd</sup> November 2015
* Bundle pre-compiled libvips and its dependencies for 64-bit Linux and Windows.
[#42](https://github.com/lovell/sharp/issues/42)
@@ -904,9 +968,9 @@ Requires libvips v8.2.0
[#309](https://github.com/lovell/sharp/pull/309)
[@papandreou](https://github.com/papandreou)
### v0.11 - "*knife*"
## v0.11 - "*knife*"
#### v0.11.4 - 5<sup>th</sup> November 2015
### v0.11.4 - 5<sup>th</sup> November 2015
* Add corners, e.g. `northeast`, to existing `gravity` option.
[#291](https://github.com/lovell/sharp/pull/291)
@@ -920,13 +984,13 @@ Requires libvips v8.2.0
[#287](https://github.com/lovell/sharp/pull/287)
[@vlapo](https://github.com/vlapo)
#### v0.11.3 - 8<sup>th</sup> September 2015
### v0.11.3 - 8<sup>th</sup> September 2015
* Intrepret blurSigma, sharpenFlat, and sharpenJagged as double precision.
[#263](https://github.com/lovell/sharp/pull/263)
[@chrisriley](https://github.com/chrisriley)
#### v0.11.2 - 28<sup>th</sup> August 2015
### v0.11.2 - 28<sup>th</sup> August 2015
* Allow crop gravity to be provided as a String.
[#255](https://github.com/lovell/sharp/pull/255)
@@ -934,7 +998,7 @@ Requires libvips v8.2.0
* Add support for io.js v3 and Node v4.
[#246](https://github.com/lovell/sharp/issues/246)
#### v0.11.1 - 12<sup>th</sup> August 2015
### v0.11.1 - 12<sup>th</sup> August 2015
* Silence MSVC warning: "C4530: C++ exception handler used, but unwind semantics are not enabled".
[#244](https://github.com/lovell/sharp/pull/244)
@@ -944,7 +1008,7 @@ Requires libvips v8.2.0
[#249](https://github.com/lovell/sharp/issues/249)
[@compeak](https://github.com/compeak)
#### v0.11.0 - 15<sup>th</sup> July 2015
### v0.11.0 - 15<sup>th</sup> July 2015
* Allow alpha transparency compositing via new `overlayWith` method.
[#97](https://github.com/lovell/sharp/issues/97)
@@ -972,9 +1036,9 @@ Requires libvips v8.2.0
[#238](https://github.com/lovell/sharp/issues/238)
[@richardadjogah](https://github.com/richardadjogah)
### v0.10 - "*judgment*"
## v0.10 - "*judgment*"
#### v0.10.1 - 1<sup>st</sup> June 2015
### v0.10.1 - 1<sup>st</sup> June 2015
* Allow embed of image with alpha transparency onto non-transparent background.
[#204](https://github.com/lovell/sharp/issues/204)
@@ -984,7 +1048,7 @@ Requires libvips v8.2.0
[#228](https://github.com/lovell/sharp/issues/228)
[@doggan](https://github.com/doggan)
#### v0.10.0 - 23<sup>rd</sup> April 2015
### v0.10.0 - 23<sup>rd</sup> April 2015
* Add support for Windows (x86).
[#19](https://github.com/lovell/sharp/issues/19)

View File

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

116
docs/firebase.json Normal file
View File

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

139
docs/index.html Normal file
View File

@@ -0,0 +1,139 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" type="image/png" href="https://pixel.plumbing/px/32x32/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://pixel.plumbing/px/152x152/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://pixel.plumbing/px/144x144/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://pixel.plumbing/px/120x120/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://pixel.plumbing/px/114x114/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/sharp-logo.svg">
<link rel="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.css">
<style>
@media (max-width: 576px) {
.shorten-strapline {
display: none;
}
}
img[alt='sharp logo'] {
margin: 0 0 16px 16px;
}
</style>
<title>sharp - High performance Node.js image processing</title>
</head>
<body>
<div id="docute"></div>
<script src="https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docute-google-analytics@1/dist/index.min.js"></script>
<script>
var docuteApiTitlePlugin = {
name: 'apiTitle',
extend(api) {
api.processMarkdown(function (md) {
var title = api.store.getters.sidebarLinks
.filter(function (sidebarLink) {
return sidebarLink.link === api.router.history.current.path
})
.map(function (sidebarLink) {
return sidebarLink.title;
})[0];
return title
? md.replace(/<!-- Generated by documentation.js. Update this documentation by updating the source code. -->/, '# ' + title)
: md;
});
}
};
new Docute({
router: { mode: 'history' },
logo: '<div style="display:flex;align-items:center">'
+ '<strong>sharp</strong> '
+ '<img src="https://pixel.plumbing/px/16x16/sharp-logo.svg" style="padding:8px" alt="#"> '
+ '<span style="opacity:0.8;white-space:pre" class="shorten-strapline">High performance </span> '
+ '<span style="opacity:0.8">Node.js image processing</span> '
+ '</div>',
darkThemeToggler: true,
detectSystemDarkTheme: true,
footer: '<a href="https://pixelplumbing.com/" target="_blank">pixelplumbing.com<a>',
plugins: [
docuteGoogleAnalytics('UA-13034748-12'),
docuteApiTitlePlugin
],
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.24.1/docs',
nav: [
{
title: 'Funding',
link: 'https://opencollective.com/libvips'
},
{
title: 'GitHub',
link: 'https://github.com/lovell/sharp'
}
],
sidebar: [
{
title: 'Home',
link: '/'
},
{
title: 'Installation',
link: '/install'
},
{
title: 'API',
children: [
{
title: 'Constructor',
link: '/api-constructor'
},
{
title: 'Input metadata',
link: '/api-input'
},
{
title: 'Output options',
link: '/api-output'
},
{
title: 'Resizing images',
link: '/api-resize'
},
{
title: 'Compositing images',
link: '/api-composite'
},
{
title: 'Image operations',
link: '/api-operation'
},
{
title: 'Colour manipulation',
link: '/api-colour'
},
{
title: 'Channel manipulation',
link: '/api-channel'
},
{
title: 'Global properties',
link: '/api-utility'
},
],
},
{
title: 'Performance',
link: '/performance'
},
{
title: 'Changelog',
link: '/changelog'
}
]
});
</script>
</body>
</html>

View File

@@ -10,18 +10,71 @@ yarn add sharp
## Prerequisites
* Node.js v8.5.0+
* Node.js v10.13.0+
### Building from source
## Prebuilt binaries
Pre-compiled binaries for sharp are provided for use with
Node versions 8, 10, 12 and 13 on
64-bit Windows, OS X and Linux platforms.
Ready-compiled sharp and libvips binaries are provided for use with
Node.js versions 10, 12 and 13 on the most common platforms:
Sharp will be built from source at install time when:
* macOS x64 (>= 10.13)
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
* Windows x64 with 64-bit `node.exe`
* a globally-installed libvips is detected,
* pre-compiled binaries do not exist for the current platform and Node version, or
A ~10MB tarball containing libvips and its most commonly used dependencies
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
This provides support for the
JPEG, PNG, WebP, TIFF, GIF (input) and SVG (input) image formats.
The following platforms have prebuilt libvips but not sharp:
* Linux ARMv6
* Linux ARMv7
* Linux ARM64 (glibc >= 2.29)
The following platforms require compilation of both libvips and sharp from source:
* Linux x86
* Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6)
* Linux ARM64 (glibc <= 2.28, musl)
* Linux PowerPC
* FreeBSD
* OpenBSD
The following platforms are completely unsupported:
* Windows x86
* Windows x64 with 32-bit `node.exe`
## Common problems
The platform and major version of Node.js used for `npm install`
must be the same as the platform and major version of Node.js used at runtime.
The `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
Check the output of running `npm install --verbose sharp` for useful error messages.
## Custom libvips
To use a custom, globally-installed version of libvips instead of the provided binaries,
make sure it is at least the version listed under `config.libvips` in the `package.json` file
and that it can be located using `pkg-config --modversion vips-cpp`.
For help compiling libvips from source, please see
[https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball](https://libvips.github.io/libvips/install.html#building-libvips-from-a-source-tarball).
The use of a globally-installed libvips is unsupported on Windows.
## Building from source
This module will be compiled from source at `npm install` time when:
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this),
* prebuilt binaries do not exist for the current platform and Node.js version, or
* when the `npm install --build-from-source` flag is used.
Building from source requires:
@@ -29,86 +82,32 @@ 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
## Custom prebuilt binaries
### Linux
This is an advanced approach that most people will not require.
[![Ubuntu 16.04 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
To install the prebuilt libvips binaries from a custom URL,
set the `sharp_dist_base_url` npm config option
or the `SHARP_DIST_BASE_URL` environment variable.
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 10MB.
Most Linux-based (glibc, musl) operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.:
* Debian 8+
* Ubuntu 14.04+
* Red Hat Enterprise 7+
* CentOS 7+
* Alpine 3.10+
* Fedora 21+
* openSUSE 13.2+
* Archlinux
* Raspbian Jessie
* Amazon Linux
* Solus
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.
This allows the use of newer versions of libvips with older versions of sharp.
For 32-bit Intel CPUs and older Linux-based operating systems such as
those based on Red Hat Enterprise 6 (e.g. 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
[community repository](https://pkgs.alpinelinux.org/packages?name=vips-dev):
For example, both of the following will result in an attempt to download the file located at
`https://hostname/path/libvips-x.y.z-platform.tar.gz`.
```sh
apk add --upgrade --no-cache vips-dev build-base \
--repository https://alpine.global.ssl.fastly.net/alpine/v3.10/community/
npm config set sharp_dist_base_url "https://hostname/path/"
npm install sharp
```
The smaller stack size of musl libc means
libvips may need to be used without a cache
via `sharp.cache(false)` to avoid a stack overflow.
```sh
SHARP_DIST_BASE_URL="https://hostname/path/" npm install sharp
```
### Mac OS
To install the prebuilt sharp binaries from a custom URL, please see
[https://github.com/prebuild/prebuild-install#custom-binaries](https://github.com/prebuild/prebuild-install#custom-binaries)
[![OS X 10.12 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
## FreeBSD
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 8MB.
To use your own version of libvips instead of the provided binaries, make sure it is
at least the version listed under `config.libvips` in the `package.json` file and
that it can be located using `pkg-config --modversion vips-cpp`.
### Windows x64
[![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 10MB.
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.
### FreeBSD
libvips must be installed before `npm install` is run.
This can be achieved via package or ports:
The `vips` package must be installed before `npm install` is run.
```sh
pkg install -y pkgconf vips
@@ -118,46 +117,25 @@ pkg install -y pkgconf vips
cd /usr/ports/graphics/vips/ && make install clean
```
FreeBSD's gcc v4 and v5 need `CXXFLAGS=-D_GLIBCXX_USE_C99` set for C++11 support due to
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=193528
## Heroku
### Heroku
Add the
[jemalloc buildpack](https://github.com/gaffneyc/heroku-buildpack-jemalloc)
to reduce the effects of memory fragmentation.
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
Set
[NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager.
### Docker
[Marc Bachmann](https://github.com/marcbachmann) maintains an
[Ubuntu-based Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
```sh
docker pull marcbachmann/libvips
```
[Will Jordan](https://github.com/wjordan) maintains an
[Alpine-based Dockerfile for libvips](https://github.com/wjordan/dockerfile-libvips).
```sh
docker pull wjordan/libvips
```
[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
## AWS Lambda
Set the Lambda runtime to `nodejs10.x`.
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.
must be for the Linux x64 platform.
On non-Linux machines such as OS X and Windows run the following:
On machines other than Linux x64, such as macOS and Windows, run the following:
```sh
rm -rf node_modules/sharp
@@ -174,113 +152,16 @@ docker run -v "$PWD":/var/task lambci/lambda:build-nodejs10.x npm install sharp
To get the best performance select the largest memory available.
A 1536 MB function provides ~12x more CPU time than a 128 MB function.
### NW.js
## Electron
Run the `nw-gyp` tool after installation.
Electron provides versions of the V8 JavaScript engine
that are incompatible with Node.js.
To ensure the correct binaries are used, run the following:
```sh
cd node-modules/sharp
nw-gyp rebuild --arch=x64 --target=[your nw version]
node node_modules/sharp/install/dll-copy
npm install
npx electron-rebuild
```
[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) |
Further help can be found at
[https://electronjs.org/docs/tutorial/using-native-node-modules](https://electronjs.org/docs/tutorial/using-native-node-modules)

View File

@@ -1,44 +1,46 @@
# Performance
### Test environment
A test to benchmark the performance of this module relative to alternatives.
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
* Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
* Node.js v12.10.0
## The contenders
### The contenders
* [jimp](https://www.npmjs.com/package/jimp) v0.8.4 - Image processing in pure JavaScript. Provides bicubic interpolation.
* [jimp](https://www.npmjs.com/package/jimp) v0.9.3 - Image processing in pure JavaScript. Provides bicubic interpolation.
* [mapnik](https://www.npmjs.org/package/mapnik) v4.3.1 - Whilst primarily a map renderer, Mapnik contains bitmap image utilities.
* [imagemagick](https://www.npmjs.com/package/imagemagick) v0.1.3 - Supports filesystem only and "*has been unmaintained for a long time*".
* [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
* sharp v0.23.1 / libvips v8.8.1 - Caching within libvips disabled to ensure a fair comparison.
* sharp v0.24.0 / libvips v8.9.0 - Caching within libvips disabled to ensure a fair comparison.
### The task
## The task
Decompress a 2725x2225 JPEG image,
resize to 720x588 using Lanczos 3 resampling (where available),
then compress to JPEG at a "quality" setting of 80.
### Results
## Test environment
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
* Ubuntu 18.04 (hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180912 ami-00035f41c82244dab)
* Node.js v12.14.1
## Results
| Module | Input | Output | Ops/sec | Speed-up |
| :----------------- | :----- | :----- | ------: | -------: |
| jimp | buffer | buffer | 0.66 | 1.0 |
| mapnik | buffer | buffer | 3.31 | 5.0 |
| gm | buffer | buffer | 3.79 | 5.7 |
| gm | file | file | 3.82 | 5.8 |
| imagemagick | file | file | 4.17 | 6.3 |
| sharp | stream | stream | 25.81 | 39.1 |
| sharp | file | file | 26.76 | 40.5 |
| sharp | buffer | buffer | 28.06 | 42.5 |
| jimp | buffer | buffer | 0.72 | 1.0 |
| mapnik | buffer | buffer | 3.02 | 4.2 |
| gm | buffer | buffer | 3.90 | 5.4 |
| gm | file | file | 3.94 | 5.5 |
| imagemagick | file | file | 4.30 | 6.0 |
| sharp | stream | stream | 23.65 | 32.8 |
| sharp | file | file | 24.66 | 34.3 |
| sharp | buffer | buffer | 25.14 | 34.9 |
Greater libvips performance can be expected with caching enabled (default)
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
### Benchmark test prerequisites
## Running the benchmark test
Requires _ImageMagick_, _GraphicsMagick_ and _Mapnik_:
@@ -56,8 +58,6 @@ sudo apt-get install imagemagick libmagick++-dev graphicsmagick libmapnik-dev
sudo yum install ImageMagick-devel ImageMagick-c++-devel GraphicsMagick mapnik-devel
```
### Running the benchmark test
```sh
git clone https://github.com/lovell/sharp.git
cd sharp

View File

@@ -23,7 +23,7 @@ const fail = function (err) {
npmLog.info('sharp', 'Are you trying to install as a root or sudo user? Try again with the --unsafe-perm flag');
}
npmLog.info('sharp', 'Attempting to build from source via node-gyp but this may fail due to the above error');
npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/page/install for required dependencies');
npmLog.info('sharp', 'Please see https://sharp.pixelplumbing.com/install for required dependencies');
process.exit(1);
};
@@ -66,8 +66,11 @@ try {
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.17.0')) {
throw new Error(`Use with glibc version ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
if (detectLibc.family === detectLibc.GLIBC && detectLibc.version) {
const minimumGlibcVersion = arch === 'arm64' ? '2.29.0' : '2.17.0';
if (semver.lt(`${detectLibc.version}.0`, minimumGlibcVersion)) {
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
}
}
// Download to per-process temporary file
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.gz';
@@ -84,7 +87,7 @@ try {
if (err) {
fail(err);
} else if (response.statusCode === 404) {
fail(new Error(`Prebuilt libvips binaries are not yet available for ${platformAndArch}`));
fail(new Error(`Prebuilt libvips ${minimumLibvipsVersion} binaries are not yet available for ${platformAndArch}`));
} else if (response.statusCode !== 200) {
fail(new Error(`Status ${response.statusCode} ${response.statusMessage}`));
} else {

View File

@@ -32,6 +32,8 @@ function removeAlpha () {
/**
* Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
*
* @since 0.21.2
*
* @example
* sharp('rgb.jpg')
* .ensureAlpha()

View File

@@ -53,6 +53,8 @@ const blend = {
* https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
* and https://www.cairographics.org/operators/
*
* @since 0.22.0
*
* @example
* sharp('input.png')
* .rotate(180)

View File

@@ -2,15 +2,13 @@
const util = require('util');
const stream = require('stream');
const events = require('events');
const is = require('./is');
require('./libvips').hasVendoredLibvips();
let sharp;
/* istanbul ignore next */
try {
sharp = require('../build/Release/sharp.node');
require('../build/Release/sharp.node');
} catch (err) {
// Bail early if bindings aren't available
const help = ['', 'Something went wrong installing the "sharp" module', '', err.message, ''];
@@ -18,22 +16,29 @@ try {
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
} else if (/invalid ELF header/.test(err.message)) {
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
} else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
help.push('- Run "brew update && brew upgrade vips"');
} else if (/Cannot find module/.test(err.message)) {
help.push('- Run "npm rebuild --verbose sharp" and look for errors');
} else {
help.push('- Remove the "node_modules/sharp" directory, run "npm install" and look for errors');
help.push(
'- Remove the "node_modules/sharp" directory then run',
' "npm install --ignore-scripts=false --verbose" and look for errors'
);
}
help.push(
'- Consult the installation documentation at https://sharp.pixelplumbing.com/en/stable/install/',
'- Consult the installation documentation at https://sharp.pixelplumbing.com/install',
'- Search for this error at https://github.com/lovell/sharp/issues', ''
);
console.error(help.join('\n'));
process.exit(1);
const error = help.join('\n');
throw new Error(error);
}
// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
/**
* @class Sharp
* @constructs sharp
*
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
*
@@ -83,6 +88,11 @@ const debuglog = util.debuglog('sharp');
* @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|Boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
* @param {Boolean} [options.sequentialRead=false] - Set this to `true` to use sequential rather than random access where possible.
* This can reduce memory usage and might improve performance on some systems.
* @param {Number} [options.density=72] - number representing the DPI for vector images.
* @param {Number} [options.pages=1] - number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages.
* @param {Number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
@@ -107,9 +117,6 @@ const Sharp = function (input, options) {
}
stream.Duplex.call(this);
this.options = {
// input options
sequentialRead: false,
limitInputPixels: Math.pow(0x3FFF, 2),
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
@@ -220,7 +227,7 @@ const Sharp = function (input, options) {
debuglog: debuglog,
// Function to notify of queue length changes
queueListener: function (queueLength) {
queue.emit('change', queueLength);
Sharp.queue.emit('change', queueLength);
}
};
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
@@ -229,38 +236,36 @@ const Sharp = function (input, options) {
util.inherits(Sharp, stream.Duplex);
/**
* An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread
* - complete
* @member
* Take a "snapshot" of the Sharp instance, returning a new instance.
* Cloned instances inherit the input of their parent instance.
* This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
*
* @example
* sharp.queue.on('change', function(queueLength) {
* console.log('Queue contains ' + queueLength + ' task(s)');
* });
* const pipeline = sharp().rotate();
* pipeline.clone().resize(800, 600).pipe(firstWritableStream);
* pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
* readableStream.pipe(pipeline);
* // firstWritableStream receives auto-rotated, resized readableStream
* // secondWritableStream receives auto-rotated, extracted region of readableStream
*
* @returns {Sharp}
*/
const queue = new events.EventEmitter();
Sharp.queue = queue;
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @example
* console.log(sharp.format);
* @returns {Object}
*/
Sharp.format = sharp.format();
/**
* An Object containing the version numbers of libvips and its dependencies.
* @member
* @example
* console.log(sharp.versions);
*/
Sharp.versions = {
vips: sharp.libvipsVersion()
};
try {
Sharp.versions = require('../vendor/versions.json');
} catch (err) {}
function clone () {
// Clone existing options
const clone = this.constructor.call();
clone.options = Object.assign({}, this.options);
// Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) {
this.on('finish', () => {
// Clone inherits input data
this._flattenBufferIn();
clone.options.bufferIn = this.options.bufferIn;
clone.emit('finish');
});
}
return clone;
}
Object.assign(Sharp.prototype, { clone });
/**
* Export constructor.

View File

@@ -1,5 +1,6 @@
'use strict';
const util = require('util');
const color = require('color');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
@@ -9,7 +10,11 @@ const sharp = require('../build/Release/sharp.node');
* @private
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = { failOnError: true };
const inputDescriptor = {
failOnError: true,
limitInputPixels: Math.pow(0x3FFF, 2),
sequentialRead: false
};
if (is.string(input)) {
// filesystem
inputDescriptor.file = input;
@@ -23,11 +28,13 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
// Raw Stream
inputDescriptor.buffer = [];
}
} else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
} else if (!is.defined(input) && !is.defined(inputOptions) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream
inputDescriptor.buffer = [];
} else {
throw new Error('Unsupported input ' + typeof input);
throw new Error(`Unsupported input '${input}' of type ${typeof input}${
is.defined(inputOptions) ? ` when also providing options of type ${typeof inputOptions}` : ''
}`);
}
if (is.object(inputOptions)) {
// Fail on error
@@ -46,6 +53,26 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
throw is.invalidParameterError('density', 'number between 1 and 2400', inputOptions.density);
}
}
// limitInputPixels
if (is.defined(inputOptions.limitInputPixels)) {
if (is.bool(inputOptions.limitInputPixels)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
? Math.pow(0x3FFF, 2)
: 0;
} else if (is.integer(inputOptions.limitInputPixels) && inputOptions.limitInputPixels >= 0) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
} else {
throw is.invalidParameterError('limitInputPixels', 'integer >= 0', inputOptions.limitInputPixels);
}
}
// sequentialRead
if (is.defined(inputOptions.sequentialRead)) {
if (is.bool(inputOptions.sequentialRead)) {
inputDescriptor.sequentialRead = inputOptions.sequentialRead;
} else {
throw is.invalidParameterError('sequentialRead', 'boolean', inputOptions.sequentialRead);
}
}
// Raw pixel input
if (is.defined(inputOptions.raw)) {
if (
@@ -152,37 +179,6 @@ 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 () {
// Clone existing options
const clone = this.constructor.call();
clone.options = Object.assign({}, this.options);
// Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) {
this.on('finish', () => {
// Clone inherits input data
this._flattenBufferIn();
clone.options.bufferIn = this.options.bufferIn;
clone.emit('finish');
});
}
return clone;
}
/**
* Fast access to (uncached) image metadata without decoding any compressed image data.
* A `Promise` is returned when `callback` is not provided.
@@ -191,14 +187,16 @@ function clone () {
* - `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)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
* - `density`: Number of pixels per inch (DPI), if present
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
* - `pageHeight`: Number of pixels high each page in a multi-page image will be.
* - `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
* - `pagePrimary`: Number of the primary page in a HEIF image
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
@@ -207,6 +205,7 @@ function clone () {
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
* - `iptc`: Buffer containing raw IPTC data, if present
* - `xmp`: Buffer containing raw XMP data, if present
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
*
* @example
* const image = sharp(inputJpg);
@@ -333,14 +332,9 @@ function stats (callback) {
}
/**
* 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) {
* @private
*/
const limitInputPixels = util.deprecate(function limitInputPixels (limit) {
// if we pass in false we represent the integer as 0 to disable
if (limit === false) {
limit = 0;
@@ -348,26 +342,20 @@ function limitInputPixels (limit) {
limit = Math.pow(0x3FFF, 2);
}
if (is.integer(limit) && limit >= 0) {
this.options.limitInputPixels = limit;
this.options.input.limitInputPixels = limit;
} else {
throw is.invalidParameterError('limitInputPixels', 'integer', limit);
}
return this;
}
}, 'limitInputPixels is deprecated, use sharp(input, { limitInputPixels: false }) instead');
/**
* 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}
* @private
*/
function sequentialRead (sequentialRead) {
this.options.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true;
const sequentialRead = util.deprecate(function sequentialRead (sequentialRead) {
this.options.input.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true;
return this;
}
}, 'sequentialRead is deprecated, use sharp(input, { sequentialRead: true }) instead');
/**
* Decorate the Sharp prototype with input-related functions.
@@ -381,9 +369,9 @@ module.exports = function (Sharp) {
_flattenBufferIn,
_isStreamInput,
// Public
clone,
metadata,
stats,
// Deprecated
limitInputPixels,
sequentialRead
});

View File

@@ -379,6 +379,8 @@ function linear (a, b) {
/**
* Recomb the image with the specified matrix.
*
* @since 0.21.1
*
* @example
* sharp(input)
* .recomb([
@@ -416,6 +418,8 @@ function recomb (inputMatrix) {
/**
* Transforms the image using brightness, saturation and hue rotation.
*
* @since 0.22.1
*
* @example
* sharp(input)
* .modulate({

View File

@@ -3,6 +3,17 @@
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
const formats = new Map([
['heic', 'heif'],
['heif', 'heif'],
['jpeg', 'jpeg'],
['jpg', 'jpeg'],
['png', 'png'],
['raw', 'raw'],
['tiff', 'tiff'],
['webp', 'webp']
]);
/**
* Write output image data to a file.
*
@@ -135,6 +146,28 @@ function withMetadata (options) {
return this;
}
/**
* Force output to a given format.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .toFormat('png')
* .toBuffer();
*
* @param {(String|Object)} format - as a String or an Object with an 'id' attribute
* @param {Object} options - output options
* @returns {Sharp}
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
if (!actualFormat) {
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
}
return this[actualFormat](options);
}
/**
* Use these JPEG options for output image.
*
@@ -444,6 +477,8 @@ function tiff (options) {
*
* Most versions of libheif support only the patent-encumbered HEVC compression format.
*
* @since 0.23.0
*
* @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
@@ -496,39 +531,6 @@ function raw () {
return this._updateFormatOut('raw');
}
const formats = new Map([
['heic', 'heif'],
['heif', 'heif'],
['jpeg', 'jpeg'],
['jpg', 'jpeg'],
['png', 'png'],
['raw', 'raw'],
['tiff', 'tiff'],
['webp', 'webp']
]);
/**
* Force output to a given format.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .toFormat('png')
* .toBuffer();
*
* @param {(String|Object)} format - as a String or an Object with an 'id' attribute
* @param {Object} options - output options
* @returns {Sharp}
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
const actualFormat = formats.get(is.object(format) && is.string(format.id) ? format.id : format);
if (!actualFormat) {
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
}
return this[actualFormat](options);
}
/**
* Use tile-based deep zoom (image pyramid) output.
* Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
@@ -575,7 +577,7 @@ function tile (options) {
if (options.overlap > this.options.tileSize) {
throw is.invalidParameterError('overlap', `<= size (${this.options.tileSize})`, options.overlap);
}
this.options.tileOverlap = tile.overlap;
this.options.tileOverlap = options.overlap;
} else {
throw is.invalidParameterError('overlap', 'integer between 0 and 8192', options.overlap);
}
@@ -777,13 +779,13 @@ module.exports = function (Sharp) {
toFile,
toBuffer,
withMetadata,
toFormat,
jpeg,
png,
webp,
tiff,
heif,
raw,
toFormat,
tile,
// Private
_updateFormatOut,

View File

@@ -173,6 +173,14 @@ const mapFitToCanvas = {
* // and no larger than the input image
* });
*
* @example
* const scaleByHalf = await sharp(input)
* .metadata()
* .then(({ width }) => sharp(input)
* .resize(Math.round(width * 0.5))
* .toBuffer()
* );
*
* @param {Number} [width] - pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* @param {Number} [height] - pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {Object} [options]
@@ -367,7 +375,10 @@ function extract (options) {
/**
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
* Images consisting entirely of a single colour will calculate "boring" using the alpha channel, if any.
*
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
*
* @param {Number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
* @returns {Sharp}
* @throws {Error} Invalid parameters

View File

@@ -1,8 +1,31 @@
'use strict';
const events = require('events');
const is = require('./is');
const sharp = require('../build/Release/sharp.node');
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @member
* @example
* console.log(sharp.format);
* @returns {Object}
*/
const format = sharp.format();
/**
* An Object containing the version numbers of libvips and its dependencies.
* @member
* @example
* console.log(sharp.versions);
*/
let versions = {
vips: sharp.libvipsVersion()
};
try {
versions = require('../vendor/versions.json');
} catch (err) {}
/**
* Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
* Existing entries in the cache will be trimmed after any change in limits.
@@ -61,6 +84,18 @@ function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}
/**
* An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread
* - complete
* @member
* @example
* sharp.queue.on('change', function(queueLength) {
* console.log('Queue contains ' + queueLength + ' task(s)');
* });
*/
const queue = new events.EventEmitter();
/**
* Provides access to internal task counters.
* - queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
@@ -110,4 +145,7 @@ module.exports = function (Sharp) {
].forEach(function (f) {
Sharp[f.name] = f;
});
Sharp.format = format;
Sharp.versions = versions;
Sharp.queue = queue;
};

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.23.2",
"version": "0.24.1",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@@ -63,23 +63,25 @@
"Jordan Prudhomme <jordan@raboland.fr>",
"Ilya Ovdin <iovdin@gmail.com>",
"Andargor <andargor@yahoo.com>",
"Paul Neave <paul.neave@gmail.com>"
"Paul Neave <paul.neave@gmail.com>",
"Brendan Kennedy <brenwken@gmail.com>",
"Brychan Bennett-Odlum <git@brychan.io>"
],
"scripts": {
"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": "semistandard && cpplint && npm run test-unit && npm run test-licensing && prebuild-ci",
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
"test-coverage": "./test/coverage/report.sh",
"test-leak": "./test/leak/leak.sh",
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md --markdown-toc=false lib/$m.js >docs/api-$m.md; done"
"docs-build": "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",
"docs-serve": "cd docs && npx serve",
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
},
"main": "lib/index.js",
"files": [
"binding.gyp",
"docs/**",
"!docs/css/**",
"install/**",
"lib/**",
"src/**"
@@ -109,34 +111,37 @@
"detect-libc": "^1.0.3",
"nan": "^2.14.0",
"npmlog": "^4.1.2",
"prebuild-install": "^5.3.2",
"semver": "^6.3.0",
"prebuild-install": "^5.3.3",
"semver": "^7.1.3",
"simple-get": "^3.1.0",
"tar": "^5.0.5",
"tar": "^6.0.1",
"tunnel-agent": "^0.6.0"
},
"devDependencies": {
"async": "^3.1.0",
"cc": "^1.0.2",
"async": "^3.1.1",
"cc": "^2.0.1",
"decompress-zip": "^0.3.2",
"documentation": "^12.1.2",
"documentation": "^12.1.4",
"exif-reader": "^1.0.3",
"icc": "^1.0.0",
"license-checker": "^25.0.1",
"mocha": "^6.2.2",
"mock-fs": "^4.10.2",
"nyc": "^14.1.1",
"prebuild": "^9.1.1",
"mocha": "^7.0.1",
"mock-fs": "^4.10.4",
"nyc": "^15.0.0",
"prebuild": "^10.0.0",
"prebuild-ci": "^3.1.0",
"rimraf": "^3.0.0",
"rimraf": "^3.0.2",
"semistandard": "^14.2.0"
},
"license": "Apache-2.0",
"config": {
"libvips": "8.8.1"
"libvips": "8.9.0"
},
"engines": {
"node": ">=8.5.0"
"node": ">=10.13.0"
},
"funding": {
"url": "https://opencollective.com/libvips"
},
"semistandard": {
"env": [
@@ -146,10 +151,7 @@
"cc": {
"linelength": "120",
"filter": [
"build/c++11",
"build/include",
"runtime/indentation_namespace",
"runtime/references"
"build/include"
]
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
#include <string.h>
#include <vector>
#include <queue>
#include <mutex>
#include <mutex> // NOLINT(build/c++11)
#include <node.h>
#include <node_buffer.h>
@@ -58,6 +58,7 @@ namespace sharp {
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
descriptor->bufferLength = node::Buffer::Length(buffer);
descriptor->buffer = node::Buffer::Data(buffer);
descriptor->isBuffer = TRUE;
buffersToPersist.push_back(buffer);
}
descriptor->failOnError = AttrTo<bool>(input, "failOnError");
@@ -85,6 +86,10 @@ namespace sharp {
descriptor->createHeight = AttrTo<uint32_t>(input, "createHeight");
descriptor->createBackground = AttrAsRgba(input, "createBackground");
}
// Limit input images to a given number of pixels, where pixels = width * height
descriptor->limitInputPixels = AttrTo<uint32_t>(input, "limitInputPixels");
// Allow switch from random to sequential access
descriptor->access = AttrTo<bool>(input, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
return descriptor;
}
@@ -196,7 +201,7 @@ namespace sharp {
std::string const loader = load;
if (EndsWith(loader, "JpegFile")) {
imageType = ImageType::JPEG;
} else if (EndsWith(loader, "Png")) {
} else if (EndsWith(loader, "PngFile")) {
imageType = ImageType::PNG;
} else if (EndsWith(loader, "WebpFile")) {
imageType = ImageType::WEBP;
@@ -243,10 +248,10 @@ namespace sharp {
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod) {
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor) {
VImage image;
ImageType imageType;
if (descriptor->buffer != nullptr) {
if (descriptor->isBuffer) {
if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
@@ -263,7 +268,7 @@ namespace sharp {
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", accessMethod)
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", descriptor->density);
@@ -277,7 +282,7 @@ namespace sharp {
}
image = VImage::new_from_buffer(descriptor->buffer, descriptor->bufferLength, nullptr, option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
image = SetDensity(image, descriptor->density);
}
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
@@ -309,7 +314,7 @@ namespace sharp {
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()
->set("access", accessMethod)
->set("access", descriptor->access)
->set("fail", descriptor->failOnError);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", descriptor->density);
@@ -323,7 +328,7 @@ namespace sharp {
}
image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
image = SetDensity(image, descriptor->density);
}
} catch (vips::VError const &err) {
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
@@ -333,6 +338,11 @@ namespace sharp {
}
}
}
// Limit input images to a given number of pixels, where pixels = width * height
if (descriptor->limitInputPixels > 0 &&
static_cast<uint64_t>(image.width() * image.height()) > static_cast<uint64_t>(descriptor->limitInputPixels)) {
throw vips::VError("Input image exceeds pixel limit");
}
return std::make_tuple(image, imageType);
}
@@ -370,15 +380,19 @@ namespace sharp {
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VImage image, int const orientation) {
image.set(VIPS_META_ORIENTATION, orientation);
VImage SetExifOrientation(VImage image, int const orientation) {
VImage copy = image.copy();
copy.set(VIPS_META_ORIENTATION, orientation);
return copy;
}
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image) {
vips_image_remove(image.get_image(), VIPS_META_ORIENTATION);
VImage RemoveExifOrientation(VImage image) {
VImage copy = image.copy();
copy.remove(VIPS_META_ORIENTATION);
return copy;
}
/*
@@ -398,11 +412,13 @@ namespace sharp {
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const double density) {
VImage 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");
VImage copy = image.copy();
copy.set("Xres", pixelsPerMm);
copy.set("Yres", pixelsPerMm);
copy.set(VIPS_META_RESOLUTION_UNIT, "in");
return copy;
}
/*

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -25,17 +25,17 @@
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 8))
#error libvips version 8.8.0+ is required - see sharp.pixelplumbing.com/page/install
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 9))
#error "libvips version 8.9.0+ is required - please see https://sharp.pixelplumbing.com/install"
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
#error GCC version 4.6+ is required for C++11 features - see sharp.pixelplumbing.com/page/install#prerequisites
#error "GCC version 4.6+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
#endif
#if (defined(__clang__) && defined(__has_feature))
#if (!__has_feature(cxx_range_for))
#error clang version 3.0+ is required for C++11 features - see sharp.pixelplumbing.com/page/install#prerequisites
#error "clang version 3.0+ is required for C++11 features - please see https://sharp.pixelplumbing.com/install"
#endif
#endif
@@ -43,12 +43,15 @@ using vips::VImage;
namespace sharp {
struct InputDescriptor {
struct InputDescriptor { // NOLINT(runtime/indentation_namespace)
std::string name;
std::string file;
char *buffer;
bool failOnError;
int limitInputPixels;
VipsAccess access;
size_t bufferLength;
bool isBuffer;
double density;
int rawChannels;
int rawWidth;
@@ -63,7 +66,10 @@ namespace sharp {
InputDescriptor():
buffer(nullptr),
failOnError(TRUE),
limitInputPixels(0x3FFF * 0x3FFF),
access(VIPS_ACCESS_RANDOM),
bufferLength(0),
isBuffer(FALSE),
density(72.0),
rawChannels(0),
rawWidth(0),
@@ -92,7 +98,7 @@ namespace sharp {
// Create an InputDescriptor instance from a v8::Object describing an input image
InputDescriptor* CreateInputDescriptor(
v8::Local<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist);
v8::Local<v8::Object> input, std::vector<v8::Local<v8::Object>> &buffersToPersist); // NOLINT(runtime/references)
enum class ImageType {
JPEG,
@@ -154,7 +160,7 @@ namespace sharp {
/*
Open an image from the given InputDescriptor (filesystem, compressed buffer, raw pixel data)
*/
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor, VipsAccess accessMethod);
std::tuple<VImage, ImageType> OpenInput(InputDescriptor *descriptor);
/*
Does this image have an embedded profile?
@@ -175,12 +181,12 @@ namespace sharp {
/*
Set EXIF Orientation of image.
*/
void SetExifOrientation(VImage image, int const orientation);
VImage SetExifOrientation(VImage image, int const orientation);
/*
Remove EXIF Orientation from image.
*/
void RemoveExifOrientation(VImage image);
VImage RemoveExifOrientation(VImage image);
/*
Does this image have a non-default density?
@@ -195,7 +201,7 @@ namespace sharp {
/*
Set pixels/mm resolution based on a pixels/inch density.
*/
void SetDensity(VImage image, const double density);
VImage SetDensity(VImage image, const double density);
/*
Check the proposed format supports the current dimensions.

View File

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

View File

@@ -169,7 +169,7 @@ VOption::set( const char *name, const char *value )
// input image
VOption *
VOption::set( const char *name, VImage value )
VOption::set( const char *name, const VImage value )
{
Pair *pair = new Pair( name );
@@ -592,7 +592,30 @@ VImage
VImage::new_from_buffer( const std::string &buf, const char *option_string,
VOption *options )
{
return( new_from_buffer( buf.c_str(), buf.size(), option_string, options ) );
return( new_from_buffer( buf.c_str(), buf.size(),
option_string, options ) );
}
VImage
VImage::new_from_source( VSource source, const char *option_string,
VOption *options )
{
const char *operation_name;
VImage out;
if( !(operation_name = vips_foreign_find_load_source(
source.get_source() )) ) {
delete options;
throw( VError() );
}
options = (options ? options : VImage::option())->
set( "source", source )->
set( "out", &out );
call_option_string( operation_name, option_string, options );
return( out );
}
VImage
@@ -679,6 +702,26 @@ VImage::write_to_buffer( const char *suffix, void **buf, size_t *size,
}
}
void
VImage::write_to_target( const char *suffix, VTarget target,
VOption *options ) const
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];
const char *operation_name;
vips__filename_split8( suffix, filename, option_string );
if( !(operation_name = vips_foreign_find_save_target( filename )) ) {
delete options;
throw VError();
}
call_option_string( operation_name, option_string,
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
#include "vips-operators.cpp"
std::vector<VImage>

View File

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

View File

@@ -1,5 +1,5 @@
// bodies for vips operations
// Wed Apr 24 15:50:21 CEST 2019
// Wed 01 Jan 2020 12:22:12 PM CET
// this file is generated automatically, do not edit!
VImage VImage::CMC2LCh( VOption *options ) const
@@ -491,6 +491,19 @@ VImage VImage::canny( VOption *options ) const
return( out );
}
VImage VImage::case_image( std::vector<VImage> cases, VOption *options ) const
{
VImage out;
call( "case",
(options ? options : VImage::option())->
set( "index", *this )->
set( "out", &out )->
set( "cases", cases ) );
return( out );
}
VImage VImage::cast( VipsBandFormat format, VOption *options ) const
{
VImage out;
@@ -1615,6 +1628,18 @@ VImage VImage::jpegload_buffer( VipsBlob *buffer, VOption *options )
return( out );
}
VImage VImage::jpegload_source( VSource source, VOption *options )
{
VImage out;
call( "jpegload_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source ) );
return( out );
}
void VImage::jpegsave( const char *filename, VOption *options ) const
{
call( "jpegsave",
@@ -1642,6 +1667,14 @@ void VImage::jpegsave_mime( VOption *options ) const
set( "in", *this ) );
}
void VImage::jpegsave_target( VTarget target, VOption *options ) const
{
call( "jpegsave_target",
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
VImage VImage::labelregions( VOption *options ) const
{
VImage mask;
@@ -2286,6 +2319,18 @@ VImage VImage::pngload_buffer( VipsBlob *buffer, VOption *options )
return( out );
}
VImage VImage::pngload_source( VSource source, VOption *options )
{
VImage out;
call( "pngload_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source ) );
return( out );
}
void VImage::pngsave( const char *filename, VOption *options ) const
{
call( "pngsave",
@@ -2306,6 +2351,14 @@ VipsBlob *VImage::pngsave_buffer( VOption *options ) const
return( buffer );
}
void VImage::pngsave_target( VTarget target, VOption *options ) const
{
call( "pngsave_target",
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
VImage VImage::ppmload( const char *filename, VOption *options )
{
VImage out;
@@ -2413,6 +2466,30 @@ VImage VImage::radload( const char *filename, VOption *options )
return( out );
}
VImage VImage::radload_buffer( VipsBlob *buffer, VOption *options )
{
VImage out;
call( "radload_buffer",
(options ? options : VImage::option())->
set( "out", &out )->
set( "buffer", buffer ) );
return( out );
}
VImage VImage::radload_source( VSource source, VOption *options )
{
VImage out;
call( "radload_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source ) );
return( out );
}
void VImage::radsave( const char *filename, VOption *options ) const
{
call( "radsave",
@@ -2433,6 +2510,14 @@ VipsBlob *VImage::radsave_buffer( VOption *options ) const
return( buffer );
}
void VImage::radsave_target( VTarget target, VOption *options ) const
{
call( "radsave_target",
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
VImage VImage::rank( int width, int height, int index, VOption *options ) const
{
VImage out;
@@ -2977,6 +3062,30 @@ VImage VImage::svgload_buffer( VipsBlob *buffer, VOption *options )
return( out );
}
VImage VImage::svgload_source( VSource source, VOption *options )
{
VImage out;
call( "svgload_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source ) );
return( out );
}
VImage VImage::switch_image( std::vector<VImage> tests, VOption *options )
{
VImage out;
call( "switch",
(options ? options : VImage::option())->
set( "out", &out )->
set( "tests", tests ) );
return( out );
}
void VImage::system( const char *cmd_format, VOption *options )
{
call( "system",
@@ -3035,6 +3144,19 @@ VImage VImage::thumbnail_image( int width, VOption *options ) const
return( out );
}
VImage VImage::thumbnail_source( VSource source, int width, VOption *options )
{
VImage out;
call( "thumbnail_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source )->
set( "width", width ) );
return( out );
}
VImage VImage::tiffload( const char *filename, VOption *options )
{
VImage out;
@@ -3059,6 +3181,18 @@ VImage VImage::tiffload_buffer( VipsBlob *buffer, VOption *options )
return( out );
}
VImage VImage::tiffload_source( VSource source, VOption *options )
{
VImage out;
call( "tiffload_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source ) );
return( out );
}
void VImage::tiffsave( const char *filename, VOption *options ) const
{
call( "tiffsave",
@@ -3170,6 +3304,18 @@ VImage VImage::webpload_buffer( VipsBlob *buffer, VOption *options )
return( out );
}
VImage VImage::webpload_source( VSource source, VOption *options )
{
VImage out;
call( "webpload_source",
(options ? options : VImage::option())->
set( "out", &out )->
set( "source", source ) );
return( out );
}
void VImage::webpsave( const char *filename, VOption *options ) const
{
call( "webpsave",
@@ -3190,6 +3336,14 @@ VipsBlob *VImage::webpsave_buffer( VOption *options ) const
return( buffer );
}
void VImage::webpsave_target( VTarget target, VOption *options ) const
{
call( "webpsave_target",
(options ? options : VImage::option())->
set( "in", *this )->
set( "target", target ) );
}
VImage VImage::worley( int width, int height, VOption *options )
{
VImage out;

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ class MetadataWorker : public Nan::AsyncWorker {
vips::VImage image;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, VIPS_ACCESS_SEQUENTIAL);
std::tie(image, imageType) = OpenInput(baton->input);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
@@ -77,6 +77,12 @@ class MetadataWorker : public Nan::AsyncWorker {
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
}
if (image.get_typeof("loop") == G_TYPE_INT) {
baton->loop = image.get_int("loop");
}
if (image.get_typeof("delay") == VIPS_TYPE_ARRAY_INT) {
baton->delay = image.get_array_int("delay");
}
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
baton->pagePrimary = image.get_int("heif-primary");
}
@@ -116,6 +122,14 @@ class MetadataWorker : public Nan::AsyncWorker {
memcpy(baton->xmp, xmp, xmpLength);
baton->xmpLength = xmpLength;
}
// TIFFTAG_PHOTOSHOP
if (image.get_typeof(VIPS_META_PHOTOSHOP_NAME) == VIPS_TYPE_BLOB) {
size_t tifftagPhotoshopLength;
void const *tifftagPhotoshop = image.get_blob(VIPS_META_PHOTOSHOP_NAME, &tifftagPhotoshopLength);
baton->tifftagPhotoshop = static_cast<char *>(g_malloc(tifftagPhotoshopLength));
memcpy(baton->tifftagPhotoshop, tifftagPhotoshop, tifftagPhotoshopLength);
baton->tifftagPhotoshopLength = tifftagPhotoshopLength;
}
}
// Clean up
@@ -161,6 +175,17 @@ class MetadataWorker : public Nan::AsyncWorker {
if (baton->pageHeight > 0) {
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
}
if (baton->loop >= 0) {
Set(info, New("loop").ToLocalChecked(), New<v8::Uint32>(baton->loop));
}
if (!baton->delay.empty()) {
int i = 0;
v8::Local<v8::Array> delay = New<v8::Array>(baton->delay.size());
for (int const d : baton->delay) {
Set(delay, i++, New<v8::Number>(d));
}
Set(info, New("delay").ToLocalChecked(), delay);
}
if (baton->pagePrimary > -1) {
Set(info, New("pagePrimary").ToLocalChecked(), New<v8::Uint32>(baton->pagePrimary));
}
@@ -189,6 +214,12 @@ class MetadataWorker : public Nan::AsyncWorker {
New("xmp").ToLocalChecked(),
Nan::NewBuffer(baton->xmp, baton->xmpLength, sharp::FreeCallback, nullptr).ToLocalChecked());
}
if (baton->tifftagPhotoshopLength > 0) {
Set(info,
New("tifftagPhotoshop").ToLocalChecked(),
Nan::NewBuffer(baton->tifftagPhotoshop, baton->tifftagPhotoshopLength, sharp::FreeCallback, nullptr)
.ToLocalChecked());
}
argv[1] = info;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -36,6 +36,8 @@ struct MetadataBaton {
int paletteBitDepth;
int pages;
int pageHeight;
int loop;
std::vector<int> delay;
int pagePrimary;
bool hasProfile;
bool hasAlpha;
@@ -48,6 +50,8 @@ struct MetadataBaton {
size_t iptcLength;
char *xmp;
size_t xmpLength;
char *tifftagPhotoshop;
size_t tifftagPhotoshopLength;
std::string err;
MetadataBaton():
@@ -60,6 +64,7 @@ struct MetadataBaton {
paletteBitDepth(0),
pages(0),
pageHeight(0),
loop(-1),
pagePrimary(-1),
hasProfile(false),
hasAlpha(false),
@@ -71,7 +76,9 @@ struct MetadataBaton {
iptc(nullptr),
iptcLength(0),
xmp(nullptr),
xmpLength(0) {}
xmpLength(0),
tifftagPhotoshop(nullptr),
tifftagPhotoshopLength(0) {}
};
NAN_METHOD(metadata);

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -191,12 +191,20 @@ namespace sharp {
VImage alpha = image[image.bands() - 1];
return RemoveAlpha(image)
.colourspace(VIPS_INTERPRETATION_LCH)
.linear({brightness, saturation, 1}, {0, 0, static_cast<double>(hue)})
.linear(
{ brightness, saturation, 1},
{ 0.0, 0.0, static_cast<double>(hue) }
)
.colourspace(VIPS_INTERPRETATION_sRGB)
.bandjoin(alpha);
} else {
return image
.colourspace(VIPS_INTERPRETATION_LCH)
.linear({brightness, saturation, 1}, {0, 0, static_cast<double>(hue)});
.linear(
{ brightness, saturation, 1 },
{ 0.0, 0.0, static_cast<double>(hue) }
)
.colourspace(VIPS_INTERPRETATION_sRGB);
}
}
@@ -258,12 +266,22 @@ namespace sharp {
if (HasAlpha(background)) {
background = background.flatten();
}
int top, width, height;
int const left = image.find_trim(&top, &width, &height, VImage::option()
int left, top, width, height;
left = image.find_trim(&top, &width, &height, VImage::option()
->set("background", background(0, 0))
->set("threshold", threshold));
if (width == 0 || height == 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance");
if (HasAlpha(image)) {
// Search alpha channel
VImage alpha = image[image.bands() - 1];
VImage backgroundAlpha = alpha.extract_area(0, 0, 1, 1);
left = alpha.find_trim(&top, &width, &height, VImage::option()
->set("background", backgroundAlpha(0, 0))
->set("threshold", threshold));
}
if (width == 0 || height == 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance");
}
}
return image.extract_area(left, top, width, height);
}

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,6 +38,9 @@
#elif defined(__APPLE__)
#define STAT64_STRUCT stat
#define STAT64_FUNCTION stat
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
#define STAT64_STRUCT stat
#define STAT64_FUNCTION stat
#else
#define STAT64_STRUCT stat64
#define STAT64_FUNCTION stat64
@@ -74,15 +77,7 @@ class PipelineWorker : public Nan::AsyncWorker {
// Open input
vips::VImage image;
ImageType inputImageType;
std::tie(image, inputImageType) = sharp::OpenInput(baton->input, baton->accessMethod);
// Limit input images to a given number of pixels, where pixels = width * height
// Ignore if 0
if (baton->limitInputPixels > 0 &&
static_cast<uint64_t>(image.width() * image.height()) > static_cast<uint64_t>(baton->limitInputPixels)) {
(baton->err).append("Input image exceeds pixel limit");
return Error();
}
std::tie(image, inputImageType) = sharp::OpenInput(baton->input);
// Calculate angle of rotation
VipsAngle rotation;
@@ -101,7 +96,7 @@ class PipelineWorker : public Nan::AsyncWorker {
if (baton->rotateBeforePreExtract) {
if (rotation != VIPS_ANGLE_D0) {
image = image.rot(rotation);
sharp::RemoveExifOrientation(image);
image = sharp::RemoveExifOrientation(image);
}
if (baton->rotationAngle != 0.0) {
std::vector<double> background;
@@ -131,6 +126,17 @@ class PipelineWorker : public Nan::AsyncWorker {
std::swap(inputWidth, inputHeight);
}
// If withoutEnlargement is specified,
// Override target width and height if exceeds respective value from input file
if (baton->withoutEnlargement) {
if (baton->width > inputWidth) {
baton->width = inputWidth;
}
if (baton->height > inputHeight) {
baton->height = inputHeight;
}
}
// Scaling calculations
double xfactor = 1.0;
double yfactor = 1.0;
@@ -218,21 +224,6 @@ class PipelineWorker : public Nan::AsyncWorker {
double xresidual = static_cast<double>(xshrink) / xfactor;
double yresidual = static_cast<double>(yshrink) / yfactor;
// Do not enlarge the output if the input width *or* height
// are already less than the required dimensions
if (baton->withoutEnlargement) {
if (inputWidth < baton->width || inputHeight < baton->height) {
xfactor = 1.0;
yfactor = 1.0;
xshrink = 1;
yshrink = 1;
xresidual = 1.0;
yresidual = 1.0;
baton->width = inputWidth;
baton->height = inputHeight;
}
}
// If integral x and y shrink are equal, try to use shrink-on-load for JPEG and WebP,
// but not when applying gamma correction, pre-resize extract or trim
int shrink_on_load = 1;
@@ -271,7 +262,7 @@ class PipelineWorker : public Nan::AsyncWorker {
if (shrink_on_load > 1) {
// Reload input using shrink-on-load
vips::VOption *option = VImage::option()
->set("access", baton->accessMethod)
->set("access", baton->input->access)
->set("shrink", shrink_on_load)
->set("fail", baton->input->failOnError);
if (baton->input->buffer != nullptr) {
@@ -308,11 +299,16 @@ class PipelineWorker : public Nan::AsyncWorker {
}
// Ensure we're using a device-independent colour space
if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) {
if (
sharp::HasProfile(image) &&
image.interpretation() != VIPS_INTERPRETATION_LABS &&
image.interpretation() != VIPS_INTERPRETATION_GREY16
) {
// Convert to sRGB using embedded profile
try {
image = image.icc_transform("srgb", VImage::option()
->set("embedded", TRUE)
->set("depth", image.interpretation() == VIPS_INTERPRETATION_RGB16 ? 16 : 8)
->set("intent", VIPS_INTENT_PERCEPTUAL));
} catch(...) {
// Ignore failure of embedded profile
@@ -401,20 +397,20 @@ class PipelineWorker : public Nan::AsyncWorker {
// Rotate post-extract 90-angle
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
image = image.rot(rotation);
sharp::RemoveExifOrientation(image);
image = sharp::RemoveExifOrientation(image);
}
// Flip (mirror about Y axis)
if (baton->flip) {
image = image.flip(VIPS_DIRECTION_VERTICAL);
sharp::RemoveExifOrientation(image);
image = sharp::RemoveExifOrientation(image);
}
// Flop (mirror about X axis)
if (baton->flop) {
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
sharp::RemoveExifOrientation(image);
image = sharp::RemoveExifOrientation(image);
}
// Join additional color channels to the image
@@ -423,7 +419,7 @@ class PipelineWorker : public Nan::AsyncWorker {
ImageType joinImageType = ImageType::UNKNOWN;
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i], baton->accessMethod);
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
image = image.bandjoin(joinImage);
}
image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
@@ -476,7 +472,7 @@ class PipelineWorker : public Nan::AsyncWorker {
baton->height = image.height();
}
image = image.tilecache(VImage::option()
->set("access", baton->accessMethod)
->set("access", VIPS_ACCESS_RANDOM)
->set("threaded", TRUE));
image = image.smartcrop(baton->width, baton->height, VImage::option()
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION));
@@ -553,7 +549,7 @@ class PipelineWorker : public Nan::AsyncWorker {
for (Composite *composite : baton->composite) {
VImage compositeImage;
ImageType compositeImageType = ImageType::UNKNOWN;
std::tie(compositeImage, compositeImageType) = OpenInput(composite->input, baton->accessMethod);
std::tie(compositeImage, compositeImageType) = OpenInput(composite->input);
// Verify within current dimensions
if (compositeImage.width() > image.width() || compositeImage.height() > image.height()) {
throw vips::VError("Image to composite must have same dimensions or smaller");
@@ -643,7 +639,7 @@ class PipelineWorker : public Nan::AsyncWorker {
if (baton->boolean != nullptr) {
VImage booleanImage;
ImageType booleanImageType = ImageType::UNKNOWN;
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean, baton->accessMethod);
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
}
@@ -697,7 +693,7 @@ class PipelineWorker : public Nan::AsyncWorker {
// Override EXIF Orientation tag
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
sharp::SetExifOrientation(image, baton->withMetadataOrientation);
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
}
// Number of channels used in output image
@@ -768,6 +764,7 @@ class PipelineWorker : public Nan::AsyncWorker {
// Write TIFF to buffer
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
baton->channels = std::min(baton->channels, 3);
}
// Cast pixel values to float, if required
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
@@ -790,7 +787,6 @@ class PipelineWorker : public Nan::AsyncWorker {
area->free_fn = nullptr;
vips_area_unref(area);
baton->formatOut = "tiff";
baton->channels = std::min(baton->channels, 3);
} else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == ImageType::HEIF)) {
// Write HEIF to buffer
VipsArea *area = VIPS_AREA(image.heifsave_buffer(VImage::option()
@@ -891,6 +887,7 @@ class PipelineWorker : public Nan::AsyncWorker {
// Write TIFF to file
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
baton->channels = std::min(baton->channels, 3);
}
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata)
@@ -905,15 +902,12 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("xres", baton->tiffXres)
->set("yres", baton->tiffYres));
baton->formatOut = "tiff";
baton->channels = std::min(baton->channels, 3);
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
(willMatchInput && inputImageType == ImageType::HEIF)) {
// Write HEIF to file
#ifdef VIPS_TYPE_FOREIGN_HEIF_COMPRESSION
if (sharp::IsAvif(baton->fileOut)) {
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
}
#endif
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata)
->set("Q", baton->heifQuality)
@@ -959,29 +953,26 @@ class PipelineWorker : public Nan::AsyncWorker {
};
suffix = AssembleSuffixString(extname, options);
}
// Remove alpha channel from tile background if image does not contain an alpha channel
if (!HasAlpha(image)) {
baton->tileBackground.pop_back();
}
// Write DZ to file
vips::VOption *options = VImage::option()
->set("strip", !baton->withMetadata)
->set("tile_size", baton->tileSize)
->set("overlap", baton->tileOverlap)
->set("container", baton->tileContainer)
->set("layout", baton->tileLayout)
->set("suffix", const_cast<char*>(suffix.data()))
->set("angle", CalculateAngleRotation(baton->tileAngle))
->set("background", baton->tileBackground)
->set("skip_blanks", baton->tileSkipBlanks);
->set("strip", !baton->withMetadata)
->set("tile_size", baton->tileSize)
->set("overlap", baton->tileOverlap)
->set("container", baton->tileContainer)
->set("layout", baton->tileLayout)
->set("suffix", const_cast<char*>(suffix.data()))
->set("angle", CalculateAngleRotation(baton->tileAngle))
->set("background", baton->tileBackground)
->set("skip_blanks", baton->tileSkipBlanks);
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
// not passing anything - libvips will handle choice
if (baton->tileDepth < VIPS_FOREIGN_DZ_DEPTH_LAST) {
options->set("depth", baton->tileDepth);
}
image.dzsave(const_cast<char*>(baton->fileOut.data()), options);
baton->formatOut = "dz";
} else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
@@ -997,7 +988,12 @@ class PipelineWorker : public Nan::AsyncWorker {
}
}
} catch (vips::VError const &err) {
(baton->err).append(err.what());
char const *what = err.what();
if (what && what[0]) {
(baton->err).append(what);
} else {
(baton->err).append("Unknown error");
}
}
// Clean up libvips' per-request data and threads
vips_error_clear();
@@ -1190,10 +1186,6 @@ NAN_METHOD(pipeline) {
// Input
baton->input = CreateInputDescriptor(AttrAs<v8::Object>(options, "input"), buffersToPersist);
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ?
VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Limit input images to a given number of pixels, where pixels = width * height
baton->limitInputPixels = AttrTo<int32_t>(options, "limitInputPixels");
// Extract image options
baton->topOffsetPre = AttrTo<int32_t>(options, "topOffsetPre");
baton->leftOffsetPre = AttrTo<int32_t>(options, "leftOffsetPre");
@@ -1370,11 +1362,9 @@ NAN_METHOD(pipeline) {
AttrAsStr(options, "tiffPredictor").data()));
baton->heifQuality = AttrTo<uint32_t>(options, "heifQuality");
baton->heifLossless = AttrTo<bool>(options, "heifLossless");
#ifdef VIPS_TYPE_FOREIGN_HEIF_COMPRESSION
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
AttrAsStr(options, "heifCompression").data()));
#endif
// Tile output
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
@@ -1407,11 +1397,19 @@ NAN_METHOD(pipeline) {
// signal that we do not want to pass any value to dzSave
baton->tileDepth = VIPS_FOREIGN_DZ_DEPTH_LAST;
}
// Force random access for certain operations
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && (
baton->trimThreshold > 0.0 || baton->normalise ||
baton->position == 16 || baton->position == 17)) {
baton->accessMethod = VIPS_ACCESS_RANDOM;
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL) {
if (
baton->trimThreshold > 0.0 ||
baton->normalise ||
baton->position == 16 || baton->position == 17 ||
baton->angle % 360 != 0 ||
fmod(baton->rotationAngle, 360.0) != 0.0 ||
baton->useExifOrientation
) {
baton->input->access = VIPS_ACCESS_RANDOM;
}
}
// Function to notify of libvips warnings

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -55,7 +55,6 @@ struct Composite {
struct PipelineBaton {
sharp::InputDescriptor *input;
int limitInputPixels;
std::string formatOut;
std::string fileOut;
void *bufferOut;
@@ -119,7 +118,6 @@ struct PipelineBaton {
int extendRight;
std::vector<double> extendBackground;
bool withoutEnlargement;
VipsAccess accessMethod;
int jpegQuality;
bool jpegProgressive;
std::string jpegChromaSubsampling;
@@ -182,7 +180,6 @@ struct PipelineBaton {
PipelineBaton():
input(nullptr),
limitInputPixels(0),
bufferOutLength(0),
topOffsetPre(-1),
topOffsetPost(-1),

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -62,7 +62,7 @@ class StatsWorker : public Nan::AsyncWorker {
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, baton->accessMethod);
std::tie(image, imageType) = OpenInput(baton->input);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
@@ -178,7 +178,6 @@ NAN_METHOD(stats) {
// 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"));

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -46,7 +46,6 @@ struct ChannelStats {
struct StatsBaton {
// Input
sharp::InputDescriptor *input;
VipsAccess accessMethod;
// Output
std::vector<ChannelStats> channelStats;

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View File

@@ -12,9 +12,9 @@
"benchmark": "^2.1.4",
"gm": "^1.23.1",
"imagemagick": "^0.1.3",
"jimp": "^0.8.4",
"jimp": "^0.9.3",
"mapnik": "^4.3.1",
"semver": "^6.3.0"
"semver": "^7.1.1"
},
"license": "Apache-2.0",
"engines": {

View File

@@ -460,8 +460,7 @@ async.series({
}).add('sharp-sequentialRead', {
defer: true,
fn: function (deferred) {
sharp(inputJpgBuffer)
.sequentialRead()
sharp(inputJpgBuffer, { sequentialRead: true })
.resize(width, height)
.toBuffer(function (err, buffer) {
if (err) {

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
test/fixtures/image-in-alpha.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -13,6 +13,7 @@ const getPath = function (filename) {
// Based on the dHash gradient method - see http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
const fingerprint = function (image, callback) {
sharp(image)
.flatten('gray')
.greyscale()
.normalise()
.resize(9, 8, { fit: sharp.fit.fill })
@@ -78,6 +79,7 @@ module.exports = {
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
inputPng16BitGreyAlpha: getPath('16-bit-grey-alpha.png'), // CC-BY-NC-SA florc http://www.colourlovers.com/pattern/50713/pat
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
inputPngAlphaPremultiplicationSmall: getPath('alpha-premultiply-1024x768-paper.png'),
@@ -87,6 +89,7 @@ module.exports = {
inputPngTruncated: getPath('truncated.png'), // gm convert 2569067123_aca715a2ee_o.jpg -resize 320x240 saw.png ; head -c 10000 saw.png > truncated.png
inputPngEmbed: getPath('embedgravitybird.png'), // Released to sharp under a CC BY 4.0
inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg)
inputPngImageInAlpha: getPath('image-in-alpha.png'), // https://github.com/lovell/sharp/issues/1597
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
@@ -95,9 +98,11 @@ module.exports = {
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
inputTifftagPhotoshop: getPath('tifftag-photoshop.tiff'), // https://github.com/lovell/sharp/issues/1600
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-gif
inputGifAnimatedLoop3: getPath('animated-loop-3.gif'), // CC-BY-SA-4.0 Petrus3743 https://commons.wikimedia.org/wiki/File:01-Goldener_Schnitt_Formel-Animation.gif
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg

BIN
test/fixtures/tifftag-photoshop.tiff vendored Normal file

Binary file not shown.

View File

@@ -5,7 +5,7 @@ if ! type valgrind >/dev/null; then
exit 1
fi
curl -s -o ./test/leak/libvips.supp https://raw.githubusercontent.com/libvips/libvips/master/libvips.supp
curl -s -o ./test/leak/libvips.supp https://raw.githubusercontent.com/libvips/libvips/master/suppressions/valgrind.supp
for test in ./test/unit/*.js; do
G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind \

View File

@@ -196,6 +196,18 @@
...
fun:FcInitLoadConfigAndFonts
}
{
leak_fontconfig_doContent
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
...
fun:doContent
fun:doProlog
fun:prologInitProcessor
fun:XML_ParseBuffer
obj:*/libfontconfig.so.*
}
# libvips
{
@@ -342,6 +354,12 @@
...
obj:/usr/bin/iojs
}
{
value_node_invoke_params
Memcheck:Value8
...
fun:_ZN2v88internal12_GLOBAL__N_16InvokeEPNS0_7IsolateERKNS1_12InvokeParamsE
}
{
leak_nodejs_ImmutableAsciiSource_CreateFromLiteral
Memcheck:Leak
@@ -538,6 +556,13 @@
...
fun:_ZN4node9inspector5Agent5StartERKSsSt10shared_ptrINS_12DebugOptionsEEb
}
{
leak_nodejs_debug_host_port
Memcheck:Leak
match-leak-kinds: possible
...
fun:_ZN4node9inspector5Agent5StartERKSsRKNS_12DebugOptionsESt10shared_ptrINS_8HostPortEEb
}
{
leak_nodejs_start
Memcheck:Leak

View File

@@ -141,7 +141,7 @@ describe('composite', () => {
it('zero offset', done => {
sharp(fixtures.inputJpg)
.resize(400)
.resize(80)
.composite([{
input: fixtures.inputPngWithTransparency16bit,
top: 0,
@@ -157,7 +157,7 @@ describe('composite', () => {
it('offset and gravity', done => {
sharp(fixtures.inputJpg)
.resize(400)
.resize(80)
.composite([{
input: fixtures.inputPngWithTransparency16bit,
left: 10,
@@ -174,7 +174,7 @@ describe('composite', () => {
it('offset, gravity and tile', done => {
sharp(fixtures.inputJpg)
.resize(400)
.resize(80)
.composite([{
input: fixtures.inputPngWithTransparency16bit,
left: 10,
@@ -192,7 +192,7 @@ describe('composite', () => {
it('offset and tile', done => {
sharp(fixtures.inputJpg)
.resize(400)
.resize(80)
.composite([{
input: fixtures.inputPngWithTransparency16bit,
left: 10,

View File

@@ -214,7 +214,27 @@ describe('Input/output', function () {
});
});
it('Sequential read, force JPEG', function (done) {
it('Invalid sequential read option throws', () => {
assert.throws(() => {
sharp({ sequentialRead: 'fail' });
}, /Expected boolean for sequentialRead but received fail of type string/);
});
it('Sequential read, force JPEG', () =>
sharp(fixtures.inputJpg, { sequentialRead: true })
.resize(320, 240)
.toFormat(sharp.format.jpeg)
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
assert.strictEqual(data.length > 0, true);
assert.strictEqual(data.length, info.size);
assert.strictEqual(info.format, 'jpeg');
assert.strictEqual(info.width, 320);
assert.strictEqual(info.height, 240);
})
);
it('Sequential read, force JPEG - deprecated', function (done) {
sharp(fixtures.inputJpg)
.sequentialRead()
.resize(320, 240)
@@ -230,7 +250,21 @@ describe('Input/output', function () {
});
});
it('Not sequential read, force JPEG', function (done) {
it('Not sequential read, force JPEG', () =>
sharp(fixtures.inputJpg, { sequentialRead: false })
.resize(320, 240)
.toFormat('jpeg')
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
assert.strictEqual(data.length > 0, true);
assert.strictEqual(data.length, info.size);
assert.strictEqual(info.format, 'jpeg');
assert.strictEqual(info.width, 320);
assert.strictEqual(info.height, 240);
})
);
it('Not sequential read, force JPEG - deprecated', function (done) {
sharp(fixtures.inputJpg)
.sequentialRead(false)
.resize(320, 240)
@@ -559,7 +593,67 @@ describe('Input/output', function () {
});
});
describe('Limit pixel count of input image', function () {
describe('Limit pixel count of input image', () => {
it('Invalid fails - negative', () => {
assert.throws(() => {
sharp({ limitInputPixels: -1 });
});
});
it('Invalid fails - float', () => {
assert.throws(() => {
sharp({ limitInputPixels: 12.3 });
});
});
it('Invalid fails - string', () => {
assert.throws(() => {
sharp({ limitInputPixels: 'fail' });
});
});
it('Same size as input works', () =>
sharp(fixtures.inputJpg)
.metadata()
.then(({ width, height }) =>
sharp(fixtures.inputJpg, { limitInputPixels: width * height }).toBuffer()
)
);
it('Disabling limit works', () =>
sharp(fixtures.inputJpgLarge, { limitInputPixels: false })
.resize(2)
.toBuffer()
);
it('Enabling default limit works and fails with a large image', () =>
sharp(fixtures.inputJpgLarge, { limitInputPixels: true })
.toBuffer()
.then(() => {
assert.fail('Expected to fail');
})
.catch(err => {
assert.strictEqual(err.message, 'Input image exceeds pixel limit');
})
);
it('Smaller than input fails', () =>
sharp(fixtures.inputJpg)
.metadata()
.then(({ width, height }) =>
sharp(fixtures.inputJpg, { limitInputPixels: width * height - 1 })
.toBuffer()
.then(() => {
assert.fail('Expected to fail');
})
.catch(err => {
assert.strictEqual(err.message, 'Input image exceeds pixel limit');
})
)
);
});
describe('Limit pixel count of input image - deprecated', function () {
it('Invalid fails - negative', function (done) {
let isValid = false;
try {
@@ -635,27 +729,40 @@ describe('Input/output', function () {
});
describe('Input options', function () {
it('Option-less', function () {
sharp();
});
it('Ignore unknown attribute', function () {
sharp({ unknown: true });
});
it('undefined with options fails', function () {
assert.throws(function () {
sharp(undefined, {});
}, /Unsupported input 'undefined' of type undefined when also providing options of type object/);
});
it('null with options fails', function () {
assert.throws(function () {
sharp(null, {});
}, /Unsupported input 'null' of type object when also providing options of type object/);
});
it('Non-Object options fails', function () {
assert.throws(function () {
sharp(null, 'zoinks');
});
sharp('test', 'zoinks');
}, /Invalid input options zoinks/);
});
it('Invalid density: string', function () {
assert.throws(function () {
sharp(null, { density: 'zoinks' });
});
});
it('Ignore unknown attribute', function () {
sharp(null, { unknown: true });
sharp({ density: 'zoinks' });
}, /Expected number between 1 and 2400 for density but received zoinks of type string/);
});
it('Invalid page property throws', function () {
assert.throws(function () {
sharp(null, { page: -1 });
sharp({ page: -1 });
}, /Expected integer between 0 and 100000 for page but received -1 of type number/);
});
it('Invalid pages property throws', function () {
assert.throws(function () {
sharp(null, { pages: '1' });
sharp({ pages: '1' });
}, /Expected integer between -1 and 100000 for pages but received 1 of type string/);
});
});
@@ -749,7 +856,7 @@ describe('Input/output', function () {
assert.strictEqual(472, info.height);
assert.strictEqual(3, info.channels);
});
const badPipeline = sharp(null, { raw: { width: 840, height: 500, channels: 3 } })
const badPipeline = sharp({ raw: { width: 840, height: 500, channels: 3 } })
.toFormat('jpeg')
.toBuffer(function (err, data, info) {
assert.strictEqual(err.message.indexOf('memory area too small') > 0, true);
@@ -757,7 +864,7 @@ describe('Input/output', function () {
const inPipeline = sharp()
.resize(840, 472)
.raw();
const goodPipeline = sharp(null, { raw: { width: 840, height: 472, channels: 3 } })
const goodPipeline = sharp({ raw: { width: 840, height: 472, channels: 3 } })
.toFormat('jpeg')
.toBuffer(function (err, data, info) {
if (err) throw err;

View File

@@ -232,6 +232,55 @@ describe('Image metadata', function () {
done();
});
});
it('Animated GIF', () =>
sharp(fixtures.inputGifAnimated)
.metadata()
.then(({
format, width, height, space, channels, depth,
isProgressive, pages, pageHeight, loop, delay,
hasProfile, hasAlpha
}) => {
assert.strictEqual(format, 'gif');
assert.strictEqual(width, 80);
assert.strictEqual(height, 80);
assert.strictEqual(space, 'srgb');
assert.strictEqual(channels, 4);
assert.strictEqual(depth, 'uchar');
assert.strictEqual(isProgressive, false);
assert.strictEqual(pages, 30);
assert.strictEqual(pageHeight, 80);
assert.strictEqual(loop, 0);
assert.deepStrictEqual(delay, Array(30).fill(30));
assert.strictEqual(hasProfile, false);
assert.strictEqual(hasAlpha, true);
})
);
it('Animated GIF with limited looping', () =>
sharp(fixtures.inputGifAnimatedLoop3)
.metadata()
.then(({
format, width, height, space, channels, depth,
isProgressive, pages, pageHeight, loop, delay,
hasProfile, hasAlpha
}) => {
assert.strictEqual(format, 'gif');
assert.strictEqual(width, 370);
assert.strictEqual(height, 285);
assert.strictEqual(space, 'srgb');
assert.strictEqual(channels, 4);
assert.strictEqual(depth, 'uchar');
assert.strictEqual(isProgressive, false);
assert.strictEqual(pages, 10);
assert.strictEqual(pageHeight, 285);
assert.strictEqual(loop, 3);
assert.deepStrictEqual(delay, [...Array(9).fill(3000), 15000]);
assert.strictEqual(hasProfile, false);
assert.strictEqual(hasAlpha, true);
})
);
it('vips', () =>
sharp(fixtures.inputV)
.metadata()
@@ -515,6 +564,21 @@ describe('Image metadata', function () {
});
});
it('16-bit TIFF with TIFFTAG_PHOTOSHOP metadata', () =>
sharp(fixtures.inputTifftagPhotoshop)
.metadata()
.then(metadata => {
assert.strictEqual(metadata.format, 'tiff');
assert.strictEqual(metadata.width, 317);
assert.strictEqual(metadata.height, 211);
assert.strictEqual(metadata.space, 'rgb16');
assert.strictEqual(metadata.channels, 3);
assert.strictEqual(typeof metadata.tifftagPhotoshop, 'object');
assert.strictEqual(metadata.tifftagPhotoshop instanceof Buffer, true);
assert.strictEqual(metadata.tifftagPhotoshop.length, 6634);
})
);
it('File input with corrupt header fails gracefully', function (done) {
sharp(fixtures.inputJpgWithCorruptHeader)
.metadata(function (err) {

View File

@@ -122,4 +122,21 @@ describe('Modulate', function () {
});
});
});
it('should be able to use linear and modulate together', function () {
const base = 'modulate-linear.jpg';
const actual = fixtures.path('output.' + base);
const expected = fixtures.expected(base);
const contrast = 1.5;
const brightness = 0.5;
return sharp(fixtures.testPattern)
.linear(contrast, -(128 * contrast) + 128)
.modulate({ brightness })
.toFile(actual)
.then(function () {
fixtures.assertMaxColourDistance(actual, expected);
});
});
});

View File

@@ -103,6 +103,15 @@ describe('PNG', function () {
});
});
it('16-bit grey+alpha PNG identity transform', function () {
const actual = fixtures.path('output.16-bit-grey-alpha-identity.png');
return sharp(fixtures.inputPng16BitGreyAlpha)
.toFile(actual)
.then(function () {
fixtures.assertMaxColourDistance(actual, fixtures.expected('16-bit-grey-alpha-identity.png'));
});
});
it('Valid PNG libimagequant palette value does not throw error', function () {
assert.doesNotThrow(function () {
sharp().png({ palette: false });

View File

@@ -307,6 +307,40 @@ describe('Resize dimensions', function () {
});
});
it('Do crop when fit = cover and withoutEnlargement = true and width >= outputWidth, and height < outputHeight', function (done) {
sharp(fixtures.inputJpg)
.resize({
width: 3000,
height: 1000,
withoutEnlargement: true
})
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(2725, info.width);
assert.strictEqual(1000, info.height);
done();
});
});
it('Do crop when fit = cover and withoutEnlargement = true and width < outputWidth, and height >= outputHeight', function (done) {
sharp(fixtures.inputJpg)
.resize({
width: 1500,
height: 2226,
withoutEnlargement: true
})
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(1500, info.width);
assert.strictEqual(2225, info.height);
done();
});
});
it('Do enlarge when input width is less than output width', function (done) {
sharp(fixtures.inputJpg)
.resize({

View File

@@ -242,11 +242,11 @@ describe('Image Stats', function () {
// red channel
assert.strictEqual(0, stats.channels[0].min);
assert.strictEqual(255, stats.channels[0].max);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 83291370));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11379783198));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 105.36169496842616));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.39412151419967));
assert.strictEqual(true, isInRange(stats.channels[0].max, 254, 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 82506996));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11213984832));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 104.36947963892487));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.379896254993135));
assert.strictEqual(true, isInteger(stats.channels[0].minX));
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[0].minY));
@@ -258,11 +258,11 @@ describe('Image Stats', function () {
// green channel
assert.strictEqual(0, stats.channels[1].min);
assert.strictEqual(255, stats.channels[1].max);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120877425));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20774687595));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 152.9072025279307));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.84143349689916));
assert.strictEqual(true, isInRange(stats.channels[1].max, 254, 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120089056));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20533721114));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 151.90993361398964));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.83370206587037));
assert.strictEqual(true, isInteger(stats.channels[1].minX));
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[1].minY));
@@ -274,11 +274,11 @@ describe('Image Stats', function () {
// blue channel
assert.strictEqual(0, stats.channels[2].min);
assert.strictEqual(255, stats.channels[2].max);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138938859));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28449125593));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 175.75450711423252));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.39929031070358));
assert.strictEqual(true, isInRange(stats.channels[2].max, 254, 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138153653));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28172033081));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 174.76123932359133));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.38276338513747));
assert.strictEqual(true, isInteger(stats.channels[2].minX));
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[2].minY));

View File

@@ -2,6 +2,7 @@
const fs = require('fs');
const assert = require('assert');
const promisify = require('util').promisify;
const rimraf = require('rimraf');
const sharp = require('../../');
@@ -150,40 +151,40 @@ describe('TIFF', function () {
});
});
it('TIFF setting xres and yres on file', function (done) {
const res = 1000.0; // inputTiff has a dpi of 300 (res*2.54)
it('TIFF setting xres and yres on file', () =>
sharp(fixtures.inputTiff)
.tiff({
xres: (res),
yres: (res)
xres: 1000,
yres: 1000
})
.toFile(fixtures.outputTiff, (err, info) => {
if (err) throw err;
assert.strictEqual('tiff', info.format);
sharp(fixtures.outputTiff).metadata(function (err, metadata) {
if (err) throw err;
assert.strictEqual(metadata.density, res * 2.54); // convert to dpi
rimraf(fixtures.outputTiff, done);
});
});
});
.toFile(fixtures.outputTiff)
.then(() => sharp(fixtures.outputTiff)
.metadata()
.then(({ density }) => {
assert.strictEqual(true,
density === 2540 || // libvips <= 8.8.2
density === 25400); // libvips >= 8.8.3
return promisify(rimraf)(fixtures.outputTiff);
})
)
);
it('TIFF setting xres and yres on buffer', function (done) {
const res = 1000.0; // inputTiff has a dpi of 300 (res*2.54)
it('TIFF setting xres and yres on buffer', () =>
sharp(fixtures.inputTiff)
.tiff({
xres: (res),
yres: (res)
xres: 1000,
yres: 1000
})
.toBuffer(function (err, data, info) {
if (err) throw err;
sharp(data).metadata(function (err, metadata) {
if (err) throw err;
assert.strictEqual(metadata.density, res * 2.54); // convert to dpi
done();
});
});
});
.toBuffer()
.then(data => sharp(data)
.metadata()
.then(({ density }) => {
assert.strictEqual(true,
density === 2540 || // libvips <= 8.8.2
density === 25400); // libvips >= 8.8.3
})
)
);
it('TIFF invalid xres value should throw an error', function () {
assert.throws(function () {
@@ -207,11 +208,48 @@ describe('TIFF', function () {
.toFile(fixtures.outputTiff, (err, info) => {
if (err) throw err;
assert.strictEqual('tiff', info.format);
assert.strictEqual(3, info.channels);
assert(info.size < startSize);
rimraf(fixtures.outputTiff, done);
});
});
it('TIFF LZW RGBA toFile', () =>
sharp({
create: {
width: 1,
height: 1,
channels: 4,
background: 'red'
}
})
.tiff({
compression: 'lzw'
})
.toFile(fixtures.outputTiff)
.then(info => {
assert.strictEqual(4, info.channels);
})
);
it('TIFF LZW RGBA toBuffer', () =>
sharp({
create: {
width: 1,
height: 1,
channels: 4,
background: 'red'
}
})
.tiff({
compression: 'lzw'
})
.toBuffer({ resolveWithObject: true })
.then(({ info }) => {
assert.strictEqual(4, info.channels);
})
);
it('TIFF ccittfax4 compression shrinks b-w test file', function (done) {
const startSize = fs.statSync(fixtures.inputTiff).size;
sharp(fixtures.inputTiff)

View File

@@ -91,6 +91,29 @@ const assertGoogleTiles = function (directory, expectedTileSize, expectedLevels,
});
};
// Verifies tiles at specified level in a given output directory are > size+overlap
const assertTileOverlap = function (directory, tileSize, done) {
// Get sorted levels
const levels = fs.readdirSync(directory).sort((a, b) => a - b);
// Select the highest tile level
const highestLevel = levels[levels.length - 1];
// Get sorted tiles from greatest level
const tiles = fs.readdirSync(path.join(directory, highestLevel)).sort();
// Select a tile from the approximate center of the image
const squareTile = path.join(directory, highestLevel, tiles[Math.floor(tiles.length / 2)]);
sharp(squareTile).metadata(function (err, metadata) {
if (err) {
throw err;
} else {
// Tile with an overlap should be larger than original size
assert.strictEqual(true, metadata.width > tileSize);
assert.strictEqual(true, metadata.height > tileSize);
done();
}
});
};
describe('Tile', function () {
it('Valid size values pass', function () {
[1, 8192].forEach(function (size) {
@@ -297,7 +320,9 @@ describe('Tile', function () {
assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual('undefined', typeof info.size);
assertDeepZoomTiles(directory, 512 + (2 * 16), 13, done);
assertDeepZoomTiles(directory, 512 + (2 * 16), 13, function () {
assertTileOverlap(directory, 512, done);
});
});
});
});

View File

@@ -10,8 +10,8 @@ describe('toBuffer', () => {
const image = sharp(fixtures.inputJpg);
image.toBuffer({ resolveWithObject: true }).then((obj) => {
image.toBuffer().then((buff) => {
assert.strict.equal(Buffer.isBuffer(buff), true);
assert.strict.equal(typeof obj, 'object');
assert.strictEqual(Buffer.isBuffer(buff), true);
assert.strictEqual(typeof obj, 'object');
done();
});
});

View File

@@ -41,6 +41,21 @@ describe('Trim borders', function () {
});
});
it('single colour PNG where alpha channel provides the image', () =>
sharp(fixtures.inputPngImageInAlpha)
.trim()
.toBuffer({ resolveWithObject: true })
.then(({ data, info }) => {
assert.strictEqual(true, data.length > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(916, info.width);
assert.strictEqual(137, info.height);
assert.strictEqual(4, info.channels);
assert.strictEqual(-6, info.trimOffsetLeft);
assert.strictEqual(-20, info.trimOffsetTop);
})
);
it('16-bit PNG with alpha channel', function (done) {
sharp(fixtures.inputPngWithTransparency16bit)
.resize(32, 32)