Compare commits

...

28 Commits

Author SHA1 Message Date
Lovell Fuller
c879df3b31 Release v0.18.0 2017-05-30 08:09:59 +01:00
Lovell Fuller
361ed98353 Remove previously-deprecated output format 'option' functions 2017-05-23 21:57:05 +01:00
Lovell Fuller
d45f8ef2d3 Document the cache-free nature of metadata extraction #796 2017-05-23 21:24:29 +01:00
Lovell Fuller
d6a63d11d7 Docs refresh 2017-05-22 21:49:37 +01:00
jingsam
4c6804eadc Add toFormat 'jpg' alias for 'jpeg' (#814) 2017-05-22 12:59:43 +01:00
Nicolas Coden
99810c0311 Add support for any rotation angle (#791)
Allow to provide any positive or negative multiple of 90 to `.rotate(...)`.
Negative angles and angles above 360 are converted to valid 0/90/180/270
rotations (0 rotations are still ignored).

Changes:
- [Node] Add `useExifOrientation` internal variable to know if the Exif
  orientation must be used instead of the provided angle. This allows to save a
  negative angle in the `angle` option, because the `-1` special case is not
  needed.

- [Node] Change check for planed-rotation in extract, to prepare a
  rotation before extraction: check with both `angle` and `useExifOrientation`
  options.
  I think this check contains a bit too much logics on rotation options. Maybe
  we could move this condition to a dedicated function.

- [C++] Separate `CalculateRotationAndFlip` into two generic functions:
  - `CalculateExifRotationAndFlip`: Calculate the angle of rotation and
    need-to-flip for the given Exif orientation.
  - `CalculateAngleRotation`: Calculate the rotation for the given angle.

  One or the other function is used to calculate the rotation, depending on
  wether the Exif orientation tag or the provided angle must be used.

- Add unit tests for `-3690`, `-450`, `-90`, `90`, `450`, `3690` and `-3780`,
  `-540`, `0`, `180`, `540`, `3780` rotations
- Add `320x240` fixture image for tests.

Unrelated changes (squashed):
- Add ncoden to the list of contributors
2017-05-22 11:08:33 +01:00
gmaliar
d15fb1ab1b Docs: add link to TailorBrands-maintained libvips Dockerfiles (#813) 2017-05-21 20:37:57 +01:00
Lovell Fuller
0a6d8b37ad Ensure double to int cast introduced in 4d1a169 is static 2017-05-21 19:05:56 +01:00
Lovell Fuller
f78ffdb9ce Upgrade to libvips v8.5.5 2017-05-21 18:31:06 +01:00
Lovell Fuller
b7b6fdbdf5 Update perf test contenders, add node-images 2017-05-13 20:08:53 +01:00
Lovell Fuller
e398b471e1 Prevent aliasing by using dynamic values for shrink(-on-load) 2017-05-13 18:46:39 +01:00
Lovell Fuller
48f69f3d88 Upgrade libpng to v1.6.29 2017-05-13 18:20:54 +01:00
Lovell Fuller
95850d75f6 Include pixel format depth when reading metadata 2017-05-07 09:29:38 +01:00
Lovell Fuller
c41d755441 Ctor single arg: allow plain object, reject null/undefined
Thank you @kub1x
2017-05-06 19:03:14 +01:00
Lovell Fuller
39a21787b7 Remove 'require' test as bufferutil now ships prebuilt 2017-05-06 15:49:50 +01:00
Lovell Fuller
36078f9903 Switch to the libvips crop strategy implementations 2017-05-06 14:46:28 +01:00
Lovell Fuller
2f534dc01c Base maximum output dimensions on limitation of format 2017-05-04 23:20:37 +01:00
Lovell Fuller
c8e59f08ec Add support for Buffer and Stream-based TIFF output 2017-05-04 16:40:49 +01:00
Lovell Fuller
19dd6a997f Doc refresh, thank you @cspotcode 2017-05-01 09:34:10 +01:00
Lovell Fuller
4d1a1694cd Improve perf/accuracy of nearest neighbour integral upsample 2017-04-30 20:54:48 +01:00
Lovell Fuller
52bea15ad7 Upgrade libvips dependency to v8.5.4, plus other bumps 2017-04-26 23:04:08 +01:00
Lovell Fuller
6592361c5a Ensure ARM64 pre-built binaries use correct C++11 ABI 2017-04-26 21:41:03 +01:00
Lovell Fuller
f3f83494f5 Credit contributor YvesBos 2017-04-26 21:40:30 +01:00
Lovell Fuller
1169afbe90 Avoid (un)premultiplication for overlay image without alpha channel
Add 'premultiplied' boolean attribute to output info, helps test
2017-04-26 21:37:43 +01:00
Lovell Fuller
301bfbd271 Expose libvips warnings via NODE_DEBUG env var 2017-04-26 21:37:43 +01:00
Lovell Fuller
46aec7eabc Upgrade libvips dependency and packaging to v8.5.1 2017-04-26 21:37:43 +01:00
YvesBos
4cd3b66761 Add support for squashing TIFF output to 1-bit (#783) 2017-04-26 17:47:29 +01:00
Jakub Podlaha
567e3dd258 Add gentoo support to glibc detection (#760) 2017-04-06 15:17:30 +01:00
63 changed files with 1133 additions and 909 deletions

View File

@@ -267,7 +267,7 @@
'vendor/lib/libstdc++-6.dll', 'vendor/lib/libstdc++-6.dll',
'vendor/lib/libtiff-5.dll', 'vendor/lib/libtiff-5.dll',
'vendor/lib/libvips-42.dll', 'vendor/lib/libvips-42.dll',
'vendor/lib/libwebp-6.dll', 'vendor/lib/libwebp-7.dll',
'vendor/lib/libxml2-2.dll', 'vendor/lib/libxml2-2.dll',
'vendor/lib/zlib1.dll' 'vendor/lib/zlib1.dll'
] ]

View File

@@ -3,7 +3,6 @@
const fs = require('fs'); const fs = require('fs');
const os = require('os'); const os = require('os');
const path = require('path'); const path = require('path');
const zlib = require('zlib');
const caw = require('caw'); const caw = require('caw');
const got = require('got'); const got = require('got');
@@ -29,21 +28,22 @@ const isFile = function (file) {
}; };
const unpack = function (tarPath, done) { const unpack = function (tarPath, done) {
const extractor = tar.Extract({ path: path.join(__dirname, 'vendor') }); const vendorPath = path.join(__dirname, 'vendor');
if (done) { fs.mkdirSync(vendorPath);
extractor.on('end', done); tar
} .extract({
extractor.on('error', error); file: tarPath,
fs.createReadStream(tarPath) cwd: vendorPath,
.on('error', error) strict: true
.pipe(zlib.Unzip()) })
.pipe(extractor); .then(done)
.catch(error);
}; };
const platformId = function () { const platformId = function () {
const platformId = [platform]; const platformId = [platform];
if (arch === 'arm' || arch === 'armhf' || arch === 'arch64') { if (arch === 'arm' || arch === 'armhf' || arch === 'arm64') {
const armVersion = (arch === 'arch64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6'; const armVersion = (arch === 'arm64') ? '8' : process.env.npm_config_armv || process.config.variables.arm_version || '6';
platformId.push('armv' + armVersion); platformId.push('armv' + armVersion);
} else { } else {
platformId.push(arch); platformId.push(arch);
@@ -73,7 +73,7 @@ module.exports.download_vips = function () {
// Ensure glibc >= 2.15 // Ensure glibc >= 2.15
const lddVersion = process.env.LDD_VERSION; const lddVersion = process.env.LDD_VERSION;
if (lddVersion) { if (lddVersion) {
if (/(glibc|gnu libc)/i.test(lddVersion)) { if (/(glibc|gnu libc|gentoo)/i.test(lddVersion)) {
const glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : ''; const glibcVersion = lddVersion ? lddVersion.split(/\n/)[0].split(' ').slice(-1)[0].trim() : '';
if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) { if (glibcVersion && semver.lt(glibcVersion + '.0', '2.13.0')) {
error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/'); error('glibc version ' + glibcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/');

View File

@@ -37,7 +37,7 @@ An alpha channel may be present, and will be unchanged by the operation.
**Parameters** **Parameters**
- `greyscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `greyscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -47,7 +47,7 @@ Alternative spelling of `greyscale`.
**Parameters** **Parameters**
- `grayscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `grayscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**

View File

@@ -11,16 +11,18 @@ Overlay (composite) an image over the processed (resized, extracted etc.) image.
The overlay image must be the same size or smaller than the processed image. The overlay image must be the same size or smaller than the processed image.
If both `top` and `left` options are provided, they take precedence over `gravity`. If both `top` and `left` options are provided, they take precedence over `gravity`.
If the overlay image contains an alpha channel then composition with premultiplication will occur.
**Parameters** **Parameters**
- `overlay` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))** Buffer containing image data or String containing the path to an image file. - `overlay` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))** Buffer containing image data or String containing the path to an image file.
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.gravity` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** gravity at which to place the overlay. (optional, default `'centre'`) - `options.gravity` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** gravity at which to place the overlay. (optional, default `'centre'`)
- `options.top` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the top edge. - `options.top` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the pixel offset from the top 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.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.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)?**

View File

@@ -14,9 +14,9 @@
- `input` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))?** if present, can be - `input` **([Buffer](https://nodejs.org/api/buffer.html) \| [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String))?** 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 not present.
- `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 input 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)?**
@@ -53,7 +53,7 @@ readableStream.pipe(transformer).pipe(writableStream);
```javascript ```javascript
// Create a blank 300x200 PNG image of semi-transluent red pixels // Create a blank 300x200 PNG image of semi-transluent red pixels
sharp(null, { sharp({
create: { create: {
width: 300, width: 300,
height: 200, height: 200,
@@ -77,7 +77,7 @@ An Object containing nested boolean values representing the available input and
**Examples** **Examples**
```javascript ```javascript
console.log(sharp.format()); 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)**

View File

@@ -28,14 +28,15 @@ Returns **Sharp**
## metadata ## metadata
Fast access to image metadata without decoding any compressed image data. Fast access to (uncached) 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.
- `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg` - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
- `width`: Number of pixels wide - `width`: Number of pixels wide
- `height`: Number of pixels high - `height`: Number of pixels high
- `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568) - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
- `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
- `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
- `density`: Number of pixels per inch (DPI), if present - `density`: Number of pixels per inch (DPI), if present
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
@@ -88,6 +89,6 @@ This will reduce memory usage and can improve performance on some systems.
**Parameters** **Parameters**
- `sequentialRead` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `sequentialRead` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**

View File

@@ -24,7 +24,10 @@
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.
Use this method without angle to determine the angle from EXIF data. If an angle is provided, it is converted to a valid 90/180/270deg rotation.
For example, `-450` will produce a 270deg rotation.
If no angle is provided, it is determined from the EXIF data.
Mirroring is supported and may infer the use of a flip operation. Mirroring is supported and may infer the use of a flip operation.
The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any. The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
@@ -34,7 +37,7 @@ for example `rotate(x).extract(y)` will produce a different result to `extract(y
**Parameters** **Parameters**
- `angle` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** 0, 90, 180 or 270. (optional, default `auto`) - `angle` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** angle of rotation, must be a multiple of 90. (optional, default `auto`)
**Examples** **Examples**
@@ -101,7 +104,7 @@ The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters** **Parameters**
- `flip` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `flip` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -112,7 +115,7 @@ The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
**Parameters** **Parameters**
- `flop` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `flop` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -126,8 +129,8 @@ Separate control over the level of sharpening in "flat" and "jagged" areas is av
**Parameters** **Parameters**
- `sigma` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`. - `sigma` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
- `flat` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the level of sharpening to apply to "flat" areas. (optional, default `1.0`) - `flat` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the level of sharpening to apply to "flat" areas. (optional, default `1.0`)
- `jagged` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`) - `jagged` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the level of sharpening to apply to "jagged" areas. (optional, default `2.0`)
- 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
@@ -184,7 +187,7 @@ Merge alpha transparency channel, if any, with `background`.
**Parameters** **Parameters**
- `flatten` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `flatten` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -194,7 +197,7 @@ Trim "boring" pixels from all edges that contain values within a percentage simi
**Parameters** **Parameters**
- `tolerance` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** value between 1 and 99 representing the percentage similarity. (optional, default `10`) - `tolerance` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** value between 1 and 99 representing the percentage similarity. (optional, default `10`)
- 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
@@ -211,7 +214,7 @@ when applying a gamma correction.
**Parameters** **Parameters**
- `gamma` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** value between 1.0 and 3.0. (optional, default `2.2`) - `gamma` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** value between 1.0 and 3.0. (optional, default `2.2`)
- 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
@@ -224,7 +227,7 @@ Produce the "negative" of the image.
**Parameters** **Parameters**
- `negate` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `negate` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -234,7 +237,7 @@ Enhance output image contrast by stretching its luminance to cover the full dyna
**Parameters** **Parameters**
- `normalise` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `normalise` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -244,7 +247,7 @@ Alternative spelling of normalise.
**Parameters** **Parameters**
- `normalize` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `normalize` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**
@@ -258,8 +261,8 @@ Convolve the image with the specified kernel.
- `kernel.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels. - `kernel.width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels.
- `kernel.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels. - `kernel.height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** width of the kernel in pixels.
- `kernel.kernel` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>** Array of length `width*height` containing the kernel values. - `kernel.kernel` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>** Array of length `width*height` containing the kernel values.
- `kernel.scale` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the scale of the kernel in pixels. (optional, default `sum`) - `kernel.scale` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the scale of the kernel in pixels. (optional, default `sum`)
- `kernel.offset` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** the offset of the kernel in pixels. (optional, default `0`) - `kernel.offset` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the offset of the kernel in pixels. (optional, default `0`)
**Examples** **Examples**
@@ -287,10 +290,10 @@ Any pixel value greather than or equal to the threshold value will be set to 255
**Parameters** **Parameters**
- `threshold` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`) - `threshold` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** a value in the range 0-255 representing the level at which the threshold will be applied. (optional, default `128`)
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.greyscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** convert to single channel greyscale. (optional, default `true`) - `options.greyscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** convert to single channel greyscale. (optional, default `true`)
- `options.grayscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** alternative spelling for greyscale. (optional, default `true`) - `options.grayscale` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** alternative spelling for greyscale. (optional, default `true`)
- 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

View File

@@ -27,7 +27,8 @@ A Promises/A+ promise is returned when `callback` is not provided.
- `fileOut` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to write the image data to. - `fileOut` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to write the image data to.
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called on completion with two arguments `(err, info)`. - `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called on completion with two arguments `(err, info)`.
`info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`. `info` contains the output image `format`, `size` (bytes), `width`, `height`,
`channels` and `premultiplied` (indicating if premultiplication was used).
- 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
@@ -37,14 +38,15 @@ Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe
## toBuffer ## toBuffer
Write output to a Buffer. Write output to a Buffer.
JPEG, PNG, WebP, and RAW output are supported. JPEG, PNG, WebP, TIFF 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, data, info)` where: `callback`, if present, gets three arguments `(err, data, info)` where:
- `err` is an error, if any. - `err` is an error, if any.
- `data` 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`,
`channels` and `premultiplied` (indicating if premultiplication was used).
A Promise is returned when `callback` is not provided. A Promise is returned when `callback` is not provided.
**Parameters** **Parameters**
@@ -78,14 +80,14 @@ Use these JPEG options for output image.
**Parameters** **Parameters**
- `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.progressive` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use progressive (interlace) scan (optional, default `false`) - `options.progressive` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use progressive (interlace) scan (optional, default `false`)
- `options.chromaSubsampling` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** set to '4:4:4' to prevent chroma subsampling when quality <= 90 (optional, default `'4:2:0'`) - `options.chromaSubsampling` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** set to '4:4:4' to prevent chroma subsampling when quality <= 90 (optional, default `'4:2:0'`)
- `options.trellisQuantisation` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** apply trellis quantisation, requires mozjpeg (optional, default `false`) - `options.trellisQuantisation` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** apply trellis quantisation, requires mozjpeg (optional, default `false`)
- `options.overshootDeringing` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** apply overshoot deringing, requires mozjpeg (optional, default `false`) - `options.overshootDeringing` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** apply overshoot deringing, requires mozjpeg (optional, default `false`)
- `options.optimiseScans` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`) - `options.optimiseScans` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** optimise progressive scans, forces progressive, requires mozjpeg (optional, default `false`)
- `options.optimizeScans` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** alternative spelling of optimiseScans (optional, default `false`) - `options.optimizeScans` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** alternative spelling of optimiseScans (optional, default `false`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force JPEG 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 JPEG output, otherwise attempt to use input format (optional, default `true`)
- 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
@@ -99,10 +101,10 @@ Use these PNG options for output image.
**Parameters** **Parameters**
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.progressive` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use progressive (interlace) scan (optional, default `false`) - `options.progressive` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use progressive (interlace) scan (optional, default `false`)
- `options.compressionLevel` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** zlib compression level (optional, default `6`) - `options.compressionLevel` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** zlib compression level (optional, default `6`)
- `options.adaptiveFiltering` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use adaptive row filtering (optional, default `true`) - `options.adaptiveFiltering` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use adaptive row filtering (optional, default `true`)
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force PNG 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 PNG output, otherwise attempt to use input format (optional, default `true`)
- 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
@@ -116,11 +118,11 @@ Use these WebP options for output image.
**Parameters** **Parameters**
- `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.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.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.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`)
- 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
@@ -134,10 +136,11 @@ Use these TIFF options for output image.
**Parameters** **Parameters**
- `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.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'`) - `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'`)
- `options.squash` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** squash 8-bit images down to 1 bit (optional, default `false`)
- 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
@@ -173,10 +176,10 @@ Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed arc
**Parameters** **Parameters**
- `tile` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** - `tile` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `tile.size` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** tile size in pixels, a value between 1 and 8192. (optional, default `256`) - `tile.size` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
- `tile.overlap` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`) - `tile.overlap` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
- `tile.container` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`) - `tile.container` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
- `tile.layout` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`) - `tile.layout` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
**Examples** **Examples**

View File

@@ -33,13 +33,13 @@ Possible enlargement interpolators are:
**Parameters** **Parameters**
- `width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels wide the resultant image should be, between 1 and 16383 (0x3FFF). Use `null` or `undefined` to auto-scale the width to match the height. - `width` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
- `height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width. - `height` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
- `options.kernel` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** the kernel to use for image reduction. (optional, default `'lanczos3'`) - `options.kernel` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the kernel to use for image reduction. (optional, default `'lanczos3'`)
- `options.interpolator` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** the interpolator to use for image enlargement. (optional, default `'bicubic'`) - `options.interpolator` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the interpolator to use for image enlargement. (optional, default `'bicubic'`)
- `options.centreSampling` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use \*magick centre sampling convention instead of corner sampling. (optional, default `false`) - `options.centreSampling` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** use \*magick centre sampling convention instead of corner sampling. (optional, default `false`)
- `options.centerSampling` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** alternative spelling of centreSampling. (optional, default `false`) - `options.centerSampling` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** alternative spelling of centreSampling. (optional, default `false`)
**Examples** **Examples**
@@ -78,7 +78,7 @@ then repeatedly ranks edge regions, discarding the edge with the lowest score ba
**Parameters** **Parameters**
- `crop` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically. (optional, default `'centre'`) - `crop` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** A member of `sharp.gravity` to crop to an edge/corner or `sharp.strategy` to crop dynamically. (optional, default `'centre'`)
**Examples** **Examples**
@@ -172,6 +172,6 @@ This is equivalent to GraphicsMagick's `>` geometry option:
**Parameters** **Parameters**
- `withoutEnlargement` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `true`) - `withoutEnlargement` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `true`)
Returns **Sharp** Returns **Sharp**

View File

@@ -17,9 +17,9 @@ useful for determining how much working memory is required for a particular task
**Parameters** **Parameters**
- `options` **([Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) \| [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** Object with the following attributes, or Boolean where true uses default cache settings and false removes all caching. - `options` **([Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) \| [Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean))** Object with the following attributes, or Boolean where true uses default cache settings and false removes all caching.
- `options.memory` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** is the maximum memory in MB to use for this cache (optional, default `50`) - `options.memory` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** is the maximum memory in MB to use for this cache (optional, default `50`)
- `options.files` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** is the maximum number of files to hold open (optional, default `20`) - `options.files` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** is the maximum number of files to hold open (optional, default `20`)
- `options.items` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** is the maximum number of operations to cache (optional, default `100`) - `options.items` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** is the maximum number of operations to cache (optional, default `100`)
**Examples** **Examples**
@@ -89,7 +89,7 @@ Versions of liborc prior to 0.4.25 are known to segfault under heavy load.
**Parameters** **Parameters**
- `simd` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** (optional, default `false`) - `simd` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** (optional, default `false`)
**Examples** **Examples**

View File

@@ -1,5 +1,67 @@
# Changelog # Changelog
### v0.18 - "*ridge*"
Requires libvips v8.5.5.
#### v0.18.0 - 30<sup>th</sup> May 2017
* Remove the previously-deprecated output format "option" functions:
quality, progressive, compressionLevel, withoutAdaptiveFiltering,
withoutChromaSubsampling, trellisQuantisation, trellisQuantization,
overshootDeringing, optimiseScans and optimizeScans.
* Ensure maximum output dimensions are based on the format to be used.
[#176](https://github.com/lovell/sharp/issues/176)
[@stephanebachelier](https://github.com/stephanebachelier)
* Avoid costly (un)premultiply when using overlayWith without alpha channel.
[#573](https://github.com/lovell/sharp/issues/573)
[@strarsis](https://github.com/strarsis)
* Include pixel depth (e.g. "uchar") when reading metadata.
[#577](https://github.com/lovell/sharp/issues/577)
[@moedusa](https://github.com/moedusa)
* Add support for Buffer and Stream-based TIFF output.
[#587](https://github.com/lovell/sharp/issues/587)
[@strarsis](https://github.com/strarsis)
* Expose warnings from libvips via NODE_DEBUG=sharp environment variable.
[#607](https://github.com/lovell/sharp/issues/607)
[@puzrin](https://github.com/puzrin)
* Switch to the libvips implementation of "attention" and "entropy" crop strategies.
[#727](https://github.com/lovell/sharp/issues/727)
* Improve performance and accuracy of nearest neighbour integral upsampling.
[#752](https://github.com/lovell/sharp/issues/752)
[@MrIbby](https://github.com/MrIbby)
* Constructor single argument API: allow plain object, reject null/undefined.
[#768](https://github.com/lovell/sharp/issues/768)
[@kub1x](https://github.com/kub1x)
* Ensure ARM64 pre-built binaries use correct C++11 ABI version.
[#772](https://github.com/lovell/sharp/issues/772)
[@ajiratech2](https://github.com/ajiratech2)
* Prevent aliasing by using dynamic values for shrink(-on-load).
[#781](https://github.com/lovell/sharp/issues/781)
[@kleisauke](https://github.com/kleisauke)
* Expose libvips' "squash" parameter to enable 1-bit TIFF output.
[#783](https://github.com/lovell/sharp/pull/783)
[@YvesBos](https://github.com/YvesBos)
* Add support for rotation using any multiple of +/-90 degrees.
[#791](https://github.com/lovell/sharp/pull/791)
[@ncoden](https://github.com/ncoden)
* Add "jpg" alias to toFormat as shortened form of "jpeg".
[#814](https://github.com/lovell/sharp/pull/814)
[@jingsam](https://github.com/jingsam)
### v0.17 - "*quill*" ### v0.17 - "*quill*"
Requires libvips v8.4.2. Requires libvips v8.4.2.

View File

@@ -22,7 +22,7 @@ the installation of any external runtime dependencies.
This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images. This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images.
Output images can be in JPEG, PNG and WebP formats as well as uncompressed raw pixel data. Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data.
Streams, Buffer objects and the filesystem can be used for input and output. Streams, Buffer objects and the filesystem can be used for input and output.
@@ -99,6 +99,7 @@ the help and code contributions of the following people:
* [Jérémy Lal](https://github.com/kapouer) * [Jérémy Lal](https://github.com/kapouer)
* [Alice Monday](https://github.com/alice0meta) * [Alice Monday](https://github.com/alice0meta)
* [Kristo Jorgenson](https://github.com/kristojorg) * [Kristo Jorgenson](https://github.com/kristojorg)
* [Yves Bos](https://github.com/YvesBos)
Thank you! Thank you!

View File

@@ -20,7 +20,7 @@ yarn add sharp
[![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp) [![Linux Build Status](https://circleci.com/gh/lovell/sharp.svg?style=svg&circle-token=6cb6d1d287a51af83722b19ed8885377fbc85e5c)](https://circleci.com/gh/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`. libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 6.5MB. This involves an automated HTTPS download of approximately 7MB.
Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.: Most recent Linux-based operating systems with glibc running on x64 and ARMv6+ CPUs should "just work", e.g.:
@@ -57,7 +57,7 @@ via `sharp.cache(false)` to avoid a stack overflow.
[![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp) [![OS X 10.9.5 Build Status](https://travis-ci.org/lovell/sharp.png?branch=master)](https://travis-ci.org/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`. libvips and its dependencies are fetched and stored within `node_modules/sharp/vendor` during `npm install`.
This involves an automated HTTPS download of approximately 6.3MB. This involves an automated HTTPS download of approximately 7MB.
To use your own version of libvips instead of the provided binaries, make sure it is To use your own version of libvips instead of the provided binaries, make sure it is
at least the version listed under `config.libvips` in the `package.json` file and at least the version listed under `config.libvips` in the `package.json` file and
@@ -68,7 +68,7 @@ that it can be located using `pkg-config --modversion vips-cpp`.
[![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp) [![Windows x64 Build Status](https://ci.appveyor.com/api/projects/status/pgtul704nkhhg6sg)](https://ci.appveyor.com/project/lovell/sharp)
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`. libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 9MB. This involves an automated HTTPS download of approximately 11MB.
Only 64-bit (x64) `node.exe` is supported. Only 64-bit (x64) `node.exe` is supported.
@@ -84,7 +84,7 @@ cd /usr/ports/graphics/vips/ && make install clean
### Heroku ### Heroku
libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`. libvips and its dependencies are fetched and stored within `node_modules\sharp\vendor` during `npm install`.
This involves an automated HTTPS download of approximately 6.5MB. This involves an automated HTTPS download of approximately 7MB.
Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior) Set [NODE_MODULES_CACHE](https://devcenter.heroku.com/articles/nodejs-support#cache-behavior)
to `false` when using the `yarn` package manager. to `false` when using the `yarn` package manager.
@@ -105,6 +105,13 @@ docker pull marcbachmann/libvips
docker pull wjordan/libvips docker pull wjordan/libvips
``` ```
[Tailor Brands](https://github.com/TailorBrands) maintain
[Debian-based Dockerfiles for libvips and nodejs](https://github.com/TailorBrands/docker-libvips).
```sh
docker pull tailor/docker-libvips
```
### AWS Lambda ### AWS Lambda
In order to use sharp on AWS Lambda, you need to [create a deployment package](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html). Because sharp In order to use sharp on AWS Lambda, you need to [create a deployment package](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-create-deployment-pkg.html). Because sharp
@@ -199,6 +206,7 @@ Use of libraries under the terms of the LGPLv3 is via the
| Library | Used under the terms of | | Library | Used under the terms of |
|---------------|----------------------------------------------------------------------------------------------------------| |---------------|----------------------------------------------------------------------------------------------------------|
| cairo | Mozilla Public License 2.0 | | cairo | Mozilla Public License 2.0 |
| expat | MIT Licence |
| fontconfig | [fontconfig Licence](https://cgit.freedesktop.org/fontconfig/tree/COPYING) (BSD-like) | | fontconfig | [fontconfig Licence](https://cgit.freedesktop.org/fontconfig/tree/COPYING) (BSD-like) |
| freetype | [freetype Licence](http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) | | freetype | [freetype Licence](http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) |
| giflib | MIT Licence | | giflib | MIT Licence |

View File

@@ -8,6 +8,8 @@ const is = require('./is');
* The overlay image must be the same size or smaller than the processed image. * The overlay image must be the same size or smaller than the processed image.
* If both `top` and `left` options are provided, they take precedence over `gravity`. * If both `top` and `left` options are provided, they take precedence over `gravity`.
* *
* If the overlay image contains an alpha channel then composition with premultiplication will occur.
*
* @example * @example
* sharp('input.png') * sharp('input.png')
* .rotate(180) * .rotate(180)
@@ -66,10 +68,7 @@ function overlayWith (overlay, options) {
} }
} }
if (is.defined(options.left) || is.defined(options.top)) { if (is.defined(options.left) || is.defined(options.top)) {
if ( if (is.integer(options.left) && options.left >= 0 && is.integer(options.top) && options.top >= 0) {
is.integer(options.left) && is.inRange(options.left, 0, this.constructor.maximum.width) &&
is.integer(options.top) && is.inRange(options.top, 0, this.constructor.maximum.height)
) {
this.options.overlayXOffset = options.left; this.options.overlayXOffset = options.left;
this.options.overlayYOffset = options.top; this.options.overlayYOffset = options.top;
} else { } else {

View File

@@ -5,6 +5,7 @@ const util = require('util');
const stream = require('stream'); const stream = require('stream');
const events = require('events'); const events = require('events');
const semver = require('semver'); const semver = require('semver');
const is = require('./is');
const sharp = require('../build/Release/sharp.node'); const sharp = require('../build/Release/sharp.node');
// Versioning // Versioning
@@ -24,12 +25,15 @@ let versions = {
} catch (err) {} } catch (err) {}
})(); })();
// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
/** /**
* @class Sharp * @class Sharp
* *
* Constructor factory to create an instance of `sharp`, to which further methods are chained. * Constructor factory to create an instance of `sharp`, to which further methods are chained.
* *
* JPEG, PNG or WebP format image data can be streamed out from this object. * JPEG, PNG, WebP or TIFF format image data can be streamed out from this object.
* When using Stream based output, derived attributes are available from the `info` event. * When using Stream based output, derived attributes are available from the `info` event.
* *
* Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class. * Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
@@ -56,7 +60,7 @@ let versions = {
* *
* @example * @example
* // Create a blank 300x200 PNG image of semi-transluent red pixels * // Create a blank 300x200 PNG image of semi-transluent red pixels
* sharp(null, { * sharp({
* create: { * create: {
* width: 300, * width: 300,
* height: 200, * height: 200,
@@ -71,7 +75,7 @@ let versions = {
* @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 not present.
* @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 input image data. See `raw()` for pixel ordering. * @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
@@ -87,6 +91,9 @@ let versions = {
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
const Sharp = function (input, options) { const Sharp = function (input, options) {
if (arguments.length === 1 && !is.defined(input)) {
throw new Error('Invalid input');
}
if (!(this instanceof Sharp)) { if (!(this instanceof Sharp)) {
return new Sharp(input, options); return new Sharp(input, options);
} }
@@ -94,7 +101,7 @@ const Sharp = function (input, options) {
this.options = { this.options = {
// input options // input options
sequentialRead: false, sequentialRead: false,
limitInputPixels: maximum.pixels, limitInputPixels: Math.pow(0x3FFF, 2),
// ICC profiles // ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep, iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options // resize options
@@ -110,6 +117,7 @@ const Sharp = function (input, options) {
height: -1, height: -1,
canvas: 'crop', canvas: 'crop',
crop: 0, crop: 0,
useExifOrientation: false,
angle: 0, angle: 0,
rotateBeforePreExtract: false, rotateBeforePreExtract: false,
flip: false, flip: false,
@@ -171,8 +179,11 @@ const Sharp = function (input, options) {
tiffQuality: 80, tiffQuality: 80,
tiffCompression: 'jpeg', tiffCompression: 'jpeg',
tiffPredictor: 'none', tiffPredictor: 'none',
tiffSquash: false,
tileSize: 256, tileSize: 256,
tileOverlap: 0, tileOverlap: 0,
// Function to notify of libvips warnings
debuglog: debuglog,
// Function to notify of queue length changes // Function to notify of queue length changes
queueListener: function (queueLength) { queueListener: function (queueLength) {
queue.emit('change', queueLength); queue.emit('change', queueLength);
@@ -183,18 +194,6 @@ const Sharp = function (input, options) {
}; };
util.inherits(Sharp, stream.Duplex); util.inherits(Sharp, stream.Duplex);
/**
* Pixel limits.
* @member
* @private
*/
const maximum = {
width: 0x3FFF,
height: 0x3FFF,
pixels: Math.pow(0x3FFF, 2)
};
Sharp.maximum = maximum;
/** /**
* An EventEmitter that emits a `change` event when a task is either: * An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread * - queued, waiting for _libuv_ to provide a worker thread
@@ -211,7 +210,7 @@ Sharp.queue = queue;
/** /**
* 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.
* @example * @example
* console.log(sharp.format()); * console.log(sharp.format);
* @returns {Object} * @returns {Object}
*/ */
Sharp.format = sharp.format(); Sharp.format = sharp.format();

View File

@@ -1,6 +1,5 @@
'use strict'; 'use strict';
const util = require('util');
const color = require('color'); 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');
@@ -17,6 +16,9 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
} else if (is.buffer(input)) { } else if (is.buffer(input)) {
// Buffer // Buffer
inputDescriptor.buffer = input; inputDescriptor.buffer = input;
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
// Plain Object descriptor, e.g. create
inputOptions = input;
} else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) { } else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream // Stream
inputDescriptor.buffer = []; inputDescriptor.buffer = [];
@@ -36,8 +38,8 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (is.defined(inputOptions.raw)) { if (is.defined(inputOptions.raw)) {
if ( if (
is.object(inputOptions.raw) && is.object(inputOptions.raw) &&
is.integer(inputOptions.raw.width) && is.inRange(inputOptions.raw.width, 1, this.constructor.maximum.width) && is.integer(inputOptions.raw.width) && inputOptions.raw.width > 0 &&
is.integer(inputOptions.raw.height) && is.inRange(inputOptions.raw.height, 1, this.constructor.maximum.height) && is.integer(inputOptions.raw.height) && inputOptions.raw.height > 0 &&
is.integer(inputOptions.raw.channels) && is.inRange(inputOptions.raw.channels, 1, 4) is.integer(inputOptions.raw.channels) && is.inRange(inputOptions.raw.channels, 1, 4)
) { ) {
inputDescriptor.rawWidth = inputOptions.raw.width; inputDescriptor.rawWidth = inputOptions.raw.width;
@@ -51,8 +53,8 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
if (is.defined(inputOptions.create)) { if (is.defined(inputOptions.create)) {
if ( if (
is.object(inputOptions.create) && is.object(inputOptions.create) &&
is.integer(inputOptions.create.width) && is.inRange(inputOptions.create.width, 1, this.constructor.maximum.width) && is.integer(inputOptions.create.width) && inputOptions.create.width > 0 &&
is.integer(inputOptions.create.height) && is.inRange(inputOptions.create.height, 1, this.constructor.maximum.height) && is.integer(inputOptions.create.height) && inputOptions.create.height > 0 &&
is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) && is.integer(inputOptions.create.channels) && is.inRange(inputOptions.create.channels, 3, 4) &&
is.defined(inputOptions.create.background) is.defined(inputOptions.create.background)
) { ) {
@@ -143,7 +145,7 @@ function clone () {
const that = this; const that = this;
// Clone existing options // Clone existing options
const clone = this.constructor.call(); const clone = this.constructor.call();
util._extend(clone.options, this.options); clone.options = Object.assign({}, this.options);
// Pass 'finish' event to clone for Stream-based input // Pass 'finish' event to clone for Stream-based input
this.on('finish', function () { this.on('finish', function () {
// Clone inherits input data // Clone inherits input data
@@ -155,14 +157,15 @@ function clone () {
} }
/** /**
* Fast access to image metadata without decoding any compressed image data. * Fast access to (uncached) 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.
* *
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg` * - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
* - `width`: Number of pixels wide * - `width`: Number of pixels wide
* - `height`: Number of pixels high * - `height`: Number of pixels high
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568) * - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK * - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
* - `density`: Number of pixels per inch (DPI), if present * - `density`: Number of pixels per inch (DPI), if present
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile * - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel * - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
@@ -240,12 +243,12 @@ function limitInputPixels (limit) {
if (limit === false) { if (limit === false) {
limit = 0; limit = 0;
} else if (limit === true) { } else if (limit === true) {
limit = this.constructor.maximum.pixels; limit = Math.pow(0x3FFF, 2);
} }
if (is.integer(limit) && limit >= 0) { if (is.integer(limit) && limit >= 0) {
this.options.limitInputPixels = limit; this.options.limitInputPixels = limit;
} else { } else {
throw new Error('Invalid pixel limit (0 to ' + this.constructor.maximum.pixels + ') ' + limit); throw is.invalidParameterError('limitInputPixels', 'integer', limit);
} }
return this; return this;
} }

View File

@@ -16,6 +16,14 @@ const object = function (val) {
return typeof val === 'object'; return typeof val === 'object';
}; };
/**
* Is this value a plain object?
* @private
*/
const plainObject = function (val) {
return object(val) && Object.prototype.toString.call(val) === '[object Object]';
};
/** /**
* Is this value a function? * Is this value a function?
* @private * @private
@@ -98,6 +106,7 @@ const invalidParameterError = function (name, expected, actual) {
module.exports = { module.exports = {
defined: defined, defined: defined,
object: object, object: object,
plainObject: plainObject,
fn: fn, fn: fn,
bool: bool, bool: bool,
buffer: buffer, buffer: buffer,

View File

@@ -6,7 +6,10 @@ const is = require('./is');
* 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.
* *
* Use this method without angle to determine the angle from EXIF data. * If an angle is provided, it is converted to a valid 90/180/270deg rotation.
* For example, `-450` will produce a 270deg rotation.
*
* If no angle is provided, it is determined from the EXIF data.
* Mirroring is supported and may infer the use of a flip operation. * Mirroring is supported and may infer the use of a flip operation.
* *
* The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any. * The use of `rotate` implies the removal of the EXIF `Orientation` tag, if any.
@@ -25,17 +28,17 @@ const is = require('./is');
* }); * });
* readableStream.pipe(pipeline); * readableStream.pipe(pipeline);
* *
* @param {Number} [angle=auto] 0, 90, 180 or 270. * @param {Number} [angle=auto] angle of rotation, must be a multiple of 90.
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
function rotate (angle) { function rotate (angle) {
if (!is.defined(angle)) { if (!is.defined(angle)) {
this.options.angle = -1; this.options.useExifOrientation = true;
} else if (is.integer(angle) && is.inArray(angle, [0, 90, 180, 270])) { } else if (is.integer(angle) && !(angle % 90)) {
this.options.angle = angle; this.options.angle = angle;
} else { } else {
throw new Error('Unsupported angle (0, 90, 180, 270) ' + angle); throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + angle);
} }
return this; return this;
} }
@@ -81,7 +84,7 @@ function extract (options) {
} }
}, this); }, this);
// Ensure existing rotation occurs before pre-resize extraction // Ensure existing rotation occurs before pre-resize extraction
if (suffix === 'Pre' && this.options.angle !== 0) { if (suffix === 'Pre' && ((this.options.angle % 360) !== 0 || this.options.useExifOrientation === true)) {
this.options.rotateBeforePreExtract = true; this.options.rotateBeforePreExtract = true;
} }
return this; return this;

View File

@@ -1,6 +1,5 @@
'use strict'; 'use strict';
const util = require('util');
const is = require('./is'); const is = require('./is');
const sharp = require('../build/Release/sharp.node'); const sharp = require('../build/Release/sharp.node');
@@ -15,7 +14,8 @@ const sharp = require('../build/Release/sharp.node');
* *
* @param {String} fileOut - the path to write the image data to. * @param {String} fileOut - the path to write the image data to.
* @param {Function} [callback] - called on completion with two arguments `(err, info)`. * @param {Function} [callback] - called on completion with two arguments `(err, info)`.
* `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`. * `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* @returns {Promise<Object>} - when no callback is provided * @returns {Promise<Object>} - when no callback is provided
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@@ -45,13 +45,14 @@ function toFile (fileOut, callback) {
/** /**
* Write output to a Buffer. * Write output to a Buffer.
* JPEG, PNG, WebP, and RAW output are supported. * JPEG, PNG, WebP, TIFF 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, data, info)` where: * `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any. * - `err` is an error, if any.
* - `data` 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`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* A Promise is returned when `callback` is not provided. * A Promise is returned when `callback` is not provided.
* *
* @param {Object} [options] * @param {Object} [options]
@@ -213,6 +214,7 @@ function webp (options) {
* @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.compression='jpeg'] - compression options: lzw, deflate, jpeg
* @param {Boolean} [options.predictor='none'] - compression predictor options: none, horizontal, float * @param {Boolean} [options.predictor='none'] - compression predictor options: none, horizontal, float
* @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid options * @throws {Error} Invalid options
*/ */
@@ -224,6 +226,13 @@ function tiff (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.squash)) {
if (is.bool(options.squash)) {
this.options.tiffSquash = options.squash;
} else {
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
}
}
// compression // compression
if (is.defined(options) && is.defined(options.compression)) { if (is.defined(options) && is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'none'])) { if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'none'])) {
@@ -264,6 +273,7 @@ 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;
} }
if (format === 'jpg') format = 'jpeg';
if (!is.inArray(format, ['jpeg', 'png', 'webp', 'tiff', 'raw'])) { if (!is.inArray(format, ['jpeg', 'png', 'webp', 'tiff', 'raw'])) {
throw new Error('Unsupported output format ' + format); throw new Error('Unsupported output format ' + format);
} }
@@ -482,66 +492,6 @@ function _pipeline (callback) {
} }
} }
// Deprecated output options
/* istanbul ignore next */
const quality = util.deprecate(function (quality) {
const formatOut = this.options.formatOut;
const options = { quality: quality };
this.jpeg(options).webp(options).tiff(options);
this.options.formatOut = formatOut;
return this;
}, 'quality: use jpeg({ quality: ... }), webp({ quality: ... }) and/or tiff({ quality: ... }) instead');
/* istanbul ignore next */
const progressive = util.deprecate(function (progressive) {
const formatOut = this.options.formatOut;
const options = { progressive: (progressive !== false) };
this.jpeg(options).png(options);
this.options.formatOut = formatOut;
return this;
}, 'progressive: use jpeg({ progressive: ... }) and/or png({ progressive: ... }) instead');
/* istanbul ignore next */
const compressionLevel = util.deprecate(function (compressionLevel) {
const formatOut = this.options.formatOut;
this.png({ compressionLevel: compressionLevel });
this.options.formatOut = formatOut;
return this;
}, 'compressionLevel: use png({ compressionLevel: ... }) instead');
/* istanbul ignore next */
const withoutAdaptiveFiltering = util.deprecate(function (withoutAdaptiveFiltering) {
const formatOut = this.options.formatOut;
this.png({ adaptiveFiltering: (withoutAdaptiveFiltering === false) });
this.options.formatOut = formatOut;
return this;
}, 'withoutAdaptiveFiltering: use png({ adaptiveFiltering: ... }) instead');
/* istanbul ignore next */
const withoutChromaSubsampling = util.deprecate(function (withoutChromaSubsampling) {
const formatOut = this.options.formatOut;
this.jpeg({ chromaSubsampling: (withoutChromaSubsampling === false) ? '4:2:0' : '4:4:4' });
this.options.formatOut = formatOut;
return this;
}, 'withoutChromaSubsampling: use jpeg({ chromaSubsampling: "4:4:4" }) instead');
/* istanbul ignore next */
const trellisQuantisation = util.deprecate(function (trellisQuantisation) {
const formatOut = this.options.formatOut;
this.jpeg({ trellisQuantisation: (trellisQuantisation !== false) });
this.options.formatOut = formatOut;
return this;
}, 'trellisQuantisation: use jpeg({ trellisQuantisation: ... }) instead');
/* istanbul ignore next */
const overshootDeringing = util.deprecate(function (overshootDeringing) {
const formatOut = this.options.formatOut;
this.jpeg({ overshootDeringing: (overshootDeringing !== false) });
this.options.formatOut = formatOut;
return this;
}, 'overshootDeringing: use jpeg({ overshootDeringing: ... }) instead');
/* istanbul ignore next */
const optimiseScans = util.deprecate(function (optimiseScans) {
const formatOut = this.options.formatOut;
this.jpeg({ optimiseScans: (optimiseScans !== false) });
this.options.formatOut = formatOut;
return this;
}, 'optimiseScans: use jpeg({ optimiseScans: ... }) instead');
/** /**
* Decorate the Sharp prototype with output-related functions. * Decorate the Sharp prototype with output-related functions.
* @private * @private
@@ -567,15 +517,4 @@ module.exports = function (Sharp) {
].forEach(function (f) { ].forEach(function (f) {
Sharp.prototype[f.name] = f; Sharp.prototype[f.name] = f;
}); });
// Deprecated
Sharp.prototype.quality = quality;
Sharp.prototype.progressive = progressive;
Sharp.prototype.compressionLevel = compressionLevel;
Sharp.prototype.withoutAdaptiveFiltering = withoutAdaptiveFiltering;
Sharp.prototype.withoutChromaSubsampling = withoutChromaSubsampling;
Sharp.prototype.trellisQuantisation = trellisQuantisation;
Sharp.prototype.trellisQuantization = trellisQuantisation;
Sharp.prototype.overshootDeringing = overshootDeringing;
Sharp.prototype.optimiseScans = optimiseScans;
Sharp.prototype.optimizeScans = optimiseScans;
}; };

View File

@@ -91,8 +91,8 @@ const interpolator = {
* // of the image data in inputBuffer * // of the image data in inputBuffer
* }); * });
* *
* @param {Number} [width] - pixels wide the resultant image should be, between 1 and 16383 (0x3FFF). Use `null` or `undefined` to auto-scale the width to match the height. * @param {Number} [width] - pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* @param {Number} [height] - pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width. * @param {Number} [height] - pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {Object} [options] * @param {Object} [options]
* @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction. * @param {String} [options.kernel='lanczos3'] - the kernel to use for image reduction.
* @param {String} [options.interpolator='bicubic'] - the interpolator to use for image enlargement. * @param {String} [options.interpolator='bicubic'] - the interpolator to use for image enlargement.
@@ -103,19 +103,19 @@ const interpolator = {
*/ */
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) && width > 0) {
this.options.width = width; this.options.width = width;
} else { } else {
throw is.invalidParameterError('width', `integer between 1 and ${this.constructor.maximum.width}`, width); throw is.invalidParameterError('width', 'positive integer', width);
} }
} else { } else {
this.options.width = -1; this.options.width = -1;
} }
if (is.defined(height)) { if (is.defined(height)) {
if (is.integer(height) && is.inRange(height, 1, this.constructor.maximum.height)) { if (is.integer(height) && height > 0) {
this.options.height = height; this.options.height = height;
} else { } else {
throw is.invalidParameterError('height', `integer between 1 and ${this.constructor.maximum.height}`, height); throw is.invalidParameterError('height', 'positive integer', height);
} }
} else { } else {
this.options.height = -1; this.options.height = -1;

View File

@@ -1,7 +1,7 @@
{ {
"name": "sharp", "name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images", "description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.17.3", "version": "0.18.0",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp", "homepage": "https://github.com/lovell/sharp",
"contributors": [ "contributors": [
@@ -34,11 +34,14 @@
"Jérémy Lal <kapouer@melix.org>", "Jérémy Lal <kapouer@melix.org>",
"Rahul Nanwani <r.nanwani@gmail.com>", "Rahul Nanwani <r.nanwani@gmail.com>",
"Alice Monday <alice0meta@gmail.com>", "Alice Monday <alice0meta@gmail.com>",
"Kristo Jorgenson <kristo.jorgenson@gmail.com>" "Kristo Jorgenson <kristo.jorgenson@gmail.com>",
"YvesBos <yves_bos@outlook.com>",
"Guy Maliar <guy@tailorbrands.com>",
"Nicolas Coden <nicolas@ncoden.fr>"
], ],
"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 && cc && cross-env VIPS_WARNING=0 nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js", "test": "semistandard && cc && 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"
@@ -66,28 +69,26 @@
"dependencies": { "dependencies": {
"caw": "^2.0.0", "caw": "^2.0.0",
"color": "^1.0.3", "color": "^1.0.3",
"got": "^6.7.1", "got": "^7.0.0",
"nan": "^2.5.1", "nan": "^2.6.2",
"semver": "^5.3.0", "semver": "^5.3.0",
"tar": "^2.2.1" "tar": "^3.1.3"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.2.0", "async": "^2.4.1",
"bufferutil": "^3.0.0", "cc": "^1.0.1",
"cc": "^1.0.0", "documentation": "^4.0.0-rc.1",
"cross-env": "^4.0.0",
"documentation": "^4.0.0-beta.18",
"exif-reader": "^1.0.2", "exif-reader": "^1.0.2",
"icc": "^1.0.0", "icc": "^1.0.0",
"mocha": "^3.2.0", "mocha": "^3.4.2",
"nyc": "^10.2.0", "nyc": "^10.3.2",
"rimraf": "^2.5.4", "rimraf": "^2.6.1",
"semistandard": "^10.0.0", "semistandard": "^11.0.0",
"unzip": "^0.1.11" "unzip": "^0.1.11"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"config": { "config": {
"libvips": "8.4.2" "libvips": "8.5.5"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=4"
@@ -100,6 +101,7 @@
"cc": { "cc": {
"linelength": "120", "linelength": "120",
"filter": [ "filter": [
"build/c++11",
"build/include", "build/include",
"runtime/indentation_namespace" "runtime/indentation_namespace"
] ]

View File

@@ -23,6 +23,11 @@ if ! type docker >/dev/null; then
exit 1 exit 1
fi fi
# Update base images
for baseimage in debian:wheezy debian:jessie debian:stretch socialdefect/raspbian-jessie-core; do
docker pull $baseimage
done
# Windows (x64) # Windows (x64)
if [ $PLATFORM = "all" ] || [ $PLATFORM = "win32-x64" ]; then if [ $PLATFORM = "all" ] || [ $PLATFORM = "win32-x64" ]; then
echo "Building win32-x64..." echo "Building win32-x64..."

View File

@@ -16,27 +16,28 @@ export CFLAGS="${FLAGS}"
export CXXFLAGS="${FLAGS}" export CXXFLAGS="${FLAGS}"
# Dependency version numbers # Dependency version numbers
VERSION_ZLIB=1.2.10 VERSION_ZLIB=1.2.11
VERSION_FFI=3.2.1 VERSION_FFI=3.2.1
VERSION_GLIB=2.50.1 VERSION_GLIB=2.53.1
VERSION_XML2=2.9.4 VERSION_XML2=2.9.4
VERSION_GSF=1.14.40 VERSION_GSF=1.14.41
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.28 VERSION_PNG16=1.6.29
VERSION_WEBP=0.5.1 VERSION_WEBP=0.6.0
VERSION_TIFF=4.0.6 VERSION_TIFF=4.0.7
VERSION_ORC=0.4.26 VERSION_ORC=0.4.26
VERSION_GDKPIXBUF=2.36.0 VERSION_GDKPIXBUF=2.36.6
VERSION_FREETYPE=2.7 VERSION_FREETYPE=2.8
VERSION_EXPAT=2.2.0
VERSION_FONTCONFIG=2.12.1 VERSION_FONTCONFIG=2.12.1
VERSION_HARFBUZZ=1.3.2 VERSION_HARFBUZZ=1.4.6
VERSION_PIXMAN=0.34.0 VERSION_PIXMAN=0.34.0
VERSION_CAIRO=1.14.6 VERSION_CAIRO=1.14.8
VERSION_PANGO=1.40.3 VERSION_PANGO=1.40.5
VERSION_CROCO=0.6.11 VERSION_CROCO=0.6.12
VERSION_SVG=2.40.16 VERSION_SVG=2.40.17
VERSION_GIF=5.1.4 VERSION_GIF=5.1.4
# Least out-of-sync Sourceforge mirror # Least out-of-sync Sourceforge mirror
@@ -56,11 +57,12 @@ cd ${DEPS}/ffi
make install-strip make install-strip
mkdir ${DEPS}/glib mkdir ${DEPS}/glib
curl -Ls https://download.gnome.org/sources/glib/2.50/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 curl -Ls https://download.gnome.org/sources/glib/2.53/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1
cd ${DEPS}/glib cd ${DEPS}/glib
echo glib_cv_stack_grows=no >>glib.cache echo glib_cv_stack_grows=no >>glib.cache
echo glib_cv_uscore=no >>glib.cache echo glib_cv_uscore=no >>glib.cache
./configure --cache-file=glib.cache --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-pcre=internal --disable-libmount ./configure --cache-file=glib.cache --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--with-pcre=internal --disable-libmount
make install-strip make install-strip
mkdir ${DEPS}/xml2 mkdir ${DEPS}/xml2
@@ -87,6 +89,9 @@ make install-strip
mkdir ${DEPS}/lcms2 mkdir ${DEPS}/lcms2
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1
cd ${DEPS}/lcms2 cd ${DEPS}/lcms2
# Apply patches for lcms2 vulnerabilities reported since v2.8
VERSION_LCMS2_GIT_MASTER_SHA=$(curl -Ls https://api.github.com/repos/mm2/Little-CMS/git/refs/heads/master | jq -r '.object.sha' | head -c7)
curl -Ls https://github.com/mm2/Little-CMS/compare/lcms2.8...master.patch | patch -p1 -t || true
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking
make install-strip make install-strip
@@ -106,15 +111,16 @@ make install-strip
mkdir ${DEPS}/webp mkdir ${DEPS}/webp
curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1 curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1
cd ${DEPS}/webp cd ${DEPS}/webp
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-neon ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-neon --enable-libwebpmux
make install-strip make install-strip
mkdir ${DEPS}/tiff mkdir ${DEPS}/tiff
curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1
cd ${DEPS}/tiff cd ${DEPS}/tiff
# Apply patches for various libtiff security vulnerabilities reported since v4.0.6 # Apply patches for libtiff vulnerabilities reported since v4.0.7
VERSION_TIFF_GIT_MASTER_SHA=$(curl -Ls https://api.github.com/repos/vadz/libtiff/git/refs/heads/master | jq -r '.object.sha' | head -c7) VERSION_TIFF_GIT_MASTER_SHA=$(curl -Ls https://api.github.com/repos/vadz/libtiff/git/refs/heads/master | jq -r '.object.sha' | head -c7)
curl -Ls https://github.com/vadz/libtiff/compare/Release-v4-0-6...master.patch | patch -p1 -t || true curl -Ls https://github.com/vadz/libtiff/compare/Release-v4-0-7...master.patch | patch -p1 -t || true
if [ -n "${CHOST}" ]; then autoreconf -fiv; fi if [ -n "${CHOST}" ]; then autoreconf -fiv; fi
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-mdi --disable-pixarlog --disable-cxx ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-mdi --disable-pixarlog --disable-cxx
make install-strip make install-strip
@@ -130,8 +136,11 @@ rm -rf liborc-test-*
mkdir ${DEPS}/gdkpixbuf mkdir ${DEPS}/gdkpixbuf
curl -Ls https://download.gnome.org/sources/gdk-pixbuf/2.36/gdk-pixbuf-${VERSION_GDKPIXBUF}.tar.xz | tar xJC ${DEPS}/gdkpixbuf --strip-components=1 curl -Ls https://download.gnome.org/sources/gdk-pixbuf/2.36/gdk-pixbuf-${VERSION_GDKPIXBUF}.tar.xz | tar xJC ${DEPS}/gdkpixbuf --strip-components=1
cd ${DEPS}/gdkpixbuf cd ${DEPS}/gdkpixbuf
touch gdk-pixbuf/loaders.cache
LD_LIBRARY_PATH=${TARGET}/lib \ LD_LIBRARY_PATH=${TARGET}/lib \
./configure --cache-file=gdkpixbuf.cache --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-introspection --disable-modules --disable-gio-sniffing --without-libtiff --without-gdiplus --with-included-loaders=png,jpeg ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-introspection --disable-modules --disable-gio-sniffing \
--without-libtiff --without-gdiplus --with-included-loaders=png,jpeg
make install-strip make install-strip
mkdir ${DEPS}/freetype mkdir ${DEPS}/freetype
@@ -140,10 +149,17 @@ cd ${DEPS}/freetype
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static
make install make install
mkdir ${DEPS}/expat
curl -Ls http://${SOURCEFORGE_MIRROR}.dl.sourceforge.net/project/expat/expat/${VERSION_EXPAT}/expat-${VERSION_EXPAT}.tar.bz2 | tar xjC ${DEPS}/expat --strip-components=1
cd ${DEPS}/expat
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static
make install
mkdir ${DEPS}/fontconfig mkdir ${DEPS}/fontconfig
curl -Ls https://www.freedesktop.org/software/fontconfig/release/fontconfig-${VERSION_FONTCONFIG}.tar.bz2 | tar xjC ${DEPS}/fontconfig --strip-components=1 curl -Ls https://www.freedesktop.org/software/fontconfig/release/fontconfig-${VERSION_FONTCONFIG}.tar.bz2 | tar xjC ${DEPS}/fontconfig --strip-components=1
cd ${DEPS}/fontconfig cd ${DEPS}/fontconfig
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --enable-libxml2 --sysconfdir=/etc ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--with-expat-includes=${TARGET}/include --with-expat-lib=${TARGET}/lib --sysconfdir=/etc
make install-strip make install-strip
mkdir ${DEPS}/harfbuzz mkdir ${DEPS}/harfbuzz
@@ -191,7 +207,7 @@ cd ${DEPS}/gif
make install-strip make install-strip
mkdir ${DEPS}/vips mkdir ${DEPS}/vips
curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.4/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1 curl -Ls https://github.com/jcupitt/libvips/releases/download/v${VERSION_VIPS}/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1
cd ${DEPS}/vips cd ${DEPS}/vips
./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \ ./configure --host=${CHOST} --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \
--disable-debug --disable-introspection --without-python --without-fftw \ --disable-debug --disable-introspection --without-python --without-fftw \
@@ -212,6 +228,7 @@ echo "{\n\
\"cairo\": \"${VERSION_CAIRO}\",\n\ \"cairo\": \"${VERSION_CAIRO}\",\n\
\"croco\": \"${VERSION_CROCO}\",\n\ \"croco\": \"${VERSION_CROCO}\",\n\
\"exif\": \"${VERSION_EXIF}\",\n\ \"exif\": \"${VERSION_EXIF}\",\n\
\"expat\": \"${VERSION_EXPAT}\",\n\
\"ffi\": \"${VERSION_FFI}\",\n\ \"ffi\": \"${VERSION_FFI}\",\n\
\"fontconfig\": \"${VERSION_FONTCONFIG}\",\n\ \"fontconfig\": \"${VERSION_FONTCONFIG}\",\n\
\"freetype\": \"${VERSION_FREETYPE}\",\n\ \"freetype\": \"${VERSION_FREETYPE}\",\n\
@@ -221,7 +238,7 @@ echo "{\n\
\"gsf\": \"${VERSION_GSF}\",\n\ \"gsf\": \"${VERSION_GSF}\",\n\
\"harfbuzz\": \"${VERSION_HARFBUZZ}\",\n\ \"harfbuzz\": \"${VERSION_HARFBUZZ}\",\n\
\"jpeg\": \"${VERSION_JPEG}\",\n\ \"jpeg\": \"${VERSION_JPEG}\",\n\
\"lcms\": \"${VERSION_LCMS2}\",\n\ \"lcms\": \"${VERSION_LCMS2}-${VERSION_LCMS2_GIT_MASTER_SHA}\",\n\
\"orc\": \"${VERSION_ORC}\",\n\ \"orc\": \"${VERSION_ORC}\",\n\
\"pango\": \"${VERSION_PANGO}\",\n\ \"pango\": \"${VERSION_PANGO}\",\n\
\"pixman\": \"${VERSION_PIXMAN}\",\n\ \"pixman\": \"${VERSION_PIXMAN}\",\n\

View File

@@ -8,14 +8,11 @@ curl -L -O https://github.com/lovell/build-win64/releases/download/v${VERSION_VI
unzip vips-dev-w64-web-${VERSION_VIPS}.zip unzip vips-dev-w64-web-${VERSION_VIPS}.zip
# Clean and zip # Clean and zip
cd /vips/vips-dev-8.4 cd /vips/vips-dev-8.5
rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll rm bin/libvipsCC-42.dll bin/libvips-cpp-42.dll bin/libgsf-win32-1-114.dll
cp bin/*.dll lib/ cp bin/*.dll lib/
cp -r lib64/* lib/ cp -r lib64/* lib/
# Temp patch for __declspec ordering
curl -L -o include/vips/VImage8.h https://raw.githubusercontent.com/lovell/libvips/e1aef0445bf123d2de000bc7f2ef97b9f788eea0/cplusplus/include/vips/VImage8.h
echo "Creating tarball" echo "Creating tarball"
tar czf /packaging/libvips-${VERSION_VIPS}-${PLATFORM}.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll tar czf /packaging/libvips-${VERSION_VIPS}-${PLATFORM}.tar.gz include lib/glib-2.0 lib/libvips.lib lib/libglib-2.0.lib lib/libgobject-2.0.lib lib/*.dll
echo "Shrinking tarball" echo "Shrinking tarball"

View File

@@ -9,10 +9,10 @@ RUN \
apt-get install -y curl && \ apt-get install -y curl && \
dpkg --add-architecture arm64 && \ dpkg --add-architecture arm64 && \
apt-get update && \ apt-get update && \
apt-get install -y crossbuild-essential-arm64 autoconf libtool nasm gtk-doc-tools texinfo advancecomp libglib2.0-dev jq apt-get install -y crossbuild-essential-arm64 autoconf libtool nasm gtk-doc-tools texinfo advancecomp libglib2.0-dev jq gettext intltool autopoint
# Compiler settings # Compiler settings
ENV \ ENV \
PLATFORM=linux-armv8 \ PLATFORM=linux-armv8 \
CHOST=aarch64-linux-gnu \ CHOST=aarch64-linux-gnu \
FLAGS="-march=armv8-a -Os" FLAGS="-march=armv8-a -Os -D_GLIBCXX_USE_CXX11_ABI=0"

View File

@@ -6,15 +6,14 @@ if ! type docker >/dev/null; then
exit 1 exit 1
fi fi
version_node=6.3.0
test="npm run clean; npm install --unsafe-perm; npm test" test="npm run clean; npm install --unsafe-perm; npm test"
# Debian 7, 8 # Debian 7, 8
# Ubuntu 14.04 # Ubuntu 14.04, 16.04
for dist in wheezy jessie trusty; do for dist in debian:jessie debian:stretch ubuntu:trusty ubuntu:xenial; do
echo "Testing $dist..." echo "Testing $dist..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/$dist:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; ./packaging/test/debian.sh; $test"; docker pull $dist
if docker run -i -t --rm -v $PWD:/v $dist >packaging/$dist.log 2>&1 sh -c "cd /v; ./packaging/test/debian.sh; $test";
then echo "$dist OK" then echo "$dist OK"
else echo "$dist fail" && cat packaging/$dist.log else echo "$dist fail" && cat packaging/$dist.log
fi fi
@@ -22,35 +21,16 @@ done
# Centos 7 # Centos 7
echo "Testing centos7..." echo "Testing centos7..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/centos7:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; $test"; docker pull centos:7
if docker run -i -t --rm -v $PWD:/v centos:7 >packaging/centos7.log 2>&1 sh -c "cd /v; ./packaging/test/centos.sh; $test";
then echo "centos7 OK" then echo "centos7 OK"
else echo "centos7 fail" && cat packaging/$dist.log else echo "centos7 fail" && cat packaging/centos7.log
fi fi
# Fedora 22 # Archlinux latest
echo "Testing fedora22..."
if docker run -i -t --rm -v $PWD:/v -e "NODE_ENV=development" nodesource/fedora22:$version_node >packaging/$dist.log 2>&1 sh -c "cd /v; $test";
then echo "fedora22 OK"
else echo "fedora22 fail" && cat packaging/$dist.log
fi
# openSUSE 13.2
echo "Testing opensuse..."
if docker run -i -t --rm -v $PWD:/v opensuse:13.2 >packaging/opensuse.log 2>&1 /bin/sh -c "cd /v; ./packaging/test/opensuse.sh; $test";
then echo "opensuse OK"
else echo "opensuse fail" && cat packaging/opensuse.log
fi
# Archlinux 2015.06.01
echo "Testing archlinux..." echo "Testing archlinux..."
docker pull pritunl/archlinux:latest
if docker run -i -t --rm -v $PWD:/v pritunl/archlinux:latest >packaging/archlinux.log 2>&1 sh -c "cd /v; ./packaging/test/archlinux.sh; $test"; if docker run -i -t --rm -v $PWD:/v pritunl/archlinux:latest >packaging/archlinux.log 2>&1 sh -c "cd /v; ./packaging/test/archlinux.sh; $test";
then echo "archlinux OK" then echo "archlinux OK"
else echo "archlinux fail" && cat packaging/archlinux.log else echo "archlinux fail" && cat packaging/archlinux.log
fi fi
# Alpine
echo "Testing alpine..."
if docker run -i -t --rm -v $PWD:/v -e "SHARP_TEST_WITHOUT_CACHE=0" alpine:edge >packaging/alpine.log 2>&1 sh -c "cd /v; ./packaging/test/alpine.sh; $test";
then echo "alpine OK"
else echo "alpine fail" && cat packaging/alpine.log
fi

View File

@@ -1,7 +0,0 @@
#!/bin/sh
# Install build dependencies
apk add --update make gcc g++ python nodejs
# Install libvips from aports/testing
apk add --update --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing vips-dev

4
packaging/test/centos.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
curl -sL https://rpm.nodesource.com/setup_6.x | bash -
yum install -y gcc-c++ make nodejs

View File

@@ -1,5 +1,6 @@
#!/bin/sh #!/bin/sh
# Install pkg-config on Debian/Ubuntu
apt-get update apt-get update
apt-get install -y pkg-config apt-get install -y build-essential python pkg-config curl
curl -sL https://deb.nodesource.com/setup_6.x | bash -
apt-get install -y nodejs

View File

@@ -1,7 +0,0 @@
#!/bin/sh
# Install Node.js on openSUSE 13.2
zypper addrepo http://download.opensuse.org/repositories/devel:languages:nodejs/openSUSE_13.2/devel:languages:nodejs.repo
zypper --gpg-auto-import-keys refresh
zypper --non-interactive install gcc-c++ make nodejs-devel npm
npm install -g npm

View File

@@ -1,4 +1,4 @@
FROM debian:jessie FROM debian:stretch
MAINTAINER Lovell Fuller <npm@lovell.info> MAINTAINER Lovell Fuller <npm@lovell.info>
# Create Debian-based container suitable for post-processing Windows x64 binaries # Create Debian-based container suitable for post-processing Windows x64 binaries

View File

@@ -16,6 +16,8 @@
#include <string> #include <string>
#include <string.h> #include <string.h>
#include <vector> #include <vector>
#include <queue>
#include <mutex>
#include <node.h> #include <node.h>
#include <node_buffer.h> #include <node_buffer.h>
@@ -345,6 +347,25 @@ namespace sharp {
image.set(VIPS_META_RESOLUTION_UNIT, "in"); image.set(VIPS_META_RESOLUTION_UNIT, "in");
} }
/*
Check the proposed format supports the current dimensions.
*/
void AssertImageTypeDimensions(VImage image, ImageType const imageType) {
if (imageType == ImageType::JPEG) {
if (image.width() > 65535 || image.height() > 65535) {
throw vips::VError("Processed image is too large for the JPEG format");
}
} else if (imageType == ImageType::PNG) {
if (image.width() > 2147483647 || image.height() > 2147483647) {
throw vips::VError("Processed image is too large for the PNG format");
}
} else if (imageType == ImageType::WEBP) {
if (image.width() > 16383 || image.height() > 16383) {
throw vips::VError("Processed image is too large for the WebP format");
}
}
}
/* /*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/ */
@@ -354,6 +375,33 @@ namespace sharp {
} }
} }
/*
Temporary buffer of warnings
*/
std::queue<std::string> vipsWarnings;
std::mutex vipsWarningsMutex;
/*
Called with warnings from the glib-registered "VIPS" domain
*/
void VipsWarningCallback(char const* log_domain, GLogLevelFlags log_level, char const* message, void* ignore) {
std::lock_guard<std::mutex> lock(vipsWarningsMutex);
vipsWarnings.emplace(message);
}
/*
Pop the oldest warning message from the queue
*/
std::string VipsWarningPop() {
std::string warning;
std::lock_guard<std::mutex> lock(vipsWarningsMutex);
if (!vipsWarnings.empty()) {
warning = vipsWarnings.front();
vipsWarnings.pop();
}
return warning;
}
/* /*
Calculate the (left, top) coordinates of the output image Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity. within the input image, applying the given gravity.

View File

@@ -25,8 +25,8 @@
// Verify platform and compiler compatibility // Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 4)) #if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 5))
#error libvips version 8.4.x required - see sharp.dimens.io/page/install #error libvips version 8.5.x required - see sharp.dimens.io/page/install
#endif #endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6))) #if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))
@@ -184,11 +184,26 @@ namespace sharp {
*/ */
void SetDensity(VImage image, const int density); void SetDensity(VImage image, const int density);
/*
Check the proposed format supports the current dimensions.
*/
void AssertImageTypeDimensions(VImage image, ImageType const imageType);
/* /*
Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows Called when a Buffer undergoes GC, required to support mixed runtime libraries in Windows
*/ */
void FreeCallback(char* data, void* hint); void FreeCallback(char* data, void* hint);
/*
Called with warnings from the glib-registered "VIPS" domain
*/
void VipsWarningCallback(char const* log_domain, GLogLevelFlags log_level, char const* message, void* ignore);
/*
Pop the oldest warning message from the queue
*/
std::string VipsWarningPop();
/* /*
Calculate the (left, top) coordinates of the output image Calculate the (left, top) coordinates of the output image
within the input image, applying the given gravity. within the input image, applying the given gravity.

View File

@@ -350,7 +350,7 @@ set_property( VipsObject *object, const char *name, const GValue *value )
if( vips_object_get_argument( object, name, if( vips_object_get_argument( object, name,
&pspec, &argument_class, &argument_instance ) ) { &pspec, &argument_class, &argument_instance ) ) {
vips_warn( NULL, "%s", vips_error_buffer() ); g_warning( "%s", vips_error_buffer() );
vips_error_clear(); vips_error_clear();
return; return;
} }
@@ -364,7 +364,7 @@ set_property( VipsObject *object, const char *name, const GValue *value )
if( (enum_value = vips_enum_from_nick( object_class->nickname, if( (enum_value = vips_enum_from_nick( object_class->nickname,
pspec_type, g_value_get_string( value ) )) < 0 ) { pspec_type, g_value_get_string( value ) )) < 0 ) {
vips_warn( NULL, "%s", vips_error_buffer() ); g_warning( "%s", vips_error_buffer() );
vips_error_clear(); vips_error_clear();
return; return;
} }
@@ -770,19 +770,19 @@ operator+( VImage a, std::vector<double> b )
} }
VImage & VImage &
operator+=( VImage a, const VImage b ) operator+=( VImage &a, const VImage b )
{ {
return( a = a + b ); return( a = a + b );
} }
VImage & VImage &
operator+=( VImage a, const double b ) operator+=( VImage &a, const double b )
{ {
return( a = a + b ); return( a = a + b );
} }
VImage & VImage &
operator+=( VImage a, std::vector<double> b ) operator+=( VImage &a, std::vector<double> b )
{ {
return( a = a + b ); return( a = a + b );
} }
@@ -818,19 +818,19 @@ operator-( VImage a, std::vector<double> b )
} }
VImage & VImage &
operator-=( VImage a, const VImage b ) operator-=( VImage &a, const VImage b )
{ {
return( a = a - b ); return( a = a - b );
} }
VImage & VImage &
operator-=( VImage a, const double b ) operator-=( VImage &a, const double b )
{ {
return( a = a - b ); return( a = a - b );
} }
VImage & VImage &
operator-=( VImage a, std::vector<double> b ) operator-=( VImage &a, std::vector<double> b )
{ {
return( a = a - b ); return( a = a - b );
} }
@@ -872,19 +872,19 @@ operator*( VImage a, std::vector<double> b )
} }
VImage & VImage &
operator*=( VImage a, const VImage b ) operator*=( VImage &a, const VImage b )
{ {
return( a = a * b ); return( a = a * b );
} }
VImage & VImage &
operator*=( VImage a, const double b ) operator*=( VImage &a, const double b )
{ {
return( a = a * b ); return( a = a * b );
} }
VImage & VImage &
operator*=( VImage a, std::vector<double> b ) operator*=( VImage &a, std::vector<double> b )
{ {
return( a = a * b ); return( a = a * b );
} }
@@ -920,19 +920,19 @@ operator/( VImage a, std::vector<double> b )
} }
VImage & VImage &
operator/=( VImage a, const VImage b ) operator/=( VImage &a, const VImage b )
{ {
return( a = a / b ); return( a = a / b );
} }
VImage & VImage &
operator/=( VImage a, const double b ) operator/=( VImage &a, const double b )
{ {
return( a = a / b ); return( a = a / b );
} }
VImage & VImage &
operator/=( VImage a, std::vector<double> b ) operator/=( VImage &a, std::vector<double> b )
{ {
return( a = a / b ); return( a = a / b );
} }
@@ -956,19 +956,19 @@ operator%( VImage a, std::vector<double> b )
} }
VImage & VImage &
operator%=( VImage a, const VImage b ) operator%=( VImage &a, const VImage b )
{ {
return( a = a % b ); return( a = a % b );
} }
VImage & VImage &
operator%=( VImage a, const double b ) operator%=( VImage &a, const double b )
{ {
return( a = a % b ); return( a = a % b );
} }
VImage & VImage &
operator%=( VImage a, std::vector<double> b ) operator%=( VImage &a, std::vector<double> b )
{ {
return( a = a % b ); return( a = a % b );
} }
@@ -982,29 +982,29 @@ operator<( VImage a, VImage b )
VImage VImage
operator<( double a, VImage b ) operator<( double a, VImage b )
{ {
return( b.relational_const( to_vector( a ), return( b.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
VIPS_OPERATION_RELATIONAL_MORE ) ); to_vector( a ) ) );
} }
VImage VImage
operator<( VImage a, double b ) operator<( VImage a, double b )
{ {
return( a.relational_const( to_vector( b ), return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
VIPS_OPERATION_RELATIONAL_LESS ) ); to_vector( b ) ) );
} }
VImage VImage
operator<( std::vector<double> a, VImage b ) operator<( std::vector<double> a, VImage b )
{ {
return( b.relational_const( a, return( b.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
VIPS_OPERATION_RELATIONAL_MORE ) ); a ) );
} }
VImage VImage
operator<( VImage a, std::vector<double> b ) operator<( VImage a, std::vector<double> b )
{ {
return( a.relational_const( b, return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
VIPS_OPERATION_RELATIONAL_LESS ) ); b ) );
} }
VImage VImage
@@ -1016,29 +1016,29 @@ operator<=( VImage a, VImage b )
VImage VImage
operator<=( double a, VImage b ) operator<=( double a, VImage b )
{ {
return( b.relational_const( to_vector( a ), return( b.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
VIPS_OPERATION_RELATIONAL_MOREEQ ) ); to_vector( a ) ) );
} }
VImage VImage
operator<=( VImage a, double b ) operator<=( VImage a, double b )
{ {
return( a.relational_const( to_vector( b ), return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
VIPS_OPERATION_RELATIONAL_LESSEQ ) ); to_vector( b ) ) );
} }
VImage VImage
operator<=( std::vector<double> a, VImage b ) operator<=( std::vector<double> a, VImage b )
{ {
return( b.relational_const( a, return( b.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
VIPS_OPERATION_RELATIONAL_MOREEQ ) ); a ) );
} }
VImage VImage
operator<=( VImage a, std::vector<double> b ) operator<=( VImage a, std::vector<double> b )
{ {
return( a.relational_const( b, return( a.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
VIPS_OPERATION_RELATIONAL_LESSEQ ) ); b ) );
} }
VImage VImage
@@ -1050,29 +1050,29 @@ operator>( VImage a, VImage b )
VImage VImage
operator>( double a, VImage b ) operator>( double a, VImage b )
{ {
return( b.relational_const( to_vector( a ), return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
VIPS_OPERATION_RELATIONAL_LESS ) ); to_vector( a ) ) );
} }
VImage VImage
operator>( VImage a, double b ) operator>( VImage a, double b )
{ {
return( a.relational_const( to_vector( b ), return( a.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
VIPS_OPERATION_RELATIONAL_MORE ) ); to_vector( b ) ) );
} }
VImage VImage
operator>( std::vector<double> a, VImage b ) operator>( std::vector<double> a, VImage b )
{ {
return( b.relational_const( a, return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESS,
VIPS_OPERATION_RELATIONAL_LESS ) ); a ) );
} }
VImage VImage
operator>( VImage a, std::vector<double> b ) operator>( VImage a, std::vector<double> b )
{ {
return( a.relational_const( b, return( a.relational_const( VIPS_OPERATION_RELATIONAL_MORE,
VIPS_OPERATION_RELATIONAL_MORE ) ); b ) );
} }
VImage VImage
@@ -1084,29 +1084,29 @@ operator>=( VImage a, VImage b )
VImage VImage
operator>=( double a, VImage b ) operator>=( double a, VImage b )
{ {
return( b.relational_const( to_vector( a ), return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
VIPS_OPERATION_RELATIONAL_LESSEQ ) ); to_vector( a ) ) );
} }
VImage VImage
operator>=( VImage a, double b ) operator>=( VImage a, double b )
{ {
return( a.relational_const( to_vector( b ), return( a.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
VIPS_OPERATION_RELATIONAL_MOREEQ ) ); to_vector( b ) ) );
} }
VImage VImage
operator>=( std::vector<double> a, VImage b ) operator>=( std::vector<double> a, VImage b )
{ {
return( b.relational_const( a, return( b.relational_const( VIPS_OPERATION_RELATIONAL_LESSEQ,
VIPS_OPERATION_RELATIONAL_LESSEQ ) ); a ) );
} }
VImage VImage
operator>=( VImage a, std::vector<double> b ) operator>=( VImage a, std::vector<double> b )
{ {
return( a.relational_const( b, return( a.relational_const( VIPS_OPERATION_RELATIONAL_MOREEQ,
VIPS_OPERATION_RELATIONAL_MOREEQ ) ); b ) );
} }
VImage VImage
@@ -1118,29 +1118,29 @@ operator==( VImage a, VImage b )
VImage VImage
operator==( double a, VImage b ) operator==( double a, VImage b )
{ {
return( b.relational_const( to_vector( a ), return( b.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
VIPS_OPERATION_RELATIONAL_EQUAL ) ); to_vector( a ) ) );
} }
VImage VImage
operator==( VImage a, double b ) operator==( VImage a, double b )
{ {
return( a.relational_const( to_vector( b ), return( a.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
VIPS_OPERATION_RELATIONAL_EQUAL ) ); to_vector( b ) ) );
} }
VImage VImage
operator==( std::vector<double> a, VImage b ) operator==( std::vector<double> a, VImage b )
{ {
return( b.relational_const( a, return( b.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
VIPS_OPERATION_RELATIONAL_EQUAL ) ); a ) );
} }
VImage VImage
operator==( VImage a, std::vector<double> b ) operator==( VImage a, std::vector<double> b )
{ {
return( a.relational_const( b, return( a.relational_const( VIPS_OPERATION_RELATIONAL_EQUAL,
VIPS_OPERATION_RELATIONAL_EQUAL ) ); b ) );
} }
VImage VImage
@@ -1152,29 +1152,29 @@ operator!=( VImage a, VImage b )
VImage VImage
operator!=( double a, VImage b ) operator!=( double a, VImage b )
{ {
return( b.relational_const( to_vector( a ), return( b.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
VIPS_OPERATION_RELATIONAL_NOTEQ ) ); to_vector( a ) ) );
} }
VImage VImage
operator!=( VImage a, double b ) operator!=( VImage a, double b )
{ {
return( a.relational_const( to_vector( b ), return( a.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
VIPS_OPERATION_RELATIONAL_NOTEQ ) ); to_vector( b ) ) );
} }
VImage VImage
operator!=( std::vector<double> a, VImage b ) operator!=( std::vector<double> a, VImage b )
{ {
return( b.relational_const( a, return( b.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
VIPS_OPERATION_RELATIONAL_NOTEQ ) ); a ) );
} }
VImage VImage
operator!=( VImage a, std::vector<double> b ) operator!=( VImage a, std::vector<double> b )
{ {
return( a.relational_const( b, return( a.relational_const( VIPS_OPERATION_RELATIONAL_NOTEQ,
VIPS_OPERATION_RELATIONAL_NOTEQ ) ); b ) );
} }
VImage VImage
@@ -1186,43 +1186,43 @@ operator&( VImage a, VImage b )
VImage VImage
operator&( double a, VImage b ) operator&( double a, VImage b )
{ {
return( b.boolean_const( to_vector( a ), return( b.boolean_const( VIPS_OPERATION_BOOLEAN_AND,
VIPS_OPERATION_BOOLEAN_AND ) ); to_vector( a ) ) );
} }
VImage VImage
operator&( VImage a, double b ) operator&( VImage a, double b )
{ {
return( a.boolean_const( to_vector( b ), return( a.boolean_const( VIPS_OPERATION_BOOLEAN_AND,
VIPS_OPERATION_BOOLEAN_AND ) ); to_vector( b ) ) );
} }
VImage VImage
operator&( std::vector<double> a, VImage b ) operator&( std::vector<double> a, VImage b )
{ {
return( b.boolean_const( a, VIPS_OPERATION_BOOLEAN_AND ) ); return( b.boolean_const( VIPS_OPERATION_BOOLEAN_AND, a ) );
} }
VImage VImage
operator&( VImage a, std::vector<double> b ) operator&( VImage a, std::vector<double> b )
{ {
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_AND ) ); return( a.boolean_const( VIPS_OPERATION_BOOLEAN_AND, b ) );
} }
VImage & VImage &
operator&=( VImage a, const VImage b ) operator&=( VImage &a, const VImage b )
{ {
return( a = a & b ); return( a = a & b );
} }
VImage & VImage &
operator&=( VImage a, const double b ) operator&=( VImage &a, const double b )
{ {
return( a = a & b ); return( a = a & b );
} }
VImage & VImage &
operator&=( VImage a, std::vector<double> b ) operator&=( VImage &a, std::vector<double> b )
{ {
return( a = a & b ); return( a = a & b );
} }
@@ -1236,43 +1236,45 @@ operator|( VImage a, VImage b )
VImage VImage
operator|( double a, VImage b ) operator|( double a, VImage b )
{ {
return( b.boolean_const( to_vector( a ), return( b.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
VIPS_OPERATION_BOOLEAN_OR ) ); to_vector( a ) ) );
} }
VImage VImage
operator|( VImage a, double b ) operator|( VImage a, double b )
{ {
return( a.boolean_const( to_vector( b ), return( a.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
VIPS_OPERATION_BOOLEAN_OR ) ); to_vector( b ) ) );
} }
VImage VImage
operator|( std::vector<double> a, VImage b ) operator|( std::vector<double> a, VImage b )
{ {
return( b.boolean_const( a, VIPS_OPERATION_BOOLEAN_OR ) ); return( b.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
a ) );
} }
VImage VImage
operator|( VImage a, std::vector<double> b ) operator|( VImage a, std::vector<double> b )
{ {
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_OR ) ); return( a.boolean_const( VIPS_OPERATION_BOOLEAN_OR,
b ) );
} }
VImage & VImage &
operator|=( VImage a, const VImage b ) operator|=( VImage &a, const VImage b )
{ {
return( a = a | b ); return( a = a | b );
} }
VImage & VImage &
operator|=( VImage a, const double b ) operator|=( VImage &a, const double b )
{ {
return( a = a | b ); return( a = a | b );
} }
VImage & VImage &
operator|=( VImage a, std::vector<double> b ) operator|=( VImage &a, std::vector<double> b )
{ {
return( a = a | b ); return( a = a | b );
} }
@@ -1286,43 +1288,45 @@ operator^( VImage a, VImage b )
VImage VImage
operator^( double a, VImage b ) operator^( double a, VImage b )
{ {
return( b.boolean_const( to_vector( a ), return( b.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
VIPS_OPERATION_BOOLEAN_EOR ) ); to_vector( a ) ) );
} }
VImage VImage
operator^( VImage a, double b ) operator^( VImage a, double b )
{ {
return( a.boolean_const( to_vector( b ), return( a.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
VIPS_OPERATION_BOOLEAN_EOR ) ); to_vector( b ) ) );
} }
VImage VImage
operator^( std::vector<double> a, VImage b ) operator^( std::vector<double> a, VImage b )
{ {
return( b.boolean_const( a, VIPS_OPERATION_BOOLEAN_EOR ) ); return( b.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
a ) );
} }
VImage VImage
operator^( VImage a, std::vector<double> b ) operator^( VImage a, std::vector<double> b )
{ {
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_EOR ) ); return( a.boolean_const( VIPS_OPERATION_BOOLEAN_EOR,
b ) );
} }
VImage & VImage &
operator^=( VImage a, const VImage b ) operator^=( VImage &a, const VImage b )
{ {
return( a = a ^ b ); return( a = a ^ b );
} }
VImage & VImage &
operator^=( VImage a, const double b ) operator^=( VImage &a, const double b )
{ {
return( a = a ^ b ); return( a = a ^ b );
} }
VImage & VImage &
operator^=( VImage a, std::vector<double> b ) operator^=( VImage &a, std::vector<double> b )
{ {
return( a = a ^ b ); return( a = a ^ b );
} }
@@ -1336,30 +1340,31 @@ operator<<( VImage a, VImage b )
VImage VImage
operator<<( VImage a, double b ) operator<<( VImage a, double b )
{ {
return( a.boolean_const( to_vector( b ), return( a.boolean_const( VIPS_OPERATION_BOOLEAN_LSHIFT,
VIPS_OPERATION_BOOLEAN_LSHIFT ) ); to_vector( b ) ) );
} }
VImage VImage
operator<<( VImage a, std::vector<double> b ) operator<<( VImage a, std::vector<double> b )
{ {
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_LSHIFT ) ); return( a.boolean_const( VIPS_OPERATION_BOOLEAN_LSHIFT,
b ) );
} }
VImage & VImage &
operator<<=( VImage a, const VImage b ) operator<<=( VImage &a, const VImage b )
{ {
return( a = a << b ); return( a = a << b );
} }
VImage & VImage &
operator<<=( VImage a, const double b ) operator<<=( VImage &a, const double b )
{ {
return( a = a << b ); return( a = a << b );
} }
VImage & VImage &
operator<<=( VImage a, std::vector<double> b ) operator<<=( VImage &a, std::vector<double> b )
{ {
return( a = a << b ); return( a = a << b );
} }
@@ -1373,30 +1378,31 @@ operator>>( VImage a, VImage b )
VImage VImage
operator>>( VImage a, double b ) operator>>( VImage a, double b )
{ {
return( a.boolean_const( to_vector( b ), return( a.boolean_const( VIPS_OPERATION_BOOLEAN_RSHIFT,
VIPS_OPERATION_BOOLEAN_RSHIFT ) ); to_vector( b ) ) );
} }
VImage VImage
operator>>( VImage a, std::vector<double> b ) operator>>( VImage a, std::vector<double> b )
{ {
return( a.boolean_const( b, VIPS_OPERATION_BOOLEAN_RSHIFT ) ); return( a.boolean_const( VIPS_OPERATION_BOOLEAN_RSHIFT,
b ) );
} }
VImage & VImage &
operator>>=( VImage a, const VImage b ) operator>>=( VImage &a, const VImage b )
{ {
return( a = a << b ); return( a = a << b );
} }
VImage & VImage &
operator>>=( VImage a, const double b ) operator>>=( VImage &a, const double b )
{ {
return( a = a << b ); return( a = a << b );
} }
VImage & VImage &
operator>>=( VImage a, std::vector<double> b ) operator>>=( VImage &a, std::vector<double> b )
{ {
return( a = a << b ); return( a = a << b );
} }

View File

@@ -1,5 +1,5 @@
// bodies for vips operations // bodies for vips operations
// Thu 18 Aug 16:01:57 BST 2016 // Mon 13 Mar 13:22:17 GMT 2017
// this file is generated automatically, do not edit! // this file is generated automatically, do not edit!
void VImage::system( char * cmd_format , VOption *options ) void VImage::system( char * cmd_format , VOption *options )
@@ -231,7 +231,7 @@ VImage VImage::round( VipsOperationRound round , VOption *options )
return( out ); return( out );
} }
VImage VImage::relational_const( std::vector<double> c , VipsOperationRelational relational , VOption *options ) VImage VImage::relational_const( VipsOperationRelational relational , std::vector<double> c , VOption *options )
{ {
VImage out; VImage out;
@@ -239,8 +239,8 @@ VImage VImage::relational_const( std::vector<double> c , VipsOperationRelational
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "c", c ) -> set( "relational", relational ) ->
set( "relational", relational ) ); set( "c", c ) );
return( out ); return( out );
} }
@@ -258,7 +258,7 @@ VImage VImage::remainder_const( std::vector<double> c , VOption *options )
return( out ); return( out );
} }
VImage VImage::boolean_const( std::vector<double> c , VipsOperationBoolean boolean , VOption *options ) VImage VImage::boolean_const( VipsOperationBoolean boolean , std::vector<double> c , VOption *options )
{ {
VImage out; VImage out;
@@ -266,13 +266,13 @@ VImage VImage::boolean_const( std::vector<double> c , VipsOperationBoolean boole
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "c", c ) -> set( "boolean", boolean ) ->
set( "boolean", boolean ) ); set( "c", c ) );
return( out ); return( out );
} }
VImage VImage::math2_const( std::vector<double> c , VipsOperationMath2 math2 , VOption *options ) VImage VImage::math2_const( VipsOperationMath2 math2 , std::vector<double> c , VOption *options )
{ {
VImage out; VImage out;
@@ -280,8 +280,8 @@ VImage VImage::math2_const( std::vector<double> c , VipsOperationMath2 math2 , V
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "c", c ) -> set( "math2", math2 ) ->
set( "math2", math2 ) ); set( "c", c ) );
return( out ); return( out );
} }
@@ -493,8 +493,8 @@ VImage VImage::copy( VOption *options )
call( "copy" , call( "copy" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -505,8 +505,8 @@ VImage VImage::tilecache( VOption *options )
call( "tilecache" , call( "tilecache" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -517,8 +517,8 @@ VImage VImage::linecache( VOption *options )
call( "linecache" , call( "linecache" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -529,8 +529,8 @@ VImage VImage::sequential( VOption *options )
call( "sequential" , call( "sequential" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -541,8 +541,8 @@ VImage VImage::cache( VOption *options )
call( "cache" , call( "cache" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -569,8 +569,8 @@ VImage VImage::flip( VipsDirection direction , VOption *options )
call( "flip" , call( "flip" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) ->
set( "direction", direction ) ); set( "direction", direction ) );
return( out ); return( out );
@@ -633,6 +633,20 @@ VImage VImage::extract_area( int left , int top , int width , int height , VOpti
return( out ); return( out );
} }
VImage VImage::smartcrop( int width , int height , VOption *options )
{
VImage out;
call( "smartcrop" ,
(options ? options : VImage::option()) ->
set( "input", *this ) ->
set( "out", &out ) ->
set( "width", width ) ->
set( "height", height ) );
return( out );
}
VImage VImage::extract_band( int band , VOption *options ) VImage VImage::extract_band( int band , VOption *options )
{ {
VImage out; VImage out;
@@ -728,8 +742,8 @@ VImage VImage::cast( VipsBandFormat format , VOption *options )
call( "cast" , call( "cast" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) ->
set( "format", format ) ); set( "format", format ) );
return( out ); return( out );
@@ -741,8 +755,8 @@ VImage VImage::rot( VipsAngle angle , VOption *options )
call( "rot" , call( "rot" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) ->
set( "angle", angle ) ); set( "angle", angle ) );
return( out ); return( out );
@@ -754,8 +768,8 @@ VImage VImage::rot45( VOption *options )
call( "rot45" , call( "rot45" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -766,8 +780,8 @@ VImage VImage::autorot( VOption *options )
call( "autorot" , call( "autorot" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -805,8 +819,8 @@ VImage VImage::bandfold( VOption *options )
call( "bandfold" , call( "bandfold" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -817,8 +831,8 @@ VImage VImage::bandunfold( VOption *options )
call( "bandunfold" , call( "bandunfold" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -829,8 +843,8 @@ VImage VImage::flatten( VOption *options )
call( "flatten" , call( "flatten" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -841,8 +855,8 @@ VImage VImage::premultiply( VOption *options )
call( "premultiply" , call( "premultiply" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -853,8 +867,8 @@ VImage VImage::unpremultiply( VOption *options )
call( "unpremultiply" , call( "unpremultiply" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -865,8 +879,8 @@ VImage VImage::grid( int tile_height , int across , int down , VOption *options
call( "grid" , call( "grid" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) ->
set( "tile-height", tile_height ) -> set( "tile-height", tile_height ) ->
set( "across", across ) -> set( "across", across ) ->
set( "down", down ) ); set( "down", down ) );
@@ -880,8 +894,8 @@ VImage VImage::scale( VOption *options )
call( "scale" , call( "scale" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -892,8 +906,8 @@ VImage VImage::wrap( VOption *options )
call( "wrap" , call( "wrap" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -944,8 +958,8 @@ VImage VImage::byteswap( VOption *options )
call( "byteswap" , call( "byteswap" ,
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "out", &out ) -> set( "in", *this ) ->
set( "in", *this ) ); set( "out", &out ) );
return( out ); return( out );
} }
@@ -1757,6 +1771,18 @@ void VImage::dzsave( char * filename , VOption *options )
set( "filename", filename ) ); set( "filename", filename ) );
} }
VipsBlob * VImage::dzsave_buffer( VOption *options )
{
VipsBlob * buffer;
call( "dzsave_buffer" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "buffer", &buffer ) );
return( buffer );
}
void VImage::pngsave( char * filename , VOption *options ) void VImage::pngsave( char * filename , VOption *options )
{ {
call( "pngsave" , call( "pngsave" ,
@@ -1832,6 +1858,18 @@ void VImage::tiffsave( char * filename , VOption *options )
set( "filename", filename ) ); set( "filename", filename ) );
} }
VipsBlob * VImage::tiffsave_buffer( VOption *options )
{
VipsBlob * buffer;
call( "tiffsave_buffer" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "buffer", &buffer ) );
return( buffer );
}
void VImage::fitssave( char * filename , VOption *options ) void VImage::fitssave( char * filename , VOption *options )
{ {
call( "fitssave" , call( "fitssave" ,
@@ -1840,6 +1878,32 @@ void VImage::fitssave( char * filename , VOption *options )
set( "filename", filename ) ); set( "filename", filename ) );
} }
VImage VImage::thumbnail( char * filename , int width , VOption *options )
{
VImage out;
call( "thumbnail" ,
(options ? options : VImage::option()) ->
set( "filename", filename ) ->
set( "out", &out ) ->
set( "width", width ) );
return( out );
}
VImage VImage::thumbnail_buffer( VipsBlob * buffer , int width , VOption *options )
{
VImage out;
call( "thumbnail_buffer" ,
(options ? options : VImage::option()) ->
set( "buffer", buffer ) ->
set( "out", &out ) ->
set( "width", width ) );
return( out );
}
VImage VImage::mapim( VImage index , VOption *options ) VImage VImage::mapim( VImage index , VOption *options )
{ {
VImage out; VImage out;

View File

@@ -25,9 +25,10 @@
class MetadataWorker : public Nan::AsyncWorker { class MetadataWorker : public Nan::AsyncWorker {
public: public:
MetadataWorker( MetadataWorker(
Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *debuglog,
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), debuglog(debuglog),
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 {
@@ -56,6 +57,7 @@ class MetadataWorker : public Nan::AsyncWorker {
baton->height = image.height(); baton->height = image.height();
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation()); baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image.interpretation());
baton->channels = image.bands(); baton->channels = image.bands();
baton->depth = vips_enum_nick(VIPS_TYPE_BAND_FORMAT, image.format());
if (sharp::HasDensity(image)) { if (sharp::HasDensity(image)) {
baton->density = sharp::GetDensity(image); baton->density = sharp::GetDensity(image);
} }
@@ -102,6 +104,7 @@ class MetadataWorker : public Nan::AsyncWorker {
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(baton->height)); Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(baton->height));
Set(info, New("space").ToLocalChecked(), New<v8::String>(baton->space).ToLocalChecked()); Set(info, New("space").ToLocalChecked(), New<v8::String>(baton->space).ToLocalChecked());
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(baton->channels)); Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(baton->channels));
Set(info, New("depth").ToLocalChecked(), New<v8::String>(baton->depth).ToLocalChecked());
if (baton->density > 0) { if (baton->density > 0) {
Set(info, New("density").ToLocalChecked(), New<v8::Uint32>(baton->density)); Set(info, New("density").ToLocalChecked(), New<v8::Uint32>(baton->density));
} }
@@ -132,12 +135,21 @@ class MetadataWorker : public Nan::AsyncWorker {
delete baton->input; delete baton->input;
delete baton; delete baton;
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message);
warning = sharp::VipsWarningPop();
}
// Return to JavaScript // Return to JavaScript
callback->Call(2, argv); callback->Call(2, argv);
} }
private: private:
MetadataBaton* baton; MetadataBaton* baton;
Nan::Callback *debuglog;
std::vector<v8::Local<v8::Object>> buffersToPersist; std::vector<v8::Local<v8::Object>> buffersToPersist;
}; };
@@ -155,9 +167,12 @@ NAN_METHOD(metadata) {
// Input // Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist); baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
// Join queue for worker thread // Join queue for worker thread
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>()); Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, buffersToPersist)); Nan::AsyncQueueWorker(new MetadataWorker(callback, baton, debuglog, buffersToPersist));
// Increment queued task counter // Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue); g_atomic_int_inc(&sharp::counterQueue);

View File

@@ -29,6 +29,7 @@ struct MetadataBaton {
int height; int height;
std::string space; std::string space;
int channels; int channels;
std::string depth;
int density; int density;
bool hasProfile; bool hasProfile;
bool hasAlpha; bool hasAlpha;

View File

@@ -29,67 +29,32 @@ using vips::VError;
namespace sharp { namespace sharp {
/* /*
Alpha composite src over dst with given gravity. Composite overlayImage over image at given position
Assumes alpha channels are already premultiplied and will be unpremultiplied after. Assumes alpha channels are already premultiplied and will be unpremultiplied after
*/ */
VImage Composite(VImage src, VImage dst, const int gravity) { VImage Composite(VImage image, VImage overlayImage, int const left, int const top) {
if (IsInputValidForComposition(src, dst)) { if (HasAlpha(overlayImage)) {
// Enlarge overlay src, if required // Alpha composite
if (src.width() < dst.width() || src.height() < dst.height()) { if (overlayImage.width() < image.width() || overlayImage.height() < image.height()) {
// Calculate the (left, top) coordinates of the output image within the input image, applying the given gravity. // Enlarge overlay
int left; std::vector<double> const background { 0.0, 0.0, 0.0, 0.0 };
int top; overlayImage = overlayImage.embed(left, top, image.width(), image.height(), VImage::option()
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), gravity);
// Embed onto transparent background
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
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 AlphaComposite(image, overlayImage);
} else {
if (HasAlpha(image)) {
// Add alpha channel to overlayImage so channels match
double const multiplier = sharp::Is16Bit(overlayImage.interpretation()) ? 256.0 : 1.0;
overlayImage = overlayImage.bandjoin(
VImage::new_matrix(overlayImage.width(), overlayImage.height()).new_from_image(255 * multiplier));
}
return image.insert(overlayImage, left, top);
} }
// If the input was not valid for composition the return the input image itself
return dst;
} }
VImage Composite(VImage src, VImage dst, const int x, const int y) { VImage AlphaComposite(VImage dst, VImage src) {
if (IsInputValidForComposition(src, dst)) {
// Enlarge overlay src, if required
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.
int left;
int top;
std::tie(left, top) = CalculateCrop(dst.width(), dst.height(), src.width(), src.height(), x, y);
// Embed onto transparent background
std::vector<double> background { 0.0, 0.0, 0.0, 0.0 };
src = src.embed(left, top, dst.width(), dst.height(), VImage::option()
->set("extend", VIPS_EXTEND_BACKGROUND)
->set("background", background));
}
return CompositeImage(src, dst);
}
// If the input was not valid for composition the return the input image itself
return dst;
}
bool IsInputValidForComposition(VImage src, VImage dst) {
using sharp::CalculateCrop;
using sharp::HasAlpha;
if (!HasAlpha(src)) {
throw VError("Overlay image must have an alpha channel");
}
if (!HasAlpha(dst)) {
throw VError("Image to be overlaid must have an alpha channel");
}
if (src.width() > dst.width() || src.height() > dst.height()) {
throw VError("Overlay image must have same dimensions or smaller");
}
return true;
}
VImage CompositeImage(VImage src, VImage dst) {
// Split src into non-alpha and alpha channels // Split src into non-alpha and alpha channels
VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1)); VImage srcWithoutAlpha = src.extract_band(0, VImage::option()->set("n", src.bands() - 1));
VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0); VImage srcAlpha = src[src.bands() - 1] * (1.0 / 255.0);
@@ -302,118 +267,6 @@ namespace sharp {
} }
} }
/*
Calculate the Shannon entropy
*/
double EntropyStrategy::operator()(VImage image) {
return image.hist_find().hist_entropy();
}
/*
Calculate the intensity of edges, skin tone and saturation
*/
double AttentionStrategy::operator()(VImage image) {
// Flatten RGBA onto a mid-grey background
if (image.bands() == 4 && HasAlpha(image)) {
double const midgrey = sharp::Is16Bit(image.interpretation()) ? 32768.0 : 128.0;
std::vector<double> background { midgrey, midgrey, midgrey };
image = image.flatten(VImage::option()->set("background", background));
}
// Convert to LAB colourspace
VImage lab = image.colourspace(VIPS_INTERPRETATION_LAB);
VImage l = lab[0];
VImage a = lab[1];
VImage b = lab[2];
// Edge detect luminosity with the Sobel operator
VImage sobel = vips::VImage::new_matrixv(3, 3,
-1.0, 0.0, 1.0,
-2.0, 0.0, 2.0,
-1.0, 0.0, 1.0);
VImage edges = l.conv(sobel).abs() + l.conv(sobel.rot90()).abs();
// Skin tone chroma thresholds trained with http://humanae.tumblr.com/
VImage skin = (a >= 3) & (a <= 22) & (b >= 4) & (b <= 31);
// Chroma >~50% saturation
VImage lch = lab.colourspace(VIPS_INTERPRETATION_LCH);
VImage c = lch[1];
VImage saturation = c > 60;
// Find maximum in combined saliency mask
VImage mask = edges + skin + saturation;
return mask.max();
}
/*
Calculate crop area based on image entropy
*/
std::tuple<int, int> Crop(
VImage image, int const outWidth, int const outHeight, std::function<double(VImage)> strategy
) {
int left = 0;
int top = 0;
int const inWidth = image.width();
int const inHeight = image.height();
if (inWidth > outWidth) {
// Reduce width by repeated removing slices from edge with lowest score
int width = inWidth;
double leftScore = 0.0;
double rightScore = 0.0;
// Max width of each slice
int const maxSliceWidth = static_cast<int>(ceil((inWidth - outWidth) / 8.0));
while (width > outWidth) {
// Width of current slice
int const slice = std::min(width - outWidth, maxSliceWidth);
if (leftScore == 0.0) {
// Update score of left slice
leftScore = strategy(image.extract_area(left, 0, slice, inHeight));
}
if (rightScore == 0.0) {
// Update score of right slice
rightScore = strategy(image.extract_area(width - slice - 1, 0, slice, inHeight));
}
// Keep slice with highest score
if (leftScore >= rightScore) {
// Discard right slice
rightScore = 0.0;
} else {
// Discard left slice
leftScore = 0.0;
left = left + slice;
}
width = width - slice;
}
}
if (inHeight > outHeight) {
// Reduce height by repeated removing slices from edge with lowest score
int height = inHeight;
double topScore = 0.0;
double bottomScore = 0.0;
// Max height of each slice
int const maxSliceHeight = static_cast<int>(ceil((inHeight - outHeight) / 8.0));
while (height > outHeight) {
// Height of current slice
int const slice = std::min(height - outHeight, maxSliceHeight);
if (topScore == 0.0) {
// Update score of top slice
topScore = strategy(image.extract_area(0, top, inWidth, slice));
}
if (bottomScore == 0.0) {
// Update score of bottom slice
bottomScore = strategy(image.extract_area(0, height - slice - 1, inWidth, slice));
}
// Keep slice with highest score
if (topScore >= bottomScore) {
// Discard bottom slice
bottomScore = 0.0;
} else {
// Discard top slice
topScore = 0.0;
top = top + slice;
}
height = height - slice;
}
}
return std::make_tuple(left, top);
}
/* /*
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

@@ -32,20 +32,14 @@ namespace sharp {
VImage Composite(VImage src, VImage dst, const int gravity); VImage Composite(VImage src, VImage dst, const int gravity);
/* /*
Alpha composite src over dst with given x and y offsets. Composite overlayImage over image at given position
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
*/ */
VImage Composite(VImage src, VImage dst, const int x, const int y); VImage Composite(VImage image, VImage overlayImage, int const x, int const y);
/* /*
Check if the src and dst Images for composition operation are valid Alpha composite overlayImage over image, assumes matching dimensions
*/ */
bool IsInputValidForComposition(VImage src, VImage dst); VImage AlphaComposite(VImage image, VImage overlayImage);
/*
Given a valid src and dst, returns the composite of the two images
*/
VImage CompositeImage(VImage src, VImage dst);
/* /*
Cutout src over dst with given gravity. Cutout src over dst with given gravity.
@@ -78,22 +72,6 @@ namespace sharp {
*/ */
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged); VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
/*
Crop strategy functors
*/
struct EntropyStrategy {
double operator()(VImage image);
};
struct AttentionStrategy {
double operator()(VImage image);
};
/*
Calculate crop area based on given strategy (Entropy, Attention)
*/
std::tuple<int, int> Crop(
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

@@ -33,9 +33,10 @@
class PipelineWorker : public Nan::AsyncWorker { class PipelineWorker : public Nan::AsyncWorker {
public: public:
PipelineWorker( PipelineWorker(
Nan::Callback *callback, PipelineBaton *baton, Nan::Callback *queueListener, Nan::Callback *callback, PipelineBaton *baton, Nan::Callback *debuglog, 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), debuglog(debuglog), 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 {
@@ -80,16 +81,12 @@ class PipelineWorker : public Nan::AsyncWorker {
// Calculate angle of rotation // Calculate angle of rotation
VipsAngle rotation; VipsAngle rotation;
bool flip; if (baton->useExifOrientation) {
bool flop; // Rotate and flip image according to Exif orientation
std::tie(rotation, flip, flop) = CalculateRotationAndFlip(baton->angle, image); // (ignore the requested rotation and flip)
if (flip && !baton->flip) { std::tie(rotation, baton->flip, baton->flop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
// Add flip operation due to EXIF mirroring } else {
baton->flip = TRUE; rotation = CalculateAngleRotation(baton->angle);
}
if (flop && !baton->flop) {
// Add flip operation due to EXIF mirroring
baton->flop = TRUE;
} }
// Rotate pre-extract // Rotate pre-extract
@@ -241,6 +238,12 @@ class PipelineWorker : public Nan::AsyncWorker {
shrink_on_load = 2; shrink_on_load = 2;
} }
} }
// Help ensure a final kernel-based reduction to prevent shrink aliasing
if (shrink_on_load > 1 && (xresidual == 1.0 || yresidual == 1.0)) {
shrink_on_load = shrink_on_load / 2;
xfactor = xfactor * 2;
yfactor = yfactor * 2;
}
if (shrink_on_load > 1) { if (shrink_on_load > 1) {
// Reload input using shrink-on-load // Reload input using shrink-on-load
vips::VOption *option = VImage::option()->set("shrink", shrink_on_load); vips::VOption *option = VImage::option()->set("shrink", shrink_on_load);
@@ -284,6 +287,13 @@ class PipelineWorker : public Nan::AsyncWorker {
std::swap(xresidual, yresidual); std::swap(xresidual, yresidual);
} }
} }
// Help ensure a final kernel-based reduction to prevent shrink aliasing
if ((xshrink > 1 || yshrink > 1) && (xresidual == 1.0 || yresidual == 1.0)) {
xshrink = xshrink / 2;
yshrink = yshrink / 2;
xresidual = xresidual / 2.0;
yresidual = yresidual / 2.0;
}
// Ensure we're using a device-independent colour space // Ensure we're using a device-independent colour space
if (sharp::HasProfile(image)) { if (sharp::HasProfile(image)) {
@@ -332,22 +342,29 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.colourspace(VIPS_INTERPRETATION_B_W); image = image.colourspace(VIPS_INTERPRETATION_B_W);
} }
// Ensure image has an alpha channel when there is an overlay // Ensure image has an alpha channel when there is an overlay with an alpha channel
bool hasOverlay = baton->overlay != nullptr; VImage overlayImage;
if (hasOverlay && !HasAlpha(image)) { ImageType overlayImageType = ImageType::UNKNOWN;
bool shouldOverlayWithAlpha = FALSE;
if (baton->overlay != nullptr) {
std::tie(overlayImage, overlayImageType) = OpenInput(baton->overlay, baton->accessMethod);
if (HasAlpha(overlayImage)) {
shouldOverlayWithAlpha = !baton->overlayCutout;
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));
} }
}
}
bool const shouldShrink = xshrink > 1 || yshrink > 1; bool const shouldShrink = xshrink > 1 || yshrink > 1;
bool const shouldReduce = xresidual != 1.0 || yresidual != 1.0; bool const shouldReduce = xresidual != 1.0 || yresidual != 1.0;
bool const shouldBlur = baton->blurSigma != 0.0; bool const shouldBlur = baton->blurSigma != 0.0;
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0; bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
bool const shouldSharpen = baton->sharpenSigma != 0.0; bool const shouldSharpen = baton->sharpenSigma != 0.0;
bool const shouldCutout = baton->overlayCutout;
bool const shouldPremultiplyAlpha = HasAlpha(image) && bool const shouldPremultiplyAlpha = HasAlpha(image) &&
(shouldShrink || shouldReduce || shouldBlur || shouldConv || shouldSharpen || (hasOverlay && !shouldCutout)); (shouldShrink || shouldReduce || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
// Premultiply image alpha channel before all transformations to avoid // Premultiply image alpha channel before all transformations to avoid
// dark fringing around bright pixels // dark fringing around bright pixels
@@ -409,19 +426,27 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("centre", baton->centreSampling)); ->set("centre", baton->centreSampling));
} }
} }
// Perform affine enlargement // Perform enlargement
if (yresidual > 1.0 || xresidual > 1.0) { if (yresidual > 1.0 || xresidual > 1.0) {
if (trunc(xresidual) == xresidual && trunc(yresidual) == yresidual && baton->interpolator == "nearest") {
// Fast, integral nearest neighbour enlargement
image = image.zoom(static_cast<int>(xresidual), static_cast<int>(yresidual));
} else {
// Floating point affine transformation
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 && xresidual > 1.0) {
image = image.affine({xresidual, 0.0, 0.0, yresidual}, VImage::option()
->set("interpolate", interpolator));
} else 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));
} } else 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));
} }
} }
} }
}
// Rotate // Rotate
if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) { if (!baton->rotateBeforePreExtract && rotation != VIPS_ANGLE_D0) {
@@ -493,24 +518,20 @@ class PipelineWorker : public Nan::AsyncWorker {
->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 top;
if (baton->crop < 9) { if (baton->crop < 9) {
// Gravity-based crop // Gravity-based crop
int left;
int top;
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) {
// Entropy-based crop
std::tie(left, top) = sharp::Crop(image, baton->width, baton->height, sharp::EntropyStrategy());
} else {
// Attention-based crop
std::tie(left, top) = sharp::Crop(image, baton->width, baton->height, sharp::AttentionStrategy());
}
int width = std::min(image.width(), baton->width); int width = std::min(image.width(), baton->width);
int height = std::min(image.height(), baton->height); int height = std::min(image.height(), baton->height);
image = image.extract_area(left, top, width, height); image = image.extract_area(left, top, width, height);
baton->cropCalcLeft = left; } else {
baton->cropCalcTop = top; // Attention-based or Entropy-based crop
image = image.smartcrop(baton->width, baton->height, VImage::option()
->set("interesting", baton->crop == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION));
}
} }
} }
@@ -583,10 +604,11 @@ class PipelineWorker : public Nan::AsyncWorker {
} }
// Composite with overlay, if present // Composite with overlay, if present
if (hasOverlay) { if (baton->overlay != nullptr) {
VImage overlayImage; // Verify overlay image is within current dimensions
ImageType overlayImageType = ImageType::UNKNOWN; if (overlayImage.width() > image.width() || overlayImage.height() > image.height()) {
std::tie(overlayImage, overlayImageType) = OpenInput(baton->overlay, baton->accessMethod); throw vips::VError("Overlay image must have same dimensions or smaller");
}
// Check if overlay is tiled // Check if overlay is tiled
if (baton->overlayTile) { if (baton->overlayTile) {
int const overlayImageWidth = overlayImage.width(); int const overlayImageWidth = overlayImage.width();
@@ -619,31 +641,34 @@ class PipelineWorker : public Nan::AsyncWorker {
// 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;
} }
if (shouldCutout) { if (baton->overlayCutout) {
// 'cut out' the image, premultiplication is not required // 'cut out' the image, premultiplication is not required
image = sharp::Cutout(overlayImage, image, baton->overlayGravity); image = sharp::Cutout(overlayImage, image, baton->overlayGravity);
} else { } else {
// Ensure overlay is sRGB
overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB);
// Ensure overlay matches premultiplication state
if (shouldPremultiplyAlpha) {
// Ensure overlay has alpha channel // Ensure overlay has alpha channel
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 overlayImage = overlayImage.premultiply();
if (!HasAlpha(image)) {
double const multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
image = image.bandjoin(
VImage::new_matrix(image.width(), image.height()).new_from_image(255 * multiplier));
} }
// Ensure overlay is premultiplied sRGB int left;
overlayImage = overlayImage.colourspace(VIPS_INTERPRETATION_sRGB).premultiply(); int top;
if (baton->overlayXOffset >= 0 && baton->overlayYOffset >= 0) { if (baton->overlayXOffset >= 0 && baton->overlayYOffset >= 0) {
// Composite images with given offsets // Composite images at given offsets
image = sharp::Composite(overlayImage, image, baton->overlayXOffset, baton->overlayYOffset); std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
overlayImage.width(), overlayImage.height(), baton->overlayXOffset, baton->overlayYOffset);
} else { } else {
// Composite images with given gravity // Composite images with given gravity
image = sharp::Composite(overlayImage, image, baton->overlayGravity); std::tie(left, top) = sharp::CalculateCrop(image.width(), image.height(),
overlayImage.width(), overlayImage.height(), baton->overlayGravity);
} }
image = sharp::Composite(image, overlayImage, left, top);
} }
} }
@@ -657,6 +682,7 @@ class PipelineWorker : public Nan::AsyncWorker {
image = image.cast(VIPS_FORMAT_UCHAR); image = image.cast(VIPS_FORMAT_UCHAR);
} }
} }
baton->premultiplied = shouldPremultiplyAlpha;
// Gamma decoding (brighten) // Gamma decoding (brighten)
if (baton->gamma >= 1 && baton->gamma <= 3) { if (baton->gamma >= 1 && baton->gamma <= 3) {
@@ -713,10 +739,11 @@ class PipelineWorker : public Nan::AsyncWorker {
baton->width = image.width(); baton->width = image.width();
baton->height = image.height(); baton->height = image.height();
// Output // Output
if (baton->fileOut == "") { if (baton->fileOut.empty()) {
// Buffer output // Buffer output
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == ImageType::JPEG)) { if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == ImageType::JPEG)) {
// Write JPEG to buffer // Write JPEG to buffer
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
VipsArea *area = VIPS_AREA(image.jpegsave_buffer(VImage::option() VipsArea *area = VIPS_AREA(image.jpegsave_buffer(VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata)
->set("Q", baton->jpegQuality) ->set("Q", baton->jpegQuality)
@@ -738,11 +765,12 @@ class PipelineWorker : public Nan::AsyncWorker {
} }
} else if (baton->formatOut == "png" || (baton->formatOut == "input" && } else if (baton->formatOut == "png" || (baton->formatOut == "input" &&
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) { (inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
// Write PNG to buffer
sharp::AssertImageTypeDimensions(image, ImageType::PNG);
// Strip profile // Strip profile
if (!baton->withMetadata) { if (!baton->withMetadata) {
vips_image_remove(image.get_image(), VIPS_META_ICC_NAME); vips_image_remove(image.get_image(), VIPS_META_ICC_NAME);
} }
// Write PNG to buffer
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)
@@ -754,6 +782,7 @@ class PipelineWorker : public Nan::AsyncWorker {
baton->formatOut = "png"; baton->formatOut = "png";
} else if (baton->formatOut == "webp" || (baton->formatOut == "input" && inputImageType == ImageType::WEBP)) { } else if (baton->formatOut == "webp" || (baton->formatOut == "input" && inputImageType == ImageType::WEBP)) {
// Write WEBP to buffer // Write WEBP to buffer
sharp::AssertImageTypeDimensions(image, ImageType::WEBP);
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)
@@ -765,6 +794,27 @@ class PipelineWorker : public Nan::AsyncWorker {
area->free_fn = nullptr; area->free_fn = nullptr;
vips_area_unref(area); vips_area_unref(area);
baton->formatOut = "webp"; baton->formatOut = "webp";
} else if (baton->formatOut == "tiff" || (baton->formatOut == "input" && inputImageType == ImageType::TIFF)) {
// Write TIFF to buffer
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
}
// Cast pixel values to float, if required
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
image = image.cast(VIPS_FORMAT_FLOAT);
}
VipsArea *area = VIPS_AREA(image.tiffsave_buffer(VImage::option()
->set("strip", !baton->withMetadata)
->set("Q", baton->tiffQuality)
->set("squash", baton->tiffSquash)
->set("compression", baton->tiffCompression)
->set("predictor", baton->tiffPredictor)));
baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length;
area->free_fn = nullptr;
vips_area_unref(area);
baton->formatOut = "tiff";
baton->channels = std::min(baton->channels, 3);
} else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) { } else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) {
// Write raw, uncompressed image data to buffer // Write raw, uncompressed image data to buffer
if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) { if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
@@ -805,6 +855,7 @@ class PipelineWorker : public Nan::AsyncWorker {
!(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip || isV); !(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip || isV);
if (baton->formatOut == "jpeg" || isJpeg || (matchInput && inputImageType == ImageType::JPEG)) { if (baton->formatOut == "jpeg" || isJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
// Write JPEG to file // Write JPEG to file
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option() image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata) ->set("strip", !baton->withMetadata)
->set("Q", baton->jpegQuality) ->set("Q", baton->jpegQuality)
@@ -818,11 +869,12 @@ class PipelineWorker : public Nan::AsyncWorker {
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 &&
(inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) { (inputImageType == ImageType::PNG || inputImageType == ImageType::GIF || inputImageType == ImageType::SVG))) {
// Write PNG to file
sharp::AssertImageTypeDimensions(image, ImageType::PNG);
// Strip profile // Strip profile
if (!baton->withMetadata) { if (!baton->withMetadata) {
vips_image_remove(image.get_image(), VIPS_META_ICC_NAME); vips_image_remove(image.get_image(), VIPS_META_ICC_NAME);
} }
// Write PNG to file
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)
@@ -830,6 +882,7 @@ class PipelineWorker : public Nan::AsyncWorker {
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
AssertImageTypeDimensions(image, ImageType::WEBP);
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)
@@ -838,16 +891,20 @@ class PipelineWorker : public Nan::AsyncWorker {
->set("alpha_q", baton->webpAlphaQuality)); ->set("alpha_q", baton->webpAlphaQuality));
baton->formatOut = "webp"; baton->formatOut = "webp";
} else if (baton->formatOut == "tiff" || isTiff || (matchInput && inputImageType == ImageType::TIFF)) { } else if (baton->formatOut == "tiff" || isTiff || (matchInput && inputImageType == ImageType::TIFF)) {
// Write TIFF to file
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
}
// Cast pixel values to float, if required // Cast pixel values to float, if required
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) { if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
image = image.cast(VIPS_FORMAT_FLOAT); image = image.cast(VIPS_FORMAT_FLOAT);
} }
// 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("squash", baton->tiffSquash)
->set("compression", baton->tiffCompression) ->set("compression", baton->tiffCompression)
->set("predictor", baton->tiffPredictor) ); ->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) {
@@ -940,6 +997,7 @@ class PipelineWorker : public Nan::AsyncWorker {
Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(width))); Set(info, New("width").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(width)));
Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(height))); Set(info, New("height").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(height)));
Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->channels))); Set(info, New("channels").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->channels)));
Set(info, New("premultiplied").ToLocalChecked(), New<v8::Boolean>(baton->premultiplied));
if (baton->cropCalcLeft != -1 && baton->cropCalcLeft != -1) { if (baton->cropCalcLeft != -1 && baton->cropCalcLeft != -1) {
Set(info, New("cropCalcLeft").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcLeft))); Set(info, New("cropCalcLeft").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcLeft)));
Set(info, New("cropCalcTop").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcTop))); Set(info, New("cropCalcTop").ToLocalChecked(), New<v8::Uint32>(static_cast<uint32_t>(baton->cropCalcTop)));
@@ -978,6 +1036,14 @@ class PipelineWorker : public Nan::AsyncWorker {
}); });
delete baton; delete baton;
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message);
warning = sharp::VipsWarningPop();
}
// Decrement processing task counter // Decrement processing task counter
g_atomic_int_dec_and_test(&sharp::counterProcess); g_atomic_int_dec_and_test(&sharp::counterProcess);
v8::Local<v8::Value> queueLength[1] = { New<v8::Uint32>(sharp::counterQueue) }; v8::Local<v8::Value> queueLength[1] = { New<v8::Uint32>(sharp::counterQueue) };
@@ -990,23 +1056,20 @@ class PipelineWorker : public Nan::AsyncWorker {
private: private:
PipelineBaton *baton; PipelineBaton *baton;
Nan::Callback *debuglog;
Nan::Callback *queueListener; Nan::Callback *queueListener;
std::vector<v8::Local<v8::Object>> buffersToPersist; std::vector<v8::Local<v8::Object>> buffersToPersist;
/* /*
Calculate the angle of rotation and need-to-flip for the output image. Calculate the angle of rotation and need-to-flip for the given Exif orientation
In order of priority: By default, returns zero, i.e. no rotation.
1. Use explicitly requested angle (supports 90, 180, 270)
2. Use input image EXIF Orientation header - supports mirroring
3. Otherwise default to zero, i.e. no rotation
*/ */
std::tuple<VipsAngle, bool, bool> std::tuple<VipsAngle, bool, bool>
CalculateRotationAndFlip(int const angle, vips::VImage image) { CalculateExifRotationAndFlip(int const exifOrientation) {
VipsAngle rotate = VIPS_ANGLE_D0; VipsAngle rotate = VIPS_ANGLE_D0;
bool flip = FALSE; bool flip = FALSE;
bool flop = FALSE; bool flop = FALSE;
if (angle == -1) { switch (exifOrientation) {
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;
@@ -1015,18 +1078,26 @@ class PipelineWorker : public Nan::AsyncWorker {
case 4: flop = TRUE; rotate = VIPS_ANGLE_D180; break; // flop 3 case 4: flop = TRUE; rotate = VIPS_ANGLE_D180; break; // flop 3
case 5: flip = TRUE; rotate = VIPS_ANGLE_D270; break; // flip 8 case 5: flip = TRUE; rotate = VIPS_ANGLE_D270; break; // flip 8
} }
} else {
if (angle == 90) {
rotate = VIPS_ANGLE_D90;
} else if (angle == 180) {
rotate = VIPS_ANGLE_D180;
} else if (angle == 270) {
rotate = VIPS_ANGLE_D270;
}
}
return std::make_tuple(rotate, flip, flop); return std::make_tuple(rotate, flip, flop);
} }
/*
Calculate the rotation for the given angle.
Supports any positive or negative angle that is a multiple of 90.
*/
VipsAngle
CalculateAngleRotation(int angle) {
angle = angle % 360;
if (angle < 0)
angle = 360 + angle;
switch (angle) {
case 90: return VIPS_ANGLE_D90;
case 180: return VIPS_ANGLE_D180;
case 270: return VIPS_ANGLE_D270;
}
return VIPS_ANGLE_D0;
}
/* /*
Assemble the suffix argument to dzsave, which is the format (by extname) Assemble the suffix argument to dzsave, which is the format (by extname)
alongisde comma-separated arguments to the corresponding `formatsave` vips alongisde comma-separated arguments to the corresponding `formatsave` vips
@@ -1151,6 +1222,7 @@ NAN_METHOD(pipeline) {
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");
baton->useExifOrientation = AttrTo<bool>(options, "useExifOrientation");
baton->angle = AttrTo<int32_t>(options, "angle"); baton->angle = AttrTo<int32_t>(options, "angle");
baton->rotateBeforePreExtract = AttrTo<bool>(options, "rotateBeforePreExtract"); baton->rotateBeforePreExtract = AttrTo<bool>(options, "rotateBeforePreExtract");
baton->flip = AttrTo<bool>(options, "flip"); baton->flip = AttrTo<bool>(options, "flip");
@@ -1204,6 +1276,7 @@ NAN_METHOD(pipeline) {
baton->webpLossless = AttrTo<bool>(options, "webpLossless"); baton->webpLossless = AttrTo<bool>(options, "webpLossless");
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless"); baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality"); baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
// tiff compression options // tiff compression options
baton->tiffCompression = static_cast<VipsForeignTiffCompression>( baton->tiffCompression = static_cast<VipsForeignTiffCompression>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION, vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION,
@@ -1237,12 +1310,15 @@ NAN_METHOD(pipeline) {
baton->accessMethod = VIPS_ACCESS_RANDOM; baton->accessMethod = VIPS_ACCESS_RANDOM;
} }
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(AttrAs<v8::Function>(options, "debuglog"));
// 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"));
// Join queue for worker thread // Join queue for worker thread
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>()); Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new PipelineWorker(callback, baton, queueListener, buffersToPersist)); Nan::AsyncQueueWorker(new PipelineWorker(callback, baton, debuglog, queueListener, buffersToPersist));
// Increment queued task counter // Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue); g_atomic_int_inc(&sharp::counterQueue);

View File

@@ -64,6 +64,7 @@ struct PipelineBaton {
int crop; int crop;
int cropCalcLeft; int cropCalcLeft;
int cropCalcTop; int cropCalcTop;
bool premultiplied;
std::string kernel; std::string kernel;
std::string interpolator; std::string interpolator;
bool centreSampling; bool centreSampling;
@@ -80,6 +81,7 @@ struct PipelineBaton {
double gamma; double gamma;
bool greyscale; bool greyscale;
bool normalise; bool normalise;
bool useExifOrientation;
int angle; int angle;
bool rotateBeforePreExtract; bool rotateBeforePreExtract;
bool flip; bool flip;
@@ -106,6 +108,7 @@ struct PipelineBaton {
int tiffQuality; int tiffQuality;
VipsForeignTiffCompression tiffCompression; VipsForeignTiffCompression tiffCompression;
VipsForeignTiffPredictor tiffPredictor; VipsForeignTiffPredictor tiffPredictor;
bool tiffSquash;
std::string err; std::string err;
bool withMetadata; bool withMetadata;
int withMetadataOrientation; int withMetadataOrientation;
@@ -142,6 +145,7 @@ struct PipelineBaton {
crop(0), crop(0),
cropCalcLeft(-1), cropCalcLeft(-1),
cropCalcTop(-1), cropCalcTop(-1),
premultiplied(false),
centreSampling(false), centreSampling(false),
flatten(false), flatten(false),
negate(false), negate(false),
@@ -155,6 +159,7 @@ struct PipelineBaton {
gamma(0.0), gamma(0.0),
greyscale(false), greyscale(false),
normalise(false), normalise(false),
useExifOrientation(false),
angle(0), angle(0),
flip(false), flip(false),
flop(false), flop(false),
@@ -176,6 +181,7 @@ struct PipelineBaton {
tiffQuality(80), tiffQuality(80),
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG), tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_NONE), tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_NONE),
tiffSquash(false),
withMetadata(false), withMetadata(false),
withMetadataOrientation(-1), withMetadataOrientation(-1),
convKernelWidth(0), convKernelWidth(0),

View File

@@ -24,6 +24,9 @@
NAN_MODULE_INIT(init) { NAN_MODULE_INIT(init) {
vips_init("sharp"); vips_init("sharp");
g_log_set_handler("VIPS", static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING),
static_cast<GLogFunc>(sharp::VipsWarningCallback), nullptr);
// Methods available to JavaScript // Methods available to JavaScript
Nan::Set(target, Nan::New("metadata").ToLocalChecked(), Nan::Set(target, Nan::New("metadata").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked()); Nan::GetFunction(Nan::New<v8::FunctionTemplate>(metadata)).ToLocalChecked());

View File

@@ -5,7 +5,7 @@
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"description": "Benchmark and performance tests for sharp", "description": "Benchmark and performance tests for sharp",
"scripts": { "scripts": {
"test": "VIPS_WARNING=0 node perf && node random && node parallel" "test": "node perf && node random && node parallel"
}, },
"devDependencies": { "devDependencies": {
"async": "^2.1.4", "async": "^2.1.4",
@@ -13,9 +13,10 @@
"gm": "^1.23.0", "gm": "^1.23.0",
"imagemagick": "^0.1.3", "imagemagick": "^0.1.3",
"imagemagick-native": "^1.9.3", "imagemagick-native": "^1.9.3",
"images": "^3.0.0",
"jimp": "^0.2.27", "jimp": "^0.2.27",
"lwip": "^0.0.9", "mapnik": "^3.6.0",
"mapnik": "^3.5.14", "pajk-lwip": "^0.2.0",
"semver": "^5.3.0" "semver": "^5.3.0"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@@ -7,11 +7,12 @@ const assert = require('assert');
const Benchmark = require('benchmark'); const Benchmark = require('benchmark');
// Contenders // Contenders
const sharp = require('../../');
const gm = require('gm'); const gm = require('gm');
const imagemagick = require('imagemagick'); const imagemagick = require('imagemagick');
const mapnik = require('mapnik'); const mapnik = require('mapnik');
const jimp = require('jimp'); const jimp = require('jimp');
const sharp = require('../../'); const images = require('images');
let imagemagickNative; let imagemagickNative;
try { try {
imagemagickNative = require('imagemagick-native'); imagemagickNative = require('imagemagick-native');
@@ -20,7 +21,7 @@ try {
} }
let lwip; let lwip;
try { try {
lwip = require('lwip'); lwip = require('pajk-lwip');
} catch (err) { } catch (err) {
console.log('Excluding lwip'); console.log('Excluding lwip');
} }
@@ -145,7 +146,7 @@ async.series({
}).add('mapnik-buffer-buffer', { }).add('mapnik-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: function (deferred) {
mapnik.Image.fromBytes(inputJpgBuffer, function (err, img) { mapnik.Image.fromBytes(inputJpgBuffer, { max_size: 3000 }, function (err, img) {
if (err) throw err; if (err) throw err;
img img
.resize(width, height, { .resize(width, height, {
@@ -266,6 +267,12 @@ async.series({
}); });
} }
}); });
// images
jpegSuite.add('images-file-file', function () {
images(fixtures.inputJpg)
.resize(width, height)
.save(fixtures.outputJpg, { quality: 80 });
});
// sharp // sharp
jpegSuite.add('sharp-buffer-file', { jpegSuite.add('sharp-buffer-file', {
defer: true, defer: true,
@@ -733,7 +740,7 @@ async.series({
}).add('mapnik-buffer-buffer', { }).add('mapnik-buffer-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: function (deferred) {
mapnik.Image.fromBytes(inputPngBuffer, function (err, img) { mapnik.Image.fromBytes(inputPngBuffer, { max_size: 3000 }, function (err, img) {
if (err) throw err; if (err) throw err;
img.premultiply(function (err, img) { img.premultiply(function (err, img) {
if (err) throw err; if (err) throw err;
@@ -819,6 +826,12 @@ async.series({
}); });
} }
}); });
// images
pngSuite.add('images-file-file', function () {
images(fixtures.inputPng)
.resize(width, height)
.save(fixtures.outputPng);
});
// sharp // sharp
pngSuite.add('sharp-buffer-file', { pngSuite.add('sharp-buffer-file', {
defer: true, defer: true,
@@ -957,7 +970,7 @@ async.series({
}).add('sharp-file-buffer', { }).add('sharp-file-buffer', {
defer: true, defer: true,
fn: function (deferred) { fn: function (deferred) {
sharp(fixtures.inputWebp) sharp(fixtures.inputWebP)
.resize(width, height) .resize(width, height)
.toBuffer(function (err, buffer) { .toBuffer(function (err, buffer) {
if (err) { if (err) {

BIN
test/fixtures/320x240.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
test/fixtures/8bit_depth.tiff vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 B

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -64,6 +64,7 @@ module.exports = {
inputJpgWithCorruptHeader: getPath('corrupt-header.jpg'), inputJpgWithCorruptHeader: getPath('corrupt-header.jpg'),
inputJpgWithLowContrast: getPath('low-contrast.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/ inputJpgWithLowContrast: getPath('low-contrast.jpg'), // http://www.flickr.com/photos/grizdave/2569067123/
inputJpgLarge: getPath('giant-image.jpg'), inputJpgLarge: getPath('giant-image.jpg'),
inputJpg320x240: getPath('320x240.jpg'), // http://www.andrewault.net/2010/01/26/create-a-test-pattern-video-with-perl/
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
inputPngWithTransparency: getPath('blackbug.png'), // public domain inputPngWithTransparency: getPath('blackbug.png'), // public domain
@@ -85,6 +86,7 @@ module.exports = {
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 inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
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

View File

@@ -161,7 +161,7 @@ describe('Crop', function () {
describe('Entropy-based strategy', function () { describe('Entropy-based strategy', function () {
it('JPEG', function (done) { it('JPEG', function (done) {
sharp(fixtures.inputJpgWithCmykProfile) sharp(fixtures.inputJpg)
.resize(80, 320) .resize(80, 320)
.crop(sharp.strategy.entropy) .crop(sharp.strategy.entropy)
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
@@ -170,9 +170,7 @@ describe('Crop', function () {
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
assert.strictEqual(80, info.width); assert.strictEqual(80, info.width);
assert.strictEqual(320, info.height); assert.strictEqual(320, info.height);
assert.strictEqual(250, info.cropCalcLeft); fixtures.assertSimilar(fixtures.expected('crop-strategy-entropy.jpg'), data, done);
assert.strictEqual(0, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.jpg'), data, done);
}); });
}); });
@@ -186,8 +184,6 @@ describe('Crop', function () {
assert.strictEqual(4, info.channels); assert.strictEqual(4, info.channels);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height); assert.strictEqual(80, info.height);
assert.strictEqual(0, info.cropCalcLeft);
assert.strictEqual(80, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
}); });
}); });
@@ -202,8 +198,6 @@ describe('Crop', function () {
assert.strictEqual(4, info.channels); assert.strictEqual(4, info.channels);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height); assert.strictEqual(80, info.height);
assert.strictEqual(0, info.cropCalcLeft);
assert.strictEqual(80, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
}); });
}); });
@@ -211,7 +205,7 @@ describe('Crop', function () {
describe('Attention strategy', function () { describe('Attention strategy', function () {
it('JPEG', function (done) { it('JPEG', function (done) {
sharp(fixtures.inputJpgWithCmykProfile) sharp(fixtures.inputJpg)
.resize(80, 320) .resize(80, 320)
.crop(sharp.strategy.attention) .crop(sharp.strategy.attention)
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
@@ -220,9 +214,7 @@ describe('Crop', function () {
assert.strictEqual(3, info.channels); assert.strictEqual(3, info.channels);
assert.strictEqual(80, info.width); assert.strictEqual(80, info.width);
assert.strictEqual(320, info.height); assert.strictEqual(320, info.height);
assert.strictEqual(250, info.cropCalcLeft); fixtures.assertSimilar(fixtures.expected('crop-strategy-attention.jpg'), data, done);
assert.strictEqual(0, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.jpg'), data, done);
}); });
}); });
@@ -236,8 +228,6 @@ describe('Crop', function () {
assert.strictEqual(4, info.channels); assert.strictEqual(4, info.channels);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height); assert.strictEqual(80, info.height);
assert.strictEqual(0, info.cropCalcLeft);
assert.strictEqual(80, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
}); });
}); });
@@ -252,8 +242,6 @@ describe('Crop', function () {
assert.strictEqual(4, info.channels); assert.strictEqual(4, info.channels);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(80, info.height); assert.strictEqual(80, info.height);
assert.strictEqual(0, info.cropCalcLeft);
assert.strictEqual(80, info.cropCalcTop);
fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done); fixtures.assertSimilar(fixtures.expected('crop-strategy.png'), data, done);
}); });
}); });

View File

@@ -35,16 +35,53 @@ describe('Interpolators and kernels', function () {
sharp.interpolator.locallyBoundedBicubic, sharp.interpolator.locallyBoundedBicubic,
sharp.interpolator.vertexSplitQuadraticBasisSpline sharp.interpolator.vertexSplitQuadraticBasisSpline
].forEach(function (interpolator) { ].forEach(function (interpolator) {
it(interpolator, function (done) { describe(interpolator, function () {
sharp(fixtures.inputJpg) it('x and y', function (done) {
.resize(320, null, { interpolator: interpolator }) sharp(fixtures.inputTiff8BitDepth)
.resize(200, 200, { interpolator: interpolator })
.png()
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual('jpeg', info.format); assert.strictEqual(200, info.width);
assert.strictEqual(320, info.width); assert.strictEqual(200, info.height);
fixtures.assertSimilar(fixtures.inputJpg, data, done); done();
}); });
}); });
it('x only', function (done) {
sharp(fixtures.inputTiff8BitDepth)
.resize(200, 21, { interpolator: interpolator })
.png()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(200, info.width);
assert.strictEqual(21, info.height);
done();
});
});
it('y only', function (done) {
sharp(fixtures.inputTiff8BitDepth)
.resize(21, 200, { interpolator: interpolator })
.png()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(21, info.width);
assert.strictEqual(200, info.height);
done();
});
});
});
});
it('nearest with integral factor', function (done) {
sharp(fixtures.inputTiff8BitDepth)
.resize(210, 210, { interpolator: 'nearest' })
.png()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(210, info.width);
assert.strictEqual(210, info.height);
done();
});
}); });
}); });

View File

@@ -253,6 +253,21 @@ describe('Input/output', function () {
}); });
}); });
it('Support output to jpg format', function (done) {
sharp(fixtures.inputPng)
.resize(320, 240)
.toFormat('jpg')
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
});
it('Fail when output File is input File', function (done) { it('Fail when output File is input File', function (done) {
sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function (err) { sharp(fixtures.inputJpg).toFile(fixtures.inputJpg, function (err) {
assert(!!err); assert(!!err);
@@ -288,7 +303,7 @@ describe('Input/output', function () {
}); });
it('Fail when input is empty Buffer', function (done) { it('Fail when input is empty Buffer', function (done) {
sharp(new Buffer(0)).toBuffer().then(function () { sharp(Buffer.alloc(0)).toBuffer().then(function () {
assert(false); assert(false);
done(); done();
}).catch(function (err) { }).catch(function (err) {
@@ -298,7 +313,7 @@ describe('Input/output', function () {
}); });
it('Fail when input is invalid Buffer', function (done) { it('Fail when input is invalid Buffer', function (done) {
sharp(new Buffer([0x1, 0x2, 0x3, 0x4])).toBuffer().then(function () { sharp(Buffer.from([0x1, 0x2, 0x3, 0x4])).toBuffer().then(function () {
assert(false); assert(false);
done(); done();
}).catch(function (err) { }).catch(function (err) {
@@ -308,6 +323,16 @@ describe('Input/output', function () {
}); });
describe('Fail for unsupported input', function () { describe('Fail for unsupported input', function () {
it('Undefined', function () {
assert.throws(function () {
sharp(undefined);
});
});
it('Null', function () {
assert.throws(function () {
sharp(null);
});
});
it('Numeric', function () { it('Numeric', function () {
assert.throws(function () { assert.throws(function () {
sharp(1); sharp(1);
@@ -318,11 +343,6 @@ describe('Input/output', function () {
sharp(true); sharp(true);
}); });
}); });
it('Empty Object', function () {
assert.throws(function () {
sharp({});
});
});
it('Error Object', function () { it('Error Object', function () {
assert.throws(function () { assert.throws(function () {
sharp(new Error()); sharp(new Error());
@@ -837,6 +857,20 @@ describe('Input/output', function () {
}); });
}); });
it('Save TIFF to Buffer', function (done) {
sharp(fixtures.inputTiff)
.resize(320, 240)
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('tiff', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
});
it('Invalid WebP quality throws error', function () { it('Invalid WebP quality throws error', function () {
assert.throws(function () { assert.throws(function () {
sharp().webp({ quality: 101 }); sharp().webp({ quality: 101 });
@@ -861,6 +895,44 @@ describe('Input/output', function () {
}); });
}); });
it('Not squashing TIFF to a bit depth of 1 should not change the file size', function (done) {
const startSize = fs.statSync(fixtures.inputTiff8BitDepth).size;
sharp(fixtures.inputTiff8BitDepth)
.toColourspace('b-w') // can only squash 1 band uchar images
.tiff({
squash: false,
compression: 'none'
})
.toFile(fixtures.outputTiff, (err, info) => {
if (err) throw err;
assert.strictEqual('tiff', info.format);
assert(info.size === startSize);
fs.unlink(fixtures.outputTiff, done);
});
});
it('Squashing TIFF to a bit depth of 1 should significantly reduce file size', function (done) {
const startSize = fs.statSync(fixtures.inputTiff8BitDepth).size;
sharp(fixtures.inputTiff8BitDepth)
.toColourspace('b-w') // can only squash 1 band uchar images
.tiff({
squash: true,
compression: 'none'
})
.toFile(fixtures.outputTiff, (err, info) => {
if (err) throw err;
assert.strictEqual('tiff', info.format);
assert(info.size < (startSize / 2));
fs.unlink(fixtures.outputTiff, done);
});
});
it('Invalid TIFF squash value throws error', function () {
assert.throws(function () {
sharp().tiff({ squash: 'true' });
});
});
it('TIFF lzw compression with horizontal predictor shrinks test file', function (done) { it('TIFF lzw compression with horizontal predictor shrinks test file', function (done) {
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size; const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
sharp(fixtures.inputTiffUncompressed) sharp(fixtures.inputTiffUncompressed)
@@ -876,7 +948,7 @@ describe('Input/output', function () {
}); });
}); });
it('TIFF deflate compression with hoizontal predictor shrinks test file', function (done) { it('TIFF deflate compression with horizontal predictor shrinks test file', function (done) {
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size; const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
sharp(fixtures.inputTiffUncompressed) sharp(fixtures.inputTiffUncompressed)
.tiff({ .tiff({
@@ -1205,27 +1277,27 @@ describe('Input/output', function () {
describe('Raw pixel input', function () { describe('Raw pixel input', function () {
it('Missing options', function () { it('Missing options', function () {
assert.throws(function () { assert.throws(function () {
sharp(null, { raw: {} }); sharp({ raw: {} });
}); });
}); });
it('Incomplete options', function () { it('Incomplete options', function () {
assert.throws(function () { assert.throws(function () {
sharp(null, { raw: { width: 1, height: 1 } }); sharp({ raw: { width: 1, height: 1 } });
}); });
}); });
it('Invalid channels', function () { it('Invalid channels', function () {
assert.throws(function () { assert.throws(function () {
sharp(null, { raw: { width: 1, height: 1, channels: 5 } }); sharp({ raw: { width: 1, height: 1, channels: 5 } });
}); });
}); });
it('Invalid height', function () { it('Invalid height', function () {
assert.throws(function () { assert.throws(function () {
sharp(null, { raw: { width: 1, height: 0, channels: 4 } }); sharp({ raw: { width: 1, height: 0, channels: 4 } });
}); });
}); });
it('Invalid width', function () { it('Invalid width', function () {
assert.throws(function () { assert.throws(function () {
sharp(null, { raw: { width: 'zoinks', height: 1, channels: 4 } }); sharp({ raw: { width: 'zoinks', height: 1, channels: 4 } });
}); });
}); });
it('RGB', function (done) { it('RGB', function (done) {
@@ -1278,7 +1350,7 @@ describe('Input/output', function () {
assert.strictEqual(256, info.width); assert.strictEqual(256, info.width);
assert.strictEqual(192, info.height); assert.strictEqual(192, info.height);
assert.strictEqual(4, info.channels); assert.strictEqual(4, info.channels);
fixtures.assertSimilar(fixtures.inputPngOverlayLayer1, data, done); fixtures.assertSimilar(fixtures.inputPngOverlayLayer1, data, { threshold: 7 }, done);
}); });
}); });
}); });
@@ -1292,7 +1364,7 @@ describe('Input/output', function () {
channels: 3, channels: 3,
background: { r: 0, g: 255, b: 0 } background: { r: 0, g: 255, b: 0 }
}; };
sharp(null, { create: create }) sharp({ create: create })
.jpeg() .jpeg()
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
if (err) throw err; if (err) throw err;
@@ -1310,7 +1382,7 @@ describe('Input/output', function () {
channels: 4, channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 128 } background: { r: 255, g: 0, b: 0, alpha: 128 }
}; };
sharp(null, { create: create }) sharp({ create: create })
.png() .png()
.toBuffer(function (err, data, info) { .toBuffer(function (err, data, info) {
if (err) throw err; if (err) throw err;
@@ -1329,7 +1401,7 @@ describe('Input/output', function () {
background: { r: 0, g: 0, b: 0 } background: { r: 0, g: 0, b: 0 }
}; };
assert.throws(function () { assert.throws(function () {
sharp(null, { create: create }); sharp({ create: create });
}); });
}); });
it('Missing background', function () { it('Missing background', function () {
@@ -1339,7 +1411,7 @@ describe('Input/output', function () {
channels: 3 channels: 3
}; };
assert.throws(function () { assert.throws(function () {
sharp(null, { create: create }); sharp({ create: create });
}); });
}); });
}); });

View File

@@ -17,6 +17,7 @@ describe('Image metadata', function () {
assert.strictEqual(2225, metadata.height); assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -35,6 +36,7 @@ describe('Image metadata', function () {
assert.strictEqual(600, metadata.height); assert.strictEqual(600, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual(72, metadata.density); assert.strictEqual(72, metadata.density);
assert.strictEqual(true, metadata.hasProfile); assert.strictEqual(true, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -56,7 +58,6 @@ describe('Image metadata', function () {
}); });
}); });
if (sharp.format.tiff.input.file) {
it('TIFF', function (done) { it('TIFF', function (done) {
sharp(fixtures.inputTiff).metadata(function (err, metadata) { sharp(fixtures.inputTiff).metadata(function (err, metadata) {
if (err) throw err; if (err) throw err;
@@ -65,6 +66,7 @@ describe('Image metadata', function () {
assert.strictEqual(3248, metadata.height); assert.strictEqual(3248, metadata.height);
assert.strictEqual('b-w', metadata.space); assert.strictEqual('b-w', metadata.space);
assert.strictEqual(1, metadata.channels); assert.strictEqual(1, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual(300, metadata.density); assert.strictEqual(300, metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -74,7 +76,6 @@ describe('Image metadata', function () {
done(); done();
}); });
}); });
}
it('PNG', function (done) { it('PNG', function (done) {
sharp(fixtures.inputPng).metadata(function (err, metadata) { sharp(fixtures.inputPng).metadata(function (err, metadata) {
@@ -84,6 +85,7 @@ describe('Image metadata', function () {
assert.strictEqual(2074, metadata.height); assert.strictEqual(2074, metadata.height);
assert.strictEqual('b-w', metadata.space); assert.strictEqual('b-w', metadata.space);
assert.strictEqual(1, metadata.channels); assert.strictEqual(1, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual(300, metadata.density); assert.strictEqual(300, metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -102,6 +104,7 @@ describe('Image metadata', function () {
assert.strictEqual(1536, metadata.height); assert.strictEqual(1536, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(4, metadata.channels); assert.strictEqual(4, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual(72, metadata.density); assert.strictEqual(72, metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(true, metadata.hasAlpha); assert.strictEqual(true, metadata.hasAlpha);
@@ -112,7 +115,6 @@ describe('Image metadata', function () {
}); });
}); });
if (sharp.format.webp.input.file) {
it('WebP', function (done) { it('WebP', function (done) {
sharp(fixtures.inputWebP).metadata(function (err, metadata) { sharp(fixtures.inputWebP).metadata(function (err, metadata) {
if (err) throw err; if (err) throw err;
@@ -121,6 +123,7 @@ describe('Image metadata', function () {
assert.strictEqual(772, metadata.height); assert.strictEqual(772, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -130,9 +133,7 @@ describe('Image metadata', function () {
done(); done();
}); });
}); });
}
if (sharp.format.gif.input.file) {
it('GIF via giflib', function (done) { it('GIF via giflib', function (done) {
sharp(fixtures.inputGif).metadata(function (err, metadata) { sharp(fixtures.inputGif).metadata(function (err, metadata) {
if (err) throw err; if (err) throw err;
@@ -140,6 +141,7 @@ describe('Image metadata', function () {
assert.strictEqual(800, metadata.width); assert.strictEqual(800, metadata.width);
assert.strictEqual(533, metadata.height); assert.strictEqual(533, metadata.height);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -156,6 +158,7 @@ describe('Image metadata', function () {
assert.strictEqual(2, metadata.width); assert.strictEqual(2, metadata.width);
assert.strictEqual(1, metadata.height); assert.strictEqual(1, metadata.height);
assert.strictEqual(2, metadata.channels); assert.strictEqual(2, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(true, metadata.hasAlpha); assert.strictEqual(true, metadata.hasAlpha);
@@ -165,27 +168,6 @@ describe('Image metadata', function () {
done(); done();
}); });
}); });
}
if (sharp.format.openslide.input.file) {
it('Aperio SVS via openslide', function (done) {
sharp(fixtures.inputSvs).metadata(function (err, metadata) {
if (err) throw err;
assert.strictEqual('openslide', metadata.format);
assert.strictEqual(2220, metadata.width);
assert.strictEqual(2967, metadata.height);
assert.strictEqual(4, metadata.channels);
assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual('rgb', metadata.space);
assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(true, metadata.hasAlpha);
assert.strictEqual('undefined', typeof metadata.orientation);
assert.strictEqual('undefined', typeof metadata.exif);
assert.strictEqual('undefined', typeof metadata.icc);
done();
});
});
}
it('File in, Promise out', function (done) { it('File in, Promise out', function (done) {
sharp(fixtures.inputJpg).metadata().then(function (metadata) { sharp(fixtures.inputJpg).metadata().then(function (metadata) {
@@ -194,6 +176,7 @@ describe('Image metadata', function () {
assert.strictEqual(2225, metadata.height); assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -222,6 +205,7 @@ describe('Image metadata', function () {
assert.strictEqual(2225, metadata.height); assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -244,6 +228,7 @@ describe('Image metadata', function () {
assert.strictEqual(2225, metadata.height); assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);
@@ -264,6 +249,7 @@ describe('Image metadata', function () {
assert.strictEqual(2225, metadata.height); assert.strictEqual(2225, metadata.height);
assert.strictEqual('srgb', metadata.space); assert.strictEqual('srgb', metadata.space);
assert.strictEqual(3, metadata.channels); assert.strictEqual(3, metadata.channels);
assert.strictEqual('uchar', metadata.depth);
assert.strictEqual('undefined', typeof metadata.density); assert.strictEqual('undefined', typeof metadata.density);
assert.strictEqual(false, metadata.hasProfile); assert.strictEqual(false, metadata.hasProfile);
assert.strictEqual(false, metadata.hasAlpha); assert.strictEqual(false, metadata.hasAlpha);

View File

@@ -155,20 +155,22 @@ describe('Overlays', function () {
}); });
} }
it('Composite JPEG onto PNG', function (done) { it('Composite JPEG onto PNG, no premultiply', function (done) {
sharp(fixtures.inputPngOverlayLayer1) sharp(fixtures.inputPngOverlayLayer1)
.overlayWith(fixtures.inputJpgWithLandscapeExif1) .overlayWith(fixtures.inputJpgWithLandscapeExif1)
.toBuffer(function (error) { .toBuffer(function (err, data, info) {
if (error) return done(error); if (err) throw err;
assert.strictEqual(false, info.premultiplied);
done(); done();
}); });
}); });
it('Composite opaque JPEG onto JPEG', function (done) { it('Composite opaque JPEG onto JPEG, no premultiply', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.overlayWith(fixtures.inputJpgWithLandscapeExif1) .overlayWith(fixtures.inputJpgWithLandscapeExif1)
.toBuffer(function (error) { .toBuffer(function (err, data, info) {
if (error) return done(error); if (err) throw err;
assert.strictEqual(false, info.premultiplied);
done(); done();
}); });
}); });
@@ -561,14 +563,15 @@ describe('Overlays', function () {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(2048, 1536) .resize(2048, 1536)
.overlayWith(data, { raw: info }) .overlayWith(data, { raw: info })
.toBuffer(function (err, data) { .toBuffer(function (err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.premultiplied);
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-rgb.jpg'), data, done); fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-rgb.jpg'), data, done);
}); });
}); });
}); });
it('Throws an error when called with an invalid file', function (done) { it('Returns an error when called with an invalid file', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.overlayWith('notfound.png') .overlayWith('notfound.png')
.toBuffer(function (err) { .toBuffer(function (err) {
@@ -576,4 +579,20 @@ describe('Overlays', function () {
done(); done();
}); });
}); });
it('Composite JPEG onto JPEG, no premultiply', function (done) {
sharp(fixtures.inputJpg)
.resize(480, 320)
.overlayWith(fixtures.inputJpgBooleanTest)
.png()
.toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(480, info.width);
assert.strictEqual(320, info.height);
assert.strictEqual(3, info.channels);
assert.strictEqual(false, info.premultiplied);
fixtures.assertSimilar(fixtures.expected('overlay-jpeg-with-jpeg.jpg'), data, done);
});
});
}); });

View File

@@ -1,15 +0,0 @@
'use strict';
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "(bufferutil|sharp)" }] */
describe('Require-time checks', function () {
/**
Including sharp alongside another C++ module that does not require
-stdlib=libc++ (for its C++11 features) has caused clang/llvm to
segfault due to the use of static function variables.
*/
it('Require alongside C++ module that does not use libc++', function () {
const bufferutil = require('bufferutil');
const sharp = require('../../');
});
});

View File

@@ -66,37 +66,47 @@ 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/); }, /Expected positive integer 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/); }, /Expected positive integer 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/); }, /Expected positive integer 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/); }, /Expected positive integer for height but received 1.5 of type number/);
}); });
it('Invalid width - too large', function () { it('Invalid width - too large', function (done) {
assert.throws(function () { sharp(fixtures.inputJpg)
sharp().resize(0x4000, 240); .resize(0x4000, 1)
}, /Expected integer between 1 and 16383 for width but received 16384 of type number/); .webp()
.toBuffer(function (err) {
assert.strictEqual(true, err instanceof Error);
assert.strictEqual('Processed image is too large for the WebP format', err.message);
done();
});
}); });
it('Invalid height - too large', function () { it('Invalid height - too large', function (done) {
assert.throws(function () { sharp(fixtures.inputJpg)
sharp().resize(320, 0x4000); .resize(1, 0x4000)
}, /Expected integer between 1 and 16383 for height but received 16384 of type number/); .webp()
.toBuffer(function (err) {
assert.strictEqual(true, err instanceof Error);
assert.strictEqual('Processed image is too large for the WebP format', err.message);
done();
});
}); });
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

@@ -34,6 +34,28 @@ describe('Rotation', function () {
}); });
}); });
[-3690, -450, -90, 90, 450, 3690].forEach(function (angle) {
it('Rotate by any 90-multiple angle (' + angle + 'deg)', function (done) {
sharp(fixtures.inputJpg320x240).rotate(angle).toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(240, info.width);
assert.strictEqual(320, info.height);
done();
});
});
});
[-3780, -540, 0, 180, 540, 3780].forEach(function (angle) {
it('Rotate by any 180-multiple angle (' + angle + 'deg)', function (done) {
sharp(fixtures.inputJpg320x240).rotate(angle).toBuffer(function (err, data, info) {
if (err) throw err;
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
});
});
it('Rotate by 270 degrees, square output ignoring aspect ratio', function (done) { it('Rotate by 270 degrees, square output ignoring aspect ratio', function (done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(240, 240) .resize(240, 240)