mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
31 Commits
v0.26.0-al
...
v0.26.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4c43d525b | ||
|
|
6c5cde363a | ||
|
|
d46b4d950f | ||
|
|
b369c4bb8a | ||
|
|
c3898487c4 | ||
|
|
ca3c5b400f | ||
|
|
97772b176c | ||
|
|
08a2965f1c | ||
|
|
76dcddfa3d | ||
|
|
79f476ae4d | ||
|
|
d406cb619c | ||
|
|
4f3890f1e4 | ||
|
|
7a9d58cc51 | ||
|
|
eef87da0e1 | ||
|
|
00e65f6f14 | ||
|
|
866e9824d1 | ||
|
|
482e6078e2 | ||
|
|
bc7ab296ef | ||
|
|
a5f4f53b56 | ||
|
|
b1227f526d | ||
|
|
78b42c8306 | ||
|
|
4b6d45ab8e | ||
|
|
7980341923 | ||
|
|
3917efdebd | ||
|
|
eaecb7347b | ||
|
|
05ca7d3129 | ||
|
|
4beae0de71 | ||
|
|
b711661784 | ||
|
|
0c4a45b1f4 | ||
|
|
ec2beb0039 | ||
|
|
cafb3e8bac |
@@ -2,6 +2,7 @@ freebsd_instance:
|
|||||||
image_family: freebsd-13-0-snap
|
image_family: freebsd-13-0-snap
|
||||||
|
|
||||||
task:
|
task:
|
||||||
|
name: FreeBSD 13.0
|
||||||
env:
|
env:
|
||||||
IGNORE_OSVERSION: yes
|
IGNORE_OSVERSION: yes
|
||||||
prerequisites_script:
|
prerequisites_script:
|
||||||
|
|||||||
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -42,10 +42,6 @@ You deserve to add your details to the [list of contributors](https://github.com
|
|||||||
|
|
||||||
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
|
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
|
||||||
|
|
||||||
| Release | WIP branch |
|
|
||||||
| ------: | :--------- |
|
|
||||||
| v0.26.0 | zoom |
|
|
||||||
|
|
||||||
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
|
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
|
||||||
|
|
||||||
### Add a new public method
|
### Add a new public method
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/installation.md
vendored
2
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -7,7 +7,7 @@ labels: installation
|
|||||||
|
|
||||||
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/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?
|
Have you ensured the architecture and platform of Node.js used for `npm install` is the same as the architecture and platform of Node.js used at runtime?
|
||||||
|
|
||||||
Are you using the latest version? Is the version currently in use as reported by `npm ls sharp` the same as the latest version as reported by `npm view sharp dist-tags.latest`?
|
Are you using the latest version? Is the version currently in use as reported by `npm ls sharp` the same as the latest version as reported by `npm view sharp dist-tags.latest`?
|
||||||
|
|
||||||
|
|||||||
12
.travis.yml
12
.travis.yml
@@ -5,7 +5,7 @@ jobs:
|
|||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- sudo docker run -dit --name sharp --env CI --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
|
- sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp centos:7
|
||||||
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_10.x | bash -"
|
- sudo docker exec sharp bash -c "curl -sL https://rpm.nodesource.com/setup_10.x | bash -"
|
||||||
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
|
- sudo docker exec sharp yum install -y gcc-c++ make git nodejs
|
||||||
install: sudo docker exec sharp bash -c "npm install --unsafe-perm"
|
install: sudo docker exec sharp bash -c "npm install --unsafe-perm"
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- sudo docker run -dit --name sharp --env CI --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10.17.0-alpine3.9 # https://github.com/nodejs/docker-node/issues/1158
|
- sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --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
|
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
||||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
@@ -69,10 +69,10 @@ jobs:
|
|||||||
dist: bionic
|
dist: bionic
|
||||||
language: shell
|
language: shell
|
||||||
before_install:
|
before_install:
|
||||||
- sudo docker run -dit --name sharp --env CI --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
- sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_10.x buster main' >/etc/apt/sources.list.d/nodesource.list"
|
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_10.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs=10.*"
|
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs=10.*"
|
||||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_12.x buster main' >/etc/apt/sources.list.d/nodesource.list"
|
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_12.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp arm64v8/debian:bullseye
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y build-essential git python2 curl"
|
||||||
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
- sudo docker exec sharp sh -c "curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -"
|
||||||
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_14.x buster main' >/etc/apt/sources.list.d/nodesource.list"
|
- sudo docker exec sharp sh -c "echo 'deb https://deb.nodesource.com/node_14.x sid main' >/etc/apt/sources.list.d/nodesource.list"
|
||||||
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
- sudo docker exec sharp sh -c "apt-get update && apt-get install -y nodejs"
|
||||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||||
script: sudo docker exec sharp sh -c "npm test"
|
script: sudo docker exec sharp sh -c "npm test"
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
'src/sharp.cc'
|
'src/sharp.cc'
|
||||||
],
|
],
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
'<!@(node -p "require(\'node-addon-api\').include")',
|
'<!(node -p "require(\'node-addon-api\').include_dir")',
|
||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['use_global_libvips == "true"', {
|
['use_global_libvips == "true"', {
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ Implements the [stream.Duplex][1] class.
|
|||||||
An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default `268402689`)
|
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][5]** Set this to `true` to use sequential rather than random access where possible.
|
- `options.sequentialRead` **[boolean][5]** 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`)
|
This can reduce memory usage and might improve performance on some systems. (optional, default `false`)
|
||||||
- `options.density` **[number][6]** number representing the DPI for vector images. (optional, default `72`)
|
- `options.density` **[number][6]** number representing the DPI for vector images in the range 1 to 100000. (optional, default `72`)
|
||||||
- `options.pages` **[number][6]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
- `options.pages` **[number][6]** number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages. (optional, default `1`)
|
||||||
- `options.page` **[number][6]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
|
- `options.page` **[number][6]** page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based. (optional, default `0`)
|
||||||
- `options.level` **[number][6]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
- `options.level` **[number][6]** level to extract from a multi-level input (OpenSlide), zero based. (optional, default `0`)
|
||||||
|
- `options.animated` **[boolean][5]** Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`). (optional, default `false`)
|
||||||
- `options.raw` **[Object][4]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
- `options.raw` **[Object][4]?** describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
- `options.raw.width` **[number][6]?**
|
- `options.raw.width` **[number][6]?**
|
||||||
- `options.raw.height` **[number][6]?**
|
- `options.raw.height` **[number][6]?**
|
||||||
@@ -78,6 +79,11 @@ sharp({
|
|||||||
.then( ... );
|
.then( ... );
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Convert an animated GIF to an animated WebP
|
||||||
|
await sharp('in.gif', { animated: true }).toFile('out.webp');
|
||||||
|
```
|
||||||
|
|
||||||
- Throws **[Error][8]** Invalid parameters
|
- Throws **[Error][8]** Invalid parameters
|
||||||
|
|
||||||
Returns **[Sharp][9]**
|
Returns **[Sharp][9]**
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ Returns **[Promise][5]<[Buffer][8]>** when no callback is provided
|
|||||||
## withMetadata
|
## withMetadata
|
||||||
|
|
||||||
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||||
This will also convert to and add a web-friendly sRGB ICC profile.
|
This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
||||||
|
output profile is provided.
|
||||||
|
|
||||||
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||||
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||||
@@ -100,6 +101,7 @@ sRGB colour space and strip all metadata, including the removal of any ICC profi
|
|||||||
|
|
||||||
- `options` **[Object][6]?**
|
- `options` **[Object][6]?**
|
||||||
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
- `options.orientation` **[number][9]?** value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
|
- `options.icc` **[string][2]?** filesystem path to output ICC profile, defaults to sRGB.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,27 @@
|
|||||||
|
|
||||||
Requires libvips v8.10.0
|
Requires libvips v8.10.0
|
||||||
|
|
||||||
### v0.26.0 - TBD
|
### v0.26.1 - 20th September 2020
|
||||||
|
|
||||||
|
* Ensure correct pageHeight when verifying multi-page image dimensions.
|
||||||
|
[#2343](https://github.com/lovell/sharp/pull/2343)
|
||||||
|
[@derom](https://github.com/derom)
|
||||||
|
|
||||||
|
* Allow input density range up to 100000 DPI.
|
||||||
|
[#2348](https://github.com/lovell/sharp/pull/2348)
|
||||||
|
[@stefanprobst](https://github.com/stefanprobst)
|
||||||
|
|
||||||
|
* Ensure animation-related properties can be set for Stream-based input.
|
||||||
|
[#2369](https://github.com/lovell/sharp/pull/2369)
|
||||||
|
[@AcrylicShrimp](https://github.com/AcrylicShrimp)
|
||||||
|
|
||||||
|
* Ensure `stats` can be calculated for 1x1 input.
|
||||||
|
[#2372](https://github.com/lovell/sharp/issues/2372)
|
||||||
|
|
||||||
|
* Ensure animated GIF output is optimised.
|
||||||
|
[#2376](https://github.com/lovell/sharp/issues/2376)
|
||||||
|
|
||||||
|
### v0.26.0 - 25th August 2020
|
||||||
|
|
||||||
* Prebuilt libvips binaries are now statically-linked and Brotli-compressed, requiring Node.js 10.16.0+.
|
* Prebuilt libvips binaries are now statically-linked and Brotli-compressed, requiring Node.js 10.16.0+.
|
||||||
|
|
||||||
@@ -27,6 +47,10 @@ Requires libvips v8.10.0
|
|||||||
[#2259](https://github.com/lovell/sharp/pull/2259)
|
[#2259](https://github.com/lovell/sharp/pull/2259)
|
||||||
[@vouillon](https://github.com/vouillon)
|
[@vouillon](https://github.com/vouillon)
|
||||||
|
|
||||||
|
* Add support to `withMetadata` for custom ICC profile.
|
||||||
|
[#2271](https://github.com/lovell/sharp/pull/2271)
|
||||||
|
[@roborourke](https://github.com/roborourke)
|
||||||
|
|
||||||
* Ensure prebuilt binaries for ARM default to v7 when using Electron.
|
* Ensure prebuilt binaries for ARM default to v7 when using Electron.
|
||||||
[#2292](https://github.com/lovell/sharp/pull/2292)
|
[#2292](https://github.com/lovell/sharp/pull/2292)
|
||||||
[@diegodev3](https://github.com/diegodev3)
|
[@diegodev3](https://github.com/diegodev3)
|
||||||
|
|||||||
@@ -194,3 +194,12 @@ GitHub: https://github.com/vouillon
|
|||||||
|
|
||||||
Name: Tomáš Szabo
|
Name: Tomáš Szabo
|
||||||
GitHub: https://github.com/deftomat
|
GitHub: https://github.com/deftomat
|
||||||
|
|
||||||
|
Name: Robert O'Rourke
|
||||||
|
GitHub: https://github.com/roborourke
|
||||||
|
|
||||||
|
Name: Denis Soldatov
|
||||||
|
GitHub: https://github.com/derom
|
||||||
|
|
||||||
|
Name: Stefan Probst
|
||||||
|
GitHub: https://github.com/stefanprobst
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; object-src 'none'; style-src 'unsafe-inline';
|
||||||
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
img-src 'unsafe-inline' data: https://pixel.plumbing/px/ https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
||||||
connect-src 'self' https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
connect-src 'self' https://cdn.jsdelivr.net/gh/lovell/ https://www.google-analytics.com;
|
||||||
script-src 'sha256-iXDHv+t2aGdJcmEvwHfqmGH4SrOSx+P8rCF6+WLLwXA=' 'unsafe-eval'
|
script-src 'unsafe-inline' 'unsafe-eval'
|
||||||
https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js
|
https://cdn.jsdelivr.net/npm/docute@4/dist/docute.min.js
|
||||||
https://www.google-analytics.com/analytics.js;">
|
https://www.google-analytics.com/analytics.js;">
|
||||||
<link rel="icon" type="image/png" href="https://pixel.plumbing/px/32x32/sharp-logo.svg">
|
<link rel="icon" type="image/png" href="https://pixel.plumbing/px/32x32/sharp-logo.svg">
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://pixel.plumbing/px/72x72/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="apple-touch-icon-precomposed" href="https://pixel.plumbing/px/57x57/sharp-logo.svg">
|
||||||
<link rel="author" href="/humans.txt" type="text/plain">
|
<link rel="author" href="/humans.txt" type="text/plain">
|
||||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.4/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@v0.26.1/docs/README.md" as="fetch" type="text/markdown" crossorigin>
|
||||||
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
|
<link rel="preload" href="https://cdn.jsdelivr.net/gh/lovell/sharp@master/docs/image/sharp-logo.svg" as="image" type="image/svg+xml" crossorigin>
|
||||||
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
<link rel="dns-prefetch" href="https://pixel.plumbing">
|
||||||
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
<link rel="dns-prefetch" href="https://www.google-analytics.com">
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
docuteApiTitlePlugin,
|
docuteApiTitlePlugin,
|
||||||
docuteApiSearchPlugin
|
docuteApiSearchPlugin
|
||||||
],
|
],
|
||||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.25.4/docs',
|
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.26.1/docs',
|
||||||
nav: [
|
nav: [
|
||||||
{
|
{
|
||||||
title: 'Funding',
|
title: 'Funding',
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ A test to benchmark the performance of this module relative to alternatives.
|
|||||||
|
|
||||||
## The contenders
|
## The contenders
|
||||||
|
|
||||||
* [jimp](https://www.npmjs.com/package/jimp) v0.9.3 - Image processing in pure JavaScript. Provides bicubic interpolation.
|
* [jimp](https://www.npmjs.com/package/jimp) v0.16.0 - 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.
|
* [mapnik](https://www.npmjs.org/package/mapnik) v4.5.2 - 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*".
|
* [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.
|
* [gm](https://www.npmjs.com/package/gm) v1.23.1 - Fully featured wrapper around GraphicsMagick's `gm` command line utility.
|
||||||
* sharp v0.24.0 / libvips v8.9.0 - Caching within libvips disabled to ensure a fair comparison.
|
* sharp v0.26.0 / libvips v8.10.0 - Caching within libvips disabled to ensure a fair comparison.
|
||||||
|
|
||||||
## The task
|
## The task
|
||||||
|
|
||||||
@@ -18,25 +18,25 @@ then compress to JPEG at a "quality" setting of 80.
|
|||||||
|
|
||||||
## Test environment
|
## Test environment
|
||||||
|
|
||||||
* AWS EC2 eu-west-1 [c5.large](https://aws.amazon.com/ec2/instance-types/c5/) (2x Xeon Platinum 8124M CPU @ 3.00GHz)
|
* AWS EC2 eu-west-1 [c5d.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)
|
* Ubuntu 20.04 (ami-0f1d11c92a9467c07)
|
||||||
* Node.js v12.14.1
|
* Node.js v14.8.0
|
||||||
|
|
||||||
## Results
|
## Results
|
||||||
|
|
||||||
| Module | Input | Output | Ops/sec | Speed-up |
|
| Module | Input | Output | Ops/sec | Speed-up |
|
||||||
| :----------------- | :----- | :----- | ------: | -------: |
|
| :----------------- | :----- | :----- | ------: | -------: |
|
||||||
| jimp | buffer | buffer | 0.72 | 1.0 |
|
| jimp | buffer | buffer | 0.75 | 1.0 |
|
||||||
| mapnik | buffer | buffer | 3.02 | 4.2 |
|
| mapnik | buffer | buffer | 3.00 | 4.0 |
|
||||||
| gm | buffer | buffer | 3.90 | 5.4 |
|
| gm | buffer | buffer | 4.12 | 5.5 |
|
||||||
| gm | file | file | 3.94 | 5.5 |
|
| gm | file | file | 4.13 | 5.5 |
|
||||||
| imagemagick | file | file | 4.30 | 6.0 |
|
| imagemagick | file | file | 4.30 | 5.7 |
|
||||||
| sharp | stream | stream | 23.65 | 32.8 |
|
| sharp | stream | stream | 22.37 | 29.8 |
|
||||||
| sharp | file | file | 24.66 | 34.3 |
|
| sharp | file | file | 23.40 | 31.2 |
|
||||||
| sharp | buffer | buffer | 25.14 | 34.9 |
|
| sharp | buffer | buffer | 24.01 | 32.0 |
|
||||||
|
|
||||||
Greater libvips performance can be expected with caching enabled (default)
|
Greater libvips performance can be expected with caching enabled (default)
|
||||||
and using 8+ core machines, especially those with larger L1/L2 CPU caches.
|
and using 4+ 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.
|
The I/O limits of the relevant (de)compression library will generally determine maximum throughput.
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ try {
|
|||||||
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
throw new Error(`Use with glibc ${detectLibc.version} requires manual installation of libvips >= ${minimumLibvipsVersion}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!semver.satisfies(process.versions.node, process.env.npm_package_engines_node)) {
|
||||||
|
throw new Error(`Expected Node.js version ${process.env.npm_package_engines_node} but found ${process.versions.node}`);
|
||||||
|
}
|
||||||
// Download to per-process temporary file
|
// Download to per-process temporary file
|
||||||
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br';
|
const tarFilename = ['libvips', minimumLibvipsVersion, platformAndArch].join('-') + '.tar.br';
|
||||||
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
const tarPathCache = path.join(libvips.cachePath(), tarFilename);
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { execFileSync } = require('child_process');
|
const { spawnSync } = require('child_process');
|
||||||
|
|
||||||
const { prebuild_upload: hasToken, APPVEYOR_REPO_TAG_NAME, TRAVIS_TAG } = process.env;
|
const { prebuild_upload: hasToken, APPVEYOR_REPO_TAG_NAME, TRAVIS_TAG } = process.env;
|
||||||
|
|
||||||
if (hasToken && (APPVEYOR_REPO_TAG_NAME || TRAVIS_TAG)) {
|
if (hasToken && (APPVEYOR_REPO_TAG_NAME || TRAVIS_TAG)) {
|
||||||
execFileSync('prebuild', ['--runtime', 'napi', '--target', '3'], { stdio: 'inherit' });
|
spawnSync('node',
|
||||||
|
['./node_modules/prebuild/bin.js', '--runtime', 'napi', '--target', '3'],
|
||||||
|
{ shell: true, stdio: 'inherit' }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* .toBuffer()
|
* .toBuffer()
|
||||||
* .then( ... );
|
* .then( ... );
|
||||||
*
|
*
|
||||||
|
* @example
|
||||||
|
* // Convert an animated GIF to an animated WebP
|
||||||
|
* await sharp('in.gif', { animated: true }).toFile('out.webp');
|
||||||
|
*
|
||||||
* @param {(Buffer|string)} [input] - if present, can be
|
* @param {(Buffer|string)} [input] - if present, can be
|
||||||
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
|
||||||
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
* a String containing the filesystem path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
|
||||||
@@ -98,10 +102,11 @@ const debuglog = util.debuglog('sharp');
|
|||||||
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
|
* 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.
|
* @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.
|
* 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.density=72] - number representing the DPI for vector images in the range 1 to 100000.
|
||||||
* @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.pages=1] - number of pages to extract for multi-page input (GIF, TIFF, PDF), use -1 for all pages.
|
||||||
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
|
* @param {number} [options.page=0] - page number to start extracting from for multi-page input (GIF, TIFF, PDF), zero based.
|
||||||
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
||||||
|
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
|
||||||
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
||||||
* @param {number} [options.raw.width]
|
* @param {number} [options.raw.width]
|
||||||
* @param {number} [options.raw.height]
|
* @param {number} [options.raw.height]
|
||||||
@@ -187,6 +192,7 @@ const Sharp = function (input, options) {
|
|||||||
streamOut: false,
|
streamOut: false,
|
||||||
withMetadata: false,
|
withMetadata: false,
|
||||||
withMetadataOrientation: -1,
|
withMetadataOrientation: -1,
|
||||||
|
withMetadataIcc: '',
|
||||||
resolveWithObject: false,
|
resolveWithObject: false,
|
||||||
// output format
|
// output format
|
||||||
jpegQuality: 80,
|
jpegQuality: 80,
|
||||||
|
|||||||
17
lib/input.js
17
lib/input.js
@@ -9,9 +9,9 @@ const sharp = require('../build/Release/sharp.node');
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function _inputOptionsFromObject (obj) {
|
function _inputOptionsFromObject (obj) {
|
||||||
const { raw, density, limitInputPixels, sequentialRead, failOnError } = obj;
|
const { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages } = obj;
|
||||||
return [raw, density, limitInputPixels, sequentialRead, failOnError].some(is.defined)
|
return [raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages].some(is.defined)
|
||||||
? { raw, density, limitInputPixels, sequentialRead, failOnError }
|
? { raw, density, limitInputPixels, sequentialRead, failOnError, animated, page, pages }
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,10 +57,10 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
}
|
}
|
||||||
// Density
|
// Density
|
||||||
if (is.defined(inputOptions.density)) {
|
if (is.defined(inputOptions.density)) {
|
||||||
if (is.inRange(inputOptions.density, 1, 2400)) {
|
if (is.inRange(inputOptions.density, 1, 100000)) {
|
||||||
inputDescriptor.density = inputOptions.density;
|
inputDescriptor.density = inputOptions.density;
|
||||||
} else {
|
} else {
|
||||||
throw is.invalidParameterError('density', 'number between 1 and 2400', inputOptions.density);
|
throw is.invalidParameterError('density', 'number between 1 and 100000', inputOptions.density);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// limitInputPixels
|
// limitInputPixels
|
||||||
@@ -99,6 +99,13 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Multi-page input (GIF, TIFF, PDF)
|
// Multi-page input (GIF, TIFF, PDF)
|
||||||
|
if (is.defined(inputOptions.animated)) {
|
||||||
|
if (is.bool(inputOptions.animated)) {
|
||||||
|
inputDescriptor.pages = inputOptions.animated ? -1 : 1;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('animated', 'boolean', inputOptions.animated);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (is.defined(inputOptions.pages)) {
|
if (is.defined(inputOptions.pages)) {
|
||||||
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
|
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
|
||||||
inputDescriptor.pages = inputOptions.pages;
|
inputDescriptor.pages = inputOptions.pages;
|
||||||
|
|||||||
@@ -119,7 +119,8 @@ function toBuffer (options, callback) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||||
* This will also convert to and add a web-friendly sRGB ICC profile.
|
* This will also convert to and add a web-friendly sRGB ICC profile unless a custom
|
||||||
|
* output profile is provided.
|
||||||
*
|
*
|
||||||
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
* The default behaviour, when `withMetadata` is not used, is to convert to the device-independent
|
||||||
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
|
||||||
@@ -132,6 +133,7 @@ function toBuffer (options, callback) {
|
|||||||
*
|
*
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
* @param {number} [options.orientation] value between 1 and 8, used to update the EXIF `Orientation` tag.
|
||||||
|
* @param {string} [options.icc] filesystem path to output ICC profile, defaults to sRGB.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid parameters
|
* @throws {Error} Invalid parameters
|
||||||
*/
|
*/
|
||||||
@@ -145,6 +147,13 @@ function withMetadata (options) {
|
|||||||
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.icc)) {
|
||||||
|
if (is.string(options.icc)) {
|
||||||
|
this.options.withMetadataIcc = options.icc;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('icc', 'string filesystem path to ICC profile', options.icc);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -399,6 +408,7 @@ function webp (options) {
|
|||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
|
/* istanbul ignore next */
|
||||||
function gif (options) {
|
function gif (options) {
|
||||||
if (!this.constructor.format.magick.output.buffer) {
|
if (!this.constructor.format.magick.output.buffer) {
|
||||||
throw new Error('The gif operation requires libvips to have been installed with support for ImageMagick');
|
throw new Error('The gif operation requires libvips to have been installed with support for ImageMagick');
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
||||||
"version": "0.26.0-alpha1",
|
"version": "0.26.1",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://github.com/lovell/sharp",
|
"homepage": "https://github.com/lovell/sharp",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -68,7 +68,8 @@
|
|||||||
"Brychan Bennett-Odlum <git@brychan.io>",
|
"Brychan Bennett-Odlum <git@brychan.io>",
|
||||||
"Edward Silverton <e.silverton@gmail.com>",
|
"Edward Silverton <e.silverton@gmail.com>",
|
||||||
"Roman Malieiev <aromaleev@gmail.com>",
|
"Roman Malieiev <aromaleev@gmail.com>",
|
||||||
"Tomas Szabo <tomas.szabo@deftomat.com>"
|
"Tomas Szabo <tomas.szabo@deftomat.com>",
|
||||||
|
"Robert O'Rourke <robert@o-rourke.org>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||||
@@ -113,7 +114,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.2",
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"node-addon-api": "^3.0.0",
|
"node-addon-api": "^3.0.2",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^5.3.5",
|
"prebuild-install": "^5.3.5",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
@@ -130,15 +131,15 @@
|
|||||||
"icc": "^2.0.0",
|
"icc": "^2.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^8.1.1",
|
"mocha": "^8.1.1",
|
||||||
"mock-fs": "^4.12.0",
|
"mock-fs": "^4.13.0",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"prebuild": "^10.0.0",
|
"prebuild": "^10.0.1",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"semistandard": "^14.2.3"
|
"semistandard": "^14.2.3"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"config": {
|
"config": {
|
||||||
"libvips": "8.10.0-rc4",
|
"libvips": "8.10.0",
|
||||||
"runtime": "napi",
|
"runtime": "napi",
|
||||||
"target": 3
|
"target": 3
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -485,8 +485,8 @@ namespace sharp {
|
|||||||
Check the proposed format supports the current dimensions.
|
Check the proposed format supports the current dimensions.
|
||||||
*/
|
*/
|
||||||
void AssertImageTypeDimensions(VImage image, ImageType const imageType) {
|
void AssertImageTypeDimensions(VImage image, ImageType const imageType) {
|
||||||
const int height = image.get_typeof("pageHeight") == G_TYPE_INT
|
const int height = image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT
|
||||||
? image.get_int("pageHeight")
|
? image.get_int(VIPS_META_PAGE_HEIGHT)
|
||||||
: image.height();
|
: image.height();
|
||||||
if (imageType == ImageType::JPEG) {
|
if (imageType == ImageType::JPEG) {
|
||||||
if (image.width() > 65535 || height > 65535) {
|
if (image.width() > 65535 || height > 65535) {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 9))
|
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10))
|
||||||
#error "libvips version 8.9.2+ is required - please see https://sharp.pixelplumbing.com/install"
|
#error "libvips version 8.10.0+ is required - please see https://sharp.pixelplumbing.com/install"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||||
|
|||||||
@@ -684,6 +684,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply output ICC profile
|
||||||
|
if (!baton->withMetadataIcc.empty()) {
|
||||||
|
image = image.icc_transform(
|
||||||
|
const_cast<char*>(baton->withMetadataIcc.data()),
|
||||||
|
VImage::option()
|
||||||
|
->set("input_profile", "srgb")
|
||||||
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
|
}
|
||||||
|
|
||||||
// Override EXIF Orientation tag
|
// Override EXIF Orientation tag
|
||||||
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
if (baton->withMetadata && baton->withMetadataOrientation != -1) {
|
||||||
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
image = sharp::SetExifOrientation(image, baton->withMetadataOrientation);
|
||||||
@@ -773,6 +782,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
||||||
VipsArea *area = VIPS_AREA(image.magicksave_buffer(VImage::option()
|
VipsArea *area = VIPS_AREA(image.magicksave_buffer(VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
|
->set("optimize_gif_frames", TRUE)
|
||||||
|
->set("optimize_gif_transparency", TRUE)
|
||||||
->set("format", "gif")));
|
->set("format", "gif")));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
@@ -916,6 +927,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
||||||
image.magicksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.magicksave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
|
->set("optimize_gif_frames", TRUE)
|
||||||
|
->set("optimize_gif_transparency", TRUE)
|
||||||
->set("format", "gif"));
|
->set("format", "gif"));
|
||||||
baton->formatOut = "gif";
|
baton->formatOut = "gif";
|
||||||
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
||||||
@@ -1319,6 +1332,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
baton->fileOut = sharp::AttrAsStr(options, "fileOut");
|
||||||
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
baton->withMetadata = sharp::AttrAsBool(options, "withMetadata");
|
||||||
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
baton->withMetadataOrientation = sharp::AttrAsUint32(options, "withMetadataOrientation");
|
||||||
|
baton->withMetadataIcc = sharp::AttrAsStr(options, "withMetadataIcc");
|
||||||
// Format-specific
|
// Format-specific
|
||||||
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
baton->jpegQuality = sharp::AttrAsUint32(options, "jpegQuality");
|
||||||
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
baton->jpegProgressive = sharp::AttrAsBool(options, "jpegProgressive");
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ struct PipelineBaton {
|
|||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
|
std::string withMetadataIcc;
|
||||||
std::unique_ptr<double[]> convKernel;
|
std::unique_ptr<double[]> convKernel;
|
||||||
int convKernelWidth;
|
int convKernelWidth;
|
||||||
int convKernelHeight;
|
int convKernelHeight;
|
||||||
|
|||||||
14
src/stats.cc
14
src/stats.cc
@@ -80,12 +80,14 @@ class StatsWorker : public Napi::AsyncWorker {
|
|||||||
// Estimate entropy via histogram of greyscale value frequency
|
// Estimate entropy via histogram of greyscale value frequency
|
||||||
baton->entropy = std::abs(greyscale.hist_find().hist_entropy());
|
baton->entropy = std::abs(greyscale.hist_find().hist_entropy());
|
||||||
// Estimate sharpness via standard deviation of greyscale laplacian
|
// Estimate sharpness via standard deviation of greyscale laplacian
|
||||||
VImage laplacian = VImage::new_matrixv(3, 3,
|
if (image.width() > 1 || image.height() > 1) {
|
||||||
0.0, 1.0, 0.0,
|
VImage laplacian = VImage::new_matrixv(3, 3,
|
||||||
1.0, -4.0, 1.0,
|
0.0, 1.0, 0.0,
|
||||||
0.0, 1.0, 0.0);
|
1.0, -4.0, 1.0,
|
||||||
laplacian.set("scale", 9.0);
|
0.0, 1.0, 0.0);
|
||||||
baton->sharpness = greyscale.conv(laplacian).deviate();
|
laplacian.set("scale", 9.0);
|
||||||
|
baton->sharpness = greyscale.conv(laplacian).deviate();
|
||||||
|
}
|
||||||
// Most dominant sRGB colour via 4096-bin 3D histogram
|
// Most dominant sRGB colour via 4096-bin 3D histogram
|
||||||
vips::VImage hist = sharp::RemoveAlpha(image)
|
vips::VImage hist = sharp::RemoveAlpha(image)
|
||||||
.colourspace(VIPS_INTERPRETATION_sRGB)
|
.colourspace(VIPS_INTERPRETATION_sRGB)
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
"gm": "^1.23.1",
|
"gm": "^1.23.1",
|
||||||
"imagemagick": "^0.1.3",
|
"imagemagick": "^0.1.3",
|
||||||
"jimp": "^0.9.3",
|
"jimp": "^0.16.0",
|
||||||
"mapnik": "^4.3.1",
|
"mapnik": "^4.5.2",
|
||||||
"semver": "^7.1.1"
|
"semver": "^7.1.1"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.5.0"
|
"node": ">=10.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -564,8 +564,9 @@ async.series({
|
|||||||
},
|
},
|
||||||
// PNG
|
// PNG
|
||||||
png: function (callback) {
|
png: function (callback) {
|
||||||
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPngAlphaPremultiplicationLarge);
|
||||||
const pngSuite = new Benchmark.Suite('png');
|
const pngSuite = new Benchmark.Suite('png');
|
||||||
|
const minSamples = 64;
|
||||||
// jimp
|
// jimp
|
||||||
pngSuite.add('jimp-buffer-buffer', {
|
pngSuite.add('jimp-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
@@ -576,6 +577,8 @@ async.series({
|
|||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.deflateLevel(6)
|
||||||
|
.filterType(0)
|
||||||
.getBuffer(jimp.MIME_PNG, function (err) {
|
.getBuffer(jimp.MIME_PNG, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -589,12 +592,14 @@ async.series({
|
|||||||
}).add('jimp-file-file', {
|
}).add('jimp-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
jimp.read(fixtures.inputPng, function (err, image) {
|
jimp.read(fixtures.inputPngAlphaPremultiplicationLarge, function (err, image) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
image
|
image
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.deflateLevel(6)
|
||||||
|
.filterType(0)
|
||||||
.write(fixtures.outputPng, function (err) {
|
.write(fixtures.outputPng, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -610,7 +615,7 @@ async.series({
|
|||||||
pngSuite.add('mapnik-file-file', {
|
pngSuite.add('mapnik-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
mapnik.Image.open(fixtures.inputPng, function (err, img) {
|
mapnik.Image.open(fixtures.inputPngAlphaPremultiplicationLarge, function (err, img) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
img.premultiply(function (err, img) {
|
img.premultiply(function (err, img) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -657,11 +662,15 @@ async.series({
|
|||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
imagemagick.resize({
|
imagemagick.resize({
|
||||||
srcPath: fixtures.inputPng,
|
srcPath: fixtures.inputPngAlphaPremultiplicationLarge,
|
||||||
dstPath: fixtures.outputPng,
|
dstPath: fixtures.outputPng,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
filter: 'Lanczos'
|
filter: 'Lanczos',
|
||||||
|
customArgs: [
|
||||||
|
'-define', 'PNG:compression-level=6',
|
||||||
|
'-define', 'PNG:compression-filter=0'
|
||||||
|
]
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -675,9 +684,11 @@ async.series({
|
|||||||
pngSuite.add('gm-file-file', {
|
pngSuite.add('gm-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
gm(fixtures.inputPng)
|
gm(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||||
.filter('Lanczos')
|
.filter('Lanczos')
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.define('PNG:compression-level=6')
|
||||||
|
.define('PNG:compression-filter=0')
|
||||||
.write(fixtures.outputPng, function (err) {
|
.write(fixtures.outputPng, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -689,9 +700,11 @@ async.series({
|
|||||||
}).add('gm-file-buffer', {
|
}).add('gm-file-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
gm(fixtures.inputPng)
|
gm(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||||
.filter('Lanczos')
|
.filter('Lanczos')
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.define('PNG:compression-level=6')
|
||||||
|
.define('PNG:compression-filter=0')
|
||||||
.toBuffer(function (err, buffer) {
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -705,9 +718,11 @@ async.series({
|
|||||||
// sharp
|
// sharp
|
||||||
pngSuite.add('sharp-buffer-file', {
|
pngSuite.add('sharp-buffer-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
minSamples,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
sharp(inputPngBuffer)
|
sharp(inputPngBuffer)
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.png({ compressionLevel: 6 })
|
||||||
.toFile(fixtures.outputPng, function (err) {
|
.toFile(fixtures.outputPng, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -718,9 +733,11 @@ async.series({
|
|||||||
}
|
}
|
||||||
}).add('sharp-buffer-buffer', {
|
}).add('sharp-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
minSamples,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
sharp(inputPngBuffer)
|
sharp(inputPngBuffer)
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.png({ compressionLevel: 6 })
|
||||||
.toBuffer(function (err, buffer) {
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -732,9 +749,11 @@ async.series({
|
|||||||
}
|
}
|
||||||
}).add('sharp-file-file', {
|
}).add('sharp-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
minSamples,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
sharp(fixtures.inputPng)
|
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.png({ compressionLevel: 6 })
|
||||||
.toFile(fixtures.outputPng, function (err) {
|
.toFile(fixtures.outputPng, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -745,9 +764,11 @@ async.series({
|
|||||||
}
|
}
|
||||||
}).add('sharp-file-buffer', {
|
}).add('sharp-file-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
minSamples,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
sharp(fixtures.inputPng)
|
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
|
.png({ compressionLevel: 6 })
|
||||||
.toBuffer(function (err, buffer) {
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -759,10 +780,11 @@ async.series({
|
|||||||
}
|
}
|
||||||
}).add('sharp-progressive', {
|
}).add('sharp-progressive', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
minSamples,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
sharp(inputPngBuffer)
|
sharp(inputPngBuffer)
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
.png({ progressive: true })
|
.png({ compressionLevel: 6, progressive: true })
|
||||||
.toBuffer(function (err, buffer) {
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -774,10 +796,27 @@ async.series({
|
|||||||
}
|
}
|
||||||
}).add('sharp-adaptiveFiltering', {
|
}).add('sharp-adaptiveFiltering', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
minSamples,
|
||||||
fn: function (deferred) {
|
fn: function (deferred) {
|
||||||
sharp(inputPngBuffer)
|
sharp(inputPngBuffer)
|
||||||
.resize(width, height)
|
.resize(width, height)
|
||||||
.png({ adaptiveFiltering: true })
|
.png({ adaptiveFiltering: true, compressionLevel: 6 })
|
||||||
|
.toBuffer(function (err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add('sharp-compressionLevel=9', {
|
||||||
|
defer: true,
|
||||||
|
minSamples,
|
||||||
|
fn: function (deferred) {
|
||||||
|
sharp(inputPngBuffer)
|
||||||
|
.resize(width, height)
|
||||||
|
.png({ compressionLevel: 9 })
|
||||||
.toBuffer(function (err, buffer) {
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
BIN
test/fixtures/big-height.webp
vendored
Normal file
BIN
test/fixtures/big-height.webp
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
3
test/fixtures/circle.svg
vendored
Normal file
3
test/fixtures/circle.svg
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8">
|
||||||
|
<circle r="3.75" cx="4" cy="4" fill="deeppink" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 122 B |
BIN
test/fixtures/expected/circle.png
vendored
Normal file
BIN
test/fixtures/expected/circle.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
test/fixtures/expected/hilutite.jpg
vendored
Normal file
BIN
test/fixtures/expected/hilutite.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
BIN
test/fixtures/expected/icc-cmyk.jpg
vendored
Normal file
BIN
test/fixtures/expected/icc-cmyk.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 MiB |
BIN
test/fixtures/hilutite.icm
vendored
Normal file
BIN
test/fixtures/hilutite.icm
vendored
Normal file
Binary file not shown.
2
test/fixtures/index.js
vendored
2
test/fixtures/index.js
vendored
@@ -95,6 +95,7 @@ module.exports = {
|
|||||||
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||||
inputWebPAnimated: getPath('rotating-squares.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
inputWebPAnimated: getPath('rotating-squares.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||||
inputWebPAnimatedLoop3: getPath('animated-loop-3.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
inputWebPAnimatedLoop3: getPath('animated-loop-3.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||||
|
inputWebPAnimatedBigHeight: getPath('big-height.webp'),
|
||||||
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
||||||
inputTiffMultipage: getPath('G31D_MULTI.TIF'), // gm convert G31D.TIF -resize 50% G31D_2.TIF ; tiffcp G31D.TIF G31D_2.TIF G31D_MULTI.TIF
|
inputTiffMultipage: getPath('G31D_MULTI.TIF'), // gm convert G31D.TIF -resize 50% G31D_2.TIF ; tiffcp G31D.TIF G31D_2.TIF G31D_MULTI.TIF
|
||||||
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
|
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
|
||||||
@@ -106,6 +107,7 @@ module.exports = {
|
|||||||
inputGifAnimated: getPath('rotating-squares.gif'), // CC0 https://loading.io/spinner/blocks/-rotating-squares-preloader-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
|
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
|
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
|
||||||
|
inputSvgSmallViewBox: getPath('circle.svg'),
|
||||||
inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg
|
inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg
|
||||||
|
|
||||||
inputJPGBig: getPath('flowers.jpeg'),
|
inputJPGBig: getPath('flowers.jpeg'),
|
||||||
|
|||||||
@@ -39,6 +39,16 @@
|
|||||||
Memcheck:Cond
|
Memcheck:Cond
|
||||||
obj:*/libjpeg.so*
|
obj:*/libjpeg.so*
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
value_jpeg_obj_static
|
||||||
|
Memcheck:Value8
|
||||||
|
obj:*/libvips.so*
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cond_jpeg_obj_static
|
||||||
|
Memcheck:Cond
|
||||||
|
obj:*/libvips.so*
|
||||||
|
}
|
||||||
{
|
{
|
||||||
param_jpeg_jpeg_finish_compress
|
param_jpeg_jpeg_finish_compress
|
||||||
Memcheck:Param
|
Memcheck:Param
|
||||||
@@ -304,6 +314,16 @@
|
|||||||
...
|
...
|
||||||
fun:vips__init
|
fun:vips__init
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
leak_rsvg_static_data
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: definite
|
||||||
|
fun:malloc
|
||||||
|
...
|
||||||
|
fun:rsvg_rust_handle_new_from_stream_sync
|
||||||
|
...
|
||||||
|
fun:vips_object_build
|
||||||
|
}
|
||||||
|
|
||||||
# libuv warnings
|
# libuv warnings
|
||||||
{
|
{
|
||||||
@@ -418,6 +438,13 @@
|
|||||||
...
|
...
|
||||||
fun:_ZN4node17CreateEnvironmentEPN2v87IsolateEP9uv_loop_sNS0_5LocalINS0_7ContextEEEiPKPKciSB_
|
fun:_ZN4node17CreateEnvironmentEPN2v87IsolateEP9uv_loop_sNS0_5LocalINS0_7ContextEEEiPKPKciSB_
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
leak_nodejs_CreateEnvironment_IsolateData
|
||||||
|
Memcheck:Leak
|
||||||
|
match-leak-kinds: possible
|
||||||
|
...
|
||||||
|
fun:_ZN4node17CreateEnvironmentEPNS_11IsolateDataEN2v85LocalINS2_7ContextEEERKSt6vectorISsSaISsEESA_NS_16EnvironmentFlags5FlagsENS_8ThreadIdESt10unique_ptrINS_21InspectorParentHandleESt14default_deleteISF_EE
|
||||||
|
}
|
||||||
{
|
{
|
||||||
leak_nodejs_Environment_Start
|
leak_nodejs_Environment_Start
|
||||||
Memcheck:Leak
|
Memcheck:Leak
|
||||||
|
|||||||
@@ -98,4 +98,36 @@ describe('GIF input', () => {
|
|||||||
sharp().gif({ delay: [65536] });
|
sharp().gif({ delay: [65536] });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work with streams when only animated is set', function (done) {
|
||||||
|
if (sharp.format.magick.output.buffer) {
|
||||||
|
fs.createReadStream(fixtures.inputGifAnimated)
|
||||||
|
.pipe(sharp({ animated: true }))
|
||||||
|
.gif()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('gif', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.inputGifAnimated, data, done);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with streams when only pages is set', function (done) {
|
||||||
|
if (sharp.format.magick.output.buffer) {
|
||||||
|
fs.createReadStream(fixtures.inputGifAnimated)
|
||||||
|
.pipe(sharp({ pages: -1 }))
|
||||||
|
.gif()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('gif', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.inputGifAnimated, data, done);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Fail when input is empty Buffer', function (done) {
|
it('Fail when input is empty Buffer', function (done) {
|
||||||
if (sharp.format.magick.input.buffer) return this.skip(); // can be removed with libvips 8.10.0+
|
if (sharp.format.magick.input.buffer) return this.skip(); // can be removed with libvips 8.10.1+
|
||||||
sharp(Buffer.alloc(0)).toBuffer().then(function () {
|
sharp(Buffer.alloc(0)).toBuffer().then(function () {
|
||||||
assert(false);
|
assert(false);
|
||||||
done();
|
done();
|
||||||
@@ -647,7 +647,16 @@ describe('Input/output', function () {
|
|||||||
it('Invalid density: string', function () {
|
it('Invalid density: string', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp({ density: 'zoinks' });
|
sharp({ density: 'zoinks' });
|
||||||
}, /Expected number between 1 and 2400 for density but received zoinks of type string/);
|
}, /Expected number between 1 and 100000 for density but received zoinks of type string/);
|
||||||
|
});
|
||||||
|
it('Setting animated property updates pages property', function () {
|
||||||
|
assert.strictEqual(sharp({ animated: false }).options.input.pages, 1);
|
||||||
|
assert.strictEqual(sharp({ animated: true }).options.input.pages, -1);
|
||||||
|
});
|
||||||
|
it('Invalid animated property throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ animated: -1 });
|
||||||
|
}, /Expected boolean for animated but received -1 of type number/);
|
||||||
});
|
});
|
||||||
it('Invalid page property throws', function () {
|
it('Invalid page property throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
|
|||||||
@@ -501,6 +501,42 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Apply CMYK output ICC profile', function (done) {
|
||||||
|
const output = fixtures.path('output.icc-cmyk.jpg');
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.withMetadata({ icc: 'cmyk' })
|
||||||
|
.toFile(output, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(output).metadata(function (err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, metadata.hasProfile);
|
||||||
|
assert.strictEqual('cmyk', metadata.space);
|
||||||
|
assert.strictEqual(4, metadata.channels);
|
||||||
|
// ICC
|
||||||
|
assert.strictEqual('object', typeof metadata.icc);
|
||||||
|
assert.strictEqual(true, metadata.icc instanceof Buffer);
|
||||||
|
const profile = icc.parse(metadata.icc);
|
||||||
|
assert.strictEqual('object', typeof profile);
|
||||||
|
assert.strictEqual('CMYK', profile.colorSpace);
|
||||||
|
assert.strictEqual('Relative', profile.intent);
|
||||||
|
assert.strictEqual('Printer', profile.deviceClass);
|
||||||
|
});
|
||||||
|
fixtures.assertSimilar(output, fixtures.path('expected/icc-cmyk.jpg'), { threshold: 0 }, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Apply custom output ICC profile', function (done) {
|
||||||
|
const output = fixtures.path('output.hilutite.jpg');
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.withMetadata({ icc: fixtures.path('hilutite.icm') })
|
||||||
|
.toFile(output, function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
fixtures.assertMaxColourDistance(output, fixtures.path('expected/hilutite.jpg'), 0);
|
||||||
|
fixtures.assertMaxColourDistance(output, fixtures.inputJpg, 16.5);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Include metadata in output, enabled via empty object', () =>
|
it('Include metadata in output, enabled via empty object', () =>
|
||||||
sharp(fixtures.inputJpgWithExif)
|
sharp(fixtures.inputJpgWithExif)
|
||||||
.withMetadata({})
|
.withMetadata({})
|
||||||
@@ -675,5 +711,10 @@ describe('Image metadata', function () {
|
|||||||
sharp().withMetadata({ orientation: 9 });
|
sharp().withMetadata({ orientation: 9 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('Non string icc', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().withMetadata({ icc: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -462,6 +462,19 @@ describe('Image Stats', function () {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it('Entropy and sharpness of 1x1 input are zero', async () => {
|
||||||
|
const { entropy, sharpness } = await sharp({
|
||||||
|
create: {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
}).stats();
|
||||||
|
assert.strictEqual(entropy, 0);
|
||||||
|
assert.strictEqual(sharpness, 0);
|
||||||
|
});
|
||||||
|
|
||||||
it('Stream in, Callback out', function (done) {
|
it('Stream in, Callback out', function (done) {
|
||||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||||
const pipeline = sharp().stats(function (err, stats) {
|
const pipeline = sharp().stats(function (err, stats) {
|
||||||
|
|||||||
@@ -48,6 +48,31 @@ describe('SVG input', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Convert SVG to PNG at DPI larger than 2400', function (done) {
|
||||||
|
const size = 1024;
|
||||||
|
sharp(fixtures.inputSvgSmallViewBox).metadata(function (err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
const density = (size / Math.max(metadata.width, metadata.height)) * metadata.density;
|
||||||
|
sharp(fixtures.inputSvgSmallViewBox, { density })
|
||||||
|
.resize(size)
|
||||||
|
.toFormat('png')
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(size, info.width);
|
||||||
|
assert.strictEqual(size, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('circle.png'), data, function (err) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(data).metadata(function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(9216, info.density);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Convert SVG to PNG at 14.4DPI', function (done) {
|
it('Convert SVG to PNG at 14.4DPI', function (done) {
|
||||||
sharp(fixtures.inputSvg, { density: 14.4 })
|
sharp(fixtures.inputSvg, { density: 14.4 })
|
||||||
.toFormat('png')
|
.toFormat('png')
|
||||||
|
|||||||
@@ -161,9 +161,7 @@ describe('TIFF', function () {
|
|||||||
.then(() => sharp(fixtures.outputTiff)
|
.then(() => sharp(fixtures.outputTiff)
|
||||||
.metadata()
|
.metadata()
|
||||||
.then(({ density }) => {
|
.then(({ density }) => {
|
||||||
assert.strictEqual(true,
|
assert.strictEqual(25400, density);
|
||||||
density === 2540 || // libvips <= 8.8.2
|
|
||||||
density === 25400); // libvips >= 8.8.3
|
|
||||||
return promisify(rimraf)(fixtures.outputTiff);
|
return promisify(rimraf)(fixtures.outputTiff);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -179,9 +177,7 @@ describe('TIFF', function () {
|
|||||||
.then(data => sharp(data)
|
.then(data => sharp(data)
|
||||||
.metadata()
|
.metadata()
|
||||||
.then(({ density }) => {
|
.then(({ density }) => {
|
||||||
assert.strictEqual(true,
|
assert.strictEqual(25400, density);
|
||||||
density === 2540 || // libvips <= 8.8.2
|
|
||||||
density === 25400); // libvips >= 8.8.3
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,4 +16,12 @@ describe('toBuffer', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('correctly process animated webp with height > 16383', (done) => {
|
||||||
|
const image = sharp(fixtures.inputWebPAnimatedBigHeight, { animated: true });
|
||||||
|
image.toBuffer().then((buff) => {
|
||||||
|
assert.strictEqual(Buffer.isBuffer(buff), true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const sharp = require('../../');
|
const sharp = require('../../');
|
||||||
@@ -184,4 +185,28 @@ describe('WebP', function () {
|
|||||||
|
|
||||||
assert.deepStrictEqual(updated.delay, expectedDelay);
|
assert.deepStrictEqual(updated.delay, expectedDelay);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work with streams when only animated is set', function (done) {
|
||||||
|
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||||
|
.pipe(sharp({ animated: true }))
|
||||||
|
.webp({ lossless: true })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.inputWebPAnimated, data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with streams when only pages is set', function (done) {
|
||||||
|
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||||
|
.pipe(sharp({ pages: -1 }))
|
||||||
|
.webp({ lossless: true })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.inputWebPAnimated, data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user