Compare commits

..

17 Commits

Author SHA1 Message Date
Lovell Fuller
fcf853712c Release v0.17.3 2017-04-01 10:20:44 +01:00
Lovell Fuller
088d36b47b Add support for TIFF float predictor 2017-04-01 10:08:47 +01:00
Lovell Fuller
27fb864ac4 Update dev deps, deconstify all the functions, API doc refresh 2017-03-31 21:42:23 +01:00
Lovell Fuller
4001c4a48a Add changelog and credit for #738 2017-03-31 21:17:19 +01:00
Sagiv Frankel
f64c18ef15 Docs: Add download info to Heroku section (#748) 2017-03-30 12:55:33 +01:00
Kristo Jorgenson
f8e72f443d Expose TIFF compression and predictor options (#738) 2017-03-29 12:12:04 +01:00
Lovell Fuller
5e015cc3ca Docs: use NODE_MODULES_CACHE=false for Heroku+yarn #722 2017-03-27 20:31:50 +01:00
Andreas Lind
9707f8c5d2 Add support for passing the crop strategy as a string (#735) 2017-03-16 14:27:09 +00:00
Lovell Fuller
6b1d698448 Add credit and changelog for #732 2017-03-16 07:37:05 +00:00
Alice Monday
72f69dda30 Add support for the "nearest" kernel for image reductions (#732) 2017-03-14 10:29:23 +00:00
Lovell Fuller
8b5d8a0577 Switch from seq to random access for normalise and 'smart' crop 2017-03-11 19:56:55 +00:00
Lovell Fuller
1aa053ce6f Create blank image (width, height, channels, background) #470 2017-03-11 11:46:01 +00:00
Lovell Fuller
701b1c4216 Document overlayWith image density parameter #729 2017-03-10 22:59:46 +00:00
Lovell Fuller
f1c4cef781 Version bump of dev dependencies 2017-03-04 22:28:01 +00:00
Lovell Fuller
6fe5b307b1 Allow toBuffer to resolve Promise with info+data #143 2017-03-04 22:15:31 +00:00
Lovell Fuller
679ce08998 Small doc updates 2017-03-04 18:37:23 +00:00
Lovell Fuller
eeb923eb5b Update docs domain name 2017-03-04 18:36:18 +00:00
32 changed files with 662 additions and 198 deletions

View File

@@ -41,7 +41,6 @@ Any change that modifies the existing public API should be added to the relevant
| Release | WIP branch | | Release | WIP branch |
| ------: | :--------- | | ------: | :--------- |
| v0.17.0 | quill |
| v0.18.0 | ridge | | v0.18.0 | ridge |
| v0.19.0 | suit | | v0.19.0 | suit |
@@ -72,12 +71,7 @@ These can be converted to Markdown by running:
npm run docs npm run docs
``` ```
The `types.d.ts` TypeScript declaration can be generated by running: Please include documentation updates in any Pull Request that modifies the public API.
```sh
npm run types
```
Please include documentation and TypeScript declaration updates in any Pull Request that modifies the public API.
## Run the tests ## Run the tests

View File

@@ -20,10 +20,16 @@ If both `top` and `left` options are provided, they take precedence over `gravit
- `options.left` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the left edge. - `options.left` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the left edge.
- `options.tile` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`) - `options.tile` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** set to true to repeat the overlay image across the entire image with the given `gravity`. (optional, default `false`)
- `options.cutout` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`) - `options.cutout` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. (optional, default `false`)
- `options.density` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** integral number representing the DPI for vector overlay image. (optional, default `72`)
- `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes overlay when using raw pixel data. - `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes overlay when using raw pixel data.
- `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** - `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** - `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** - `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a blank overlay to be created.
- `options.create.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
- `options.create.background` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))?** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
**Examples** **Examples**

View File

@@ -17,10 +17,15 @@
JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined. JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined.
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** if present, is an Object with optional attributes. - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** if present, is an Object with optional attributes.
- `options.density` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** integral number representing the DPI for vector images. (optional, default `72`) - `options.density` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** integral number representing the DPI for vector images. (optional, default `72`)
- `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes raw pixel image data. See `raw()` for pixel ordering. - `options.raw` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes raw pixel input image data. See `raw()` for pixel ordering.
- `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** - `options.raw.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** - `options.raw.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** - `options.raw.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 1-4
- `options.create` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** describes a new image to be created.
- `options.create.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
- `options.create.channels` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 3-4
- `options.create.background` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))?** parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
**Examples** **Examples**
@@ -46,6 +51,21 @@ var transformer = sharp()
readableStream.pipe(transformer).pipe(writableStream); readableStream.pipe(transformer).pipe(writableStream);
``` ```
```javascript
// Create a blank 300x200 PNG image of semi-transluent red pixels
sharp(null, {
create: {
width: 300,
height: 200,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 }
}
})
.png()
.toBuffer()
.then( ... );
```
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters - Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
Returns **[Sharp](#sharp)** Returns **[Sharp](#sharp)**

View File

@@ -40,15 +40,17 @@ Write output to a Buffer.
JPEG, PNG, WebP, and RAW output are supported. JPEG, PNG, WebP, and RAW output are supported.
By default, the format will match the input image, except GIF and SVG input which become PNG output. By default, the format will match the input image, except GIF and SVG input which become PNG output.
`callback`, if present, gets three arguments `(err, buffer, info)` where: `callback`, if present, gets three arguments `(err, data, info)` where:
- `err` is an error message, if any. - `err` is an error, if any.
- `buffer` is the output image data. - `data` is the output image data.
- `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`. - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
A Promises/A+ promise is returned when `callback` is not provided. A Promise is returned when `callback` is not provided.
**Parameters** **Parameters**
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.resolveWithObject` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** - `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?**
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** when no callback is provided Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** when no callback is provided
@@ -134,6 +136,8 @@ Use these TIFF options for output image.
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
- `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`) - `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force TIFF output, otherwise attempt to use input format (optional, default `true`) - `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force TIFF output, otherwise attempt to use input format (optional, default `true`)
- `options.compression` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** compression options: lzw, deflate, jpeg (optional, default `'jpeg'`)
- `options.predictor` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** compression predictor options: none, horizontal, float (optional, default `'none'`)
- Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options - Throws **[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid options

View File

@@ -17,6 +17,7 @@ By default, the resized image is centre cropped to the exact size specified.
Possible reduction kernels are: Possible reduction kernels are:
- `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
- `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline). - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
- `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`. - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
- `lanczos3`: Use a Lanczos kernel with `a=3` (the default). - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).

View File

@@ -4,6 +4,24 @@
Requires libvips v8.4.2. Requires libvips v8.4.2.
#### v0.17.3 - 1<sup>st</sup> April 2017
* Allow toBuffer to optionally resolve a Promise with both info and data.
[#143](https://github.com/lovell/sharp/issues/143)
[@salzhrani](https://github.com/salzhrani)
* Create blank image of given width, height, channels and background.
[#470](https://github.com/lovell/sharp/issues/470)
[@pjarts](https://github.com/pjarts)
* Add support for the "nearest" kernel for image reductions.
[#732](https://github.com/lovell/sharp/pull/732)
[@alice0meta](https://github.com/alice0meta)
* Add support for TIFF compression and predictor options.
[#738](https://github.com/lovell/sharp/pull/738)
[@kristojorg](https://github.com/kristojorg)
#### v0.17.2 - 11<sup>th</sup> February 2017 #### v0.17.2 - 11<sup>th</sup> February 2017
* Ensure Readable side of Stream can start flowing after Writable side has finished. * Ensure Readable side of Stream can start flowing after Writable side has finished.

View File

@@ -97,6 +97,8 @@ the help and code contributions of the following people:
* [Matthias Thoemmes](https://github.com/cmtt) * [Matthias Thoemmes](https://github.com/cmtt)
* [Patrick Paskaris](https://github.com/ppaskaris) * [Patrick Paskaris](https://github.com/ppaskaris)
* [Jérémy Lal](https://github.com/kapouer) * [Jérémy Lal](https://github.com/kapouer)
* [Alice Monday](https://github.com/alice0meta)
* [Kristo Jorgenson](https://github.com/kristojorg)
Thank you! Thank you!

View File

@@ -12,7 +12,7 @@ yarn add sharp
* Node v4+ * Node v4+
* C++11 compatible compiler such as gcc 4.8+, clang 3.0+ or MSVC 2013+ * C++11 compatible compiler such as gcc 4.8+, clang 3.0+ or MSVC 2013+
* [node-gyp](https://github.com/TooTallNate/node-gyp#installation) and its dependencies * [node-gyp](https://github.com/TooTallNate/node-gyp#installation) and its dependencies (includes Python)
### Linux ### Linux
@@ -27,7 +27,7 @@ Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ C
* Debian 7, 8 * Debian 7, 8
* Ubuntu 12.04, 14.04, 16.04 * Ubuntu 12.04, 14.04, 16.04
* Centos 7 * Centos 7
* Fedora 23, 24 * Fedora
* openSUSE 13.2 * openSUSE 13.2
* Archlinux * Archlinux
* Raspbian Jessie * Raspbian Jessie
@@ -83,9 +83,11 @@ cd /usr/ports/graphics/vips/ && make install clean
### Heroku ### Heroku
[Alessandro Tagliapietra](https://github.com/alex88) maintains an libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
[Heroku buildpack for libvips](https://github.com/alex88/heroku-buildpack-vips) This involves an automated HTTPS download of approximately 6.5MB.
and its dependencies.
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager.
### Docker ### Docker
@@ -135,7 +137,6 @@ You can now download your deployment ZIP using `scp` and upload it to Lambda. Be
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive) * [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp) * [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)
### CLI tools ### CLI tools
* [sharp-cli](https://www.npmjs.com/package/sharp-cli) * [sharp-cli](https://www.npmjs.com/package/sharp-cli)

View File

@@ -27,7 +27,7 @@ const bool = {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid channel * @throws {Error} Invalid channel
*/ */
const extractChannel = function extractChannel (channel) { function extractChannel (channel) {
if (channel === 'red') { if (channel === 'red') {
channel = 0; channel = 0;
} else if (channel === 'green') { } else if (channel === 'green') {
@@ -41,7 +41,7 @@ const extractChannel = function extractChannel (channel) {
throw new Error('Cannot extract invalid channel ' + channel); throw new Error('Cannot extract invalid channel ' + channel);
} }
return this; return this;
}; }
/** /**
* Join one or more channels to the image. * Join one or more channels to the image.
@@ -59,7 +59,7 @@ const extractChannel = function extractChannel (channel) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const joinChannel = function joinChannel (images, options) { function joinChannel (images, options) {
if (Array.isArray(images)) { if (Array.isArray(images)) {
images.forEach(function (image) { images.forEach(function (image) {
this.options.joinChannelIn.push(this._createInputDescriptor(image, options)); this.options.joinChannelIn.push(this._createInputDescriptor(image, options));
@@ -68,7 +68,7 @@ const joinChannel = function joinChannel (images, options) {
this.options.joinChannelIn.push(this._createInputDescriptor(images, options)); this.options.joinChannelIn.push(this._createInputDescriptor(images, options));
} }
return this; return this;
}; }
/** /**
* Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image. * Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
@@ -86,14 +86,14 @@ const joinChannel = function joinChannel (images, options) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const bandbool = function bandbool (boolOp) { function bandbool (boolOp) {
if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) { if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) {
this.options.bandBoolOp = boolOp; this.options.bandBoolOp = boolOp;
} else { } else {
throw new Error('Invalid bandbool operation ' + boolOp); throw new Error('Invalid bandbool operation ' + boolOp);
} }
return this; return this;
}; }
/** /**
* Decorate the Sharp prototype with channel-related functions. * Decorate the Sharp prototype with channel-related functions.

View File

@@ -27,7 +27,7 @@ const colourspace = {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameter * @throws {Error} Invalid parameter
*/ */
const background = function background (rgba) { function background (rgba) {
const colour = color(rgba); const colour = color(rgba);
this.options.background = [ this.options.background = [
colour.red(), colour.red(),
@@ -36,7 +36,7 @@ const background = function background (rgba) {
Math.round(colour.alpha() * 255) Math.round(colour.alpha() * 255)
]; ];
return this; return this;
}; }
/** /**
* Convert to 8-bit greyscale; 256 shades of grey. * Convert to 8-bit greyscale; 256 shades of grey.
@@ -48,19 +48,19 @@ const background = function background (rgba) {
* @param {Boolean} [greyscale=true] * @param {Boolean} [greyscale=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const greyscale = function greyscale (greyscale) { function greyscale (greyscale) {
this.options.greyscale = is.bool(greyscale) ? greyscale : true; this.options.greyscale = is.bool(greyscale) ? greyscale : true;
return this; return this;
}; }
/** /**
* Alternative spelling of `greyscale`. * Alternative spelling of `greyscale`.
* @param {Boolean} [grayscale=true] * @param {Boolean} [grayscale=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const grayscale = function grayscale (grayscale) { function grayscale (grayscale) {
return this.greyscale(grayscale); return this.greyscale(grayscale);
}; }
/** /**
* Set the output colourspace. * Set the output colourspace.
@@ -69,13 +69,13 @@ const grayscale = function grayscale (grayscale) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const toColourspace = function toColourspace (colourspace) { function toColourspace (colourspace) {
if (!is.string(colourspace)) { if (!is.string(colourspace)) {
throw new Error('Invalid output colourspace ' + colourspace); throw new Error('Invalid output colourspace ' + colourspace);
} }
this.options.colourspace = colourspace; this.options.colourspace = colourspace;
return this; return this;
}; }
/** /**
* Alternative spelling of `toColourspace`. * Alternative spelling of `toColourspace`.
@@ -83,9 +83,9 @@ const toColourspace = function toColourspace (colourspace) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const toColorspace = function toColorspace (colorspace) { function toColorspace (colorspace) {
return this.toColourspace(colorspace); return this.toColourspace(colorspace);
}; }
/** /**
* Decorate the Sharp prototype with colour-related functions. * Decorate the Sharp prototype with colour-related functions.

View File

@@ -33,14 +33,20 @@ const is = require('./is');
* @param {Number} [options.left] - the pixel offset from the left edge. * @param {Number} [options.left] - the pixel offset from the left edge.
* @param {Boolean} [options.tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`. * @param {Boolean} [options.tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
* @param {Boolean} [options.cutout=false] - set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another. * @param {Boolean} [options.cutout=false] - set to true to apply only the alpha channel of the overlay image to the input image, giving the appearance of one image being cut out of another.
* @param {Number} [options.density=72] - integral number representing the DPI for vector overlay image.
* @param {Object} [options.raw] - describes overlay when using raw pixel data. * @param {Object} [options.raw] - describes overlay when using raw pixel data.
* @param {Number} [options.raw.width] * @param {Number} [options.raw.width]
* @param {Number} [options.raw.height] * @param {Number} [options.raw.height]
* @param {Number} [options.raw.channels] * @param {Number} [options.raw.channels]
* @param {Object} [options.create] - describes a blank overlay to be created.
* @param {Number} [options.create.width]
* @param {Number} [options.create.height]
* @param {Number} [options.create.channels] - 3-4
* @param {String|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const overlayWith = function overlayWith (overlay, options) { function overlayWith (overlay, options) {
this.options.overlay = this._createInputDescriptor(overlay, options, { this.options.overlay = this._createInputDescriptor(overlay, options, {
allowStream: false allowStream: false
}); });
@@ -81,7 +87,7 @@ const overlayWith = function overlayWith (overlay, options) {
} }
} }
return this; return this;
}; }
/** /**
* Decorate the Sharp prototype with composite-related functions. * Decorate the Sharp prototype with composite-related functions.

View File

@@ -54,16 +54,35 @@ let versions = {
* }); * });
* readableStream.pipe(transformer).pipe(writableStream); * readableStream.pipe(transformer).pipe(writableStream);
* *
* @example
* // Create a blank 300x200 PNG image of semi-transluent red pixels
* sharp(null, {
* create: {
* width: 300,
* height: 200,
* channels: 4,
* background: { r: 255, g: 0, b: 0, alpha: 128 }
* }
* })
* .png()
* .toBuffer()
* .then( ... );
*
* @param {(Buffer|String)} [input] - if present, can be * @param {(Buffer|String)} [input] - if present, can be
* a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or * a Buffer containing JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data, or
* a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file. * a String containing the path to an JPEG, PNG, WebP, GIF, SVG or TIFF image file.
* JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined. * JPEG, PNG, WebP, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when null or undefined.
* @param {Object} [options] - if present, is an Object with optional attributes. * @param {Object} [options] - if present, is an Object with optional attributes.
* @param {Number} [options.density=72] - integral number representing the DPI for vector images. * @param {Number} [options.density=72] - integral number representing the DPI for vector images.
* @param {Object} [options.raw] - describes raw pixel image data. See `raw()` for pixel ordering. * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {Number} [options.raw.width] * @param {Number} [options.raw.width]
* @param {Number} [options.raw.height] * @param {Number} [options.raw.height]
* @param {Number} [options.raw.channels] * @param {Number} [options.raw.channels] - 1-4
* @param {Object} [options.create] - describes a new image to be created.
* @param {Number} [options.create.width]
* @param {Number} [options.create.height]
* @param {Number} [options.create.channels] - 3-4
* @param {String|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@@ -134,6 +153,7 @@ const Sharp = function (input, options) {
streamOut: false, streamOut: false,
withMetadata: false, withMetadata: false,
withMetadataOrientation: -1, withMetadataOrientation: -1,
resolveWithObject: false,
// output format // output format
jpegQuality: 80, jpegQuality: 80,
jpegProgressive: false, jpegProgressive: false,
@@ -149,6 +169,8 @@ const Sharp = function (input, options) {
webpLossless: false, webpLossless: false,
webpNearLossless: false, webpNearLossless: false,
tiffQuality: 80, tiffQuality: 80,
tiffCompression: 'jpeg',
tiffPredictor: 'none',
tileSize: 256, tileSize: 256,
tileOverlap: 0, tileOverlap: 0,
// Function to notify of queue length changes // Function to notify of queue length changes

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
const util = require('util'); const util = require('util');
const color = require('color');
const is = require('./is'); const is = require('./is');
const sharp = require('../build/Release/sharp.node'); const sharp = require('../build/Release/sharp.node');
@@ -8,7 +9,7 @@ const sharp = require('../build/Release/sharp.node');
* Create Object containing input and input-related options. * Create Object containing input and input-related options.
* @private * @private
*/ */
const _createInputDescriptor = function _createInputDescriptor (input, inputOptions, containerOptions) { function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = {}; const inputDescriptor = {};
if (is.string(input)) { if (is.string(input)) {
// filesystem // filesystem
@@ -46,11 +47,35 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
throw new Error('Expected width, height and channels for raw pixel input'); throw new Error('Expected width, height and channels for raw pixel input');
} }
} }
// Create new image
if (is.defined(inputOptions.create)) {
if (
is.object(inputOptions.create) &&
is.integer(inputOptions.create.width) && is.inRange(inputOptions.create.width, 1, this.constructor.maximum.width) &&
is.integer(inputOptions.create.height) && is.inRange(inputOptions.create.height, 1, this.constructor.maximum.height) &&
is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) &&
is.defined(inputOptions.create.background)
) {
inputDescriptor.createWidth = inputOptions.create.width;
inputDescriptor.createHeight = inputOptions.create.height;
inputDescriptor.createChannels = inputOptions.create.channels;
const background = color(inputOptions.create.background);
inputDescriptor.createBackground = [
background.red(),
background.green(),
background.blue(),
Math.round(background.alpha() * 255)
];
delete inputDescriptor.buffer;
} else {
throw new Error('Expected width, height, channels and background to create a new input image');
}
}
} else if (is.defined(inputOptions)) { } else if (is.defined(inputOptions)) {
throw new Error('Invalid input options ' + inputOptions); throw new Error('Invalid input options ' + inputOptions);
} }
return inputDescriptor; return inputDescriptor;
}; }
/** /**
* Handle incoming Buffer chunk on Writable Stream. * Handle incoming Buffer chunk on Writable Stream.
@@ -59,7 +84,7 @@ const _createInputDescriptor = function _createInputDescriptor (input, inputOpti
* @param {String} encoding - unused * @param {String} encoding - unused
* @param {Function} callback * @param {Function} callback
*/ */
const _write = function _write (chunk, encoding, callback) { function _write (chunk, encoding, callback) {
/* istanbul ignore else */ /* istanbul ignore else */
if (Array.isArray(this.options.input.buffer)) { if (Array.isArray(this.options.input.buffer)) {
/* istanbul ignore else */ /* istanbul ignore else */
@@ -78,26 +103,26 @@ const _write = function _write (chunk, encoding, callback) {
} else { } else {
callback(new Error('Unexpected data on Writable Stream')); callback(new Error('Unexpected data on Writable Stream'));
} }
}; }
/** /**
* Flattens the array of chunks accumulated in input.buffer. * Flattens the array of chunks accumulated in input.buffer.
* @private * @private
*/ */
const _flattenBufferIn = function _flattenBufferIn () { function _flattenBufferIn () {
if (this._isStreamInput()) { if (this._isStreamInput()) {
this.options.input.buffer = Buffer.concat(this.options.input.buffer); this.options.input.buffer = Buffer.concat(this.options.input.buffer);
} }
}; }
/** /**
* Are we expecting Stream-based input? * Are we expecting Stream-based input?
* @private * @private
* @returns {Boolean} * @returns {Boolean}
*/ */
const _isStreamInput = function _isStreamInput () { function _isStreamInput () {
return Array.isArray(this.options.input.buffer); return Array.isArray(this.options.input.buffer);
}; }
/** /**
* Take a "snapshot" of the Sharp instance, returning a new instance. * Take a "snapshot" of the Sharp instance, returning a new instance.
@@ -114,7 +139,7 @@ const _isStreamInput = function _isStreamInput () {
* *
* @returns {Sharp} * @returns {Sharp}
*/ */
const clone = function clone () { function clone () {
const that = this; const that = this;
// Clone existing options // Clone existing options
const clone = this.constructor.call(); const clone = this.constructor.call();
@@ -127,7 +152,7 @@ const clone = function clone () {
clone.emit('finish'); clone.emit('finish');
}); });
return clone; return clone;
}; }
/** /**
* Fast access to image metadata without decoding any compressed image data. * Fast access to image metadata without decoding any compressed image data.
@@ -162,7 +187,7 @@ const clone = function clone () {
* @param {Function} [callback] - called with the arguments `(err, metadata)` * @param {Function} [callback] - called with the arguments `(err, metadata)`
* @returns {Promise<Object>|Sharp} * @returns {Promise<Object>|Sharp}
*/ */
const metadata = function metadata (callback) { function metadata (callback) {
const that = this; const that = this;
if (is.fn(callback)) { if (is.fn(callback)) {
if (this._isStreamInput()) { if (this._isStreamInput()) {
@@ -200,7 +225,7 @@ const metadata = function metadata (callback) {
}); });
} }
} }
}; }
/** /**
* Do not process input images where the number of pixels (width * height) exceeds this limit. * Do not process input images where the number of pixels (width * height) exceeds this limit.
@@ -210,7 +235,7 @@ const metadata = function metadata (callback) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid limit * @throws {Error} Invalid limit
*/ */
const limitInputPixels = function limitInputPixels (limit) { function limitInputPixels (limit) {
// if we pass in false we represent the integer as 0 to disable // if we pass in false we represent the integer as 0 to disable
if (limit === false) { if (limit === false) {
limit = 0; limit = 0;
@@ -223,7 +248,7 @@ const limitInputPixels = function limitInputPixels (limit) {
throw new Error('Invalid pixel limit (0 to ' + this.constructor.maximum.pixels + ') ' + limit); throw new Error('Invalid pixel limit (0 to ' + this.constructor.maximum.pixels + ') ' + limit);
} }
return this; return this;
}; }
/** /**
* An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. * An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`.
@@ -231,10 +256,10 @@ const limitInputPixels = function limitInputPixels (limit) {
* @param {Boolean} [sequentialRead=true] * @param {Boolean} [sequentialRead=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const sequentialRead = function sequentialRead (sequentialRead) { function sequentialRead (sequentialRead) {
this.options.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true; this.options.sequentialRead = is.bool(sequentialRead) ? sequentialRead : true;
return this; return this;
}; }
/** /**
* Decorate the Sharp prototype with input-related functions. * Decorate the Sharp prototype with input-related functions.

View File

@@ -29,7 +29,7 @@ const is = require('./is');
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const rotate = function rotate (angle) { function rotate (angle) {
if (!is.defined(angle)) { if (!is.defined(angle)) {
this.options.angle = -1; this.options.angle = -1;
} else if (is.integer(angle) && is.inArray(angle, [0, 90, 180, 270])) { } else if (is.integer(angle) && is.inArray(angle, [0, 90, 180, 270])) {
@@ -38,7 +38,7 @@ const rotate = function rotate (angle) {
throw new Error('Unsupported angle (0, 90, 180, 270) ' + angle); throw new Error('Unsupported angle (0, 90, 180, 270) ' + angle);
} }
return this; return this;
}; }
/** /**
* Extract a region of the image. * Extract a region of the image.
@@ -70,7 +70,7 @@ const rotate = function rotate (angle) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const extract = function extract (options) { function extract (options) {
const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post'; const suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
['left', 'top', 'width', 'height'].forEach(function (name) { ['left', 'top', 'width', 'height'].forEach(function (name) {
const value = options[name]; const value = options[name];
@@ -85,7 +85,7 @@ const extract = function extract (options) {
this.options.rotateBeforePreExtract = true; this.options.rotateBeforePreExtract = true;
} }
return this; return this;
}; }
/** /**
* Flip the image about the vertical Y axis. This always occurs after rotation, if any. * Flip the image about the vertical Y axis. This always occurs after rotation, if any.
@@ -93,10 +93,10 @@ const extract = function extract (options) {
* @param {Boolean} [flip=true] * @param {Boolean} [flip=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const flip = function flip (flip) { function flip (flip) {
this.options.flip = is.bool(flip) ? flip : true; this.options.flip = is.bool(flip) ? flip : true;
return this; return this;
}; }
/** /**
* Flop the image about the horizontal X axis. This always occurs after rotation, if any. * Flop the image about the horizontal X axis. This always occurs after rotation, if any.
@@ -104,10 +104,10 @@ const flip = function flip (flip) {
* @param {Boolean} [flop=true] * @param {Boolean} [flop=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const flop = function flop (flop) { function flop (flop) {
this.options.flop = is.bool(flop) ? flop : true; this.options.flop = is.bool(flop) ? flop : true;
return this; return this;
}; }
/** /**
* Sharpen the image. * Sharpen the image.
@@ -121,7 +121,7 @@ const flop = function flop (flop) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const sharpen = function sharpen (sigma, flat, jagged) { function sharpen (sigma, flat, jagged) {
if (!is.defined(sigma)) { if (!is.defined(sigma)) {
// No arguments: default to mild sharpen // No arguments: default to mild sharpen
this.options.sharpenSigma = -1; this.options.sharpenSigma = -1;
@@ -151,7 +151,7 @@ const sharpen = function sharpen (sigma, flat, jagged) {
throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma); throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
} }
return this; return this;
}; }
/** /**
* Blur the image. * Blur the image.
@@ -161,7 +161,7 @@ const sharpen = function sharpen (sigma, flat, jagged) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const blur = function blur (sigma) { function blur (sigma) {
if (!is.defined(sigma)) { if (!is.defined(sigma)) {
// No arguments: default to mild blur // No arguments: default to mild blur
this.options.blurSigma = -1; this.options.blurSigma = -1;
@@ -175,7 +175,7 @@ const blur = function blur (sigma) {
throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma); throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
} }
return this; return this;
}; }
/** /**
* Extends/pads the edges of the image with the colour provided to the `background` method. * Extends/pads the edges of the image with the colour provided to the `background` method.
@@ -198,7 +198,7 @@ const blur = function blur (sigma) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const extend = function extend (extend) { function extend (extend) {
if (is.integer(extend) && extend > 0) { if (is.integer(extend) && extend > 0) {
this.options.extendTop = extend; this.options.extendTop = extend;
this.options.extendBottom = extend; this.options.extendBottom = extend;
@@ -219,17 +219,17 @@ const extend = function extend (extend) {
throw new Error('Invalid edge extension ' + extend); throw new Error('Invalid edge extension ' + extend);
} }
return this; return this;
}; }
/** /**
* Merge alpha transparency channel, if any, with `background`. * Merge alpha transparency channel, if any, with `background`.
* @param {Boolean} [flatten=true] * @param {Boolean} [flatten=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const flatten = function flatten (flatten) { function flatten (flatten) {
this.options.flatten = is.bool(flatten) ? flatten : true; this.options.flatten = is.bool(flatten) ? flatten : true;
return this; return this;
}; }
/** /**
* Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel. * Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
@@ -237,7 +237,7 @@ const flatten = function flatten (flatten) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const trim = function trim (tolerance) { function trim (tolerance) {
if (!is.defined(tolerance)) { if (!is.defined(tolerance)) {
this.options.trimTolerance = 10; this.options.trimTolerance = 10;
} else if (is.integer(tolerance) && is.inRange(tolerance, 1, 99)) { } else if (is.integer(tolerance) && is.inRange(tolerance, 1, 99)) {
@@ -246,7 +246,7 @@ const trim = function trim (tolerance) {
throw new Error('Invalid trim tolerance (1 to 99) ' + tolerance); throw new Error('Invalid trim tolerance (1 to 99) ' + tolerance);
} }
return this; return this;
}; }
/** /**
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` * Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
@@ -258,7 +258,7 @@ const trim = function trim (tolerance) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const gamma = function gamma (gamma) { function gamma (gamma) {
if (!is.defined(gamma)) { if (!is.defined(gamma)) {
// Default gamma correction of 2.2 (sRGB) // Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2; this.options.gamma = 2.2;
@@ -268,36 +268,36 @@ const gamma = function gamma (gamma) {
throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma); throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma);
} }
return this; return this;
}; }
/** /**
* Produce the "negative" of the image. * Produce the "negative" of the image.
* @param {Boolean} [negate=true] * @param {Boolean} [negate=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const negate = function negate (negate) { function negate (negate) {
this.options.negate = is.bool(negate) ? negate : true; this.options.negate = is.bool(negate) ? negate : true;
return this; return this;
}; }
/** /**
* Enhance output image contrast by stretching its luminance to cover the full dynamic range. * Enhance output image contrast by stretching its luminance to cover the full dynamic range.
* @param {Boolean} [normalise=true] * @param {Boolean} [normalise=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const normalise = function normalise (normalise) { function normalise (normalise) {
this.options.normalise = is.bool(normalise) ? normalise : true; this.options.normalise = is.bool(normalise) ? normalise : true;
return this; return this;
}; }
/** /**
* Alternative spelling of normalise. * Alternative spelling of normalise.
* @param {Boolean} [normalize=true] * @param {Boolean} [normalize=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const normalize = function normalize (normalize) { function normalize (normalize) {
return this.normalise(normalize); return this.normalise(normalize);
}; }
/** /**
* Convolve the image with the specified kernel. * Convolve the image with the specified kernel.
@@ -324,7 +324,7 @@ const normalize = function normalize (normalize) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const convolve = function convolve (kernel) { function convolve (kernel) {
if (!is.object(kernel) || !Array.isArray(kernel.kernel) || if (!is.object(kernel) || !Array.isArray(kernel.kernel) ||
!is.integer(kernel.width) || !is.integer(kernel.height) || !is.integer(kernel.width) || !is.integer(kernel.height) ||
!is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) || !is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) ||
@@ -348,7 +348,7 @@ const convolve = function convolve (kernel) {
} }
this.options.convKernel = kernel; this.options.convKernel = kernel;
return this; return this;
}; }
/** /**
* Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0. * Any pixel value greather than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
@@ -359,7 +359,7 @@ const convolve = function convolve (kernel) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const threshold = function threshold (threshold, options) { function threshold (threshold, options) {
if (!is.defined(threshold)) { if (!is.defined(threshold)) {
this.options.threshold = 128; this.options.threshold = 128;
} else if (is.bool(threshold)) { } else if (is.bool(threshold)) {
@@ -375,7 +375,7 @@ const threshold = function threshold (threshold, options) {
this.options.thresholdGrayscale = false; this.options.thresholdGrayscale = false;
} }
return this; return this;
}; }
/** /**
* Perform a bitwise boolean operation with operand image. * Perform a bitwise boolean operation with operand image.
@@ -393,7 +393,7 @@ const threshold = function threshold (threshold, options) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const boolean = function boolean (operand, operator, options) { function boolean (operand, operator, options) {
this.options.boolean = this._createInputDescriptor(operand, options); this.options.boolean = this._createInputDescriptor(operand, options);
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) { if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
this.options.booleanOp = operator; this.options.booleanOp = operator;
@@ -401,7 +401,7 @@ const boolean = function boolean (operand, operator, options) {
throw new Error('Invalid boolean operator ' + operator); throw new Error('Invalid boolean operator ' + operator);
} }
return this; return this;
}; }
/** /**
* Decorate the Sharp prototype with operation-related functions. * Decorate the Sharp prototype with operation-related functions.

View File

@@ -19,7 +19,7 @@ const sharp = require('../build/Release/sharp.node');
* @returns {Promise<Object>} - when no callback is provided * @returns {Promise<Object>} - when no callback is provided
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const toFile = function toFile (fileOut, callback) { function toFile (fileOut, callback) {
if (!fileOut || fileOut.length === 0) { if (!fileOut || fileOut.length === 0) {
const errOutputInvalid = new Error('Invalid output'); const errOutputInvalid = new Error('Invalid output');
if (is.fn(callback)) { if (is.fn(callback)) {
@@ -41,25 +41,32 @@ const toFile = function toFile (fileOut, callback) {
} }
} }
return this; return this;
}; }
/** /**
* Write output to a Buffer. * Write output to a Buffer.
* JPEG, PNG, WebP, and RAW output are supported. * JPEG, PNG, WebP, and RAW output are supported.
* By default, the format will match the input image, except GIF and SVG input which become PNG output. * By default, the format will match the input image, except GIF and SVG input which become PNG output.
* *
* `callback`, if present, gets three arguments `(err, buffer, info)` where: * `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error message, if any. * - `err` is an error, if any.
* - `buffer` is the output image data. * - `data` is the output image data.
* - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`. * - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
* A Promises/A+ promise is returned when `callback` is not provided. * A Promise is returned when `callback` is not provided.
* *
* @param {Object} [options]
* @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {Function} [callback] * @param {Function} [callback]
* @returns {Promise<Buffer>} - when no callback is provided * @returns {Promise<Buffer>} - when no callback is provided
*/ */
const toBuffer = function toBuffer (callback) { function toBuffer (options, callback) {
return this._pipeline(callback); if (is.object(options)) {
}; if (is.bool(options.resolveWithObject)) {
this.options.resolveWithObject = options.resolveWithObject;
}
}
return this._pipeline(is.fn(options) ? options : callback);
}
/** /**
* Include all metadata (EXIF, XMP, IPTC) from the input image in the output image. * Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
@@ -70,7 +77,7 @@ const toBuffer = function toBuffer (callback) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const withMetadata = function withMetadata (withMetadata) { function withMetadata (withMetadata) {
this.options.withMetadata = is.bool(withMetadata) ? withMetadata : true; this.options.withMetadata = is.bool(withMetadata) ? withMetadata : true;
if (is.object(withMetadata)) { if (is.object(withMetadata)) {
if (is.defined(withMetadata.orientation)) { if (is.defined(withMetadata.orientation)) {
@@ -82,7 +89,7 @@ const withMetadata = function withMetadata (withMetadata) {
} }
} }
return this; return this;
}; }
/** /**
* Use these JPEG options for output image. * Use these JPEG options for output image.
@@ -98,7 +105,7 @@ const withMetadata = function withMetadata (withMetadata) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
const jpeg = function jpeg (options) { function jpeg (options) {
if (is.object(options)) { if (is.object(options)) {
if (is.defined(options.quality)) { if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) { if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
@@ -133,7 +140,7 @@ const jpeg = function jpeg (options) {
} }
} }
return this._updateFormatOut('jpeg', options); return this._updateFormatOut('jpeg', options);
}; }
/** /**
* Use these PNG options for output image. * Use these PNG options for output image.
@@ -145,7 +152,7 @@ const jpeg = function jpeg (options) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
const png = function png (options) { function png (options) {
if (is.object(options)) { if (is.object(options)) {
if (is.defined(options.progressive)) { if (is.defined(options.progressive)) {
this._setBooleanOption('pngProgressive', options.progressive); this._setBooleanOption('pngProgressive', options.progressive);
@@ -162,7 +169,7 @@ const png = function png (options) {
} }
} }
return this._updateFormatOut('png', options); return this._updateFormatOut('png', options);
}; }
/** /**
* Use these WebP options for output image. * Use these WebP options for output image.
@@ -175,7 +182,7 @@ const png = function png (options) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
const webp = function webp (options) { function webp (options) {
if (is.object(options) && is.defined(options.quality)) { if (is.object(options) && is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) { if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.webpQuality = options.quality; this.options.webpQuality = options.quality;
@@ -197,17 +204,19 @@ const webp = function webp (options) {
this._setBooleanOption('webpNearLossless', options.nearLossless); this._setBooleanOption('webpNearLossless', options.nearLossless);
} }
return this._updateFormatOut('webp', options); return this._updateFormatOut('webp', options);
}; }
/** /**
* Use these TIFF options for output image. * Use these TIFF options for output image.
* @param {Object} [options] - output options * @param {Object} [options] - output options
* @param {Number} [options.quality=80] - quality, integer 1-100 * @param {Number} [options.quality=80] - quality, integer 1-100
* @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format * @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg
* @param {Boolean} [options.predictor='none'] - compression predictor options: none, horizontal, float
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
const tiff = function tiff (options) { function tiff (options) {
if (is.object(options) && is.defined(options.quality)) { if (is.object(options) && is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) { if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.tiffQuality = options.quality; this.options.tiffQuality = options.quality;
@@ -215,16 +224,34 @@ const tiff = function tiff (options) {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality); throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
} }
} }
// compression
if (is.defined(options) && is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'none'])) {
this.options.tiffCompression = options.compression;
} else {
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, none`;
throw new Error(message);
}
}
// predictor
if (is.defined(options) && is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
this.options.tiffPredictor = options.predictor;
} else {
const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
throw new Error(message);
}
}
return this._updateFormatOut('tiff', options); return this._updateFormatOut('tiff', options);
}; }
/** /**
* Force output to be raw, uncompressed uint8 pixel data. * Force output to be raw, uncompressed uint8 pixel data.
* @returns {Sharp} * @returns {Sharp}
*/ */
const raw = function raw () { function raw () {
return this._updateFormatOut('raw'); return this._updateFormatOut('raw');
}; }
/** /**
* Force output to a given format. * Force output to a given format.
@@ -233,7 +260,7 @@ const raw = function raw () {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} unsupported format or options * @throws {Error} unsupported format or options
*/ */
const toFormat = function toFormat (format, options) { function toFormat (format, options) {
if (is.object(format) && is.string(format.id)) { if (is.object(format) && is.string(format.id)) {
format = format.id; format = format.id;
} }
@@ -241,7 +268,7 @@ const toFormat = function toFormat (format, options) {
throw new Error('Unsupported output format ' + format); throw new Error('Unsupported output format ' + format);
} }
return this[format](options); return this[format](options);
}; }
/** /**
* Use tile-based deep zoom (image pyramid) output. * Use tile-based deep zoom (image pyramid) output.
@@ -267,7 +294,7 @@ const toFormat = function toFormat (format, options) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const tile = function tile (tile) { function tile (tile) {
if (is.object(tile)) { if (is.object(tile)) {
// Size of square tiles, in pixels // Size of square tiles, in pixels
if (is.defined(tile.size)) { if (is.defined(tile.size)) {
@@ -312,7 +339,7 @@ const tile = function tile (tile) {
throw new Error('Invalid tile format ' + this.options.formatOut); throw new Error('Invalid tile format ' + this.options.formatOut);
} }
return this._updateFormatOut('dz'); return this._updateFormatOut('dz');
}; }
/** /**
* Update the output format unless options.force is false, * Update the output format unless options.force is false,
@@ -323,10 +350,10 @@ const tile = function tile (tile) {
* @param {Boolean} [options.force=true] - force output format, otherwise attempt to use input format * @param {Boolean} [options.force=true] - force output format, otherwise attempt to use input format
* @returns {Sharp} * @returns {Sharp}
*/ */
const _updateFormatOut = function _updateFormatOut (formatOut, options) { function _updateFormatOut (formatOut, options) {
this.options.formatOut = (is.object(options) && options.force === false) ? 'input' : formatOut; this.options.formatOut = (is.object(options) && options.force === false) ? 'input' : formatOut;
return this; return this;
}; }
/** /**
* Update a Boolean attribute of the this.options Object. * Update a Boolean attribute of the this.options Object.
@@ -335,31 +362,31 @@ const _updateFormatOut = function _updateFormatOut (formatOut, options) {
* @param {Boolean} val * @param {Boolean} val
* @throws {Error} Invalid key * @throws {Error} Invalid key
*/ */
const _setBooleanOption = function _setBooleanOption (key, val) { function _setBooleanOption (key, val) {
if (is.bool(val)) { if (is.bool(val)) {
this.options[key] = val; this.options[key] = val;
} else { } else {
throw new Error('Invalid ' + key + ' (boolean) ' + val); throw new Error('Invalid ' + key + ' (boolean) ' + val);
} }
}; }
/** /**
* Called by a WriteableStream to notify us it is ready for data. * Called by a WriteableStream to notify us it is ready for data.
* @private * @private
*/ */
const _read = function _read () { function _read () {
if (!this.options.streamOut) { if (!this.options.streamOut) {
this.options.streamOut = true; this.options.streamOut = true;
this._pipeline(); this._pipeline();
} }
}; }
/** /**
* Invoke the C++ image processing pipeline * Invoke the C++ image processing pipeline
* Supports callback, stream and promise variants * Supports callback, stream and promise variants
* @private * @private
*/ */
const _pipeline = function _pipeline (callback) { function _pipeline (callback) {
const that = this; const that = this;
if (typeof callback === 'function') { if (typeof callback === 'function') {
// output=file/buffer // output=file/buffer
@@ -423,11 +450,15 @@ const _pipeline = function _pipeline (callback) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
that.on('finish', function () { that.on('finish', function () {
that._flattenBufferIn(); that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data) { sharp.pipeline(that.options, function (err, data, info) {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
resolve(data); if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
}
} }
}); });
}); });
@@ -435,17 +466,21 @@ const _pipeline = function _pipeline (callback) {
} else { } else {
// output=promise, input=file/buffer // output=promise, input=file/buffer
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
sharp.pipeline(that.options, function (err, data) { sharp.pipeline(that.options, function (err, data, info) {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
resolve(data); if (that.options.resolveWithObject) {
resolve({ data: data, info: info });
} else {
resolve(data);
}
} }
}); });
}); });
} }
} }
}; }
// Deprecated output options // Deprecated output options
/* istanbul ignore next */ /* istanbul ignore next */

View File

@@ -36,6 +36,7 @@ const strategy = {
* @private * @private
*/ */
const kernel = { const kernel = {
nearest: 'nearest',
cubic: 'cubic', cubic: 'cubic',
lanczos2: 'lanczos2', lanczos2: 'lanczos2',
lanczos3: 'lanczos3' lanczos3: 'lanczos3'
@@ -62,6 +63,7 @@ const interpolator = {
* By default, the resized image is centre cropped to the exact size specified. * By default, the resized image is centre cropped to the exact size specified.
* *
* Possible reduction kernels are: * Possible reduction kernels are:
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
* - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline). * - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`. * - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default). * - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
@@ -99,7 +101,7 @@ const interpolator = {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const resize = function resize (width, height, options) { function resize (width, height, options) {
if (is.defined(width)) { if (is.defined(width)) {
if (is.integer(width) && is.inRange(width, 1, this.constructor.maximum.width)) { if (is.integer(width) && is.inRange(width, 1, this.constructor.maximum.width)) {
this.options.width = width; this.options.width = width;
@@ -142,7 +144,7 @@ const resize = function resize (width, height, options) {
} }
} }
return this; return this;
}; }
/** /**
* Crop the resized image to the exact size specified, the default behaviour. * Crop the resized image to the exact size specified, the default behaviour.
@@ -170,7 +172,7 @@ const resize = function resize (width, height, options) {
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const crop = function crop (crop) { function crop (crop) {
this.options.canvas = 'crop'; this.options.canvas = 'crop';
if (!is.defined(crop)) { if (!is.defined(crop)) {
// Default // Default
@@ -184,11 +186,14 @@ const crop = function crop (crop) {
} else if (is.integer(crop) && crop >= strategy.entropy) { } else if (is.integer(crop) && crop >= strategy.entropy) {
// Strategy // Strategy
this.options.crop = crop; this.options.crop = crop;
} else if (is.string(crop) && is.integer(strategy[crop])) {
// Strategy (string)
this.options.crop = strategy[crop];
} else { } else {
throw is.invalidParameterError('crop', 'valid crop id/name/strategy', crop); throw is.invalidParameterError('crop', 'valid crop id/name/strategy', crop);
} }
return this; return this;
}; }
/** /**
* Preserving aspect ratio, resize the image to the maximum `width` or `height` specified * Preserving aspect ratio, resize the image to the maximum `width` or `height` specified
@@ -213,10 +218,10 @@ const crop = function crop (crop) {
* *
* @returns {Sharp} * @returns {Sharp}
*/ */
const embed = function embed () { function embed () {
this.options.canvas = 'embed'; this.options.canvas = 'embed';
return this; return this;
}; }
/** /**
* Preserving aspect ratio, resize the image to be as large as possible * Preserving aspect ratio, resize the image to be as large as possible
@@ -237,10 +242,10 @@ const embed = function embed () {
* *
* @returns {Sharp} * @returns {Sharp}
*/ */
const max = function max () { function max () {
this.options.canvas = 'max'; this.options.canvas = 'max';
return this; return this;
}; }
/** /**
* Preserving aspect ratio, resize the image to be as small as possible * Preserving aspect ratio, resize the image to be as small as possible
@@ -250,20 +255,20 @@ const max = function max () {
* *
* @returns {Sharp} * @returns {Sharp}
*/ */
const min = function min () { function min () {
this.options.canvas = 'min'; this.options.canvas = 'min';
return this; return this;
}; }
/** /**
* Ignoring the aspect ratio of the input, stretch the image to * Ignoring the aspect ratio of the input, stretch the image to
* the exact `width` and/or `height` provided via `resize`. * the exact `width` and/or `height` provided via `resize`.
* @returns {Sharp} * @returns {Sharp}
*/ */
const ignoreAspectRatio = function ignoreAspectRatio () { function ignoreAspectRatio () {
this.options.canvas = 'ignore_aspect'; this.options.canvas = 'ignore_aspect';
return this; return this;
}; }
/** /**
* Do not enlarge the output image if the input image width *or* height are already less than the required dimensions. * Do not enlarge the output image if the input image width *or* height are already less than the required dimensions.
@@ -272,10 +277,10 @@ const ignoreAspectRatio = function ignoreAspectRatio () {
* @param {Boolean} [withoutEnlargement=true] * @param {Boolean} [withoutEnlargement=true]
* @returns {Sharp} * @returns {Sharp}
*/ */
const withoutEnlargement = function withoutEnlargement (withoutEnlargement) { function withoutEnlargement (withoutEnlargement) {
this.options.withoutEnlargement = is.bool(withoutEnlargement) ? withoutEnlargement : true; this.options.withoutEnlargement = is.bool(withoutEnlargement) ? withoutEnlargement : true;
return this; return this;
}; }
/** /**
* Decorate the Sharp prototype with resize-related functions. * Decorate the Sharp prototype with resize-related functions.

View File

@@ -22,7 +22,7 @@ const sharp = require('../build/Release/sharp.node');
* @param {Number} [options.items=100] - is the maximum number of operations to cache * @param {Number} [options.items=100] - is the maximum number of operations to cache
* @returns {Object} * @returns {Object}
*/ */
const cache = function cache (options) { function cache (options) {
if (is.bool(options)) { if (is.bool(options)) {
if (options) { if (options) {
// Default cache settings of 50MB, 20 files, 100 items // Default cache settings of 50MB, 20 files, 100 items
@@ -35,7 +35,7 @@ const cache = function cache (options) {
} else { } else {
return sharp.cache(); return sharp.cache();
} }
}; }
cache(true); cache(true);
/** /**
@@ -57,9 +57,9 @@ cache(true);
* @param {Number} [concurrency] * @param {Number} [concurrency]
* @returns {Number} concurrency * @returns {Number} concurrency
*/ */
const concurrency = function concurrency (concurrency) { function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null); return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}; }
/** /**
* Provides access to internal task counters. * Provides access to internal task counters.
@@ -71,9 +71,9 @@ const concurrency = function concurrency (concurrency) {
* *
* @returns {Object} * @returns {Object}
*/ */
const counters = function counters () { function counters () {
return sharp.counters(); return sharp.counters();
}; }
/** /**
* Get and set use of SIMD vector unit instructions. * Get and set use of SIMD vector unit instructions.
@@ -95,9 +95,9 @@ const counters = function counters () {
* @param {Boolean} [simd=false] * @param {Boolean} [simd=false]
* @returns {Boolean} * @returns {Boolean}
*/ */
const simd = function simd (simd) { function simd (simd) {
return sharp.simd(is.bool(simd) ? simd : null); return sharp.simd(is.bool(simd) ? simd : null);
}; }
simd(false); simd(false);
/** /**

View File

@@ -1,14 +1,13 @@
site_name: sharp site_name: sharp
site_url: http://sharp.dimens.io/ site_url: http://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://dimens.io/">dimens.io</a> copyright: <a href="https://pixelplumbing.com/">pixelplumbing.com</a>
google_analytics: ['UA-13034748-12', 'sharp.dimens.io'] google_analytics: ['UA-13034748-12', 'sharp.pixelplumbing.com']
theme: readthedocs theme: readthedocs
markdown_extensions: markdown_extensions:
- toc: - toc:
permalink: True permalink: True
dev_addr: 0.0.0.0:10101
pages: pages:
- Home: index.md - Home: index.md
- Installation: install.md - Installation: install.md

View File

@@ -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.17.2", "version": "0.17.3",
"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": [
@@ -32,7 +32,9 @@
"Matthias Thoemmes <thoemmes@gmail.com>", "Matthias Thoemmes <thoemmes@gmail.com>",
"Patrick Paskaris <patrick@paskaris.gr>", "Patrick Paskaris <patrick@paskaris.gr>",
"Jérémy Lal <kapouer@melix.org>", "Jérémy Lal <kapouer@melix.org>",
"Rahul Nanwani <r.nanwani@gmail.com>" "Rahul Nanwani <r.nanwani@gmail.com>",
"Alice Monday <alice0meta@gmail.com>",
"Kristo Jorgenson <kristo.jorgenson@gmail.com>"
], ],
"scripts": { "scripts": {
"clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*", "clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*",
@@ -70,17 +72,17 @@
"tar": "^2.2.1" "tar": "^2.2.1"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.1.4", "async": "^2.2.0",
"bufferutil": "^2.0.1", "bufferutil": "^3.0.0",
"cc": "^1.0.0", "cc": "^1.0.0",
"cross-env": "^3.1.4", "cross-env": "^4.0.0",
"documentation": "^4.0.0-beta.18", "documentation": "^4.0.0-beta.18",
"exif-reader": "^1.0.2", "exif-reader": "^1.0.2",
"icc": "^1.0.0", "icc": "^1.0.0",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"nyc": "^10.1.2", "nyc": "^10.2.0",
"rimraf": "^2.5.4", "rimraf": "^2.5.4",
"semistandard": "^9.2.1", "semistandard": "^10.0.0",
"unzip": "^0.1.11" "unzip": "^0.1.11"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@@ -44,7 +44,7 @@ namespace sharp {
InputDescriptor *descriptor = new InputDescriptor; InputDescriptor *descriptor = new InputDescriptor;
if (HasAttr(input, "file")) { if (HasAttr(input, "file")) {
descriptor->file = AttrAsStr(input, "file"); descriptor->file = AttrAsStr(input, "file");
} else { } else if (HasAttr(input, "buffer")) {
v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer"); v8::Local<v8::Object> buffer = AttrAs<v8::Object>(input, "buffer");
descriptor->bufferLength = node::Buffer::Length(buffer); descriptor->bufferLength = node::Buffer::Length(buffer);
descriptor->buffer = node::Buffer::Data(buffer); descriptor->buffer = node::Buffer::Data(buffer);
@@ -60,6 +60,16 @@ namespace sharp {
descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth"); descriptor->rawWidth = AttrTo<uint32_t>(input, "rawWidth");
descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight"); descriptor->rawHeight = AttrTo<uint32_t>(input, "rawHeight");
} }
// Create new image
if (HasAttr(input, "createChannels")) {
descriptor->createChannels = AttrTo<uint32_t>(input, "createChannels");
descriptor->createWidth = AttrTo<uint32_t>(input, "createWidth");
descriptor->createHeight = AttrTo<uint32_t>(input, "createHeight");
v8::Local<v8::Object> createBackground = AttrAs<v8::Object>(input, "createBackground");
for (unsigned int i = 0; i < 4; i++) {
descriptor->createBackground[i] = AttrTo<double>(createBackground, i);
}
}
return descriptor; return descriptor;
} }
@@ -192,7 +202,6 @@ namespace sharp {
VImage image; VImage image;
ImageType imageType; ImageType imageType;
if (descriptor->buffer != nullptr) { if (descriptor->buffer != nullptr) {
// From buffer
if (descriptor->rawChannels > 0) { if (descriptor->rawChannels > 0) {
// Raw, uncompressed pixel data // Raw, uncompressed pixel data
image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength, image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength,
@@ -227,26 +236,41 @@ namespace sharp {
} }
} }
} else { } else {
// From filesystem if (descriptor->createChannels > 0) {
imageType = DetermineImageType(descriptor->file.data()); // Create new image
if (imageType != ImageType::UNKNOWN) { std::vector<double> background = {
try { descriptor->createBackground[0],
vips::VOption *option = VImage::option()->set("access", accessMethod); descriptor->createBackground[1],
if (imageType == ImageType::SVG || imageType == ImageType::PDF) { descriptor->createBackground[2]
option->set("dpi", static_cast<double>(descriptor->density)); };
} if (descriptor->createChannels == 4) {
if (imageType == ImageType::MAGICK) { background.push_back(descriptor->createBackground[3]);
option->set("density", std::to_string(descriptor->density).data());
}
image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
}
} catch (...) {
throw vips::VError("Input file has corrupt header");
} }
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
imageType = ImageType::RAW;
} else { } else {
throw vips::VError("Input file is missing or of an unsupported image format"); // From filesystem
imageType = DetermineImageType(descriptor->file.data());
if (imageType != ImageType::UNKNOWN) {
try {
vips::VOption *option = VImage::option()->set("access", accessMethod);
if (imageType == ImageType::SVG || imageType == ImageType::PDF) {
option->set("dpi", static_cast<double>(descriptor->density));
}
if (imageType == ImageType::MAGICK) {
option->set("density", std::to_string(descriptor->density).data());
}
image = VImage::new_from_file(descriptor->file.data(), option);
if (imageType == ImageType::SVG || imageType == ImageType::PDF || imageType == ImageType::MAGICK) {
SetDensity(image, descriptor->density);
}
} catch (...) {
throw vips::VError("Input file has corrupt header");
}
} else {
throw vips::VError("Input file is missing or of an unsupported image format");
}
} }
} }
return std::make_tuple(image, imageType); return std::make_tuple(image, imageType);

View File

@@ -52,6 +52,10 @@ namespace sharp {
int rawChannels; int rawChannels;
int rawWidth; int rawWidth;
int rawHeight; int rawHeight;
int createChannels;
int createWidth;
int createHeight;
double createBackground[4];
InputDescriptor(): InputDescriptor():
buffer(nullptr), buffer(nullptr),
@@ -59,7 +63,15 @@ namespace sharp {
density(72), density(72),
rawChannels(0), rawChannels(0),
rawWidth(0), rawWidth(0),
rawHeight(0) {} rawHeight(0),
createChannels(0),
createWidth(0),
createHeight(0) {
createBackground[0] = 0.0;
createBackground[1] = 0.0;
createBackground[2] = 0.0;
createBackground[3] = 255.0;
}
}; };
// Convenience methods to access the attributes of a v8::Object // Convenience methods to access the attributes of a v8::Object

View File

@@ -392,7 +392,10 @@ class PipelineWorker : public Nan::AsyncWorker {
if (yresidual < 1.0 || xresidual < 1.0) { if (yresidual < 1.0 || xresidual < 1.0) {
VipsKernel kernel = static_cast<VipsKernel>( VipsKernel kernel = static_cast<VipsKernel>(
vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data())); vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data()));
if (kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 && kernel != VIPS_KERNEL_LANCZOS3) { if (
kernel != VIPS_KERNEL_NEAREST && kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 &&
kernel != VIPS_KERNEL_LANCZOS3
) {
throw vips::VError("Unknown kernel"); throw vips::VError("Unknown kernel");
} }
if (yresidual < 1.0) { if (yresidual < 1.0) {
@@ -835,11 +838,16 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("alpha_q", baton->webpAlphaQuality)); ->set("alpha_q", baton->webpAlphaQuality));
baton->formatOut = "webp"; baton->formatOut = "webp";
} else if (baton->formatOut == "tiff" || isTiff || (matchInput && inputImageType == ImageType::TIFF)) { } else if (baton->formatOut == "tiff" || isTiff || (matchInput && inputImageType == ImageType::TIFF)) {
// Cast pixel values to float, if required
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
image = image.cast(VIPS_FORMAT_FLOAT);
}
// Write TIFF to file // Write TIFF to file
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option() image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata)
->set("Q", baton->tiffQuality) ->set("Q", baton->tiffQuality)
->set("compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG)); ->set("compression", baton->tiffCompression)
->set("predictor", baton->tiffPredictor) );
baton->formatOut = "tiff"; baton->formatOut = "tiff";
baton->channels = std::min(baton->channels, 3); baton->channels = std::min(baton->channels, 3);
} else if (baton->formatOut == "dz" || isDz || isDzZip) { } else if (baton->formatOut == "dz" || isDz || isDzZip) {
@@ -1100,7 +1108,7 @@ NAN_METHOD(pipeline) {
// Background colour // Background colour
v8::Local<v8::Object> background = AttrAs<v8::Object>(options, "background"); v8::Local<v8::Object> background = AttrAs<v8::Object>(options, "background");
for (unsigned int i = 0; i < 4; i++) { for (unsigned int i = 0; i < 4; i++) {
baton->background[i] = AttrTo<uint32_t>(background, i); baton->background[i] = AttrTo<double>(background, i);
} }
// Overlay options // Overlay options
if (HasAttr(options, "overlay")) { if (HasAttr(options, "overlay")) {
@@ -1140,9 +1148,6 @@ NAN_METHOD(pipeline) {
baton->threshold = AttrTo<int32_t>(options, "threshold"); baton->threshold = AttrTo<int32_t>(options, "threshold");
baton->thresholdGrayscale = AttrTo<bool>(options, "thresholdGrayscale"); baton->thresholdGrayscale = AttrTo<bool>(options, "thresholdGrayscale");
baton->trimTolerance = AttrTo<int32_t>(options, "trimTolerance"); baton->trimTolerance = AttrTo<int32_t>(options, "trimTolerance");
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && baton->trimTolerance != 0) {
baton->accessMethod = VIPS_ACCESS_RANDOM;
}
baton->gamma = AttrTo<double>(options, "gamma"); baton->gamma = AttrTo<double>(options, "gamma");
baton->greyscale = AttrTo<bool>(options, "greyscale"); baton->greyscale = AttrTo<bool>(options, "greyscale");
baton->normalise = AttrTo<bool>(options, "normalise"); baton->normalise = AttrTo<bool>(options, "normalise");
@@ -1199,6 +1204,14 @@ NAN_METHOD(pipeline) {
baton->webpLossless = AttrTo<bool>(options, "webpLossless"); baton->webpLossless = AttrTo<bool>(options, "webpLossless");
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless"); baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality"); baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
// tiff compression options
baton->tiffCompression = static_cast<VipsForeignTiffCompression>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION,
AttrAsStr(options, "tiffCompression").data()));
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
AttrAsStr(options, "tiffPredictor").data()));
// Tile output // Tile output
baton->tileSize = AttrTo<uint32_t>(options, "tileSize"); baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap"); baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
@@ -1217,6 +1230,12 @@ NAN_METHOD(pipeline) {
baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_DZ; baton->tileLayout = VIPS_FOREIGN_DZ_LAYOUT_DZ;
} }
baton->tileFormat = AttrAsStr(options, "tileFormat"); baton->tileFormat = AttrAsStr(options, "tileFormat");
// Force random access for certain operations
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && (
baton->trimTolerance != 0 || baton->normalise ||
baton->crop == 16 || baton->crop == 17)) {
baton->accessMethod = VIPS_ACCESS_RANDOM;
}
// Function to notify of queue length changes // Function to notify of queue length changes
Nan::Callback *queueListener = new Nan::Callback(AttrAs<v8::Function>(options, "queueListener")); Nan::Callback *queueListener = new Nan::Callback(AttrAs<v8::Function>(options, "queueListener"));

View File

@@ -104,6 +104,8 @@ struct PipelineBaton {
bool webpNearLossless; bool webpNearLossless;
bool webpLossless; bool webpLossless;
int tiffQuality; int tiffQuality;
VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor;
std::string err; std::string err;
bool withMetadata; bool withMetadata;
int withMetadataOrientation; int withMetadataOrientation;
@@ -172,6 +174,8 @@ struct PipelineBaton {
pngAdaptiveFiltering(true), pngAdaptiveFiltering(true),
webpQuality(80), webpQuality(80),
tiffQuality(80), tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_NONE),
withMetadata(false), withMetadata(false),
withMetadataOrientation(-1), withMetadataOrientation(-1),
convKernelWidth(0), convKernelWidth(0),

View File

@@ -15,7 +15,7 @@ const min = 320;
const max = 960; const max = 960;
const randomDimension = function () { const randomDimension = function () {
return Math.ceil(Math.random() * (max - min) + min); return Math.ceil((Math.random() * (max - min)) + min);
}; };
new Benchmark.Suite('random').add('imagemagick', { new Benchmark.Suite('random').add('imagemagick', {

BIN
test/fixtures/expected/create-rgb.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

BIN
test/fixtures/expected/create-rgba.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

View File

@@ -25,8 +25,8 @@ const fingerprint = function (image, callback) {
let fingerprint = ''; let fingerprint = '';
for (let col = 0; col < 8; col++) { for (let col = 0; col < 8; col++) {
for (let row = 0; row < 8; row++) { for (let row = 0; row < 8; row++) {
const left = data[row * 8 + col]; const left = data[(row * 8) + col];
const right = data[row * 8 + col + 1]; const right = data[(row * 8) + col + 1];
fingerprint = fingerprint + (left < right ? '1' : '0'); fingerprint = fingerprint + (left < right ? '1' : '0');
} }
} }
@@ -84,6 +84,7 @@ module.exports = {
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646 inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
@@ -102,6 +103,7 @@ module.exports = {
outputPng: getPath('output.png'), outputPng: getPath('output.png'),
outputWebP: getPath('output.webp'), outputWebP: getPath('output.webp'),
outputV: getPath('output.v'), outputV: getPath('output.v'),
outputTiff: getPath('output.tiff'),
outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension
// Path for tests requiring human inspection // Path for tests requiring human inspection

BIN
test/fixtures/uncompressed_tiff.tiff vendored Normal file

Binary file not shown.

View File

@@ -191,6 +191,22 @@ describe('Crop', function () {
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
}); });
}); });
it('supports the strategy passed as a string', function (done) {
sharp(fixtures.inputPngWithTransparency)
.resize(320, 80)
.crop('entropy')
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(4, info.channels);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
assert.strictEqual(0, info.cropCalcLeft);
assert.strictEqual(80, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
});
});
}); });
describe('Attention strategy', function () { describe('Attention strategy', function () {
@@ -225,5 +241,21 @@ describe('Crop', function () {
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
}); });
}); });
it('supports the strategy passed as a string', function (done) {
sharp(fixtures.inputPngWithTransparency)
.resize(320, 80)
.crop('attention')
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(4, info.channels);
assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height);
assert.strictEqual(0, info.cropCalcLeft);
assert.strictEqual(80, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
});
});
}); });
}); });

View File

@@ -8,6 +8,7 @@ const fixtures = require('../fixtures');
describe('Interpolators and kernels', function () { describe('Interpolators and kernels', function () {
describe('Reducers', function () { describe('Reducers', function () {
[ [
sharp.kernel.nearest,
sharp.kernel.cubic, sharp.kernel.cubic,
sharp.kernel.lanczos2, sharp.kernel.lanczos2,
sharp.kernel.lanczos3 sharp.kernel.lanczos3

View File

@@ -77,16 +77,58 @@ describe('Input/output', function () {
readable.pipe(pipeline); readable.pipe(pipeline);
}); });
it('Read from Stream and write to Buffer via Promise', function (done) { it('Read from Stream and write to Buffer via Promise resolved with Buffer', function () {
const readable = fs.createReadStream(fixtures.inputJpg);
const pipeline = sharp().resize(1, 1); const pipeline = sharp().resize(1, 1);
pipeline.toBuffer().then(function (data) { fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
assert.strictEqual(true, data.length > 0); return pipeline
done(); .toBuffer({resolveWithObject: false})
}).catch(function (err) { .then(function (data) {
throw err; assert.strictEqual(true, data instanceof Buffer);
}); assert.strictEqual(true, data.length > 0);
readable.pipe(pipeline); });
});
it('Read from Stream and write to Buffer via Promise resolved with Object', function () {
const pipeline = sharp().resize(1, 1);
fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
return pipeline
.toBuffer({resolveWithObject: true})
.then(function (object) {
assert.strictEqual('object', typeof object);
assert.strictEqual('object', typeof object.info);
assert.strictEqual('jpeg', object.info.format);
assert.strictEqual(1, object.info.width);
assert.strictEqual(1, object.info.height);
assert.strictEqual(3, object.info.channels);
assert.strictEqual(true, object.data instanceof Buffer);
assert.strictEqual(true, object.data.length > 0);
});
});
it('Read from File and write to Buffer via Promise resolved with Buffer', function () {
return sharp(fixtures.inputJpg)
.resize(1, 1)
.toBuffer({resolveWithObject: false})
.then(function (data) {
assert.strictEqual(true, data instanceof Buffer);
assert.strictEqual(true, data.length > 0);
});
});
it('Read from File and write to Buffer via Promise resolved with Object', function () {
return sharp(fixtures.inputJpg)
.resize(1, 1)
.toBuffer({resolveWithObject: true})
.then(function (object) {
assert.strictEqual('object', typeof object);
assert.strictEqual('object', typeof object.info);
assert.strictEqual('jpeg', object.info.format);
assert.strictEqual(1, object.info.width);
assert.strictEqual(1, object.info.height);
assert.strictEqual(3, object.info.channels);
assert.strictEqual(true, object.data instanceof Buffer);
assert.strictEqual(true, object.data.length > 0);
});
}); });
it('Read from Stream and write to Stream', function (done) { it('Read from Stream and write to Stream', function (done) {
@@ -819,6 +861,134 @@ describe('Input/output', function () {
}); });
}); });
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);
fs.unlink(fixtures.outputTiff, done);
});
});
it('TIFF deflate compression with hoizontal 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);
fs.unlink(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);
fs.unlink(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);
fs.unlink(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);
fs.unlink(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('Input and output formats match when not forcing', function (done) { it('Input and output formats match when not forcing', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
@@ -1002,7 +1172,7 @@ describe('Input/output', function () {
sharp(fixtures.inputJpg).metadata(function (err, metadata) { sharp(fixtures.inputJpg).metadata(function (err, metadata) {
if (err) throw err; if (err) throw err;
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.limitInputPixels(metadata.width * metadata.height - 1) .limitInputPixels((metadata.width * metadata.height) - 1)
.toBuffer(function (err) { .toBuffer(function (err) {
assert.strictEqual(true, !!err); assert.strictEqual(true, !!err);
done(); done();
@@ -1114,6 +1284,66 @@ describe('Input/output', function () {
}); });
}); });
describe('create new image', function () {
it('RGB', function (done) {
const create = {
width: 10,
height: 20,
channels: 3,
background: { r: 0, g: 255, b: 0 }
};
sharp(null, { create: create })
.jpeg()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(create.width, info.width);
assert.strictEqual(create.height, info.height);
assert.strictEqual(create.channels, info.channels);
assert.strictEqual('jpeg', info.format);
fixtures.assertSimilar(fixtures.expected('create-rgb.jpg'), data, done);
});
});
it('RGBA', function (done) {
const create = {
width: 20,
height: 10,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 }
};
sharp(null, { create: create })
.png()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(create.width, info.width);
assert.strictEqual(create.height, info.height);
assert.strictEqual(create.channels, info.channels);
assert.strictEqual('png', info.format);
fixtures.assertSimilar(fixtures.expected('create-rgba.png'), data, done);
});
});
it('Invalid channels', function () {
const create = {
width: 10,
height: 20,
channels: 2,
background: { r: 0, g: 0, b: 0 }
};
assert.throws(function () {
sharp(null, { create: create });
});
});
it('Missing background', function () {
const create = {
width: 10,
height: 20,
channels: 3
};
assert.throws(function () {
sharp(null, { create: create });
});
});
});
it('Queue length change events', function (done) { it('Queue length change events', function (done) {
let eventCounter = 0; let eventCounter = 0;
const queueListener = function (queueLength) { const queueListener = function (queueLength) {

View File

@@ -187,7 +187,7 @@ describe('Tile', function () {
assert.strictEqual(2225, info.height); assert.strictEqual(2225, info.height);
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
assert.strictEqual('undefined', typeof info.size); assert.strictEqual('undefined', typeof info.size);
assertDeepZoomTiles(directory, 512 + 2 * 16, 13, done); assertDeepZoomTiles(directory, 512 + (2 * 16), 13, done);
}); });
}); });
}); });