Compare commits

...

35 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
Lovell Fuller
142c431745 Release v0.17.2 2017-02-11 10:35:34 +00:00
Lovell Fuller
81f5589411 Add use of 'cc' to improve C++ code style linting 2017-02-11 09:59:23 +00:00
Mark van Seventer
04f5c884a4 Add CLI tools section to installation guide (#691) 2017-01-25 21:34:23 +00:00
Lovell Fuller
d8df503404 Ensure Readable can start flowing after Writable finish #671 2017-01-22 14:03:06 +00:00
Lovell Fuller
d241efcdbe Add changelog entry and credit for #685 2017-01-22 13:58:11 +00:00
Rahul Nanwani
a1b8efe721 Expose WebP alpha quality, lossless and near-lossless output options (#685) 2017-01-19 13:45:32 +00:00
Lovell Fuller
815d076b35 Release v0.17.1 2017-01-15 15:23:49 +00:00
Lovell Fuller
86b4816b3f Allow HTTP-over-HTTPS proxy when d/l pre-compiled deps #679 2017-01-14 11:23:13 +00:00
Lovell Fuller
473055468a Docs: ensure alpha attribute is used for transparency 2017-01-13 21:24:57 +00:00
Brandon Aaron
c6a28db8b1 Packaging: bump zlib to 1.2.10 and png16 to 1.6.28 (#676) 2017-01-10 18:19:23 +00:00
子龙山人
971f567571 Docs: correct alpha attribute in extend background example (#675) 2017-01-09 10:47:02 +00:00
Lovell Fuller
7e2eca3d1e Credit recent new contributors, thank you! 2017-01-05 22:27:30 +00:00
Lovell Fuller
86b0053bf0 Removed preinstall.sh script 2017-01-05 22:21:41 +00:00
Lovell Fuller
cfc4b282f0 Doc refresh for d9b667e 2017-01-05 22:20:33 +00:00
Lovell Fuller
b85d2aa565 Merge branch 'master' of https://github.com/lovell/sharp 2017-01-05 22:17:16 +00:00
Lovell Fuller
70a3d4fb5e Improve error messages for invalid resize parameters
Dependency version bumps and doc refresh
2017-01-05 22:17:04 +00:00
Adam Coyne
d9b667e346 Docs: remove parentheses from sharp.format (#663) 2016-12-31 19:32:18 +00:00
Jérémy Lal
3a1db53d5a Simpler expression for finding vips-cpp libdir (#656)
Fixes: #655
2016-12-18 08:57:50 +00:00
55 changed files with 1287 additions and 812 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

@@ -74,7 +74,7 @@ covers reporting bugs, requesting features and submitting code changes.
### Licence ### Licence
Copyright 2013, 2014, 2015, 2016 Lovell Fuller and contributors. Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -107,7 +107,7 @@
['OS == "linux"', { ['OS == "linux"', {
'defines': [ 'defines': [
# Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible. # Inspect libvips-cpp.so to determine which C++11 ABI version was used and set _GLIBCXX_USE_CXX11_ABI accordingly. This is quite horrible.
'_GLIBCXX_USE_CXX11_ABI=<!(if readelf -Ws "$(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --libs-only-L vips-cpp | cut -c 3- | sed -e "s/[[:space:]]*$//" | sed -e "s/^$/\/usr\/lib/")/libvips-cpp.so" | c++filt | grep -qF __cxx11;then echo "1";else echo "0";fi)' '_GLIBCXX_USE_CXX11_ABI=<!(if readelf -Ws "$(PKG_CONFIG_PATH="<(pkg_config_path)" pkg-config --variable libdir vips-cpp)/libvips-cpp.so" | c++filt | grep -qF __cxx11;then echo "1";else echo "0";fi)'
] ]
}] }]
] ]

View File

@@ -98,11 +98,11 @@ module.exports.download_vips = function () {
} catch (err) {} } catch (err) {}
}); });
}); });
const gotOpt = {}; const gotOpt = {
if (process.env.npm_config_https_proxy) { agent: caw(null, {
// Use the NPM-configured HTTPS proxy protocol: 'https'
gotOpt.agent = caw(process.env.npm_config_https_proxy); })
} };
const url = distBaseUrl + tarFilename; const url = distBaseUrl + tarFilename;
got.stream(url, gotOpt).on('response', function (response) { got.stream(url, gotOpt).on('response', function (response) {
if (response.statusCode !== 200) { if (response.statusCode !== 200) {

View File

@@ -1,6 +1,12 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# extractChannel ### Table of Contents
- [extractChannel](#extractchannel)
- [joinChannel](#joinchannel)
- [bandbool](#bandbool)
## extractChannel
Extract a single channel from a multi-channel image. Extract a single channel from a multi-channel image.
@@ -23,7 +29,7 @@ sharp(input)
Returns **Sharp** Returns **Sharp**
# joinChannel ## joinChannel
Join one or more channels to the image. Join one or more channels to the image.
The meaning of the added channels depends on the output colourspace, set with `toColourspace()`. The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
@@ -46,7 +52,7 @@ For raw pixel input, the `options` object should contain a `raw` attribute, whic
Returns **Sharp** Returns **Sharp**
# bandbool ## bandbool
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.

View File

@@ -1,6 +1,14 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# background ### Table of Contents
- [background](#background)
- [greyscale](#greyscale)
- [grayscale](#grayscale)
- [toColourspace](#tocolourspace)
- [toColorspace](#tocolorspace)
## background
Set the background for the `embed`, `flatten` and `extend` operations. Set the background for the `embed`, `flatten` and `extend` operations.
The default background is `{r: 0, g: 0, b: 0, alpha: 1}`, black without transparency. The default background is `{r: 0, g: 0, b: 0, alpha: 1}`, black without transparency.
@@ -18,7 +26,7 @@ The alpha value is a float between `0` (transparent) and `1` (opaque).
Returns **Sharp** Returns **Sharp**
# greyscale ## greyscale
Convert to 8-bit greyscale; 256 shades of grey. Convert to 8-bit greyscale; 256 shades of grey.
This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results. This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
@@ -33,7 +41,7 @@ An alpha channel may be present, and will be unchanged by the operation.
Returns **Sharp** Returns **Sharp**
# grayscale ## grayscale
Alternative spelling of `greyscale`. Alternative spelling of `greyscale`.
@@ -43,7 +51,7 @@ Alternative spelling of `greyscale`.
Returns **Sharp** Returns **Sharp**
# toColourspace ## toColourspace
Set the output colourspace. Set the output colourspace.
By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
@@ -57,7 +65,7 @@ By default output image will be web-friendly sRGB, with additional channels inte
Returns **Sharp** Returns **Sharp**
# toColorspace ## toColorspace
Alternative spelling of `toColourspace`. Alternative spelling of `toColourspace`.

View File

@@ -1,6 +1,10 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# overlayWith ### Table of Contents
- [overlayWith](#overlaywith)
## overlayWith
Overlay (composite) an image over the processed (resized, extracted etc.) image. Overlay (composite) an image over the processed (resized, extracted etc.) image.
@@ -16,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

@@ -1,6 +1,13 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# Sharp ### Table of Contents
- [Sharp](#sharp)
- [format](#format)
- [versions](#versions)
- [queue](#queue)
## Sharp
**Parameters** **Parameters**
@@ -10,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**
@@ -39,11 +51,26 @@ 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)**
## format ### format
An Object containing nested boolean values representing the available input and output formats/methods. An Object containing nested boolean values representing the available input and output formats/methods.
@@ -55,7 +82,7 @@ console.log(sharp.format());
Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
## versions ### versions
An Object containing the version numbers of libvips and its dependencies. An Object containing the version numbers of libvips and its dependencies.
@@ -65,7 +92,7 @@ An Object containing the version numbers of libvips and its dependencies.
console.log(sharp.versions); console.log(sharp.versions);
``` ```
# queue ## queue
An EventEmitter that emits a `change` event when a task is either: An EventEmitter that emits a `change` event when a task is either:

View File

@@ -1,6 +1,13 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# clone ### Table of Contents
- [clone](#clone)
- [metadata](#metadata)
- [limitInputPixels](#limitinputpixels)
- [sequentialRead](#sequentialread)
## clone
Take a "snapshot" of the Sharp instance, returning a new instance. Take a "snapshot" of the Sharp instance, returning a new instance.
Cloned instances inherit the input of their parent instance. Cloned instances inherit the input of their parent instance.
@@ -19,7 +26,7 @@ readableStream.pipe(pipeline);
Returns **Sharp** Returns **Sharp**
# metadata ## metadata
Fast access to image metadata without decoding any compressed image data. Fast access to image metadata without decoding any compressed image data.
A Promises/A+ promise is returned when `callback` is not provided. A Promises/A+ promise is returned when `callback` is not provided.
@@ -59,7 +66,7 @@ image
Returns **([Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | Sharp)** Returns **([Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | Sharp)**
# limitInputPixels ## limitInputPixels
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.
Assumes image dimensions contained in the input metadata can be trusted. Assumes image dimensions contained in the input metadata can be trusted.
@@ -74,7 +81,7 @@ The default limit is 268402689 (0x3FFF _ 0x3FFF) pixels.
Returns **Sharp** Returns **Sharp**
# sequentialRead ## sequentialRead
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`.
This will reduce memory usage and can improve performance on some systems. This will reduce memory usage and can improve performance on some systems.

View File

@@ -1,6 +1,25 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# rotate ### Table of Contents
- [rotate](#rotate)
- [extract](#extract)
- [flip](#flip)
- [flop](#flop)
- [sharpen](#sharpen)
- [blur](#blur)
- [extend](#extend)
- [flatten](#flatten)
- [trim](#trim)
- [gamma](#gamma)
- [negate](#negate)
- [normalise](#normalise)
- [normalize](#normalize)
- [convolve](#convolve)
- [threshold](#threshold)
- [boolean](#boolean)
## rotate
Rotate the output image by either an explicit angle Rotate the output image by either an explicit angle
or auto-orient based on the EXIF `Orientation` tag. or auto-orient based on the EXIF `Orientation` tag.
@@ -35,7 +54,7 @@ readableStream.pipe(pipeline);
Returns **Sharp** Returns **Sharp**
# extract ## extract
Extract a region of the image. Extract a region of the image.
@@ -75,7 +94,7 @@ sharp(input)
Returns **Sharp** Returns **Sharp**
# flip ## flip
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.
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any. The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
@@ -86,7 +105,7 @@ The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
Returns **Sharp** Returns **Sharp**
# flop ## flop
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.
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any. The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
@@ -97,7 +116,7 @@ The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
Returns **Sharp** Returns **Sharp**
# sharpen ## sharpen
Sharpen the image. Sharpen the image.
When used without parameters, performs a fast, mild sharpen of the output image. When used without parameters, performs a fast, mild sharpen of the output image.
@@ -115,7 +134,7 @@ Separate control over the level of sharpening in "flat" and "jagged" areas is av
Returns **Sharp** Returns **Sharp**
# blur ## blur
Blur the image. Blur the image.
When used without parameters, performs a fast, mild blur of the output image. When used without parameters, performs a fast, mild blur of the output image.
@@ -130,7 +149,7 @@ When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
Returns **Sharp** Returns **Sharp**
# extend ## extend
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.
This operation will always occur after resizing and extraction, if any. This operation will always occur after resizing and extraction, if any.
@@ -150,7 +169,7 @@ This operation will always occur after resizing and extraction, if any.
// to the top, left and right edges and 20 to the bottom edge // to the top, left and right edges and 20 to the bottom edge
sharp(input) sharp(input)
.resize(140) .resize(140)
.background({r: 0, g: 0, b: 0, a: 0}) .background({r: 0, g: 0, b: 0, alpha: 0})
.extend({top: 10, bottom: 20, left: 10, right: 10}) .extend({top: 10, bottom: 20, left: 10, right: 10})
... ...
``` ```
@@ -159,7 +178,7 @@ sharp(input)
Returns **Sharp** Returns **Sharp**
# flatten ## flatten
Merge alpha transparency channel, if any, with `background`. Merge alpha transparency channel, if any, with `background`.
@@ -169,7 +188,7 @@ Merge alpha transparency channel, if any, with `background`.
Returns **Sharp** Returns **Sharp**
# trim ## trim
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.
@@ -182,7 +201,7 @@ Trim "boring" pixels from all edges that contain values within a percentage simi
Returns **Sharp** Returns **Sharp**
# gamma ## gamma
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`
then increasing the encoding (brighten) post-resize at a factor of `gamma`. then increasing the encoding (brighten) post-resize at a factor of `gamma`.
@@ -199,7 +218,7 @@ when applying a gamma correction.
Returns **Sharp** Returns **Sharp**
# negate ## negate
Produce the "negative" of the image. Produce the "negative" of the image.
@@ -209,7 +228,7 @@ Produce the "negative" of the image.
Returns **Sharp** Returns **Sharp**
# normalise ## normalise
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.
@@ -219,7 +238,7 @@ Enhance output image contrast by stretching its luminance to cover the full dyna
Returns **Sharp** Returns **Sharp**
# normalize ## normalize
Alternative spelling of normalise. Alternative spelling of normalise.
@@ -229,7 +248,7 @@ Alternative spelling of normalise.
Returns **Sharp** Returns **Sharp**
# convolve ## convolve
Convolve the image with the specified kernel. Convolve the image with the specified kernel.
@@ -262,7 +281,7 @@ sharp(input)
Returns **Sharp** Returns **Sharp**
# threshold ## threshold
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.
@@ -278,7 +297,7 @@ Any pixel value greather than or equal to the threshold value will be set to 255
Returns **Sharp** Returns **Sharp**
# boolean ## boolean
Perform a bitwise boolean operation with operand image. Perform a bitwise boolean operation with operand image.

View File

@@ -1,6 +1,19 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# toFile ### Table of Contents
- [toFile](#tofile)
- [toBuffer](#tobuffer)
- [withMetadata](#withmetadata)
- [jpeg](#jpeg)
- [png](#png)
- [webp](#webp)
- [tiff](#tiff)
- [raw](#raw)
- [toFormat](#toformat)
- [tile](#tile)
## toFile
Write output image data to a file. Write output image data to a file.
@@ -21,26 +34,28 @@ A Promises/A+ promise is returned when `callback` is not provided.
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)>** when no callback is provided Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)&lt;[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)>** when no callback is provided
# toBuffer ## toBuffer
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.
**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)&lt;[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)&lt;[Buffer](https://nodejs.org/api/buffer.html)>** when no callback is provided
# withMetadata ## withMetadata
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image. Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space. The default behaviour, when `withMetadata` is not used, is to strip all metadata and convert to the device-independent sRGB colour space.
@@ -56,7 +71,7 @@ This will also convert to and add a web-friendly sRGB ICC profile.
Returns **Sharp** Returns **Sharp**
# jpeg ## jpeg
Use these JPEG options for output image. Use these JPEG options for output image.
@@ -77,7 +92,7 @@ Use these JPEG options for output image.
Returns **Sharp** Returns **Sharp**
# png ## png
Use these PNG options for output image. Use these PNG options for output image.
@@ -94,7 +109,7 @@ Use these PNG options for output image.
Returns **Sharp** Returns **Sharp**
# webp ## webp
Use these WebP options for output image. Use these WebP options for output image.
@@ -102,6 +117,9 @@ Use these WebP 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.alphaQuality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality of alpha layer, integer 0-100 (optional, default `100`)
- `options.lossless` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use lossless compression mode (optional, default `false`)
- `options.nearLossless` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use near_lossless compression mode (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force WebP 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 WebP output, otherwise attempt to use input format (optional, default `true`)
@@ -109,7 +127,7 @@ Use these WebP options for output image.
Returns **Sharp** Returns **Sharp**
# tiff ## tiff
Use these TIFF options for output image. Use these TIFF options for output image.
@@ -118,19 +136,21 @@ 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
Returns **Sharp** Returns **Sharp**
# raw ## raw
Force output to be raw, uncompressed uint8 pixel data. Force output to be raw, uncompressed uint8 pixel data.
Returns **Sharp** Returns **Sharp**
# toFormat ## toFormat
Force output to a given format. Force output to a given format.
@@ -144,7 +164,7 @@ Force output to a given format.
Returns **Sharp** Returns **Sharp**
# tile ## tile
Use tile-based deep zoom (image pyramid) output. Use tile-based deep zoom (image pyramid) output.
Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions. Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.

View File

@@ -1,12 +1,23 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# resize ### Table of Contents
- [resize](#resize)
- [crop](#crop)
- [embed](#embed)
- [max](#max)
- [min](#min)
- [ignoreAspectRatio](#ignoreaspectratio)
- [withoutEnlargement](#withoutenlargement)
## resize
Resize image to `width` x `height`. Resize image to `width` x `height`.
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).
@@ -52,7 +63,7 @@ sharp(inputBuffer)
Returns **Sharp** Returns **Sharp**
# crop ## crop
Crop the resized image to the exact size specified, the default behaviour. Crop the resized image to the exact size specified, the default behaviour.
@@ -87,7 +98,7 @@ readableStream.pipe(transformer).pipe(writableStream);
Returns **Sharp** Returns **Sharp**
# embed ## embed
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
then embed on a background of the exact `width` and `height` specified. then embed on a background of the exact `width` and `height` specified.
@@ -100,7 +111,7 @@ contain an alpha channel, even when the input image does not.
```javascript ```javascript
sharp('input.gif') sharp('input.gif')
.resize(200, 300) .resize(200, 300)
.background({r: 0, g: 0, b: 0, a: 0}) .background({r: 0, g: 0, b: 0, alpha: 0})
.embed() .embed()
.toFormat(sharp.format.webp) .toFormat(sharp.format.webp)
.toBuffer(function(err, outputBuffer) { .toBuffer(function(err, outputBuffer) {
@@ -114,7 +125,7 @@ sharp('input.gif')
Returns **Sharp** Returns **Sharp**
# max ## max
Preserving aspect ratio, resize the image to be as large as possible Preserving aspect ratio, resize the image to be as large as possible
while ensuring its dimensions are less than or equal to the `width` and `height` specified. while ensuring its dimensions are less than or equal to the `width` and `height` specified.
@@ -137,7 +148,7 @@ sharp(inputBuffer)
Returns **Sharp** Returns **Sharp**
# min ## min
Preserving aspect ratio, resize the image to be as small as possible Preserving aspect ratio, resize the image to be as small as possible
while ensuring its dimensions are greater than or equal to the `width` and `height` specified. while ensuring its dimensions are greater than or equal to the `width` and `height` specified.
@@ -146,14 +157,14 @@ Both `width` and `height` must be provided via `resize` otherwise the behaviour
Returns **Sharp** Returns **Sharp**
# ignoreAspectRatio ## ignoreAspectRatio
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**
# withoutEnlargement ## withoutEnlargement
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.
This is equivalent to GraphicsMagick's `>` geometry option: This is equivalent to GraphicsMagick's `>` geometry option:

View File

@@ -1,6 +1,13 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> <!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# cache ### Table of Contents
- [cache](#cache)
- [concurrency](#concurrency)
- [counters](#counters)
- [simd](#simd)
## cache
Gets, or when options are provided sets, the limits of _libvips'_ operation cache. Gets, or when options are provided sets, the limits of _libvips'_ operation cache.
Existing entries in the cache will be trimmed after any change in limits. Existing entries in the cache will be trimmed after any change in limits.
@@ -28,7 +35,7 @@ sharp.cache(false);
Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
# concurrency ## concurrency
Gets, or when a concurrency is provided sets, Gets, or when a concurrency is provided sets,
the number of threads _libvips'_ should create to process each image. the number of threads _libvips'_ should create to process each image.
@@ -54,7 +61,7 @@ sharp.concurrency(0); // 4
Returns **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** concurrency Returns **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** concurrency
# counters ## counters
Provides access to internal task counters. Provides access to internal task counters.
@@ -69,7 +76,7 @@ const counters = sharp.counters(); // { queue: 2, process: 4 }
Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
# simd ## simd
Get and set use of SIMD vector unit instructions. Get and set use of SIMD vector unit instructions.
Requires libvips to have been compiled with liborc support. Requires libvips to have been compiled with liborc support.

View File

@@ -4,6 +4,47 @@
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
* Ensure Readable side of Stream can start flowing after Writable side has finished.
[#671](https://github.com/lovell/sharp/issues/671)
[@danhaller](https://github.com/danhaller)
* Expose WebP alpha quality, lossless and near-lossless output options.
[#685](https://github.com/lovell/sharp/pull/685)
[@rnanwani](https://github.com/rnanwani)
#### v0.17.1 - 15<sup>th</sup> January 2017
* Improve error messages for invalid parameters.
[@spikeon](https://github.com/spikeon)
[#644](https://github.com/lovell/sharp/pull/644)
* Simplify expression for finding vips-cpp libdir.
[#656](https://github.com/lovell/sharp/pull/656)
* Allow HTTPS-over-HTTP proxy when downloading pre-compiled dependencies.
[@wangzhiwei1888](https://github.com/wangzhiwei1888)
[#679](https://github.com/lovell/sharp/issues/679)
#### v0.17.0 - 11<sup>th</sup> December 2016 #### v0.17.0 - 11<sup>th</sup> December 2016
* Drop support for versions of Node prior to v4. * Drop support for versions of Node prior to v4.

View File

@@ -94,12 +94,17 @@ the help and code contributions of the following people:
* [Kleis Auke Wolthuizen](https://github.com/kleisauke) * [Kleis Auke Wolthuizen](https://github.com/kleisauke)
* [Matt Hirsch](https://github.com/mhirsch) * [Matt Hirsch](https://github.com/mhirsch)
* [Rahul Nanwani](https://github.com/rnanwani) * [Rahul Nanwani](https://github.com/rnanwani)
* [Matthias Thoemmes](https://github.com/cmtt)
* [Patrick Paskaris](https://github.com/ppaskaris)
* [Jérémy Lal](https://github.com/kapouer)
* [Alice Monday](https://github.com/alice0meta)
* [Kristo Jorgenson](https://github.com/kristojorg)
Thank you! Thank you!
### Licence ### Licence
Copyright 2013, 2014, 2015, 2016 Lovell Fuller and contributors. Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

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,6 +137,10 @@ 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
* [sharp-cli](https://www.npmjs.com/package/sharp-cli)
### Security ### Security
Many users of this module process untrusted, user-supplied images, Many users of this module process untrusted, user-supplied images,

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,
@@ -145,7 +165,12 @@ const Sharp = function (input, options) {
pngCompressionLevel: 6, pngCompressionLevel: 6,
pngAdaptiveFiltering: true, pngAdaptiveFiltering: true,
webpQuality: 80, webpQuality: 80,
webpAlphaQuality: 100,
webpLossless: 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,11 +84,17 @@ 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 */
if (is.buffer(chunk)) { if (is.buffer(chunk)) {
if (this.options.input.buffer.length === 0) {
const that = this;
this.on('finish', function () {
that.streamInFinished = true;
});
}
this.options.input.buffer.push(chunk); this.options.input.buffer.push(chunk);
callback(); callback();
} else { } else {
@@ -72,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.
@@ -108,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();
@@ -121,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.
@@ -156,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()) {
@@ -194,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.
@@ -204,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;
@@ -217,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`.
@@ -225,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

@@ -80,6 +80,21 @@ const inArray = function (val, list) {
return list.indexOf(val) !== -1; return list.indexOf(val) !== -1;
}; };
/**
* Create an Error with a message relating to an invalid parameter.
*
* @param {String} name - parameter name.
* @param {String} expected - description of the type/value/range expected.
* @param {*} actual - the value received.
* @returns {Error} Containing the formatted message.
* @private
*/
const invalidParameterError = function (name, expected, actual) {
return new Error(
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
);
};
module.exports = { module.exports = {
defined: defined, defined: defined,
object: object, object: object,
@@ -90,5 +105,6 @@ module.exports = {
number: number, number: number,
integer: integer, integer: integer,
inRange: inRange, inRange: inRange,
inArray: inArray inArray: inArray,
invalidParameterError: invalidParameterError
}; };

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.
@@ -186,7 +186,7 @@ const blur = function blur (sigma) {
* // to the top, left and right edges and 20 to the bottom edge * // to the top, left and right edges and 20 to the bottom edge
* sharp(input) * sharp(input)
* .resize(140) * .resize(140)
* .background({r: 0, g: 0, b: 0, a: 0}) * .background({r: 0, g: 0, b: 0, alpha: 0})
* .extend({top: 10, bottom: 20, left: 10, right: 10}) * .extend({top: 10, bottom: 20, left: 10, right: 10})
* ... * ...
* *
@@ -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,17 +169,20 @@ 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.
* @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 {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
* @param {Boolean} [options.lossless=false] - use lossless compression mode
* @param {Boolean} [options.nearLossless=false] - use near_lossless compression mode
* @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format * @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
* @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;
@@ -180,18 +190,33 @@ const webp = function webp (options) {
throw new Error('Invalid quality (integer, 1-100) ' + options.quality); throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
} }
} }
if (is.object(options) && is.defined(options.alphaQuality)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 1, 100)) {
this.options.webpAlphaQuality = options.alphaQuality;
} else {
throw new Error('Invalid webp alpha quality (integer, 1-100) ' + options.alphaQuality);
}
}
if (is.object(options) && is.defined(options.lossless)) {
this._setBooleanOption('webpLossless', options.lossless);
}
if (is.object(options) && is.defined(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;
@@ -199,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.
@@ -217,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;
} }
@@ -225,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.
@@ -251,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)) {
@@ -296,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,
@@ -307,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.
@@ -319,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
@@ -362,9 +405,9 @@ const _pipeline = function _pipeline (callback) {
// output=stream // output=stream
if (this._isStreamInput()) { if (this._isStreamInput()) {
// output=stream, input=stream // output=stream, input=stream
this.on('finish', function () { if (this.streamInFinished) {
that._flattenBufferIn(); this._flattenBufferIn();
sharp.pipeline(that.options, function (err, data, info) { sharp.pipeline(this.options, function (err, data, info) {
if (err) { if (err) {
that.emit('error', err); that.emit('error', err);
} else { } else {
@@ -373,7 +416,20 @@ const _pipeline = function _pipeline (callback) {
} }
that.push(null); that.push(null);
}); });
}); } else {
this.on('finish', function () {
that._flattenBufferIn();
sharp.pipeline(that.options, function (err, data, info) {
if (err) {
that.emit('error', err);
} else {
that.emit('info', info);
that.push(data);
}
that.push(null);
});
});
}
} else { } else {
// output=stream, input=file/buffer // output=stream, input=file/buffer
sharp.pipeline(this.options, function (err, data, info) { sharp.pipeline(this.options, function (err, data, info) {
@@ -394,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);
}
} }
}); });
}); });
@@ -406,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,12 +101,12 @@ 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;
} else { } else {
throw new Error('Invalid width (1 to ' + this.constructor.maximum.width + ') ' + width); throw is.invalidParameterError('width', `integer between 1 and ${this.constructor.maximum.width}`, width);
} }
} else { } else {
this.options.width = -1; this.options.width = -1;
@@ -113,7 +115,7 @@ const resize = function resize (width, height, options) {
if (is.integer(height) && is.inRange(height, 1, this.constructor.maximum.height)) { if (is.integer(height) && is.inRange(height, 1, this.constructor.maximum.height)) {
this.options.height = height; this.options.height = height;
} else { } else {
throw new Error('Invalid height (1 to ' + this.constructor.maximum.height + ') ' + height); throw is.invalidParameterError('height', `integer between 1 and ${this.constructor.maximum.height}`, height);
} }
} else { } else {
this.options.height = -1; this.options.height = -1;
@@ -124,7 +126,7 @@ const resize = function resize (width, height, options) {
if (is.string(kernel[options.kernel])) { if (is.string(kernel[options.kernel])) {
this.options.kernel = kernel[options.kernel]; this.options.kernel = kernel[options.kernel];
} else { } else {
throw new Error('Invalid kernel ' + options.kernel); throw is.invalidParameterError('kernel', 'valid kernel name', options.kernel);
} }
} }
// Interpolator // Interpolator
@@ -132,7 +134,7 @@ const resize = function resize (width, height, options) {
if (is.string(interpolator[options.interpolator])) { if (is.string(interpolator[options.interpolator])) {
this.options.interpolator = interpolator[options.interpolator]; this.options.interpolator = interpolator[options.interpolator];
} else { } else {
throw new Error('Invalid interpolator ' + options.interpolator); throw is.invalidParameterError('interpolator', 'valid interpolator name', options.interpolator);
} }
} }
// Centre sampling // Centre sampling
@@ -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 new Error('Unsupported crop ' + 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
@@ -200,7 +205,7 @@ const crop = function crop (crop) {
* @example * @example
* sharp('input.gif') * sharp('input.gif')
* .resize(200, 300) * .resize(200, 300)
* .background({r: 0, g: 0, b: 0, a: 0}) * .background({r: 0, g: 0, b: 0, alpha: 0})
* .embed() * .embed()
* .toFormat(sharp.format.webp) * .toFormat(sharp.format.webp)
* .toBuffer(function(err, outputBuffer) { * .toBuffer(function(err, outputBuffer) {
@@ -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,8 +1,9 @@
{ {
"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.0", "version": "0.17.3",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [ "contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>", "Pierre Inglebert <pierre.inglebert@gmail.com>",
"Jonathan Ong <jonathanrichardong@gmail.com>", "Jonathan Ong <jonathanrichardong@gmail.com>",
@@ -29,11 +30,15 @@
"Kleis Auke Wolthuizen <info@kleisauke.nl>", "Kleis Auke Wolthuizen <info@kleisauke.nl>",
"Matt Hirsch <mhirsch@media.mit.edu>", "Matt Hirsch <mhirsch@media.mit.edu>",
"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>",
"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.*",
"test": "semistandard && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js", "test": "semistandard && cc && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
"test-leak": "./test/leak/leak.sh", "test-leak": "./test/leak/leak.sh",
"test-packaging": "./packaging/test-linux-x64.sh", "test-packaging": "./packaging/test-linux-x64.sh",
"docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md lib/$m.js >docs/api-$m.md; done" "docs": "for m in constructor input resize composite operation colour channel output utility; do documentation build --shallow --format=md lib/$m.js >docs/api-$m.md; done"
@@ -60,24 +65,24 @@
], ],
"dependencies": { "dependencies": {
"caw": "^2.0.0", "caw": "^2.0.0",
"color": "^1.0.2", "color": "^1.0.3",
"got": "^6.6.3", "got": "^6.7.1",
"nan": "^2.4.0", "nan": "^2.5.1",
"semver": "^5.3.0", "semver": "^5.3.0",
"tar": "^2.2.1" "tar": "^2.2.1"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.1.4", "async": "^2.2.0",
"bufferutil": "^1.3.0", "bufferutil": "^3.0.0",
"cross-env": "^3.1.3", "cc": "^1.0.0",
"documentation": "^4.0.0-beta16", "cross-env": "^4.0.0",
"exif-reader": "^1.0.1", "documentation": "^4.0.0-beta.18",
"icc": "^0.0.2", "exif-reader": "^1.0.2",
"icc": "^1.0.0",
"mocha": "^3.2.0", "mocha": "^3.2.0",
"node-cpplint": "^0.4.0", "nyc": "^10.2.0",
"nyc": "^10.0.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",
@@ -91,5 +96,12 @@
"env": [ "env": [
"mocha" "mocha"
] ]
},
"cc": {
"linelength": "120",
"filter": [
"build/include",
"runtime/indentation_namespace"
]
} }
} }

View File

@@ -16,7 +16,7 @@ export CFLAGS="${FLAGS}"
export CXXFLAGS="${FLAGS}" export CXXFLAGS="${FLAGS}"
# Dependency version numbers # Dependency version numbers
VERSION_ZLIB=1.2.8 VERSION_ZLIB=1.2.10
VERSION_FFI=3.2.1 VERSION_FFI=3.2.1
VERSION_GLIB=2.50.1 VERSION_GLIB=2.50.1
VERSION_XML2=2.9.4 VERSION_XML2=2.9.4
@@ -24,7 +24,7 @@ VERSION_GSF=1.14.40
VERSION_EXIF=0.6.21 VERSION_EXIF=0.6.21
VERSION_LCMS2=2.8 VERSION_LCMS2=2.8
VERSION_JPEG=1.5.1 VERSION_JPEG=1.5.1
VERSION_PNG16=1.6.25 VERSION_PNG16=1.6.28
VERSION_WEBP=0.5.1 VERSION_WEBP=0.5.1
VERSION_TIFF=4.0.6 VERSION_TIFF=4.0.6
VERSION_ORC=0.4.26 VERSION_ORC=0.4.26

View File

@@ -1,316 +0,0 @@
#!/bin/sh
# Use of this script is deprecated
echo
echo "WARNING: THIS SCRIPT WILL STOP WORKING AT THE END OF 2016"
echo
echo "WARNING: THIS SCRIPT IS NO LONGER REQUIRED ON MOST 64-BIT LINUX SYSTEMS WHEN USING SHARP V0.12.0+"
echo
echo "See http://sharp.dimens.io/page/install#linux"
echo
echo "If you really, really need this script, it will attempt"
echo "to globally install libvips if not already available."
echo
sleep 5
vips_version_minimum=8.4.2
vips_version_latest_major_minor=8.4
vips_version_latest_patch=2
openslide_version_minimum=3.4.0
openslide_version_latest_major_minor=3.4
openslide_version_latest_patch=1
install_libvips_from_source() {
echo "Compiling libvips $vips_version_latest_major_minor.$vips_version_latest_patch from source"
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major_minor/vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
tar zvxf vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
cd vips-$vips_version_latest_major_minor.$vips_version_latest_patch
CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" ./configure --disable-debug --disable-docs --disable-static --disable-introspection --disable-dependency-tracking --enable-cxx=yes --without-python --without-orc --without-fftw $1
make
make install
cd ..
rm -rf vips-$vips_version_latest_major_minor.$vips_version_latest_patch
rm vips-$vips_version_latest_major_minor.$vips_version_latest_patch.tar.gz
ldconfig
echo "Installed libvips $(PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig pkg-config --modversion vips)"
}
install_libopenslide_from_source() {
echo "Compiling openslide $openslide_version_latest_major_minor.$openslide_version_latest_patch from source"
curl -O -L https://github.com/openslide/openslide/releases/download/v$openslide_version_latest_major_minor.$openslide_version_latest_patch/openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
tar xzvf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
cd openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
PKG_CONFIG_PATH=$pkg_config_path ./configure $1
make
make install
cd ..
rm -rf openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch
rm openslide-$openslide_version_latest_major_minor.$openslide_version_latest_patch.tar.gz
ldconfig
echo "Installed libopenslide $openslide_version_latest_major_minor.$openslide_version_latest_patch"
}
sorry() {
echo "Sorry, I don't yet know how to install lib$1 on $2"
exit 1
}
pkg_config_path="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/lib/pkgconfig"
check_if_library_exists() {
PKG_CONFIG_PATH=$pkg_config_path pkg-config --exists $1
if [ $? -eq 0 ]; then
version_found=$(PKG_CONFIG_PATH=$pkg_config_path pkg-config --modversion $1)
PKG_CONFIG_PATH=$pkg_config_path pkg-config --atleast-version=$2 $1
if [ $? -eq 0 ]; then
# Found suitable version of libvips
echo "Found lib$1 $version_found"
return 1
fi
echo "Found lib$1 $version_found but require $2"
else
echo "Could not find lib$1 using a PKG_CONFIG_PATH of '$pkg_config_path'"
fi
return 0
}
enable_openslide=0
# Is libvips already installed, and is it at least the minimum required version?
if [ $# -eq 1 ]; then
if [ "$1" = "--with-openslide" ]; then
echo "Installing vips with openslide support"
enable_openslide=1
else
echo "Sorry, $1 is not supported. Did you mean --with-openslide?"
exit 1
fi
fi
if ! type pkg-config >/dev/null; then
sorry "vips" "a system without pkg-config"
fi
openslide_exists=0
if [ $enable_openslide -eq 1 ]; then
check_if_library_exists "openslide" "$openslide_version_minimum"
openslide_exists=$?
fi
check_if_library_exists "vips" "$vips_version_minimum"
vips_exists=$?
if [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 1 ]; then
if [ $openslide_exists -eq 1 ]; then
# Check if vips compiled with openslide support
vips_with_openslide=`vips list classes | grep -i opensli`
if [ -z $vips_with_openslide ]; then
echo "Vips compiled without openslide support."
else
exit 0
fi
fi
elif [ $vips_exists -eq 1 ] && [ $enable_openslide -eq 0 ]; then
exit 0
fi
# Verify root/sudo access
if [ "$(id -u)" -ne "0" ]; then
echo "Sorry, I need root/sudo access to continue"
exit 1
fi
# Deprecation warning
if [ "$(arch)" == "x86_64" ]; then
echo "This script is no longer required on most 64-bit Linux systems when using sharp v0.12.0+"
fi
# OS-specific installations of libopenslide follows
# Either openslide does not exist, or vips is installed without openslide support
if [ $enable_openslide -eq 1 ] && [ -z $vips_with_openslide ] && [ $openslide_exists -eq 0 ]; then
if [ -f /etc/debian_version ]; then
# Debian Linux
DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in
jessie|vivid|wily|xenial)
# Debian 8, Ubuntu 15
echo "Installing libopenslide via apt-get"
apt-get install -y libopenslide-dev
;;
trusty|utopic|qiana|rebecca|rafaela|freya)
# Ubuntu 14, Mint 17
echo "Installing libopenslide dependencies via apt-get"
apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
install_libopenslide_from_source
;;
precise|wheezy|maya)
# Debian 7, Ubuntu 12.04, Mint 13
echo "Installing libopenslide dependencies via apt-get"
apt-get install -y automake build-essential curl zlib1g-dev libopenjpeg-dev libpng12-dev libjpeg-dev libtiff5-dev libgdk-pixbuf2.0-dev libxml2-dev libsqlite3-dev libcairo2-dev libglib2.0-dev sqlite3 libsqlite3-dev
install_libopenslide_from_source
;;
*)
# Unsupported Debian-based OS
sorry "openslide" "Debian-based $DISTRO"
;;
esac
elif [ -f /etc/redhat-release ]; then
# Red Hat Linux
RELEASE=$(cat /etc/redhat-release)
echo "Detected Red Hat Linux '$RELEASE'"
case $RELEASE in
"Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
# RHEL/CentOS 7
echo "Installing libopenslide dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
install_libopenslide_from_source "--prefix=/usr"
;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS 6
echo "Installing libopenslide dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y tar curl libpng-devel libjpeg-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel gdk-pixbuf2-devel sqlite-devel cairo-devel glib2-devel
install_libopenslide_from_source "--prefix=/usr"
;;
"Fedora release 21 "*|"Fedora release 22 "*)
# Fedora 21, 22
echo "Installing libopenslide via yum"
yum install -y openslide-devel
;;
*)
# Unsupported RHEL-based OS
sorry "openslide" "$RELEASE"
;;
esac
elif [ -f /etc/os-release ]; then
RELEASE=$(cat /etc/os-release | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.2"*)
echo "Installing libopenslide via zypper"
zypper --gpg-auto-import-keys install -y libopenslide-devel
;;
esac
elif [ -f /etc/SuSE-brand ]; then
RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.1")
echo "Installing libopenslide dependencies via zypper"
zypper --gpg-auto-import-keys install -y --type pattern devel_basis
zypper --gpg-auto-import-keys install -y tar curl libpng16-devel libjpeg-turbo libjpeg8-devel libxml2-devel zlib-devel openjpeg-devel libtiff-devel libgdk_pixbuf-2_0-0 sqlite3-devel cairo-devel glib2-devel
install_libopenslide_from_source
;;
esac
else
# Unsupported OS
sorry "openslide" "$(uname -a)"
fi
fi
# OS-specific installations of libvips follows
if [ -f /etc/debian_version ]; then
# Debian Linux
DISTRO=$(lsb_release -c -s)
echo "Detected Debian Linux '$DISTRO'"
case "$DISTRO" in
jessie|trusty|utopic|vivid|wily|xenial|qiana|rebecca|rafaela|freya)
# Debian 8, Ubuntu 14.04+, Mint 17
echo "Installing libvips dependencies via apt-get"
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
install_libvips_from_source
;;
precise|wheezy|maya)
# Debian 7, Ubuntu 12.04, Mint 13
echo "Installing libvips dependencies via apt-get"
add-apt-repository -y ppa:lyrasis/precise-backports
apt-get update
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev libgsf-1-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
install_libvips_from_source
;;
*)
# Unsupported Debian-based OS
sorry "vips" "Debian-based $DISTRO"
;;
esac
elif [ -f /etc/redhat-release ]; then
# Red Hat Linux
RELEASE=$(cat /etc/redhat-release)
echo "Detected Red Hat Linux '$RELEASE'"
case $RELEASE in
"Red Hat Enterprise Linux release 7."*|"CentOS Linux release 7."*|"Scientific Linux release 7."*)
# RHEL/CentOS 7
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
install_libvips_from_source "--prefix=/usr"
;;
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
# RHEL/CentOS 6
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y tar curl gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms-devel ImageMagick-devel
yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
yum install -y --enablerepo=nux-dextop gobject-introspection-devel
yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
yum install -y --enablerepo=remi libwebp-devel
install_libvips_from_source "--prefix=/usr"
;;
"Fedora"*)
# Fedora 21, 22, 23
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gcc-c++ gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr"
;;
*)
# Unsupported RHEL-based OS
sorry "vips" "$RELEASE"
;;
esac
elif [ -f /etc/system-release ]; then
# Probably Amazon Linux
RELEASE=$(cat /etc/system-release)
case $RELEASE in
"Amazon Linux AMI release 2015.03"|"Amazon Linux AMI release 2015.09")
# Amazon Linux
echo "Detected '$RELEASE'"
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel libgsf-devel lcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr"
;;
*)
# Unsupported Amazon Linux version
sorry "vips" "$RELEASE"
;;
esac
elif [ -f /etc/os-release ]; then
RELEASE=$(cat /etc/os-release | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.2"*)
echo "Installing libvips dependencies via zypper"
zypper --gpg-auto-import-keys install -y --type pattern devel_basis
zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
install_libvips_from_source
;;
esac
elif [ -f /etc/SuSE-brand ]; then
RELEASE=$(cat /etc/SuSE-brand | grep VERSION)
echo "Detected OpenSuse Linux '$RELEASE'"
case $RELEASE in
*"13.1")
echo "Installing libvips dependencies via zypper"
zypper --gpg-auto-import-keys install -y --type pattern devel_basis
zypper --gpg-auto-import-keys install -y tar curl gtk-doc libxml2-devel libjpeg-turbo libjpeg8-devel libpng16-devel libtiff-devel libexif-devel liblcms2-devel ImageMagick-devel gobject-introspection-devel libwebp-devel
install_libvips_from_source
;;
esac
else
# Unsupported OS
sorry "vips" "$(uname -a)"
fi

View File

@@ -1,12 +1,27 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <vector>
#include <node.h> #include <node.h>
#include <node_buffer.h> #include <node_buffer.h>
#include <nan.h>
#include <vips/vips8> #include <vips/vips8>
#include "nan.h"
#include "common.h" #include "common.h"
using vips::VImage; using vips::VImage;
@@ -29,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);
@@ -45,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;
} }
@@ -177,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,
@@ -212,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);
@@ -254,8 +293,7 @@ namespace sharp {
return ( return (
(bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) || (bands == 2 && interpretation == VIPS_INTERPRETATION_B_W) ||
(bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) || (bands == 4 && interpretation != VIPS_INTERPRETATION_CMYK) ||
(bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK) (bands == 5 && interpretation == VIPS_INTERPRETATION_CMYK));
);
} }
/* /*
@@ -378,23 +416,23 @@ namespace sharp {
int top = 0; int top = 0;
// assign only if valid // assign only if valid
if(x >= 0 && x < (inWidth - outWidth)) { if (x >= 0 && x < (inWidth - outWidth)) {
left = x; left = x;
} else if(x >= (inWidth - outWidth)) { } else if (x >= (inWidth - outWidth)) {
left = inWidth - outWidth; left = inWidth - outWidth;
} }
if(y >= 0 && y < (inHeight - outHeight)) { if (y >= 0 && y < (inHeight - outHeight)) {
top = y; top = y;
} else if(y >= (inHeight - outHeight)) { } else if (y >= (inHeight - outHeight)) {
top = inHeight - outHeight; top = inHeight - outHeight;
} }
// the resulting left and top could have been outside the image after calculation from bottom/right edges // the resulting left and top could have been outside the image after calculation from bottom/right edges
if(left < 0) { if (left < 0) {
left = 0; left = 0;
} }
if(top < 0) { if (top < 0) {
top = 0; top = 0;
} }
@@ -421,8 +459,7 @@ namespace sharp {
*/ */
VipsOperationBoolean GetBooleanOperation(std::string const opStr) { VipsOperationBoolean GetBooleanOperation(std::string const opStr) {
return static_cast<VipsOperationBoolean>( return static_cast<VipsOperationBoolean>(
vips_enum_from_nick(nullptr, VIPS_TYPE_OPERATION_BOOLEAN, opStr.data()) vips_enum_from_nick(nullptr, VIPS_TYPE_OPERATION_BOOLEAN, opStr.data()));
);
} }
/* /*
@@ -430,8 +467,7 @@ namespace sharp {
*/ */
VipsInterpretation GetInterpretation(std::string const typeStr) { VipsInterpretation GetInterpretation(std::string const typeStr) {
return static_cast<VipsInterpretation>( return static_cast<VipsInterpretation>(
vips_enum_from_nick(nullptr, VIPS_TYPE_INTERPRETATION, typeStr.data()) vips_enum_from_nick(nullptr, VIPS_TYPE_INTERPRETATION, typeStr.data()));
);
} }
/* /*

View File

@@ -1,14 +1,28 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_COMMON_H_ #ifndef SRC_COMMON_H_
#define SRC_COMMON_H_ #define SRC_COMMON_H_
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <vector>
#include <node.h> #include <node.h>
#include <nan.h>
#include <vips/vips8> #include <vips/vips8>
#include "nan.h"
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 4)) #if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 4))
@@ -38,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),
@@ -45,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
@@ -63,8 +89,7 @@ namespace sharp {
// Create an InputDescriptor instance from a v8::Object describing an input image // Create an InputDescriptor instance from a v8::Object describing an input image
InputDescriptor* CreateInputDescriptor( InputDescriptor* CreateInputDescriptor(
v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> buffersToPersist v8::Handle<v8::Object> input, std::vector<v8::Local<v8::Object>> buffersToPersist);
);
enum class ImageType { enum class ImageType {
JPEG, JPEG,

View File

@@ -1,9 +1,24 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <numeric> #include <numeric>
#include <vector>
#include <node.h> #include <node.h>
#include <nan.h>
#include <vips/vips8> #include <vips/vips8>
#include "nan.h"
#include "common.h" #include "common.h"
#include "metadata.h" #include "metadata.h"
@@ -11,15 +26,14 @@ class MetadataWorker : public Nan::AsyncWorker {
public: public:
MetadataWorker( MetadataWorker(
Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *callback, MetadataBaton *baton,
std::vector<v8::Local<v8::Object>> const buffersToPersist std::vector<v8::Local<v8::Object>> const buffersToPersist)
) : Nan::AsyncWorker(callback), baton(baton), buffersToPersist(buffersToPersist) { : Nan::AsyncWorker(callback), baton(baton), buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index // Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0, std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t { [this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer); SaveToPersistent(index, buffer);
return index + 1; return index + 1;
} });
);
} }
~MetadataWorker() {} ~MetadataWorker() {}
@@ -72,7 +86,7 @@ class MetadataWorker : public Nan::AsyncWorker {
vips_thread_shutdown(); vips_thread_shutdown();
} }
void HandleOKCallback () { void HandleOKCallback() {
using Nan::New; using Nan::New;
using Nan::Set; using Nan::Set;
Nan::HandleScope(); Nan::HandleScope();
@@ -99,14 +113,12 @@ class MetadataWorker : public Nan::AsyncWorker {
if (baton->exifLength > 0) { if (baton->exifLength > 0) {
Set(info, Set(info,
New("exif").ToLocalChecked(), New("exif").ToLocalChecked(),
Nan::NewBuffer(baton->exif, baton->exifLength, sharp::FreeCallback, nullptr).ToLocalChecked() Nan::NewBuffer(baton->exif, baton->exifLength, sharp::FreeCallback, nullptr).ToLocalChecked());
);
} }
if (baton->iccLength > 0) { if (baton->iccLength > 0) {
Set(info, Set(info,
New("icc").ToLocalChecked(), New("icc").ToLocalChecked(),
Nan::NewBuffer(baton->icc, baton->iccLength, sharp::FreeCallback, nullptr).ToLocalChecked() Nan::NewBuffer(baton->icc, baton->iccLength, sharp::FreeCallback, nullptr).ToLocalChecked());
);
} }
argv[1] = info; argv[1] = info;
} }
@@ -116,8 +128,7 @@ class MetadataWorker : public Nan::AsyncWorker {
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t { [this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index); GetFromPersistent(index);
return index + 1; return index + 1;
} });
);
delete baton->input; delete baton->input;
delete baton; delete baton;

View File

@@ -1,8 +1,24 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_METADATA_H_ #ifndef SRC_METADATA_H_
#define SRC_METADATA_H_ #define SRC_METADATA_H_
#include "nan.h" #include <string>
#include "common.h" #include <nan.h>
#include "./common.h"
struct MetadataBaton { struct MetadataBaton {
// Input // Input

View File

@@ -1,7 +1,23 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <vector>
#include <vips/vips8> #include <vips/vips8>
#include "common.h" #include "common.h"
@@ -17,7 +33,7 @@ namespace sharp {
Assumes alpha channels are already premultiplied and will be unpremultiplied after. Assumes alpha channels are already premultiplied and will be unpremultiplied after.
*/ */
VImage Composite(VImage src, VImage dst, const int gravity) { VImage Composite(VImage src, VImage dst, const int gravity) {
if(IsInputValidForComposition(src, dst)) { if (IsInputValidForComposition(src, dst)) {
// Enlarge overlay src, if required // Enlarge overlay src, if required
if (src.width() < dst.width() || src.height() < dst.height()) { if (src.width() < dst.width() || src.height() < dst.height()) {
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity. // Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
@@ -28,8 +44,7 @@ namespace sharp {
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 }; std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
src = src.embed(left, top, dst.width(), dst.height(), VImage::option() src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND) ->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background) ->set("background", background));
);
} }
return CompositeImage(src, dst); return CompositeImage(src, dst);
} }
@@ -38,7 +53,7 @@ namespace sharp {
} }
VImage Composite(VImage src, VImage dst, const int x, const int y) { VImage Composite(VImage src, VImage dst, const int x, const int y) {
if(IsInputValidForComposition(src, dst)) { if (IsInputValidForComposition(src, dst)) {
// Enlarge overlay src, if required // Enlarge overlay src, if required
if (src.width() < dst.width() || src.height() < dst.height()) { if (src.width() < dst.width() || src.height() < dst.height()) {
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity. // Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity.
@@ -49,8 +64,7 @@ namespace sharp {
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 }; std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
src = src.embed(left, top, dst.width(), dst.height(), VImage::option() src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND) ->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background) ->set("background", background));
);
} }
return CompositeImage(src, dst); return CompositeImage(src, dst);
} }
@@ -145,12 +159,11 @@ namespace sharp {
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 }; std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
mask = mask.embed(left, top, dst.width(), dst.height(), VImage::option() mask = mask.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND) ->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background) ->set("background", background));
);
} }
// we use the mask alpha if it has alpha // we use the mask alpha if it has alpha
if(maskHasAlpha) { if (maskHasAlpha) {
mask = mask.extract_band(mask.bands() - 1, VImage::option()->set("n", 1));; mask = mask.extract_band(mask.bands() - 1, VImage::option()->set("n", 1));;
} }
@@ -284,8 +297,8 @@ namespace sharp {
colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB; colourspaceBeforeSharpen = VIPS_INTERPRETATION_sRGB;
} }
return image.sharpen( return image.sharpen(
VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged) VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged))
).colourspace(colourspaceBeforeSharpen); .colourspace(colourspaceBeforeSharpen);
} }
} }
@@ -415,12 +428,11 @@ namespace sharp {
->set("tile_height", 10) ->set("tile_height", 10)
->set("max_tiles", static_cast<int>(round(1.0 + need_lines / 10.0))) ->set("max_tiles", static_cast<int>(round(1.0 + need_lines / 10.0)))
->set("access", VIPS_ACCESS_SEQUENTIAL) ->set("access", VIPS_ACCESS_SEQUENTIAL)
->set("threaded", TRUE) ->set("threaded", TRUE));
);
} }
VImage Threshold(VImage image, double const threshold, bool const thresholdGrayscale) { VImage Threshold(VImage image, double const threshold, bool const thresholdGrayscale) {
if(!thresholdGrayscale) { if (!thresholdGrayscale) {
return image >= threshold; return image >= threshold;
} }
return image.colourspace(VIPS_INTERPRETATION_B_W) >= threshold; return image.colourspace(VIPS_INTERPRETATION_B_W) >= threshold;
@@ -485,7 +497,7 @@ namespace sharp {
int width = right - left; int width = right - left;
int height = bottom - top; int height = bottom - top;
if(width <= 0 || height <= 0) { if (width <= 0 || height <= 0) {
throw VError("Unexpected error while trimming. Try to lower the tolerance"); throw VError("Unexpected error while trimming. Try to lower the tolerance");
} }

View File

@@ -1,3 +1,17 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_OPERATIONS_H_ #ifndef SRC_OPERATIONS_H_
#define SRC_OPERATIONS_H_ #define SRC_OPERATIONS_H_
@@ -78,8 +92,7 @@ namespace sharp {
Calculate crop area based on given strategy (Entropy, Attention) Calculate crop area based on given strategy (Entropy, Attention)
*/ */
std::tuple<int, int> Crop( std::tuple<int, int> Crop(
VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy);
);
/* /*
Insert a tile cache to prevent over-computation of any previous operations in the pipeline Insert a tile cache to prevent over-computation of any previous operations in the pipeline

View File

@@ -1,15 +1,31 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <tuple> #include <map>
#include <utility>
#include <memory> #include <memory>
#include <numeric> #include <numeric>
#include <map> #include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <vips/vips8> #include <vips/vips8>
#include <node.h> #include <node.h>
#include <nan.h>
#include "nan.h"
#include "common.h" #include "common.h"
#include "operations.h" #include "operations.h"
#include "pipeline.h" #include "pipeline.h"
@@ -18,15 +34,14 @@ class PipelineWorker : public Nan::AsyncWorker {
public: public:
PipelineWorker( PipelineWorker(
Nan::Callback *callback, PipelineBaton *baton, Nan::Callback *queueListener, Nan::Callback *callback, PipelineBaton *baton, Nan::Callback *queueListener,
std::vector<v8::Local<v8::Object>> const buffersToPersist std::vector<v8::Local<v8::Object>> const buffersToPersist)
) : Nan::AsyncWorker(callback), baton(baton), queueListener(queueListener), buffersToPersist(buffersToPersist) { : Nan::AsyncWorker(callback), baton(baton), queueListener(queueListener), buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index // Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0, std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t { [this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer); SaveToPersistent(index, buffer);
return index + 1; return index + 1;
} });
);
} }
~PipelineWorker() {} ~PipelineWorker() {}
@@ -84,7 +99,7 @@ class PipelineWorker : public Nan::AsyncWorker {
} }
// Trim // Trim
if(baton->trimTolerance != 0) { if (baton->trimTolerance != 0) {
image = sharp::Trim(image, baton->trimTolerance); image = sharp::Trim(image, baton->trimTolerance);
} }
@@ -277,8 +292,7 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.icc_transform( image = image.icc_transform(
const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option() const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option()
->set("embedded", TRUE) ->set("embedded", TRUE)
->set("intent", VIPS_INTENT_PERCEPTUAL) ->set("intent", VIPS_INTENT_PERCEPTUAL));
);
} catch(...) { } catch(...) {
// Ignore failure of embedded profile // Ignore failure of embedded profile
} }
@@ -286,8 +300,7 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.icc_transform( image = image.icc_transform(
const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option() const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option()
->set("input_profile", profileMap[VIPS_INTERPRETATION_CMYK].data()) ->set("input_profile", profileMap[VIPS_INTERPRETATION_CMYK].data())
->set("intent", VIPS_INTENT_PERCEPTUAL) ->set("intent", VIPS_INTENT_PERCEPTUAL));
);
} }
// Flatten image to remove alpha channel // Flatten image to remove alpha channel
@@ -301,8 +314,7 @@ class PipelineWorker : public Nan::AsyncWorker {
baton->background[2] * multiplier baton->background[2] * multiplier
}; };
image = image.flatten(VImage::option() image = image.flatten(VImage::option()
->set("background", background) ->set("background", background));
);
} }
// Negate the colours in the image // Negate the colours in the image
@@ -325,8 +337,7 @@ class PipelineWorker : public Nan::AsyncWorker {
if (hasOverlay && !HasAlpha(image)) { if (hasOverlay && !HasAlpha(image)) {
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0; double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
image = image.bandjoin( image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier) VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
);
} }
bool const shouldShrink = xshrink > 1 || yshrink > 1; bool const shouldShrink = xshrink > 1 || yshrink > 1;
@@ -380,22 +391,22 @@ class PipelineWorker : public Nan::AsyncWorker {
// Perform kernel-based reduction // Perform kernel-based reduction
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 (
if (kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 && kernel != VIPS_KERNEL_LANCZOS3) { 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) {
image = image.reducev(1.0 / yresidual, VImage::option() image = image.reducev(1.0 / yresidual, VImage::option()
->set("kernel", kernel) ->set("kernel", kernel)
->set("centre", baton->centreSampling) ->set("centre", baton->centreSampling));
);
} }
if (xresidual < 1.0) { if (xresidual < 1.0) {
image = image.reduceh(1.0 / xresidual, VImage::option() image = image.reduceh(1.0 / xresidual, VImage::option()
->set("kernel", kernel) ->set("kernel", kernel)
->set("centre", baton->centreSampling) ->set("centre", baton->centreSampling));
);
} }
} }
// Perform affine enlargement // Perform affine enlargement
@@ -403,13 +414,11 @@ class PipelineWorker : public Nan::AsyncWorker {
vips::VInterpolate interpolator = vips::VInterpolate::new_from_name(baton->interpolator.data()); vips::VInterpolate interpolator = vips::VInterpolate::new_from_name(baton->interpolator.data());
if (yresidual > 1.0) { if (yresidual > 1.0) {
image = image.affine({1.0, 0.0, 0.0, yresidual}, VImage::option() image = image.affine({1.0, 0.0, 0.0, yresidual}, VImage::option()
->set("interpolate", interpolator) ->set("interpolate", interpolator));
);
} }
if (xresidual > 1.0) { if (xresidual > 1.0) {
image = image.affine({xresidual, 0.0, 0.0, 1.0}, VImage::option() image = image.affine({xresidual, 0.0, 0.0, 1.0}, VImage::option()
->set("interpolate", interpolator) ->set("interpolate", interpolator));
);
} }
} }
} }
@@ -433,13 +442,12 @@ class PipelineWorker : public Nan::AsyncWorker {
} }
// Join additional color channels to the image // Join additional color channels to the image
if(baton->joinChannelIn.size() > 0) { if (baton->joinChannelIn.size() > 0) {
VImage joinImage; VImage joinImage;
ImageType joinImageType = ImageType::UNKNOWN; ImageType joinImageType = ImageType::UNKNOWN;
for(unsigned int i = 0; i < baton->joinChannelIn.size(); i++) { for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i], baton->accessMethod); std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i], baton->accessMethod);
image = image.bandjoin(joinImage); image = image.bandjoin(joinImage);
} }
image = image.copy(VImage::option()->set("interpretation", baton->colourspace)); image = image.copy(VImage::option()->set("interpretation", baton->colourspace));
@@ -463,8 +471,8 @@ class PipelineWorker : public Nan::AsyncWorker {
background = { multiplier * ( background = { multiplier * (
0.2126 * baton->background[0] + 0.2126 * baton->background[0] +
0.7152 * baton->background[1] + 0.7152 * baton->background[1] +
0.0722 * baton->background[2] 0.0722 * baton->background[2])
)}; };
} }
// Add alpha channel to background colour // Add alpha channel to background colour
if (baton->background[3] < 255.0 || HasAlpha(image)) { if (baton->background[3] < 255.0 || HasAlpha(image)) {
@@ -475,16 +483,14 @@ class PipelineWorker : public Nan::AsyncWorker {
// Add non-transparent alpha channel, if required // Add non-transparent alpha channel, if required
if (baton->background[3] < 255.0 && !HasAlpha(image)) { if (baton->background[3] < 255.0 && !HasAlpha(image)) {
image = image.bandjoin( image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier) VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
);
} }
// Embed // Embed
int left = static_cast<int>(round((baton->width - image.width()) / 2)); int left = static_cast<int>(round((baton->width - image.width()) / 2));
int top = static_cast<int>(round((baton->height - image.height()) / 2)); int top = static_cast<int>(round((baton->height - image.height()) / 2));
image = image.embed(left, top, baton->width, baton->height, VImage::option() image = image.embed(left, top, baton->width, baton->height, VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND) ->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background) ->set("background", background));
);
} else if (baton->canvas != Canvas::IGNORE_ASPECT) { } else if (baton->canvas != Canvas::IGNORE_ASPECT) {
// Crop/max/min // Crop/max/min
int left; int left;
@@ -492,8 +498,7 @@ class PipelineWorker : public Nan::AsyncWorker {
if (baton->crop < 9) { if (baton->crop < 9) {
// Gravity-based crop // Gravity-based crop
std::tie(left, top) = sharp::CalculateCrop( std::tie(left, top) = sharp::CalculateCrop(
image.width(), image.height(), baton->width, baton->height, baton->crop image.width(), image.height(), baton->width, baton->height, baton->crop);
);
} else if (baton->crop == 16) { } else if (baton->crop == 16) {
// Entropy-based crop // Entropy-based crop
std::tie(left, top) = sharp::Crop(image, baton->width, baton->height, sharp::EntropyStrategy()); std::tie(left, top) = sharp::Crop(image, baton->width, baton->height, sharp::EntropyStrategy());
@@ -512,8 +517,7 @@ class PipelineWorker : public Nan::AsyncWorker {
// Post extraction // Post extraction
if (baton->topOffsetPost != -1) { if (baton->topOffsetPost != -1) {
image = image.extract_area( image = image.extract_area(
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost);
);
} }
// Extend edges // Extend edges
@@ -533,8 +537,8 @@ class PipelineWorker : public Nan::AsyncWorker {
background = { multiplier * ( background = { multiplier * (
0.2126 * baton->background[0] + 0.2126 * baton->background[0] +
0.7152 * baton->background[1] + 0.7152 * baton->background[1] +
0.0722 * baton->background[2] 0.0722 * baton->background[2])
)}; };
} }
// Add alpha channel to background colour // Add alpha channel to background colour
if (baton->background[3] < 255.0 || HasAlpha(image)) { if (baton->background[3] < 255.0 || HasAlpha(image)) {
@@ -545,8 +549,7 @@ class PipelineWorker : public Nan::AsyncWorker {
// Add non-transparent alpha channel, if required // Add non-transparent alpha channel, if required
if (baton->background[3] < 255.0 && !HasAlpha(image)) { if (baton->background[3] < 255.0 && !HasAlpha(image)) {
image = image.bandjoin( image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier) VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
);
} }
// Embed // Embed
baton->width = image.width() + baton->extendLeft + baton->extendRight; baton->width = image.width() + baton->extendLeft + baton->extendRight;
@@ -571,8 +574,7 @@ class PipelineWorker : public Nan::AsyncWorker {
image = sharp::Convolve(image, image = sharp::Convolve(image,
baton->convKernelWidth, baton->convKernelHeight, baton->convKernelWidth, baton->convKernelHeight,
baton->convKernelScale, baton->convKernelOffset, baton->convKernelScale, baton->convKernelOffset,
baton->convKernel baton->convKernel);
);
} }
// Sharpen // Sharpen
@@ -606,17 +608,13 @@ class PipelineWorker : public Nan::AsyncWorker {
// the overlayX/YOffsets will now be used to CalculateCrop for extract_area // the overlayX/YOffsets will now be used to CalculateCrop for extract_area
std::tie(left, top) = sharp::CalculateCrop( std::tie(left, top) = sharp::CalculateCrop(
overlayImage.width(), overlayImage.height(), image.width(), image.height(), overlayImage.width(), overlayImage.height(), image.width(), image.height(),
baton->overlayXOffset, baton->overlayYOffset baton->overlayXOffset, baton->overlayYOffset);
);
} else { } else {
// the overlayGravity will now be used to CalculateCrop for extract_area // the overlayGravity will now be used to CalculateCrop for extract_area
std::tie(left, top) = sharp::CalculateCrop( std::tie(left, top) = sharp::CalculateCrop(
overlayImage.width(), overlayImage.height(), image.width(), image.height(), baton->overlayGravity overlayImage.width(), overlayImage.height(), image.width(), image.height(), baton->overlayGravity);
);
} }
overlayImage = overlayImage.extract_area( overlayImage = overlayImage.extract_area(left, top, image.width(), image.height());
left, top, image.width(), image.height()
);
} }
// the overlayGravity was used for extract_area, therefore set it back to its default value of 0 // the overlayGravity was used for extract_area, therefore set it back to its default value of 0
baton->overlayGravity = 0; baton->overlayGravity = 0;
@@ -629,15 +627,13 @@ class PipelineWorker : public Nan::AsyncWorker {
if (!HasAlpha(overlayImage)) { if (!HasAlpha(overlayImage)) {
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0; double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
overlayImage = overlayImage.bandjoin( overlayImage = overlayImage.bandjoin(
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier) VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
);
} }
// Ensure image has alpha channel // Ensure image has alpha channel
if (!HasAlpha(image)) { if (!HasAlpha(image)) {
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0; double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
image = image.bandjoin( image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier) VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
);
} }
// Ensure overlay is premultiplied sRGB // Ensure overlay is premultiplied sRGB
overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB).premultiply(); overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB).premultiply();
@@ -686,8 +682,8 @@ class PipelineWorker : public Nan::AsyncWorker {
} }
// Extract an image channel (aka vips band) // Extract an image channel (aka vips band)
if(baton->extractChannel > -1) { if (baton->extractChannel > -1) {
if(baton->extractChannel >= image.bands()) { if (baton->extractChannel >= image.bands()) {
(baton->err).append("Cannot extract channel from image. Too few channels in image."); (baton->err).append("Cannot extract channel from image. Too few channels in image.");
return Error(); return Error();
} }
@@ -701,12 +697,9 @@ class PipelineWorker : public Nan::AsyncWorker {
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess // Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation())); image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
// Transform colours from embedded profile to output profile // Transform colours from embedded profile to output profile
if (baton->withMetadata && if (baton->withMetadata && sharp::HasProfile(image) && profileMap[baton->colourspace] != std::string()) {
sharp::HasProfile(image) &&
profileMap[baton->colourspace] != std::string()) {
image = image.icc_transform(const_cast<char*>(profileMap[baton->colourspace].data()), image = image.icc_transform(const_cast<char*>(profileMap[baton->colourspace].data()),
VImage::option()->set("embedded", TRUE) VImage::option()->set("embedded", TRUE));
);
} }
} }
@@ -732,14 +725,13 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("trellis_quant", baton->jpegTrellisQuantisation) ->set("trellis_quant", baton->jpegTrellisQuantisation)
->set("overshoot_deringing", baton->jpegOvershootDeringing) ->set("overshoot_deringing", baton->jpegOvershootDeringing)
->set("optimize_scans", baton->jpegOptimiseScans) ->set("optimize_scans", baton->jpegOptimiseScans)
->set("optimize_coding", TRUE) ->set("optimize_coding", TRUE)));
));
baton->bufferOut = static_cast<char*>(area->data); baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length; baton->bufferOutLength = area->length;
area->free_fn = nullptr; area->free_fn = nullptr;
vips_area_unref(area); vips_area_unref(area);
baton->formatOut = "jpeg"; baton->formatOut = "jpeg";
if(baton->colourspace == VIPS_INTERPRETATION_CMYK) { if (baton->colourspace == VIPS_INTERPRETATION_CMYK) {
baton->channels = std::min(baton->channels, 4); baton->channels = std::min(baton->channels, 4);
} else { } else {
baton->channels = std::min(baton->channels, 3); baton->channels = std::min(baton->channels, 3);
@@ -754,9 +746,7 @@ class PipelineWorker : public Nan::AsyncWorker {
VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option() VipsArea *area = VIPS_AREA(image.pngsave_buffer(VImage::option()
->set("interlace", baton->pngProgressive) ->set("interlace", baton->pngProgressive)
->set("compression", baton->pngCompressionLevel) ->set("compression", baton->pngCompressionLevel)
->set("filter", baton->pngAdaptiveFiltering ? ->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE)));
VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE )
));
baton->bufferOut = static_cast<char*>(area->data); baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length; baton->bufferOutLength = area->length;
area->free_fn = nullptr; area->free_fn = nullptr;
@@ -767,7 +757,9 @@ class PipelineWorker : public Nan::AsyncWorker {
VipsArea *area = VIPS_AREA(image.webpsave_buffer(VImage::option() VipsArea *area = VIPS_AREA(image.webpsave_buffer(VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata)
->set("Q", baton->webpQuality) ->set("Q", baton->webpQuality)
)); ->set("lossless", baton->webpLossless)
->set("near_lossless", baton->webpNearLossless)
->set("alpha_q", baton->webpAlphaQuality)));
baton->bufferOut = static_cast<char*>(area->data); baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length; baton->bufferOutLength = area->length;
area->free_fn = nullptr; area->free_fn = nullptr;
@@ -821,8 +813,7 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("trellis_quant", baton->jpegTrellisQuantisation) ->set("trellis_quant", baton->jpegTrellisQuantisation)
->set("overshoot_deringing", baton->jpegOvershootDeringing) ->set("overshoot_deringing", baton->jpegOvershootDeringing)
->set("optimize_scans", baton->jpegOptimiseScans) ->set("optimize_scans", baton->jpegOptimiseScans)
->set("optimize_coding", TRUE) ->set("optimize_coding", TRUE));
);
baton->formatOut = "jpeg"; baton->formatOut = "jpeg";
baton->channels = std::min(baton->channels, 3); baton->channels = std::min(baton->channels, 3);
} else if (baton->formatOut == "png" || isPng || (matchInput && } else if (baton->formatOut == "png" || isPng || (matchInput &&
@@ -835,24 +826,28 @@ class PipelineWorker : public Nan::AsyncWorker {
image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option() image.pngsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("interlace", baton->pngProgressive) ->set("interlace", baton->pngProgressive)
->set("compression", baton->pngCompressionLevel) ->set("compression", baton->pngCompressionLevel)
->set("filter", baton->pngAdaptiveFiltering ? ->set("filter", baton->pngAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE));
VIPS_FOREIGN_PNG_FILTER_ALL : VIPS_FOREIGN_PNG_FILTER_NONE )
);
baton->formatOut = "png"; baton->formatOut = "png";
} else if (baton->formatOut == "webp" || isWebp || (matchInput && inputImageType == ImageType::WEBP)) { } else if (baton->formatOut == "webp" || isWebp || (matchInput && inputImageType == ImageType::WEBP)) {
// Write WEBP to file // Write WEBP to file
image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option() image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata)
->set("Q", baton->webpQuality) ->set("Q", baton->webpQuality)
); ->set("lossless", baton->webpLossless)
->set("near_lossless", baton->webpNearLossless)
->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) {
@@ -870,7 +865,10 @@ class PipelineWorker : public Nan::AsyncWorker {
suffix = AssembleSuffixString(".png", options); suffix = AssembleSuffixString(".png", options);
} else if (baton->tileFormat == "webp") { } else if (baton->tileFormat == "webp") {
std::vector<std::pair<std::string, std::string>> options { std::vector<std::pair<std::string, std::string>> options {
{"Q", std::to_string(baton->webpQuality)} {"Q", std::to_string(baton->webpQuality)},
{"alpha_q", std::to_string(baton->webpAlphaQuality)},
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"}
}; };
suffix = AssembleSuffixString(".webp", options); suffix = AssembleSuffixString(".webp", options);
} else { } else {
@@ -895,14 +893,12 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("overlap", baton->tileOverlap) ->set("overlap", baton->tileOverlap)
->set("container", baton->tileContainer) ->set("container", baton->tileContainer)
->set("layout", baton->tileLayout) ->set("layout", baton->tileLayout)
->set("suffix", const_cast<char*>(suffix.data())) ->set("suffix", const_cast<char*>(suffix.data())));
);
baton->formatOut = "dz"; baton->formatOut = "dz";
} else if (baton->formatOut == "v" || isV || (matchInput && inputImageType == ImageType::VIPS)) { } else if (baton->formatOut == "v" || isV || (matchInput && inputImageType == ImageType::VIPS)) {
// Write V to file // Write V to file
image.vipssave(const_cast<char*>(baton->fileOut.data()), VImage::option() image.vipssave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata));
);
baton->formatOut = "v"; baton->formatOut = "v";
} else { } else {
// Unsupported output format // Unsupported output format
@@ -918,7 +914,7 @@ class PipelineWorker : public Nan::AsyncWorker {
vips_thread_shutdown(); vips_thread_shutdown();
} }
void HandleOKCallback () { void HandleOKCallback() {
using Nan::New; using Nan::New;
using Nan::Set; using Nan::Set;
Nan::HandleScope(); Nan::HandleScope();
@@ -952,8 +948,8 @@ class PipelineWorker : public Nan::AsyncWorker {
if (baton->bufferOutLength > 0) { if (baton->bufferOutLength > 0) {
// Pass ownership of output data to Buffer instance // Pass ownership of output data to Buffer instance
argv[1] = Nan::NewBuffer( argv[1] = Nan::NewBuffer(
static_cast<char*>(baton->bufferOut), baton->bufferOutLength, sharp::FreeCallback, nullptr static_cast<char*>(baton->bufferOut), baton->bufferOutLength, sharp::FreeCallback, nullptr)
).ToLocalChecked(); .ToLocalChecked();
// Add buffer size to info // Add buffer size to info
Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->bufferOutLength))); Set(info, New("size").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->bufferOutLength)));
argv[2] = info; argv[2] = info;
@@ -972,16 +968,14 @@ class PipelineWorker : public Nan::AsyncWorker {
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t { [this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index); GetFromPersistent(index);
return index + 1; return index + 1;
} });
);
delete baton->input; delete baton->input;
delete baton->overlay; delete baton->overlay;
delete baton->boolean; delete baton->boolean;
for_each(baton->joinChannelIn.begin(), baton->joinChannelIn.end(), for_each(baton->joinChannelIn.begin(), baton->joinChannelIn.end(),
[this](sharp::InputDescriptor *joinChannelIn) { [this](sharp::InputDescriptor *joinChannelIn) {
delete joinChannelIn; delete joinChannelIn;
} });
);
delete baton; delete baton;
// Decrement processing task counter // Decrement processing task counter
@@ -1012,7 +1006,7 @@ class PipelineWorker : public Nan::AsyncWorker {
bool flip = FALSE; bool flip = FALSE;
bool flop = FALSE; bool flop = FALSE;
if (angle == -1) { if (angle == -1) {
switch(sharp::ExifOrientation(image)) { switch (sharp::ExifOrientation(image)) {
case 6: rotate = VIPS_ANGLE_D90; break; case 6: rotate = VIPS_ANGLE_D90; break;
case 3: rotate = VIPS_ANGLE_D180; break; case 3: rotate = VIPS_ANGLE_D180; break;
case 8: rotate = VIPS_ANGLE_D270; break; case 8: rotate = VIPS_ANGLE_D270; break;
@@ -1114,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")) {
@@ -1132,12 +1126,12 @@ NAN_METHOD(pipeline) {
baton->interpolator = AttrAsStr(options, "interpolator"); baton->interpolator = AttrAsStr(options, "interpolator");
baton->centreSampling = AttrTo<bool>(options, "centreSampling"); baton->centreSampling = AttrTo<bool>(options, "centreSampling");
// Join Channel Options // Join Channel Options
if(HasAttr(options, "joinChannelIn")) { if (HasAttr(options, "joinChannelIn")) {
v8::Local<v8::Object> joinChannelObject = Nan::Get(options, Nan::New("joinChannelIn").ToLocalChecked()) v8::Local<v8::Object> joinChannelObject = Nan::Get(options, Nan::New("joinChannelIn").ToLocalChecked())
.ToLocalChecked().As<v8::Object>(); .ToLocalChecked().As<v8::Object>();
v8::Local<v8::Array> joinChannelArray = joinChannelObject.As<v8::Array>(); v8::Local<v8::Array> joinChannelArray = joinChannelObject.As<v8::Array>();
int joinChannelArrayLength = AttrTo<int32_t>(joinChannelObject, "length"); int joinChannelArrayLength = AttrTo<int32_t>(joinChannelObject, "length");
for(int i = 0; i < joinChannelArrayLength; i++) { for (int i = 0; i < joinChannelArrayLength; i++) {
baton->joinChannelIn.push_back( baton->joinChannelIn.push_back(
CreateInputDescriptor( CreateInputDescriptor(
Nan::Get(joinChannelArray, i).ToLocalChecked().As<v8::Object>(), Nan::Get(joinChannelArray, i).ToLocalChecked().As<v8::Object>(),
@@ -1154,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");
@@ -1209,7 +1200,18 @@ NAN_METHOD(pipeline) {
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel"); baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering"); baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality"); baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
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");
@@ -1228,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

@@ -1,12 +1,28 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_PIPELINE_H_ #ifndef SRC_PIPELINE_H_
#define SRC_PIPELINE_H_ #define SRC_PIPELINE_H_
#include <memory> #include <memory>
#include <string>
#include <vector>
#include <nan.h>
#include <vips/vips8> #include <vips/vips8>
#include "nan.h" #include "./common.h"
#include "common.h"
NAN_METHOD(pipeline); NAN_METHOD(pipeline);
@@ -84,7 +100,12 @@ struct PipelineBaton {
int pngCompressionLevel; int pngCompressionLevel;
bool pngAdaptiveFiltering; bool pngAdaptiveFiltering;
int webpQuality; int webpQuality;
int webpAlphaQuality;
bool webpNearLossless;
bool webpLossless;
int tiffQuality; int tiffQuality;
VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor;
std::string err; std::string err;
bool withMetadata; bool withMetadata;
int withMetadataOrientation; int withMetadataOrientation;
@@ -153,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

@@ -1,7 +1,20 @@
#include <node.h> // Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
#include <vips/vips8> //
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "nan.h" #include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h" #include "common.h"
#include "metadata.h" #include "metadata.h"

View File

@@ -1,10 +1,25 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cmath> #include <cmath>
#include <string>
#include <node.h> #include <node.h>
#include <nan.h>
#include <vips/vips8> #include <vips/vips8>
#include <vips/vector.h> #include <vips/vector.h>
#include "nan.h"
#include "common.h" #include "common.h"
#include "operations.h" #include "operations.h"
#include "utilities.h" #include "utilities.h"
@@ -45,14 +60,11 @@ NAN_METHOD(cache) {
// Get memory stats // Get memory stats
Local<Object> memory = New<Object>(); Local<Object> memory = New<Object>();
Set(memory, New("current").ToLocalChecked(), Set(memory, New("current").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576))) New<Integer>(static_cast<int>(round(vips_tracked_get_mem() / 1048576))));
);
Set(memory, New("high").ToLocalChecked(), Set(memory, New("high").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576))) New<Integer>(static_cast<int>(round(vips_tracked_get_mem_highwater() / 1048576))));
);
Set(memory, New("max").ToLocalChecked(), Set(memory, New("max").ToLocalChecked(),
New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576))) New<Integer>(static_cast<int>(round(vips_cache_get_max_mem() / 1048576))));
);
// Get file stats // Get file stats
Local<Object> files = New<Object>(); Local<Object> files = New<Object>();
Set(files, New("current").ToLocalChecked(), New<Integer>(vips_tracked_get_files())); Set(files, New("current").ToLocalChecked(), New<Integer>(vips_tracked_get_files()));

View File

@@ -1,7 +1,21 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_UTILITIES_H_ #ifndef SRC_UTILITIES_H_
#define SRC_UTILITIES_H_ #define SRC_UTILITIES_H_
#include "nan.h" #include <nan.h>
NAN_METHOD(cache); NAN_METHOD(cache);
NAN_METHOD(concurrency); NAN_METHOD(concurrency);

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

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

@@ -1,48 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const cpplint = require('node-cpplint/lib/');
describe('cpplint', function () {
// Ignore cpplint failures, possibly newline-related, on Windows
if (process.platform !== 'win32') {
// List C++ source files
fs.readdirSync(path.join(__dirname, '..', '..', 'src')).filter(function (source) {
return source !== 'libvips';
}).forEach(function (source) {
const file = path.join('src', source);
it(file, function (done) {
// Lint each source file
cpplint({
files: [file],
linelength: 120,
filters: {
legal: {
copyright: false
},
build: {
include: false
},
whitespace: {
parens: false
},
runtime: {
indentation_namespace: false
}
}
}, function (err, report) {
if (err) {
throw err;
}
const expected = {};
expected[file] = [];
assert.deepEqual(expected, report);
done();
});
});
});
}
});

View File

@@ -141,16 +141,16 @@ describe('Crop', function () {
it('Invalid values fail', function () { it('Invalid values fail', function () {
assert.throws(function () { assert.throws(function () {
sharp().crop(9); sharp().crop(9);
}); }, /Expected valid crop id\/name\/strategy for crop but received 9 of type number/);
assert.throws(function () { assert.throws(function () {
sharp().crop(1.1); sharp().crop(1.1);
}); }, /Expected valid crop id\/name\/strategy for crop but received 1.1 of type number/);
assert.throws(function () { assert.throws(function () {
sharp().crop(-1); sharp().crop(-1);
}); }, /Expected valid crop id\/name\/strategy for crop but received -1 of type number/);
assert.throws(function () { assert.throws(function () {
sharp().crop('zoinks'); sharp().crop('zoinks');
}); }, /Expected valid crop id\/name\/strategy for crop but received zoinks of type string/);
}); });
it('Uses default value when none specified', function () { it('Uses default value when none specified', function () {
@@ -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) {
@@ -157,6 +199,28 @@ describe('Input/output', function () {
readableButNotAnImage.pipe(writable); readableButNotAnImage.pipe(writable);
}); });
it('Readable side of Stream can start flowing after Writable side has finished', function (done) {
const readable = fs.createReadStream(fixtures.inputJpg);
const writable = fs.createWriteStream(fixtures.outputJpg);
writable.on('finish', function () {
sharp(fixtures.outputJpg).toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fs.unlinkSync(fixtures.outputJpg);
done();
});
});
const pipeline = sharp().resize(320, 240);
readable.pipe(pipeline);
pipeline.on('finish', function () {
pipeline.pipe(writable);
});
});
it('Sequential read, force JPEG', function (done) { it('Sequential read, force JPEG', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.sequentialRead() .sequentialRead()
@@ -372,6 +436,50 @@ describe('Input/output', function () {
done(); done();
}); });
}); });
it('should work for webp alpha quality', function (done) {
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.webp({alphaQuality: 80})
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('webp', info.format);
fixtures.assertSimilar(fixtures.expected('webp-alpha-80.webp'), data, done);
});
});
it('should work for webp lossless', function (done) {
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.webp({lossless: true})
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('webp', info.format);
fixtures.assertSimilar(fixtures.expected('webp-lossless.webp'), data, done);
});
});
it('should work for webp near-lossless', function (done) {
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.webp({nearLossless: true, quality: 50})
.toBuffer(function (err50, data50, info50) {
if (err50) throw err50;
assert.strictEqual(true, data50.length > 0);
assert.strictEqual('webp', info50.format);
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
});
});
it('should use near-lossless when both lossless and nearLossless are specified', function (done) {
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
.webp({nearLossless: true, quality: 50, lossless: true})
.toBuffer(function (err50, data50, info50) {
if (err50) throw err50;
assert.strictEqual(true, data50.length > 0);
assert.strictEqual('webp', info50.format);
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
});
});
} }
it('Invalid output format', function (done) { it('Invalid output format', function (done) {
@@ -735,6 +843,12 @@ describe('Input/output', function () {
}); });
}); });
it('Invalid WebP alpha quality throws error', function () {
assert.throws(function () {
sharp().webp({ alphaQuality: 101 });
});
});
it('Invalid TIFF quality throws error', function () { it('Invalid TIFF quality throws error', function () {
assert.throws(function () { assert.throws(function () {
sharp().tiff({ quality: 101 }); sharp().tiff({ quality: 101 });
@@ -747,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)
@@ -930,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();
@@ -1042,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

@@ -66,37 +66,37 @@ describe('Resize dimensions', function () {
it('Invalid width - NaN', function () { it('Invalid width - NaN', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize('spoons', 240); sharp().resize('spoons', 240);
}); }, /Expected integer between 1 and 16383 for width but received spoons of type string/);
}); });
it('Invalid height - NaN', function () { it('Invalid height - NaN', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize(320, 'spoons'); sharp().resize(320, 'spoons');
}); }, /Expected integer between 1 and 16383 for height but received spoons of type string/);
}); });
it('Invalid width - float', function () { it('Invalid width - float', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize(1.5, 240); sharp().resize(1.5, 240);
}); }, /Expected integer between 1 and 16383 for width but received 1.5 of type number/);
}); });
it('Invalid height - float', function () { it('Invalid height - float', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize(320, 1.5); sharp().resize(320, 1.5);
}); }, /Expected integer between 1 and 16383 for height but received 1.5 of type number/);
}); });
it('Invalid width - too large', function () { it('Invalid width - too large', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize(0x4000, 240); sharp().resize(0x4000, 240);
}); }, /Expected integer between 1 and 16383 for width but received 16384 of type number/);
}); });
it('Invalid height - too large', function () { it('Invalid height - too large', function () {
assert.throws(function () { assert.throws(function () {
sharp().resize(320, 0x4000); sharp().resize(320, 0x4000);
}); }, /Expected integer between 1 and 16383 for height but received 16384 of type number/);
}); });
it('WebP shrink-on-load rounds to zero, ensure recalculation is correct', function (done) { it('WebP shrink-on-load rounds to zero, ensure recalculation is correct', function (done) {

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);
}); });
}); });
}); });