mirror of
https://github.com/lovell/sharp.git
synced 2026-02-05 14:16:17 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56fa9c95a1 | ||
|
|
32a34a8841 | ||
|
|
98797445de | ||
|
|
bd377438b6 | ||
|
|
9dd6510de6 | ||
|
|
93ad9d4a4a | ||
|
|
4c01a099ea | ||
|
|
8e70579e47 | ||
|
|
ee8bfa3980 | ||
|
|
c5dfa49cae | ||
|
|
0822404129 | ||
|
|
144f39cd45 | ||
|
|
87f191fd05 | ||
|
|
37ed436202 | ||
|
|
88e490356d | ||
|
|
7c631c0787 | ||
|
|
f5d3721fe0 | ||
|
|
cc633589d9 | ||
|
|
cc1d4c1a6d | ||
|
|
30ca424942 | ||
|
|
813831acf0 | ||
|
|
a54fe9f77c | ||
|
|
8c6da5548a | ||
|
|
a2aa7d69e7 |
12
.travis.yml
12
.travis.yml
@@ -21,14 +21,12 @@ matrix:
|
|||||||
after_success:
|
after_success:
|
||||||
- npm install coveralls
|
- npm install coveralls
|
||||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||||
- name: "Linux (glibc) - Node 11 (Experimental)"
|
- name: "Linux (glibc) - Node 11"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: false
|
sudo: false
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "11"
|
node_js: "11"
|
||||||
before_install:
|
|
||||||
- unset PREBUILD_TOKEN
|
|
||||||
- name: "Linux (musl) - Node 8"
|
- name: "Linux (musl) - Node 8"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
@@ -49,13 +47,13 @@ matrix:
|
|||||||
- 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"
|
||||||
- name: "Linux (musl) - Node 11 (Experimental)"
|
- name: "Linux (musl) - Node 11"
|
||||||
os: linux
|
os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: true
|
sudo: true
|
||||||
language: minimal
|
language: minimal
|
||||||
before_install:
|
before_install:
|
||||||
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:11-alpine
|
- sudo docker run -dit --name sharp --env CI --env PREBUILD_TOKEN --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:11-alpine
|
||||||
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
- 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"
|
||||||
@@ -74,10 +72,8 @@ matrix:
|
|||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "10"
|
node_js: "10"
|
||||||
- name: "OS X - Node 11 (Experimental)"
|
- name: "OS X - Node 11"
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode9.2
|
osx_image: xcode9.2
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: "11"
|
node_js: "11"
|
||||||
before_install:
|
|
||||||
- unset PREBUILD_TOKEN
|
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -1,5 +1,7 @@
|
|||||||
# sharp
|
# sharp
|
||||||
|
|
||||||
|
<img src="docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install sharp
|
npm install sharp
|
||||||
```
|
```
|
||||||
@@ -22,7 +24,7 @@ As well as image resizing, operations such as
|
|||||||
rotation, extraction, compositing and gamma correction are available.
|
rotation, extraction, compositing and gamma correction are available.
|
||||||
|
|
||||||
Most modern 64-bit OS X, Windows and Linux systems running
|
Most modern 64-bit OS X, Windows and Linux systems running
|
||||||
Node versions 6, 8 and 10
|
Node versions 6, 8, 10 and 11
|
||||||
do not require any additional install or runtime dependencies.
|
do not require any additional install or runtime dependencies.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -63,15 +65,15 @@ readableStream
|
|||||||
.pipe(writableStream);
|
.pipe(writableStream);
|
||||||
```
|
```
|
||||||
|
|
||||||
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
Visit [sharp.pixelplumbing.com](http://sharp.pixelplumbing.com/) for complete
|
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
||||||
[installation instructions](http://sharp.pixelplumbing.com/page/install),
|
[installation instructions](https://sharp.pixelplumbing.com/page/install),
|
||||||
[API documentation](http://sharp.pixelplumbing.com/page/api),
|
[API documentation](https://sharp.pixelplumbing.com/page/api),
|
||||||
[benchmark tests](http://sharp.pixelplumbing.com/page/performance) and
|
[benchmark tests](https://sharp.pixelplumbing.com/page/performance) and
|
||||||
[changelog](http://sharp.pixelplumbing.com/page/changelog).
|
[changelog](https://sharp.pixelplumbing.com/page/changelog).
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
@@ -80,12 +82,12 @@ covers reporting bugs, requesting features and submitting code changes.
|
|||||||
|
|
||||||
### Licensing
|
### Licensing
|
||||||
|
|
||||||
Copyright 2013, 2014, 2015, 2016, 2017, 2018 Lovell Fuller and contributors.
|
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
|
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ environment:
|
|||||||
- nodejs_version: "8"
|
- nodejs_version: "8"
|
||||||
- nodejs_version: "10"
|
- nodejs_version: "10"
|
||||||
- nodejs_version: "11"
|
- nodejs_version: "11"
|
||||||
PREBUILD_TOKEN: ""
|
|
||||||
install:
|
install:
|
||||||
- ps: Install-Product node $env:nodejs_version x64
|
- ps: Install-Product node $env:nodejs_version x64
|
||||||
- npm install -g npm@5
|
- npm install -g npm@5
|
||||||
|
|||||||
@@ -16,6 +16,22 @@ sharp('rgba.png')
|
|||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
|
||||||
|
## ensureAlpha
|
||||||
|
|
||||||
|
Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharp('rgb.jpg')
|
||||||
|
.ensureAlpha()
|
||||||
|
.toFile('rgba.png', function(err, info) {
|
||||||
|
// rgba.png is a 4 channel image with a fully opaque alpha channel
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
## extractChannel
|
## extractChannel
|
||||||
|
|
||||||
Extract a single channel from a multi-channel image.
|
Extract a single channel from a multi-channel image.
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ If the overlay image contains an alpha channel then composition with premultipli
|
|||||||
sharp('input.png')
|
sharp('input.png')
|
||||||
.rotate(180)
|
.rotate(180)
|
||||||
.resize(300)
|
.resize(300)
|
||||||
.flatten()
|
.flatten( { background: '#ff6600' } )
|
||||||
.background('#ff6600')
|
|
||||||
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
|
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
|
||||||
.sharpen()
|
.sharpen()
|
||||||
.withMetadata()
|
.withMetadata()
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ A Promises/A+ promise is returned when `callback` is not provided.
|
|||||||
- `density`: Number of pixels per inch (DPI), if present
|
- `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
|
- `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
|
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
|
- `pages`: Number of pages this TIFF, GIF or PDF image contains.
|
||||||
|
- `pageHeight`: Number of pixels high each page in this PDF image will be.
|
||||||
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
- `orientation`: Number value of the EXIF Orientation header, if present
|
- `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
|||||||
@@ -154,6 +154,11 @@ Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
|
|||||||
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
- `options.progressive` **[Boolean][6]** use progressive (interlace) scan (optional, default `false`)
|
||||||
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
- `options.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
||||||
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
||||||
|
- `options.palette` **[Boolean][6]** quantise to a palette-based image with alpha transparency support, requires libimagequant (optional, default `false`)
|
||||||
|
- `options.quality` **[Number][8]** use the lowest number of colours needed to achieve given quality, requires libimagequant (optional, default `100`)
|
||||||
|
- `options.colours` **[Number][8]** maximum number of palette entries, requires libimagequant (optional, default `256`)
|
||||||
|
- `options.colors` **[Number][8]** alternative spelling of `options.colours`, requires libimagequant (optional, default `256`)
|
||||||
|
- `options.dither` **[Number][8]** level of Floyd-Steinberg error diffusion, requires libimagequant (optional, default `1.0`)
|
||||||
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@@ -4,6 +4,31 @@
|
|||||||
|
|
||||||
Requires libvips v8.7.0.
|
Requires libvips v8.7.0.
|
||||||
|
|
||||||
|
#### v0.21.2 - 13<sup>th</sup> January 2019
|
||||||
|
|
||||||
|
* Ensure all metadata is removed from PNG output unless `withMetadata` used.
|
||||||
|
|
||||||
|
* Ensure shortest edge is at least one pixel after resizing.
|
||||||
|
[#1003](https://github.com/lovell/sharp/issues/1003)
|
||||||
|
|
||||||
|
* Add `ensureAlpha` operation to add an alpha channel, if missing.
|
||||||
|
[#1153](https://github.com/lovell/sharp/issues/1153)
|
||||||
|
|
||||||
|
* Expose `pages` and `pageHeight` metadata for multi-page input images.
|
||||||
|
[#1205](https://github.com/lovell/sharp/issues/1205)
|
||||||
|
|
||||||
|
* Expose PNG output options requiring libimagequant.
|
||||||
|
[#1484](https://github.com/lovell/sharp/issues/1484)
|
||||||
|
|
||||||
|
* Expose underlying error message for invalid input.
|
||||||
|
[#1505](https://github.com/lovell/sharp/issues/1505)
|
||||||
|
|
||||||
|
* Prevent mutatation of options passed to `jpeg`.
|
||||||
|
[#1516](https://github.com/lovell/sharp/issues/1516)
|
||||||
|
|
||||||
|
* Ensure forced output format applied correctly when output chaining.
|
||||||
|
[#1528](https://github.com/lovell/sharp/issues/1528)
|
||||||
|
|
||||||
#### v0.21.1 - 7<sup>th</sup> December 2018
|
#### v0.21.1 - 7<sup>th</sup> December 2018
|
||||||
|
|
||||||
* Install: support `sharp_dist_base_url` npm config, like existing `SHARP_DIST_BASE_URL`.
|
* Install: support `sharp_dist_base_url` npm config, like existing `SHARP_DIST_BASE_URL`.
|
||||||
|
|||||||
5
docs/image/sharp-logo.svg
Normal file
5
docs/image/sharp-logo.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="86 86 550 550">
|
||||||
|
<!-- Copyright 2019 Lovell Fuller. This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) License. -->
|
||||||
|
<path fill="none" stroke="#9c0" stroke-width="80" d="M258.411 285.777l200.176-26.8M244.113 466.413L451.44 438.66M451.441 438.66V238.484M451.441 88.363v171.572l178.725-23.917M270.323 255.602V477.22M272.71 634.17V462.591L93.984 486.515"/>
|
||||||
|
<path fill="none" stroke="#090" stroke-width="80" d="M451.441 610.246V438.66l178.725-23.91M269.688 112.59v171.58L90.964 308.093"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 592 B |
@@ -1,5 +1,7 @@
|
|||||||
# sharp
|
# sharp
|
||||||
|
|
||||||
|
<img src="image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
||||||
|
|
||||||
The typical use case for this high speed Node.js module
|
The typical use case for this high speed Node.js module
|
||||||
is to convert large images in common formats to
|
is to convert large images in common formats to
|
||||||
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
||||||
@@ -127,12 +129,12 @@ Thank you!
|
|||||||
|
|
||||||
### Licensing
|
### Licensing
|
||||||
|
|
||||||
Copyright 2013, 2014, 2015, 2016, 2017, 2018 Lovell Fuller and contributors.
|
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
|
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ yarn add sharp
|
|||||||
### Building from source
|
### Building from source
|
||||||
|
|
||||||
Pre-compiled binaries for sharp are provided for use with
|
Pre-compiled binaries for sharp are provided for use with
|
||||||
Node versions 6, 8 and 10 on
|
Node versions 6, 8, 10 and 11 on
|
||||||
64-bit Windows, OS X and Linux platforms.
|
64-bit Windows, OS X and Linux platforms.
|
||||||
|
|
||||||
Sharp will be built from source at install time when:
|
Sharp will be built from source at install time when:
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ try {
|
|||||||
if (response.statusCode !== 200) {
|
if (response.statusCode !== 200) {
|
||||||
throw new Error(`Status ${response.statusCode}`);
|
throw new Error(`Status ${response.statusCode}`);
|
||||||
}
|
}
|
||||||
response.pipe(tmpFile);
|
response
|
||||||
|
.on('error', fail)
|
||||||
|
.pipe(tmpFile);
|
||||||
});
|
});
|
||||||
tmpFile
|
tmpFile
|
||||||
.on('error', fail)
|
.on('error', fail)
|
||||||
|
|||||||
@@ -29,6 +29,23 @@ function removeAlpha () {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* sharp('rgb.jpg')
|
||||||
|
* .ensureAlpha()
|
||||||
|
* .toFile('rgba.png', function(err, info) {
|
||||||
|
* // rgba.png is a 4 channel image with a fully opaque alpha channel
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @returns {Sharp}
|
||||||
|
*/
|
||||||
|
function ensureAlpha () {
|
||||||
|
this.options.ensureAlpha = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a single channel from a multi-channel image.
|
* Extract a single channel from a multi-channel image.
|
||||||
*
|
*
|
||||||
@@ -120,6 +137,7 @@ module.exports = function (Sharp) {
|
|||||||
Object.assign(Sharp.prototype, {
|
Object.assign(Sharp.prototype, {
|
||||||
// Public instance functions
|
// Public instance functions
|
||||||
removeAlpha,
|
removeAlpha,
|
||||||
|
ensureAlpha,
|
||||||
extractChannel,
|
extractChannel,
|
||||||
joinChannel,
|
joinChannel,
|
||||||
bandbool
|
bandbool
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ const is = require('./is');
|
|||||||
* sharp('input.png')
|
* sharp('input.png')
|
||||||
* .rotate(180)
|
* .rotate(180)
|
||||||
* .resize(300)
|
* .resize(300)
|
||||||
* .flatten()
|
* .flatten( { background: '#ff6600' } )
|
||||||
* .background('#ff6600')
|
|
||||||
* .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
|
* .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
|
||||||
* .sharpen()
|
* .sharpen()
|
||||||
* .withMetadata()
|
* .withMetadata()
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ const Sharp = function (input, options) {
|
|||||||
joinChannelIn: [],
|
joinChannelIn: [],
|
||||||
extractChannel: -1,
|
extractChannel: -1,
|
||||||
removeAlpha: false,
|
removeAlpha: false,
|
||||||
|
ensureAlpha: false,
|
||||||
colourspace: 'srgb',
|
colourspace: 'srgb',
|
||||||
// overlay
|
// overlay
|
||||||
overlayGravity: 0,
|
overlayGravity: 0,
|
||||||
@@ -170,6 +171,10 @@ const Sharp = function (input, options) {
|
|||||||
pngProgressive: false,
|
pngProgressive: false,
|
||||||
pngCompressionLevel: 9,
|
pngCompressionLevel: 9,
|
||||||
pngAdaptiveFiltering: false,
|
pngAdaptiveFiltering: false,
|
||||||
|
pngPalette: false,
|
||||||
|
pngQuality: 100,
|
||||||
|
pngColours: 256,
|
||||||
|
pngDither: 1,
|
||||||
webpQuality: 80,
|
webpQuality: 80,
|
||||||
webpAlphaQuality: 100,
|
webpAlphaQuality: 100,
|
||||||
webpLossless: false,
|
webpLossless: false,
|
||||||
|
|||||||
@@ -186,6 +186,8 @@ function clone () {
|
|||||||
* - `density`: Number of pixels per inch (DPI), if present
|
* - `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
|
* - `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
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
|
* - `pages`: Number of pages this TIFF, GIF or PDF image contains.
|
||||||
|
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
|
||||||
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
* - `orientation`: Number value of the EXIF Orientation header, if present
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
|||||||
@@ -175,30 +175,30 @@ function jpeg (options) {
|
|||||||
throw new Error('Invalid chromaSubsampling (4:2:0, 4:4:4) ' + options.chromaSubsampling);
|
throw new Error('Invalid chromaSubsampling (4:2:0, 4:4:4) ' + options.chromaSubsampling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options.trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
|
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
|
||||||
if (is.defined(options.trellisQuantisation)) {
|
if (is.defined(trellisQuantisation)) {
|
||||||
this._setBooleanOption('jpegTrellisQuantisation', options.trellisQuantisation);
|
this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
|
||||||
}
|
}
|
||||||
if (is.defined(options.overshootDeringing)) {
|
if (is.defined(options.overshootDeringing)) {
|
||||||
this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
|
this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
|
||||||
}
|
}
|
||||||
options.optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
|
const optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
|
||||||
if (is.defined(options.optimiseScans)) {
|
if (is.defined(optimiseScans)) {
|
||||||
this._setBooleanOption('jpegOptimiseScans', options.optimiseScans);
|
this._setBooleanOption('jpegOptimiseScans', optimiseScans);
|
||||||
if (options.optimiseScans) {
|
if (optimiseScans) {
|
||||||
this.options.jpegProgressive = true;
|
this.options.jpegProgressive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options.optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
|
const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
|
||||||
if (is.defined(options.optimiseCoding)) {
|
if (is.defined(optimiseCoding)) {
|
||||||
this._setBooleanOption('jpegOptimiseCoding', options.optimiseCoding);
|
this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
|
||||||
}
|
}
|
||||||
options.quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
|
const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
|
||||||
if (is.defined(options.quantisationTable)) {
|
if (is.defined(quantisationTable)) {
|
||||||
if (is.integer(options.quantisationTable) && is.inRange(options.quantisationTable, 0, 8)) {
|
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
|
||||||
this.options.jpegQuantisationTable = options.quantisationTable;
|
this.options.jpegQuantisationTable = quantisationTable;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid quantisation table (integer, 0-8) ' + options.quantisationTable);
|
throw new Error('Invalid quantisation table (integer, 0-8) ' + quantisationTable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,6 +221,11 @@ function jpeg (options) {
|
|||||||
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
|
* @param {Boolean} [options.progressive=false] - use progressive (interlace) scan
|
||||||
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
|
* @param {Number} [options.compressionLevel=9] - zlib compression level, 0-9
|
||||||
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
||||||
|
* @param {Boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libimagequant
|
||||||
|
* @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libimagequant
|
||||||
|
* @param {Number} [options.colours=256] - maximum number of palette entries, requires libimagequant
|
||||||
|
* @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libimagequant
|
||||||
|
* @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libimagequant
|
||||||
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
@@ -240,6 +245,33 @@ function png (options) {
|
|||||||
if (is.defined(options.adaptiveFiltering)) {
|
if (is.defined(options.adaptiveFiltering)) {
|
||||||
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
|
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
|
||||||
}
|
}
|
||||||
|
if (is.defined(options.palette)) {
|
||||||
|
this._setBooleanOption('pngPalette', options.palette);
|
||||||
|
if (this.options.pngPalette) {
|
||||||
|
if (is.defined(options.quality)) {
|
||||||
|
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
|
||||||
|
this.options.pngQuality = options.quality;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const colours = options.colours || options.colors;
|
||||||
|
if (is.defined(colours)) {
|
||||||
|
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||||
|
this.options.pngColours = colours;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.dither)) {
|
||||||
|
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
||||||
|
this.options.pngDither = options.dither;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this._updateFormatOut('png', options);
|
return this._updateFormatOut('png', options);
|
||||||
}
|
}
|
||||||
@@ -541,7 +573,9 @@ function tile (tile) {
|
|||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
*/
|
*/
|
||||||
function _updateFormatOut (formatOut, options) {
|
function _updateFormatOut (formatOut, options) {
|
||||||
this.options.formatOut = (is.object(options) && options.force === false) ? 'input' : formatOut;
|
if (!(is.object(options) && options.force === false)) {
|
||||||
|
this.options.formatOut = formatOut;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
site_name: sharp
|
site_name: sharp
|
||||||
site_url: http://sharp.pixelplumbing.com/
|
site_url: https://sharp.pixelplumbing.com/
|
||||||
repo_url: https://github.com/lovell/sharp
|
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
|
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>
|
copyright: <a href="https://pixelplumbing.com/">pixelplumbing.com</a>
|
||||||
|
|||||||
10
package.json
10
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.21.1",
|
"version": "0.21.2",
|
||||||
"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": [
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
"color": "^3.1.0",
|
"color": "^3.1.0",
|
||||||
"detect-libc": "^1.0.3",
|
"detect-libc": "^1.0.3",
|
||||||
"fs-copy-file-sync": "^1.1.1",
|
"fs-copy-file-sync": "^1.1.1",
|
||||||
"nan": "^2.11.1",
|
"nan": "^2.12.1",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^5.2.2",
|
"prebuild-install": "^5.2.2",
|
||||||
"semver": "^5.6.0",
|
"semver": "^5.6.0",
|
||||||
@@ -109,16 +109,16 @@
|
|||||||
"async": "^2.6.1",
|
"async": "^2.6.1",
|
||||||
"cc": "^1.0.2",
|
"cc": "^1.0.2",
|
||||||
"decompress-zip": "^0.3.1",
|
"decompress-zip": "^0.3.1",
|
||||||
"documentation": "^8.1.2",
|
"documentation": "^9.1.1",
|
||||||
"exif-reader": "^1.0.2",
|
"exif-reader": "^1.0.2",
|
||||||
"icc": "^1.0.0",
|
"icc": "^1.0.0",
|
||||||
"license-checker": "^24.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"mock-fs": "^4.7.0",
|
"mock-fs": "^4.7.0",
|
||||||
"nyc": "^13.1.0",
|
"nyc": "^13.1.0",
|
||||||
"prebuild": "^8.1.2",
|
"prebuild": "^8.1.2",
|
||||||
"prebuild-ci": "^2.3.0",
|
"prebuild-ci": "^2.3.0",
|
||||||
"rimraf": "^2.6.2",
|
"rimraf": "^2.6.3",
|
||||||
"semistandard": "^13.0.1"
|
"semistandard": "^13.0.1"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -245,8 +245,8 @@ namespace sharp {
|
|||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
SetDensity(image, descriptor->density);
|
SetDensity(image, descriptor->density);
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (vips::VError const &err) {
|
||||||
throw vips::VError("Input buffer has corrupt header");
|
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw vips::VError("Input buffer contains unsupported image format");
|
throw vips::VError("Input buffer contains unsupported image format");
|
||||||
@@ -287,8 +287,8 @@ namespace sharp {
|
|||||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||||
SetDensity(image, descriptor->density);
|
SetDensity(image, descriptor->density);
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (vips::VError const &err) {
|
||||||
throw vips::VError("Input file has corrupt header");
|
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw vips::VError("Input file is missing or of an unsupported image format");
|
throw vips::VError("Input file is missing or of an unsupported image format");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
|
|
||||||
// Verify platform and compiler compatibility
|
// Verify platform and compiler compatibility
|
||||||
|
|
||||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 6))
|
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 7))
|
||||||
#error libvips version 8.6.1+ is required - see sharp.pixelplumbing.com/page/install
|
#error libvips version 8.7.0+ is required - see sharp.pixelplumbing.com/page/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)))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -68,6 +68,15 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
if (image.get_typeof("interlaced") == G_TYPE_INT) {
|
if (image.get_typeof("interlaced") == G_TYPE_INT) {
|
||||||
baton->isProgressive = image.get_int("interlaced") == 1;
|
baton->isProgressive = image.get_int("interlaced") == 1;
|
||||||
}
|
}
|
||||||
|
if (image.get_typeof("palette-bit-depth") == G_TYPE_INT) {
|
||||||
|
baton->paletteBitDepth = image.get_int("palette-bit-depth");
|
||||||
|
}
|
||||||
|
if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT) {
|
||||||
|
baton->pages = image.get_int(VIPS_META_N_PAGES);
|
||||||
|
}
|
||||||
|
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
||||||
|
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
||||||
|
}
|
||||||
baton->hasProfile = sharp::HasProfile(image);
|
baton->hasProfile = sharp::HasProfile(image);
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = sharp::HasAlpha(image);
|
baton->hasAlpha = sharp::HasAlpha(image);
|
||||||
@@ -140,6 +149,15 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
New<v8::String>(baton->chromaSubsampling).ToLocalChecked());
|
New<v8::String>(baton->chromaSubsampling).ToLocalChecked());
|
||||||
}
|
}
|
||||||
Set(info, New("isProgressive").ToLocalChecked(), New<v8::Boolean>(baton->isProgressive));
|
Set(info, New("isProgressive").ToLocalChecked(), New<v8::Boolean>(baton->isProgressive));
|
||||||
|
if (baton->paletteBitDepth > 0) {
|
||||||
|
Set(info, New("paletteBitDepth").ToLocalChecked(), New<v8::Uint32>(baton->paletteBitDepth));
|
||||||
|
}
|
||||||
|
if (baton->pages > 0) {
|
||||||
|
Set(info, New("pages").ToLocalChecked(), New<v8::Uint32>(baton->pages));
|
||||||
|
}
|
||||||
|
if (baton->pageHeight > 0) {
|
||||||
|
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
|
||||||
|
}
|
||||||
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
||||||
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -33,6 +33,9 @@ struct MetadataBaton {
|
|||||||
int density;
|
int density;
|
||||||
std::string chromaSubsampling;
|
std::string chromaSubsampling;
|
||||||
bool isProgressive;
|
bool isProgressive;
|
||||||
|
int paletteBitDepth;
|
||||||
|
int pages;
|
||||||
|
int pageHeight;
|
||||||
bool hasProfile;
|
bool hasProfile;
|
||||||
bool hasAlpha;
|
bool hasAlpha;
|
||||||
int orientation;
|
int orientation;
|
||||||
@@ -53,6 +56,9 @@ struct MetadataBaton {
|
|||||||
channels(0),
|
channels(0),
|
||||||
density(0),
|
density(0),
|
||||||
isProgressive(false),
|
isProgressive(false),
|
||||||
|
paletteBitDepth(0),
|
||||||
|
pages(0),
|
||||||
|
pageHeight(0),
|
||||||
hasProfile(false),
|
hasProfile(false),
|
||||||
hasAlpha(false),
|
hasAlpha(false),
|
||||||
orientation(0),
|
orientation(0),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -38,6 +38,18 @@ namespace sharp {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ensures alpha channel, if missing.
|
||||||
|
*/
|
||||||
|
VImage EnsureAlpha(VImage image) {
|
||||||
|
if (!HasAlpha(image)) {
|
||||||
|
std::vector<double> alpha;
|
||||||
|
alpha.push_back(sharp::MaximumImageAlpha(image.interpretation()));
|
||||||
|
image = image.bandjoin_const(alpha);
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Composite overlayImage over image at given position
|
Composite overlayImage over image at given position
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -30,6 +30,11 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VImage RemoveAlpha(VImage image);
|
VImage RemoveAlpha(VImage image);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ensures alpha channel, if missing.
|
||||||
|
*/
|
||||||
|
VImage EnsureAlpha(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Alpha composite src over dst with given gravity.
|
Alpha composite src over dst with given gravity.
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -297,7 +297,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we're using a device-independent colour space
|
// Ensure we're using a device-independent colour space
|
||||||
if (sharp::HasProfile(image)) {
|
if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) {
|
||||||
// Convert to sRGB using embedded profile
|
// Convert to sRGB using embedded profile
|
||||||
try {
|
try {
|
||||||
image = image.icc_transform(
|
image = image.icc_transform(
|
||||||
@@ -385,6 +385,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
) {
|
) {
|
||||||
throw vips::VError("Unknown kernel");
|
throw vips::VError("Unknown kernel");
|
||||||
}
|
}
|
||||||
|
// Ensure shortest edge is at least 1 pixel
|
||||||
|
if (image.width() / xfactor < 0.5) {
|
||||||
|
xfactor = 2 * image.width();
|
||||||
|
baton->width = 1;
|
||||||
|
}
|
||||||
|
if (image.height() / yfactor < 0.5) {
|
||||||
|
yfactor = 2 * image.height();
|
||||||
|
baton->height = 1;
|
||||||
|
}
|
||||||
image = image.resize(1.0 / xfactor, VImage::option()
|
image = image.resize(1.0 / xfactor, VImage::option()
|
||||||
->set("vscale", 1.0 / yfactor)
|
->set("vscale", 1.0 / yfactor)
|
||||||
->set("kernel", kernel));
|
->set("kernel", kernel));
|
||||||
@@ -668,6 +677,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
image = sharp::RemoveAlpha(image);
|
image = sharp::RemoveAlpha(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure alpha channel, if missing
|
||||||
|
if (baton->ensureAlpha) {
|
||||||
|
image = sharp::EnsureAlpha(image);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert image to sRGB, if not already
|
// Convert image to sRGB, if not already
|
||||||
if (sharp::Is16Bit(image.interpretation())) {
|
if (sharp::Is16Bit(image.interpretation())) {
|
||||||
image = image.cast(VIPS_FORMAT_USHORT);
|
image = image.cast(VIPS_FORMAT_USHORT);
|
||||||
@@ -721,14 +735,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
|
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
|
||||||
// Write PNG to buffer
|
// Write PNG to buffer
|
||||||
sharp::AssertImageTypeDimensions(image, ImageType::PNG);
|
sharp::AssertImageTypeDimensions(image, ImageType::PNG);
|
||||||
// Strip profile
|
|
||||||
if (!baton->withMetadata) {
|
|
||||||
vips_image_remove(image.get_image(), VIPS_META_ICC_NAME);
|
|
||||||
}
|
|
||||||
VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
|
VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
|
||||||
|
->set("strip", !baton->withMetadata)
|
||||||
->set("interlace", baton->pngProgressive)
|
->set("interlace", baton->pngProgressive)
|
||||||
->set("compression", baton->pngCompressionLevel)
|
->set("compression", baton->pngCompressionLevel)
|
||||||
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)));
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
||||||
|
->set("palette", baton->pngPalette)
|
||||||
|
->set("Q", baton->pngQuality)
|
||||||
|
->set("colours", baton->pngColours)
|
||||||
|
->set("dither", baton->pngDither)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
area->free_fn = nullptr;
|
area->free_fn = nullptr;
|
||||||
@@ -834,14 +849,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
|
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
|
||||||
// Write PNG to file
|
// Write PNG to file
|
||||||
sharp::AssertImageTypeDimensions(image, ImageType::PNG);
|
sharp::AssertImageTypeDimensions(image, ImageType::PNG);
|
||||||
// Strip profile
|
|
||||||
if (!baton->withMetadata) {
|
|
||||||
vips_image_remove(image.get_image(), VIPS_META_ICC_NAME);
|
|
||||||
}
|
|
||||||
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
|
->set("strip", !baton->withMetadata)
|
||||||
->set("interlace", baton->pngProgressive)
|
->set("interlace", baton->pngProgressive)
|
||||||
->set("compression", baton->pngCompressionLevel)
|
->set("compression", baton->pngCompressionLevel)
|
||||||
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE));
|
->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)
|
||||||
|
->set("palette", baton->pngPalette)
|
||||||
|
->set("Q", baton->pngQuality)
|
||||||
|
->set("colours", baton->pngColours)
|
||||||
|
->set("dither", baton->pngDither));
|
||||||
baton->formatOut = "png";
|
baton->formatOut = "png";
|
||||||
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
||||||
(willMatchInput && inputImageType == ImageType::WEBP)) {
|
(willMatchInput && inputImageType == ImageType::WEBP)) {
|
||||||
@@ -1227,6 +1243,7 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
|
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
|
||||||
|
|
||||||
baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
|
baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
|
||||||
|
baton->ensureAlpha = AttrTo<bool>(options, "ensureAlpha");
|
||||||
if (HasAttr(options, "boolean")) {
|
if (HasAttr(options, "boolean")) {
|
||||||
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
|
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
|
||||||
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
|
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
|
||||||
@@ -1275,6 +1292,10 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
|
baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
|
||||||
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
||||||
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
|
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
|
||||||
|
baton->pngPalette = AttrTo<bool>(options, "pngPalette");
|
||||||
|
baton->pngQuality = AttrTo<uint32_t>(options, "pngQuality");
|
||||||
|
baton->pngColours = AttrTo<uint32_t>(options, "pngColours");
|
||||||
|
baton->pngDither = AttrTo<double>(options, "pngDither");
|
||||||
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
|
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
|
||||||
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
||||||
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@@ -115,6 +115,10 @@ struct PipelineBaton {
|
|||||||
bool pngProgressive;
|
bool pngProgressive;
|
||||||
int pngCompressionLevel;
|
int pngCompressionLevel;
|
||||||
bool pngAdaptiveFiltering;
|
bool pngAdaptiveFiltering;
|
||||||
|
bool pngPalette;
|
||||||
|
int pngQuality;
|
||||||
|
int pngColours;
|
||||||
|
double pngDither;
|
||||||
int webpQuality;
|
int webpQuality;
|
||||||
int webpAlphaQuality;
|
int webpAlphaQuality;
|
||||||
bool webpNearLossless;
|
bool webpNearLossless;
|
||||||
@@ -142,6 +146,7 @@ struct PipelineBaton {
|
|||||||
VipsOperationBoolean bandBoolOp;
|
VipsOperationBoolean bandBoolOp;
|
||||||
int extractChannel;
|
int extractChannel;
|
||||||
bool removeAlpha;
|
bool removeAlpha;
|
||||||
|
bool ensureAlpha;
|
||||||
VipsInterpretation colourspace;
|
VipsInterpretation colourspace;
|
||||||
int tileSize;
|
int tileSize;
|
||||||
int tileOverlap;
|
int tileOverlap;
|
||||||
@@ -215,6 +220,10 @@ struct PipelineBaton {
|
|||||||
pngProgressive(false),
|
pngProgressive(false),
|
||||||
pngCompressionLevel(9),
|
pngCompressionLevel(9),
|
||||||
pngAdaptiveFiltering(false),
|
pngAdaptiveFiltering(false),
|
||||||
|
pngPalette(false),
|
||||||
|
pngQuality(100),
|
||||||
|
pngColours(256),
|
||||||
|
pngDither(1.0),
|
||||||
webpQuality(80),
|
webpQuality(80),
|
||||||
tiffQuality(80),
|
tiffQuality(80),
|
||||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||||
@@ -237,6 +246,7 @@ struct PipelineBaton {
|
|||||||
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||||
extractChannel(-1),
|
extractChannel(-1),
|
||||||
removeAlpha(false),
|
removeAlpha(false),
|
||||||
|
ensureAlpha(false),
|
||||||
colourspace(VIPS_INTERPRETATION_LAST),
|
colourspace(VIPS_INTERPRETATION_LAST),
|
||||||
tileSize(256),
|
tileSize(256),
|
||||||
tileOverlap(0),
|
tileOverlap(0),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
|
// Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019 Lovell Fuller and contributors.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|||||||
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@@ -70,6 +70,7 @@ module.exports = {
|
|||||||
inputJpgCenteredImage: getPath('centered_image.jpeg'),
|
inputJpgCenteredImage: getPath('centered_image.jpeg'),
|
||||||
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
|
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
|
||||||
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
|
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
|
||||||
|
inputJpgLossless: getPath('testimgl.jpg'), // Lossless JPEG from ftp://ftp.fu-berlin.de/unix/X11/graphics/ImageMagick/delegates/ljpeg-6b.tar.gz
|
||||||
|
|
||||||
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||||
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
||||||
|
|||||||
BIN
test/fixtures/testimgl.jpg
vendored
Normal file
BIN
test/fixtures/testimgl.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -115,6 +115,7 @@ describe('Alpha transparency', function () {
|
|||||||
fixtures.inputWebP
|
fixtures.inputWebP
|
||||||
].map(function (input) {
|
].map(function (input) {
|
||||||
return sharp(input)
|
return sharp(input)
|
||||||
|
.resize(10)
|
||||||
.removeAlpha()
|
.removeAlpha()
|
||||||
.toBuffer({ resolveWithObject: true })
|
.toBuffer({ resolveWithObject: true })
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
@@ -122,4 +123,24 @@ describe('Alpha transparency', function () {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Ensures alpha from fixtures without transparency, ignores those with', function () {
|
||||||
|
return Promise.all([
|
||||||
|
fixtures.inputPngWithTransparency,
|
||||||
|
fixtures.inputPngWithTransparency16bit,
|
||||||
|
fixtures.inputWebPWithTransparency,
|
||||||
|
fixtures.inputJpg,
|
||||||
|
fixtures.inputPng,
|
||||||
|
fixtures.inputWebP
|
||||||
|
].map(function (input) {
|
||||||
|
return sharp(input)
|
||||||
|
.resize(10)
|
||||||
|
.ensureAlpha()
|
||||||
|
.png()
|
||||||
|
.toBuffer({ resolveWithObject: true })
|
||||||
|
.then(function (result) {
|
||||||
|
assert.strictEqual(4, result.info.channels);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
1047
test/unit/io.js
1047
test/unit/io.js
File diff suppressed because it is too large
Load Diff
262
test/unit/jpeg.js
Normal file
262
test/unit/jpeg.js
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('JPEG', function () {
|
||||||
|
it('JPEG quality', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ quality: 70 })
|
||||||
|
.toBuffer(function (err, buffer70) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.toBuffer(function (err, buffer80) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ quality: 90 })
|
||||||
|
.toBuffer(function (err, buffer90) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert(buffer70.length < buffer80.length);
|
||||||
|
assert(buffer80.length < buffer90.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Invalid JPEG quality', function () {
|
||||||
|
[-1, 88.2, 'test'].forEach(function (quality) {
|
||||||
|
it(quality.toString(), function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().jpeg({ quality: quality });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Invalid JPEG quantisation table', function () {
|
||||||
|
[-1, 88.2, 'test'].forEach(function (table) {
|
||||||
|
it(table.toString(), function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().jpeg({ quantisationTable: table });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Progressive JPEG image', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ progressive: false })
|
||||||
|
.toBuffer(function (err, nonProgressiveData, nonProgressiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, nonProgressiveData.length > 0);
|
||||||
|
assert.strictEqual(nonProgressiveData.length, nonProgressiveInfo.size);
|
||||||
|
assert.strictEqual('jpeg', nonProgressiveInfo.format);
|
||||||
|
assert.strictEqual(320, nonProgressiveInfo.width);
|
||||||
|
assert.strictEqual(240, nonProgressiveInfo.height);
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ progressive: true })
|
||||||
|
.toBuffer(function (err, progressiveData, progressiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, progressiveData.length > 0);
|
||||||
|
assert.strictEqual(progressiveData.length, progressiveInfo.size);
|
||||||
|
assert.strictEqual(false, progressiveData.length === nonProgressiveData.length);
|
||||||
|
assert.strictEqual('jpeg', progressiveInfo.format);
|
||||||
|
assert.strictEqual(320, progressiveInfo.width);
|
||||||
|
assert.strictEqual(240, progressiveInfo.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Without chroma subsampling generates larger file', function (done) {
|
||||||
|
// First generate with chroma subsampling (default)
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ chromaSubsampling: '4:2:0' })
|
||||||
|
.toBuffer(function (err, withChromaSubsamplingData, withChromaSubsamplingInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withChromaSubsamplingData.length > 0);
|
||||||
|
assert.strictEqual(withChromaSubsamplingData.length, withChromaSubsamplingInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withChromaSubsamplingInfo.format);
|
||||||
|
assert.strictEqual(320, withChromaSubsamplingInfo.width);
|
||||||
|
assert.strictEqual(240, withChromaSubsamplingInfo.height);
|
||||||
|
// Then generate without
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ chromaSubsampling: '4:4:4' })
|
||||||
|
.toBuffer(function (err, withoutChromaSubsamplingData, withoutChromaSubsamplingInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutChromaSubsamplingData.length > 0);
|
||||||
|
assert.strictEqual(withoutChromaSubsamplingData.length, withoutChromaSubsamplingInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutChromaSubsamplingInfo.format);
|
||||||
|
assert.strictEqual(320, withoutChromaSubsamplingInfo.width);
|
||||||
|
assert.strictEqual(240, withoutChromaSubsamplingInfo.height);
|
||||||
|
assert.strictEqual(true, withChromaSubsamplingData.length < withoutChromaSubsamplingData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid JPEG chromaSubsampling value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().jpeg({ chromaSubsampling: '4:2:2' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Trellis quantisation', function (done) {
|
||||||
|
// First generate without
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ trellisQuantisation: false })
|
||||||
|
.toBuffer(function (err, withoutData, withoutInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutData.length > 0);
|
||||||
|
assert.strictEqual(withoutData.length, withoutInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutInfo.format);
|
||||||
|
assert.strictEqual(320, withoutInfo.width);
|
||||||
|
assert.strictEqual(240, withoutInfo.height);
|
||||||
|
// Then generate with
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ trellisQuantization: true })
|
||||||
|
.toBuffer(function (err, withData, withInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withData.length > 0);
|
||||||
|
assert.strictEqual(withData.length, withInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withInfo.format);
|
||||||
|
assert.strictEqual(320, withInfo.width);
|
||||||
|
assert.strictEqual(240, withInfo.height);
|
||||||
|
// Verify image is same (as mozjpeg may not be present) size or less
|
||||||
|
assert.strictEqual(true, withData.length <= withoutData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Overshoot deringing', function (done) {
|
||||||
|
// First generate without
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ overshootDeringing: false })
|
||||||
|
.toBuffer(function (err, withoutData, withoutInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutData.length > 0);
|
||||||
|
assert.strictEqual(withoutData.length, withoutInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutInfo.format);
|
||||||
|
assert.strictEqual(320, withoutInfo.width);
|
||||||
|
assert.strictEqual(240, withoutInfo.height);
|
||||||
|
// Then generate with
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ overshootDeringing: true })
|
||||||
|
.toBuffer(function (err, withData, withInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withData.length > 0);
|
||||||
|
assert.strictEqual(withData.length, withInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withInfo.format);
|
||||||
|
assert.strictEqual(320, withInfo.width);
|
||||||
|
assert.strictEqual(240, withInfo.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Optimise scans generates different output length', function (done) {
|
||||||
|
// First generate without
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimiseScans: false })
|
||||||
|
.toBuffer(function (err, withoutData, withoutInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutData.length > 0);
|
||||||
|
assert.strictEqual(withoutData.length, withoutInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutInfo.format);
|
||||||
|
assert.strictEqual(320, withoutInfo.width);
|
||||||
|
assert.strictEqual(240, withoutInfo.height);
|
||||||
|
// Then generate with
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimizeScans: true })
|
||||||
|
.toBuffer(function (err, withData, withInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withData.length > 0);
|
||||||
|
assert.strictEqual(withData.length, withInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withInfo.format);
|
||||||
|
assert.strictEqual(320, withInfo.width);
|
||||||
|
assert.strictEqual(240, withInfo.height);
|
||||||
|
// Verify image is of a different size (progressive output even without mozjpeg)
|
||||||
|
assert.notStrictEqual(withData.length, withoutData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Optimise coding generates smaller output length', function (done) {
|
||||||
|
// First generate with optimize coding enabled (default)
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, withOptimiseCoding, withInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withOptimiseCoding.length > 0);
|
||||||
|
assert.strictEqual(withOptimiseCoding.length, withInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withInfo.format);
|
||||||
|
assert.strictEqual(320, withInfo.width);
|
||||||
|
assert.strictEqual(240, withInfo.height);
|
||||||
|
// Then generate with coding disabled
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimizeCoding: false })
|
||||||
|
.toBuffer(function (err, withoutOptimiseCoding, withoutInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutOptimiseCoding.length > 0);
|
||||||
|
assert.strictEqual(withoutOptimiseCoding.length, withoutInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutInfo.format);
|
||||||
|
assert.strictEqual(320, withoutInfo.width);
|
||||||
|
assert.strictEqual(240, withoutInfo.height);
|
||||||
|
// Verify optimised image is of a smaller size
|
||||||
|
assert.strictEqual(true, withOptimiseCoding.length < withoutOptimiseCoding.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Specifying quantisation table provides different JPEG', function (done) {
|
||||||
|
// First generate with default quantisation table
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimiseCoding: false })
|
||||||
|
.toBuffer(function (err, withDefaultQuantisationTable, withInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withDefaultQuantisationTable.length > 0);
|
||||||
|
assert.strictEqual(withDefaultQuantisationTable.length, withInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withInfo.format);
|
||||||
|
assert.strictEqual(320, withInfo.width);
|
||||||
|
assert.strictEqual(240, withInfo.height);
|
||||||
|
// Then generate with different quantisation table
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg({ optimiseCoding: false, quantisationTable: 3 })
|
||||||
|
.toBuffer(function (err, withQuantTable3, withoutInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withQuantTable3.length > 0);
|
||||||
|
assert.strictEqual(withQuantTable3.length, withoutInfo.size);
|
||||||
|
assert.strictEqual('jpeg', withoutInfo.format);
|
||||||
|
assert.strictEqual(320, withoutInfo.width);
|
||||||
|
assert.strictEqual(240, withoutInfo.height);
|
||||||
|
|
||||||
|
// Verify image is same (as mozjpeg may not be present) size or less
|
||||||
|
assert.strictEqual(true, withQuantTable3.length <= withDefaultQuantisationTable.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -103,6 +103,29 @@ describe('Image metadata', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Multipage TIFF', function (done) {
|
||||||
|
sharp(fixtures.inputTiffMultipage).metadata(function (err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', metadata.format);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.size);
|
||||||
|
assert.strictEqual(2464, metadata.width);
|
||||||
|
assert.strictEqual(3248, metadata.height);
|
||||||
|
assert.strictEqual('b-w', metadata.space);
|
||||||
|
assert.strictEqual(1, metadata.channels);
|
||||||
|
assert.strictEqual('uchar', metadata.depth);
|
||||||
|
assert.strictEqual(300, metadata.density);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.chromaSubsampling);
|
||||||
|
assert.strictEqual(false, metadata.isProgressive);
|
||||||
|
assert.strictEqual(2, metadata.pages);
|
||||||
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
|
assert.strictEqual(1, metadata.orientation);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.exif);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.icc);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('PNG', function (done) {
|
it('PNG', function (done) {
|
||||||
sharp(fixtures.inputPng).metadata(function (err, metadata) {
|
sharp(fixtures.inputPng).metadata(function (err, metadata) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -434,6 +457,7 @@ describe('Image metadata', function () {
|
|||||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
.metadata(function (err) {
|
.metadata(function (err) {
|
||||||
assert.strictEqual(true, !!err);
|
assert.strictEqual(true, !!err);
|
||||||
|
assert.strictEqual(true, /Input file has corrupt header: VipsJpeg: Premature end of JPEG file/.test(err.message));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -442,6 +466,16 @@ describe('Image metadata', function () {
|
|||||||
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
|
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
|
||||||
.metadata(function (err) {
|
.metadata(function (err) {
|
||||||
assert.strictEqual(true, !!err);
|
assert.strictEqual(true, !!err);
|
||||||
|
assert.strictEqual(true, /Input buffer has corrupt header: VipsJpeg: Premature end of JPEG file/.test(err.message));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Unsupported lossless JPEG passes underlying error message', function (done) {
|
||||||
|
sharp(fixtures.inputJpgLossless)
|
||||||
|
.metadata(function (err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
assert.strictEqual(true, /Input file has corrupt header: VipsJpeg: Unsupported JPEG process: SOF type 0xc3/.test(err.message));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
171
test/unit/png.js
Normal file
171
test/unit/png.js
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('PNG', function () {
|
||||||
|
it('compression level is valid', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().png({ compressionLevel: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('compression level is invalid', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ compressionLevel: -1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('default compressionLevel generates smaller file than compressionLevel=6', function (done) {
|
||||||
|
// First generate with default compressionLevel
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png()
|
||||||
|
.toBuffer(function (err, defaultData, defaultInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, defaultData.length > 0);
|
||||||
|
assert.strictEqual('png', defaultInfo.format);
|
||||||
|
// Then generate with compressionLevel=6
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ compressionLevel: 6 })
|
||||||
|
.toBuffer(function (err, largerData, largerInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, largerData.length > 0);
|
||||||
|
assert.strictEqual('png', largerInfo.format);
|
||||||
|
assert.strictEqual(true, defaultData.length < largerData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('without adaptiveFiltering generates smaller file', function (done) {
|
||||||
|
// First generate with adaptive filtering
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ adaptiveFiltering: true })
|
||||||
|
.toBuffer(function (err, adaptiveData, adaptiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, adaptiveData.length > 0);
|
||||||
|
assert.strictEqual(adaptiveData.length, adaptiveInfo.size);
|
||||||
|
assert.strictEqual('png', adaptiveInfo.format);
|
||||||
|
assert.strictEqual(320, adaptiveInfo.width);
|
||||||
|
assert.strictEqual(240, adaptiveInfo.height);
|
||||||
|
// Then generate without
|
||||||
|
sharp(fixtures.inputPng)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ adaptiveFiltering: false })
|
||||||
|
.toBuffer(function (err, withoutAdaptiveData, withoutAdaptiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, withoutAdaptiveData.length > 0);
|
||||||
|
assert.strictEqual(withoutAdaptiveData.length, withoutAdaptiveInfo.size);
|
||||||
|
assert.strictEqual('png', withoutAdaptiveInfo.format);
|
||||||
|
assert.strictEqual(320, withoutAdaptiveInfo.width);
|
||||||
|
assert.strictEqual(240, withoutAdaptiveInfo.height);
|
||||||
|
assert.strictEqual(true, withoutAdaptiveData.length < adaptiveData.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG adaptiveFiltering value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ adaptiveFiltering: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Progressive PNG image', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png({ progressive: false })
|
||||||
|
.toBuffer(function (err, nonProgressiveData, nonProgressiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, nonProgressiveData.length > 0);
|
||||||
|
assert.strictEqual(nonProgressiveData.length, nonProgressiveInfo.size);
|
||||||
|
assert.strictEqual('png', nonProgressiveInfo.format);
|
||||||
|
assert.strictEqual(320, nonProgressiveInfo.width);
|
||||||
|
assert.strictEqual(240, nonProgressiveInfo.height);
|
||||||
|
sharp(nonProgressiveData)
|
||||||
|
.png({ progressive: true })
|
||||||
|
.toBuffer(function (err, progressiveData, progressiveInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, progressiveData.length > 0);
|
||||||
|
assert.strictEqual(progressiveData.length, progressiveInfo.size);
|
||||||
|
assert.strictEqual(true, progressiveData.length > nonProgressiveData.length);
|
||||||
|
assert.strictEqual('png', progressiveInfo.format);
|
||||||
|
assert.strictEqual(320, progressiveInfo.width);
|
||||||
|
assert.strictEqual(240, progressiveInfo.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant palette value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().png({ palette: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant palette value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant quality value produces image of same size or smaller', function () {
|
||||||
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
|
return Promise.all([
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, quality: 80 }).toBuffer(),
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, quality: 100 }).toBuffer()
|
||||||
|
]).then(function (data) {
|
||||||
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant quality value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, quality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant colours value produces image of same size or smaller', function () {
|
||||||
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
|
return Promise.all([
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, colours: 100 }).toBuffer(),
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, colours: 200 }).toBuffer()
|
||||||
|
]).then(function (data) {
|
||||||
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant colours value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, colours: -1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant colors value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, colors: 0.1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid PNG libimagequant dither value produces image of same size or smaller', function () {
|
||||||
|
const inputPngBuffer = fs.readFileSync(fixtures.inputPng);
|
||||||
|
return Promise.all([
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, dither: 0.1 }).toBuffer(),
|
||||||
|
sharp(inputPngBuffer).resize(10).png({ palette: true, dither: 0.9 }).toBuffer()
|
||||||
|
]).then(function (data) {
|
||||||
|
assert.strictEqual(true, data[0].length <= data[1].length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid PNG libimagequant dither value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().png({ palette: true, dither: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
145
test/unit/raw.js
Normal file
145
test/unit/raw.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('Raw pixel data', function () {
|
||||||
|
describe('Raw pixel input', function () {
|
||||||
|
it('Missing options', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ raw: {} });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Incomplete options', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ raw: { width: 1, height: 1 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid channels', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ raw: { width: 1, height: 1, channels: 5 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid height', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ raw: { width: 1, height: 0, channels: 4 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid width', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp({ raw: { width: 'zoinks', height: 1, channels: 4 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('RGB', function (done) {
|
||||||
|
// Convert to raw pixel data
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(256)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(256, info.width);
|
||||||
|
assert.strictEqual(209, info.height);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
// Convert back to JPEG
|
||||||
|
sharp(data, {
|
||||||
|
raw: {
|
||||||
|
width: info.width,
|
||||||
|
height: info.height,
|
||||||
|
channels: info.channels
|
||||||
|
} })
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(256, info.width);
|
||||||
|
assert.strictEqual(209, info.height);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
|
fixtures.assertSimilar(fixtures.inputJpg, data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('RGBA', function (done) {
|
||||||
|
// Convert to raw pixel data
|
||||||
|
sharp(fixtures.inputPngOverlayLayer1)
|
||||||
|
.resize(256)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(256, info.width);
|
||||||
|
assert.strictEqual(192, info.height);
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
// Convert back to PNG
|
||||||
|
sharp(data, {
|
||||||
|
raw: {
|
||||||
|
width: info.width,
|
||||||
|
height: info.height,
|
||||||
|
channels: info.channels
|
||||||
|
} })
|
||||||
|
.png()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(256, info.width);
|
||||||
|
assert.strictEqual(192, info.height);
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
fixtures.assertSimilar(fixtures.inputPngOverlayLayer1, data, { threshold: 7 }, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Ouput raw, uncompressed image data', function () {
|
||||||
|
it('1 channel greyscale image', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.greyscale()
|
||||||
|
.resize(32, 24)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(32 * 24 * 1, info.size);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('raw', info.format);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(24, info.height);
|
||||||
|
assert.strictEqual(1, info.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('3 channel colour image without transparency', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(32, 24)
|
||||||
|
.toFormat('raw')
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(32 * 24 * 3, info.size);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('raw', info.format);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(24, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('4 channel colour image with transparency', function (done) {
|
||||||
|
sharp(fixtures.inputPngWithTransparency)
|
||||||
|
.resize(32, 24)
|
||||||
|
.toFormat(sharp.format.raw)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(32 * 24 * 4, info.size);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('raw', info.format);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(24, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -116,7 +116,7 @@ describe('Resize fit=contain', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('TIFF in LAB colourspace onto RGBA background', function (done) {
|
it('TIFF in LAB colourspace onto RGBA background', function (done) {
|
||||||
sharp(fixtures.inputTiffCielab)
|
sharp(fixtures.inputTiffCielab)
|
||||||
.resize(64, 128, {
|
.resize(64, 128, {
|
||||||
fit: 'contain',
|
fit: 'contain',
|
||||||
|
|||||||
@@ -525,6 +525,40 @@ describe('Resize dimensions', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Ensure shortest edge (height) is at least 1 pixel', function () {
|
||||||
|
return sharp({
|
||||||
|
create: {
|
||||||
|
width: 10,
|
||||||
|
height: 2,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.resize(2)
|
||||||
|
.toBuffer({ resolveWithObject: true })
|
||||||
|
.then(function (output) {
|
||||||
|
assert.strictEqual(2, output.info.width);
|
||||||
|
assert.strictEqual(1, output.info.height);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ensure shortest edge (width) is at least 1 pixel', function () {
|
||||||
|
return sharp({
|
||||||
|
create: {
|
||||||
|
width: 2,
|
||||||
|
height: 10,
|
||||||
|
channels: 3,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.resize(null, 2)
|
||||||
|
.toBuffer({ resolveWithObject: true })
|
||||||
|
.then(function (output) {
|
||||||
|
assert.strictEqual(1, output.info.width);
|
||||||
|
assert.strictEqual(2, output.info.height);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('unknown kernel throws', function () {
|
it('unknown kernel throws', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().resize(null, null, { kernel: 'unknown' });
|
sharp().resize(null, null, { kernel: 'unknown' });
|
||||||
|
|||||||
77
test/unit/svg.js
Normal file
77
test/unit/svg.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('SVG input', function () {
|
||||||
|
it('Convert SVG to PNG at default 72DPI', function (done) {
|
||||||
|
sharp(fixtures.inputSvg)
|
||||||
|
.resize(1024)
|
||||||
|
.extract({ left: 290, top: 760, width: 40, height: 40 })
|
||||||
|
.toFormat('png')
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(40, info.width);
|
||||||
|
assert.strictEqual(40, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('svg72.png'), data, function (err) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(data).metadata(function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(72, info.density);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Convert SVG to PNG at 1200DPI', function (done) {
|
||||||
|
sharp(fixtures.inputSvg, { density: 1200 })
|
||||||
|
.resize(1024)
|
||||||
|
.extract({ left: 290, top: 760, width: 40, height: 40 })
|
||||||
|
.toFormat('png')
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(40, info.width);
|
||||||
|
assert.strictEqual(40, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('svg1200.png'), data, function (err) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(data).metadata(function (err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(1200, info.density);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Convert SVG to PNG at 14.4DPI', function (done) {
|
||||||
|
sharp(fixtures.inputSvg, { density: 14.4 })
|
||||||
|
.toFormat('png')
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(20, info.width);
|
||||||
|
assert.strictEqual(20, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('svg14.4.png'), data, function (err) {
|
||||||
|
if (err) throw err;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Convert SVG with embedded images to PNG, respecting dimensions, autoconvert to PNG', function (done) {
|
||||||
|
sharp(fixtures.inputSvgWithEmbeddedImages)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(480, info.width);
|
||||||
|
assert.strictEqual(360, info.height);
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('svg-embedded.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
424
test/unit/tiff.js
Normal file
424
test/unit/tiff.js
Normal file
@@ -0,0 +1,424 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const assert = require('assert');
|
||||||
|
const rimraf = require('rimraf');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('TIFF', function () {
|
||||||
|
it('Load TIFF from Buffer', function (done) {
|
||||||
|
const inputTiffBuffer = fs.readFileSync(fixtures.inputTiff);
|
||||||
|
sharp(inputTiffBuffer)
|
||||||
|
.resize(320, 240)
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Load multi-page TIFF from file', function (done) {
|
||||||
|
sharp(fixtures.inputTiffMultipage) // defaults to page 0
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, defaultData, defaultInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, defaultData.length > 0);
|
||||||
|
assert.strictEqual(defaultData.length, defaultInfo.size);
|
||||||
|
assert.strictEqual('jpeg', defaultInfo.format);
|
||||||
|
|
||||||
|
sharp(fixtures.inputTiffMultipage, { page: 1 }) // 50%-scale copy of page 0
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, scaledData, scaledInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, scaledData.length > 0);
|
||||||
|
assert.strictEqual(scaledData.length, scaledInfo.size);
|
||||||
|
assert.strictEqual('jpeg', scaledInfo.format);
|
||||||
|
assert.strictEqual(defaultInfo.width, scaledInfo.width * 2);
|
||||||
|
assert.strictEqual(defaultInfo.height, scaledInfo.height * 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Load multi-page TIFF from Buffer', function (done) {
|
||||||
|
const inputTiffBuffer = fs.readFileSync(fixtures.inputTiffMultipage);
|
||||||
|
sharp(inputTiffBuffer) // defaults to page 0
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, defaultData, defaultInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, defaultData.length > 0);
|
||||||
|
assert.strictEqual(defaultData.length, defaultInfo.size);
|
||||||
|
assert.strictEqual('jpeg', defaultInfo.format);
|
||||||
|
|
||||||
|
sharp(inputTiffBuffer, { page: 1 }) // 50%-scale copy of page 0
|
||||||
|
.jpeg()
|
||||||
|
.toBuffer(function (err, scaledData, scaledInfo) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, scaledData.length > 0);
|
||||||
|
assert.strictEqual(scaledData.length, scaledInfo.size);
|
||||||
|
assert.strictEqual('jpeg', scaledInfo.format);
|
||||||
|
assert.strictEqual(defaultInfo.width, scaledInfo.width * 2);
|
||||||
|
assert.strictEqual(defaultInfo.height, scaledInfo.height * 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Save TIFF to Buffer', function (done) {
|
||||||
|
sharp(fixtures.inputTiff)
|
||||||
|
.resize(320, 240)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF quality throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ quality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Missing TIFF quality does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Not squashing TIFF to a bit depth of 1 should not change the file size', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiff8BitDepth).size;
|
||||||
|
sharp(fixtures.inputTiff8BitDepth)
|
||||||
|
.toColourspace('b-w') // can only squash 1 band uchar images
|
||||||
|
.tiff({
|
||||||
|
squash: false,
|
||||||
|
compression: 'none',
|
||||||
|
predictor: 'none'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size === startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Squashing TIFF to a bit depth of 1 should significantly reduce file size', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiff8BitDepth).size;
|
||||||
|
sharp(fixtures.inputTiff8BitDepth)
|
||||||
|
.toColourspace('b-w') // can only squash 1 band uchar images
|
||||||
|
.tiff({
|
||||||
|
squash: true,
|
||||||
|
compression: 'none',
|
||||||
|
predictor: 'none'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < (startSize / 2));
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF squash value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ squash: 'true' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF setting xres and yres on file', function (done) {
|
||||||
|
const res = 1000.0; // inputTiff has a dpi of 300 (res*2.54)
|
||||||
|
sharp(fixtures.inputTiff)
|
||||||
|
.tiff({
|
||||||
|
xres: (res),
|
||||||
|
yres: (res)
|
||||||
|
})
|
||||||
|
.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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF setting xres and yres on buffer', function (done) {
|
||||||
|
const res = 1000.0; // inputTiff has a dpi of 300 (res*2.54)
|
||||||
|
sharp(fixtures.inputTiff)
|
||||||
|
.tiff({
|
||||||
|
xres: (res),
|
||||||
|
yres: (res)
|
||||||
|
})
|
||||||
|
.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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF invalid xres value should throw an error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ xres: '1000.0' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF invalid yres value should throw an error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ yres: '1000.0' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF lzw compression with horizontal predictor shrinks test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'lzw',
|
||||||
|
predictor: 'horizontal'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF ccittfax4 compression shrinks b-w test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiff).size;
|
||||||
|
sharp(fixtures.inputTiff)
|
||||||
|
.toColourspace('b-w')
|
||||||
|
.tiff({
|
||||||
|
squash: true,
|
||||||
|
compression: 'ccittfax4'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF deflate compression with horizontal predictor shrinks test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'deflate',
|
||||||
|
predictor: 'horizontal'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF deflate compression with float predictor shrinks test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'deflate',
|
||||||
|
predictor: 'float'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF deflate compression without predictor shrinks test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'deflate',
|
||||||
|
predictor: 'none'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF jpeg compression shrinks test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'jpeg'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size < startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF none compression does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ compression: 'none' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF lzw compression does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ compression: 'lzw' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF deflate compression does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ compression: 'deflate' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF invalid compression option throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ compression: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF invalid compression option throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ compression: 'a' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF invalid predictor option throws', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ predictor: 'a' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF horizontal predictor does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ predictor: 'horizontal' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF float predictor does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ predictor: 'float' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF none predictor does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ predictor: 'none' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF tiled pyramid image without compression enlarges test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'none',
|
||||||
|
pyramid: true,
|
||||||
|
tile: true,
|
||||||
|
tileHeight: 256,
|
||||||
|
tileWidth: 256
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size > startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF pyramid true value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ pyramid: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF pyramid value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ pyramid: 'true' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tile value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tile: 'true' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF tile true value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ tile: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid TIFF tileHeight value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ tileHeight: 512 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid TIFF tileWidth value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ tileWidth: 512 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileHeight value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileHeight: '256' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileWidth value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileWidth: '256' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileHeight value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileHeight: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileWidth value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileWidth: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF file input with invalid page fails gracefully', function (done) {
|
||||||
|
sharp(fixtures.inputTiffMultipage, { page: 2 })
|
||||||
|
.toBuffer(function (err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF buffer input with invalid page fails gracefully', function (done) {
|
||||||
|
sharp(fs.readFileSync(fixtures.inputTiffMultipage), { page: 2 })
|
||||||
|
.toBuffer(function (err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
78
test/unit/webp.js
Normal file
78
test/unit/webp.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('WebP', function () {
|
||||||
|
it('WebP output', function (done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.toFormat(sharp.format.webp)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid WebP quality throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().webp({ quality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid WebP alpha quality throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().webp({ alphaQuality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for webp alpha quality', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({ alphaQuality: 80 })
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-alpha-80.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for webp lossless', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.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.expected('webp-lossless.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for webp near-lossless', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({ nearLossless: true, quality: 50 })
|
||||||
|
.toBuffer(function (err50, data50, info50) {
|
||||||
|
if (err50) throw err50;
|
||||||
|
assert.strictEqual(true, data50.length > 0);
|
||||||
|
assert.strictEqual('webp', info50.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use near-lossless when both lossless and nearLossless are specified', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({ nearLossless: true, quality: 50, lossless: true })
|
||||||
|
.toBuffer(function (err50, data50, info50) {
|
||||||
|
if (err50) throw err50;
|
||||||
|
assert.strictEqual(true, data50.length > 0);
|
||||||
|
assert.strictEqual('webp', info50.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user