mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 21:56:18 +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:
|
||||
- npm install coveralls
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||
- name: "Linux (glibc) - Node 11 (Experimental)"
|
||||
- name: "Linux (glibc) - Node 11"
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js: "11"
|
||||
before_install:
|
||||
- unset PREBUILD_TOKEN
|
||||
- name: "Linux (musl) - Node 8"
|
||||
os: linux
|
||||
dist: trusty
|
||||
@@ -49,13 +47,13 @@ matrix:
|
||||
- sudo docker exec sharp apk add build-base git python2 --update-cache
|
||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||
script: sudo docker exec sharp sh -c "npm test"
|
||||
- name: "Linux (musl) - Node 11 (Experimental)"
|
||||
- name: "Linux (musl) - Node 11"
|
||||
os: linux
|
||||
dist: trusty
|
||||
sudo: true
|
||||
language: minimal
|
||||
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
|
||||
install: sudo docker exec sharp sh -c "npm install --unsafe-perm"
|
||||
script: sudo docker exec sharp sh -c "npm test"
|
||||
@@ -74,10 +72,8 @@ matrix:
|
||||
osx_image: xcode9.2
|
||||
language: node_js
|
||||
node_js: "10"
|
||||
- name: "OS X - Node 11 (Experimental)"
|
||||
- name: "OS X - Node 11"
|
||||
os: osx
|
||||
osx_image: xcode9.2
|
||||
language: node_js
|
||||
node_js: "11"
|
||||
before_install:
|
||||
- unset PREBUILD_TOKEN
|
||||
|
||||
20
README.md
20
README.md
@@ -1,5 +1,7 @@
|
||||
# sharp
|
||||
|
||||
<img src="docs/image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
||||
|
||||
```sh
|
||||
npm install sharp
|
||||
```
|
||||
@@ -22,7 +24,7 @@ As well as image resizing, operations such as
|
||||
rotation, extraction, compositing and gamma correction are available.
|
||||
|
||||
Most modern 64-bit OS X, Windows and Linux systems running
|
||||
Node versions 6, 8 and 10
|
||||
Node versions 6, 8, 10 and 11
|
||||
do not require any additional install or runtime dependencies.
|
||||
|
||||
## Examples
|
||||
@@ -63,15 +65,15 @@ readableStream
|
||||
.pipe(writableStream);
|
||||
```
|
||||
|
||||
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
||||
[](https://coveralls.io/r/lovell/sharp?branch=master)
|
||||
|
||||
### Documentation
|
||||
|
||||
Visit [sharp.pixelplumbing.com](http://sharp.pixelplumbing.com/) for complete
|
||||
[installation instructions](http://sharp.pixelplumbing.com/page/install),
|
||||
[API documentation](http://sharp.pixelplumbing.com/page/api),
|
||||
[benchmark tests](http://sharp.pixelplumbing.com/page/performance) and
|
||||
[changelog](http://sharp.pixelplumbing.com/page/changelog).
|
||||
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
||||
[installation instructions](https://sharp.pixelplumbing.com/page/install),
|
||||
[API documentation](https://sharp.pixelplumbing.com/page/api),
|
||||
[benchmark tests](https://sharp.pixelplumbing.com/page/performance) and
|
||||
[changelog](https://sharp.pixelplumbing.com/page/changelog).
|
||||
|
||||
### Contributing
|
||||
|
||||
@@ -80,12 +82,12 @@ covers reporting bugs, requesting features and submitting code changes.
|
||||
|
||||
### 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");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -8,7 +8,6 @@ environment:
|
||||
- nodejs_version: "8"
|
||||
- nodejs_version: "10"
|
||||
- nodejs_version: "11"
|
||||
PREBUILD_TOKEN: ""
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version x64
|
||||
- npm install -g npm@5
|
||||
|
||||
@@ -16,6 +16,22 @@ sharp('rgba.png')
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## ensureAlpha
|
||||
|
||||
Ensure alpha channel, if missing. The added alpha channel will be fully opaque. This is a no-op if the image already has an alpha channel.
|
||||
|
||||
### Examples
|
||||
|
||||
```javascript
|
||||
sharp('rgb.jpg')
|
||||
.ensureAlpha()
|
||||
.toFile('rgba.png', function(err, info) {
|
||||
// rgba.png is a 4 channel image with a fully opaque alpha channel
|
||||
});
|
||||
```
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## extractChannel
|
||||
|
||||
Extract a single channel from a multi-channel image.
|
||||
|
||||
@@ -35,8 +35,7 @@ If the overlay image contains an alpha channel then composition with premultipli
|
||||
sharp('input.png')
|
||||
.rotate(180)
|
||||
.resize(300)
|
||||
.flatten()
|
||||
.background('#ff6600')
|
||||
.flatten( { background: '#ff6600' } )
|
||||
.overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
|
||||
.sharpen()
|
||||
.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
|
||||
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||
- `pages`: Number of pages 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
|
||||
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||
- `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.compressionLevel` **[Number][8]** zlib compression level, 0-9 (optional, default `9`)
|
||||
- `options.adaptiveFiltering` **[Boolean][6]** use adaptive row filtering (optional, default `false`)
|
||||
- `options.palette` **[Boolean][6]** quantise to a palette-based image with alpha transparency support, requires libimagequant (optional, default `false`)
|
||||
- `options.quality` **[Number][8]** use the lowest number of colours needed to achieve given quality, requires libimagequant (optional, default `100`)
|
||||
- `options.colours` **[Number][8]** maximum number of palette entries, requires libimagequant (optional, default `256`)
|
||||
- `options.colors` **[Number][8]** alternative spelling of `options.colours`, requires libimagequant (optional, default `256`)
|
||||
- `options.dither` **[Number][8]** level of Floyd-Steinberg error diffusion, requires libimagequant (optional, default `1.0`)
|
||||
- `options.force` **[Boolean][6]** force PNG output, otherwise attempt to use input format (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -4,6 +4,31 @@
|
||||
|
||||
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
|
||||
|
||||
* 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
|
||||
|
||||
<img src="image/sharp-logo.svg" width="160" height="160" alt="sharp logo" align="right">
|
||||
|
||||
The typical use case for this high speed Node.js module
|
||||
is to convert large images in common formats to
|
||||
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
||||
@@ -127,12 +129,12 @@ Thank you!
|
||||
|
||||
### 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");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
@@ -15,7 +15,7 @@ yarn add sharp
|
||||
### Building from source
|
||||
|
||||
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.
|
||||
|
||||
Sharp will be built from source at install time when:
|
||||
|
||||
@@ -79,7 +79,9 @@ try {
|
||||
if (response.statusCode !== 200) {
|
||||
throw new Error(`Status ${response.statusCode}`);
|
||||
}
|
||||
response.pipe(tmpFile);
|
||||
response
|
||||
.on('error', fail)
|
||||
.pipe(tmpFile);
|
||||
});
|
||||
tmpFile
|
||||
.on('error', fail)
|
||||
|
||||
@@ -29,6 +29,23 @@ function removeAlpha () {
|
||||
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.
|
||||
*
|
||||
@@ -120,6 +137,7 @@ module.exports = function (Sharp) {
|
||||
Object.assign(Sharp.prototype, {
|
||||
// Public instance functions
|
||||
removeAlpha,
|
||||
ensureAlpha,
|
||||
extractChannel,
|
||||
joinChannel,
|
||||
bandbool
|
||||
|
||||
@@ -14,8 +14,7 @@ const is = require('./is');
|
||||
* sharp('input.png')
|
||||
* .rotate(180)
|
||||
* .resize(300)
|
||||
* .flatten()
|
||||
* .background('#ff6600')
|
||||
* .flatten( { background: '#ff6600' } )
|
||||
* .overlayWith('overlay.png', { gravity: sharp.gravity.southeast } )
|
||||
* .sharpen()
|
||||
* .withMetadata()
|
||||
|
||||
@@ -144,6 +144,7 @@ const Sharp = function (input, options) {
|
||||
joinChannelIn: [],
|
||||
extractChannel: -1,
|
||||
removeAlpha: false,
|
||||
ensureAlpha: false,
|
||||
colourspace: 'srgb',
|
||||
// overlay
|
||||
overlayGravity: 0,
|
||||
@@ -170,6 +171,10 @@ const Sharp = function (input, options) {
|
||||
pngProgressive: false,
|
||||
pngCompressionLevel: 9,
|
||||
pngAdaptiveFiltering: false,
|
||||
pngPalette: false,
|
||||
pngQuality: 100,
|
||||
pngColours: 256,
|
||||
pngDither: 1,
|
||||
webpQuality: 80,
|
||||
webpAlphaQuality: 100,
|
||||
webpLossless: false,
|
||||
|
||||
@@ -186,6 +186,8 @@ function clone () {
|
||||
* - `density`: Number of pixels per inch (DPI), if present
|
||||
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||
* - `pages`: Number of pages 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
|
||||
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||
* - `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);
|
||||
}
|
||||
}
|
||||
options.trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
|
||||
if (is.defined(options.trellisQuantisation)) {
|
||||
this._setBooleanOption('jpegTrellisQuantisation', options.trellisQuantisation);
|
||||
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
|
||||
if (is.defined(trellisQuantisation)) {
|
||||
this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
|
||||
}
|
||||
if (is.defined(options.overshootDeringing)) {
|
||||
this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
|
||||
}
|
||||
options.optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
|
||||
if (is.defined(options.optimiseScans)) {
|
||||
this._setBooleanOption('jpegOptimiseScans', options.optimiseScans);
|
||||
if (options.optimiseScans) {
|
||||
const optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
|
||||
if (is.defined(optimiseScans)) {
|
||||
this._setBooleanOption('jpegOptimiseScans', optimiseScans);
|
||||
if (optimiseScans) {
|
||||
this.options.jpegProgressive = true;
|
||||
}
|
||||
}
|
||||
options.optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
|
||||
if (is.defined(options.optimiseCoding)) {
|
||||
this._setBooleanOption('jpegOptimiseCoding', options.optimiseCoding);
|
||||
const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
|
||||
if (is.defined(optimiseCoding)) {
|
||||
this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
|
||||
}
|
||||
options.quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
|
||||
if (is.defined(options.quantisationTable)) {
|
||||
if (is.integer(options.quantisationTable) && is.inRange(options.quantisationTable, 0, 8)) {
|
||||
this.options.jpegQuantisationTable = options.quantisationTable;
|
||||
const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
|
||||
if (is.defined(quantisationTable)) {
|
||||
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
|
||||
this.options.jpegQuantisationTable = quantisationTable;
|
||||
} else {
|
||||
throw new Error('Invalid quantisation table (integer, 0-8) ' + 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 {Number} [options.compressionLevel=9] - zlib compression level, 0-9
|
||||
* @param {Boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
|
||||
* @param {Boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support, requires libimagequant
|
||||
* @param {Number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, requires libimagequant
|
||||
* @param {Number} [options.colours=256] - maximum number of palette entries, requires libimagequant
|
||||
* @param {Number} [options.colors=256] - alternative spelling of `options.colours`, requires libimagequant
|
||||
* @param {Number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, requires libimagequant
|
||||
* @param {Boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
@@ -240,6 +245,33 @@ function png (options) {
|
||||
if (is.defined(options.adaptiveFiltering)) {
|
||||
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
|
||||
}
|
||||
if (is.defined(options.palette)) {
|
||||
this._setBooleanOption('pngPalette', options.palette);
|
||||
if (this.options.pngPalette) {
|
||||
if (is.defined(options.quality)) {
|
||||
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
|
||||
this.options.pngQuality = options.quality;
|
||||
} else {
|
||||
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
|
||||
}
|
||||
}
|
||||
const colours = options.colours || options.colors;
|
||||
if (is.defined(colours)) {
|
||||
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
|
||||
this.options.pngColours = colours;
|
||||
} else {
|
||||
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.dither)) {
|
||||
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
|
||||
this.options.pngDither = options.dither;
|
||||
} else {
|
||||
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._updateFormatOut('png', options);
|
||||
}
|
||||
@@ -541,7 +573,9 @@ function tile (tile) {
|
||||
* @returns {Sharp}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
site_name: sharp
|
||||
site_url: http://sharp.pixelplumbing.com/
|
||||
site_url: https://sharp.pixelplumbing.com/
|
||||
repo_url: https://github.com/lovell/sharp
|
||||
site_description: High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images
|
||||
copyright: <a href="https://pixelplumbing.com/">pixelplumbing.com</a>
|
||||
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
||||
"version": "0.21.1",
|
||||
"version": "0.21.2",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -97,7 +97,7 @@
|
||||
"color": "^3.1.0",
|
||||
"detect-libc": "^1.0.3",
|
||||
"fs-copy-file-sync": "^1.1.1",
|
||||
"nan": "^2.11.1",
|
||||
"nan": "^2.12.1",
|
||||
"npmlog": "^4.1.2",
|
||||
"prebuild-install": "^5.2.2",
|
||||
"semver": "^5.6.0",
|
||||
@@ -109,16 +109,16 @@
|
||||
"async": "^2.6.1",
|
||||
"cc": "^1.0.2",
|
||||
"decompress-zip": "^0.3.1",
|
||||
"documentation": "^8.1.2",
|
||||
"documentation": "^9.1.1",
|
||||
"exif-reader": "^1.0.2",
|
||||
"icc": "^1.0.0",
|
||||
"license-checker": "^24.0.1",
|
||||
"license-checker": "^25.0.1",
|
||||
"mocha": "^5.2.0",
|
||||
"mock-fs": "^4.7.0",
|
||||
"nyc": "^13.1.0",
|
||||
"prebuild": "^8.1.2",
|
||||
"prebuild-ci": "^2.3.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"rimraf": "^2.6.3",
|
||||
"semistandard": "^13.0.1"
|
||||
},
|
||||
"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");
|
||||
// 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) {
|
||||
SetDensity(image, descriptor->density);
|
||||
}
|
||||
} catch (...) {
|
||||
throw vips::VError("Input buffer has corrupt header");
|
||||
} catch (vips::VError const &err) {
|
||||
throw vips::VError(std::string("Input buffer has corrupt header: ") + err.what());
|
||||
}
|
||||
} else {
|
||||
throw vips::VError("Input buffer contains unsupported image format");
|
||||
@@ -287,8 +287,8 @@ namespace sharp {
|
||||
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
|
||||
SetDensity(image, descriptor->density);
|
||||
}
|
||||
} catch (...) {
|
||||
throw vips::VError("Input file has corrupt header");
|
||||
} catch (vips::VError const &err) {
|
||||
throw vips::VError(std::string("Input file has corrupt header: ") + err.what());
|
||||
}
|
||||
} else {
|
||||
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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
// Verify platform and compiler compatibility
|
||||
|
||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 6))
|
||||
#error libvips version 8.6.1+ is required - see sharp.pixelplumbing.com/page/install
|
||||
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 7))
|
||||
#error libvips version 8.7.0+ is required - see sharp.pixelplumbing.com/page/install
|
||||
#endif
|
||||
|
||||
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
|
||||
|
||||
@@ -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");
|
||||
// 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) {
|
||||
baton->isProgressive = image.get_int("interlaced") == 1;
|
||||
}
|
||||
if (image.get_typeof("palette-bit-depth") == G_TYPE_INT) {
|
||||
baton->paletteBitDepth = image.get_int("palette-bit-depth");
|
||||
}
|
||||
if (image.get_typeof(VIPS_META_N_PAGES) == G_TYPE_INT) {
|
||||
baton->pages = image.get_int(VIPS_META_N_PAGES);
|
||||
}
|
||||
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
||||
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
||||
}
|
||||
baton->hasProfile = sharp::HasProfile(image);
|
||||
// Derived attributes
|
||||
baton->hasAlpha = sharp::HasAlpha(image);
|
||||
@@ -140,6 +149,15 @@ class MetadataWorker : public Nan::AsyncWorker {
|
||||
New<v8::String>(baton->chromaSubsampling).ToLocalChecked());
|
||||
}
|
||||
Set(info, New("isProgressive").ToLocalChecked(), New<v8::Boolean>(baton->isProgressive));
|
||||
if (baton->paletteBitDepth > 0) {
|
||||
Set(info, New("paletteBitDepth").ToLocalChecked(), New<v8::Uint32>(baton->paletteBitDepth));
|
||||
}
|
||||
if (baton->pages > 0) {
|
||||
Set(info, New("pages").ToLocalChecked(), New<v8::Uint32>(baton->pages));
|
||||
}
|
||||
if (baton->pageHeight > 0) {
|
||||
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
|
||||
}
|
||||
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
||||
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
||||
if (baton->orientation > 0) {
|
||||
|
||||
@@ -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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,9 @@ struct MetadataBaton {
|
||||
int density;
|
||||
std::string chromaSubsampling;
|
||||
bool isProgressive;
|
||||
int paletteBitDepth;
|
||||
int pages;
|
||||
int pageHeight;
|
||||
bool hasProfile;
|
||||
bool hasAlpha;
|
||||
int orientation;
|
||||
@@ -53,6 +56,9 @@ struct MetadataBaton {
|
||||
channels(0),
|
||||
density(0),
|
||||
isProgressive(false),
|
||||
paletteBitDepth(0),
|
||||
pages(0),
|
||||
pageHeight(0),
|
||||
hasProfile(false),
|
||||
hasAlpha(false),
|
||||
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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -38,6 +38,18 @@ namespace sharp {
|
||||
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
|
||||
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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -30,6 +30,11 @@ namespace sharp {
|
||||
*/
|
||||
VImage RemoveAlpha(VImage image);
|
||||
|
||||
/*
|
||||
Ensures alpha channel, if missing.
|
||||
*/
|
||||
VImage EnsureAlpha(VImage image);
|
||||
|
||||
/*
|
||||
Alpha composite src over dst with given gravity.
|
||||
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");
|
||||
// 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
|
||||
if (sharp::HasProfile(image)) {
|
||||
if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) {
|
||||
// Convert to sRGB using embedded profile
|
||||
try {
|
||||
image = image.icc_transform(
|
||||
@@ -385,6 +385,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
) {
|
||||
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()
|
||||
->set("vscale", 1.0 / yfactor)
|
||||
->set("kernel", kernel));
|
||||
@@ -668,6 +677,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
image = sharp::RemoveAlpha(image);
|
||||
}
|
||||
|
||||
// Ensure alpha channel, if missing
|
||||
if (baton->ensureAlpha) {
|
||||
image = sharp::EnsureAlpha(image);
|
||||
}
|
||||
|
||||
// Convert image to sRGB, if not already
|
||||
if (sharp::Is16Bit(image.interpretation())) {
|
||||
image = image.cast(VIPS_FORMAT_USHORT);
|
||||
@@ -721,14 +735,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
|
||||
// Write PNG to buffer
|
||||
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()
|
||||
->set("strip", !baton->withMetadata)
|
||||
->set("interlace", baton->pngProgressive)
|
||||
->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->bufferOutLength = area->length;
|
||||
area->free_fn = nullptr;
|
||||
@@ -834,14 +849,15 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
|
||||
// Write PNG to file
|
||||
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()
|
||||
->set("strip", !baton->withMetadata)
|
||||
->set("interlace", baton->pngProgressive)
|
||||
->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";
|
||||
} else if (baton->formatOut == "webp" || (mightMatchInput && isWebp) ||
|
||||
(willMatchInput && inputImageType == ImageType::WEBP)) {
|
||||
@@ -1227,6 +1243,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
|
||||
|
||||
baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
|
||||
baton->ensureAlpha = AttrTo<bool>(options, "ensureAlpha");
|
||||
if (HasAttr(options, "boolean")) {
|
||||
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
|
||||
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
|
||||
@@ -1275,6 +1292,10 @@ NAN_METHOD(pipeline) {
|
||||
baton->pngProgressive = AttrTo<bool>(options, "pngProgressive");
|
||||
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
||||
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->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
||||
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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -115,6 +115,10 @@ struct PipelineBaton {
|
||||
bool pngProgressive;
|
||||
int pngCompressionLevel;
|
||||
bool pngAdaptiveFiltering;
|
||||
bool pngPalette;
|
||||
int pngQuality;
|
||||
int pngColours;
|
||||
double pngDither;
|
||||
int webpQuality;
|
||||
int webpAlphaQuality;
|
||||
bool webpNearLossless;
|
||||
@@ -142,6 +146,7 @@ struct PipelineBaton {
|
||||
VipsOperationBoolean bandBoolOp;
|
||||
int extractChannel;
|
||||
bool removeAlpha;
|
||||
bool ensureAlpha;
|
||||
VipsInterpretation colourspace;
|
||||
int tileSize;
|
||||
int tileOverlap;
|
||||
@@ -215,6 +220,10 @@ struct PipelineBaton {
|
||||
pngProgressive(false),
|
||||
pngCompressionLevel(9),
|
||||
pngAdaptiveFiltering(false),
|
||||
pngPalette(false),
|
||||
pngQuality(100),
|
||||
pngColours(256),
|
||||
pngDither(1.0),
|
||||
webpQuality(80),
|
||||
tiffQuality(80),
|
||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||
@@ -237,6 +246,7 @@ struct PipelineBaton {
|
||||
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||
extractChannel(-1),
|
||||
removeAlpha(false),
|
||||
ensureAlpha(false),
|
||||
colourspace(VIPS_INTERPRETATION_LAST),
|
||||
tileSize(256),
|
||||
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");
|
||||
// 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");
|
||||
// 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");
|
||||
// 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");
|
||||
// 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");
|
||||
// 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'),
|
||||
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
|
||||
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
|
||||
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
|
||||
].map(function (input) {
|
||||
return sharp(input)
|
||||
.resize(10)
|
||||
.removeAlpha()
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.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) {
|
||||
sharp(fixtures.inputPng).metadata(function (err, metadata) {
|
||||
if (err) throw err;
|
||||
@@ -434,6 +457,7 @@ describe('Image metadata', function () {
|
||||
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||
.metadata(function (err) {
|
||||
assert.strictEqual(true, !!err);
|
||||
assert.strictEqual(true, /Input file has corrupt header: VipsJpeg: Premature end of JPEG file/.test(err.message));
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -442,6 +466,16 @@ describe('Image metadata', function () {
|
||||
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
|
||||
.metadata(function (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();
|
||||
});
|
||||
});
|
||||
|
||||
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)
|
||||
.resize(64, 128, {
|
||||
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 () {
|
||||
assert.throws(function () {
|
||||
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