mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cda700ef73 | ||
|
|
d32901da8d | ||
|
|
83ebe12061 | ||
|
|
fe34548bad | ||
|
|
855945bef2 | ||
|
|
8421e3aa5f | ||
|
|
c93f79daa7 | ||
|
|
35c53f78c8 | ||
|
|
c158d51f8b | ||
|
|
8e9a8dfede | ||
|
|
67dc694cfb | ||
|
|
74704a132c | ||
|
|
b86674f91f | ||
|
|
5dab3c8482 | ||
|
|
a190ae6b08 | ||
|
|
464fb1726d | ||
|
|
065ce6454b | ||
|
|
850c2ecdd6 | ||
|
|
926c5603aa | ||
|
|
d3225fa193 | ||
|
|
f026a835fd | ||
|
|
47241db789 | ||
|
|
34a9970bd9 | ||
|
|
57203f841a |
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
|
"maxparams": 4,
|
||||||
|
"maxcomplexity": 11,
|
||||||
"globals": {
|
"globals": {
|
||||||
"describe": true,
|
"describe": true,
|
||||||
"it": true
|
"it": true
|
||||||
|
|||||||
84
CONTRIBUTING.md
Executable file
84
CONTRIBUTING.md
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
# Contributing to sharp
|
||||||
|
|
||||||
|
Hello, thank you for your interest in helping!
|
||||||
|
|
||||||
|
## Submit a new bug report
|
||||||
|
|
||||||
|
Please create a [new issue](https://github.com/lovell/sharp/issues/new) containing the steps to reproduce the problem.
|
||||||
|
|
||||||
|
New bugs are assigned a `triage` label whilst under investigation.
|
||||||
|
|
||||||
|
If you're having problems with `npm install sharp`, please include the output of the following commands, perhaps as a [gist](https://gist.github.com/):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vips -v
|
||||||
|
pkg-config --print-provides vips
|
||||||
|
npm install --verbose sharp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Submit a new feature request
|
||||||
|
|
||||||
|
If a [similar request](https://github.com/lovell/sharp/labels/enhancement) exists, it's probably fastest to add a comment to it about your requirement.
|
||||||
|
|
||||||
|
Implementation is usually straightforward if _libvips_ [already supports](http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/ch03.html) the feature you need.
|
||||||
|
|
||||||
|
## Submit a Pull Request to fix a bug
|
||||||
|
|
||||||
|
Thank you! To prevent the problem occurring again, please add unit tests that would have failed.
|
||||||
|
|
||||||
|
Please select the `master` branch as the destination for your Pull Request so your fix can be included in the next minor release.
|
||||||
|
|
||||||
|
Please squash your changes into a single commit using a command like `git rebase -i upstream/master`.
|
||||||
|
|
||||||
|
## Submit a Pull Request with a new feature
|
||||||
|
|
||||||
|
Please add JavaScript [unit tests](https://github.com/lovell/sharp/tree/master/test/unit) to cover your new feature. A test coverage report for the JavaScript code is generated in the `coverage/lcov-report` directory.
|
||||||
|
|
||||||
|
You deserve to add your details to the [list of contributors](https://github.com/lovell/sharp/blob/master/package.json#L5).
|
||||||
|
|
||||||
|
Any change that modifies the existing public API should be added to the relevant work-in-progress branch for inclusion in the next major release.
|
||||||
|
|
||||||
|
| Release | WIP branch |
|
||||||
|
| ------: | :--------- |
|
||||||
|
| v0.9.0 | intake |
|
||||||
|
| v0.10.0 | judgement |
|
||||||
|
| v0.11.0 | knife |
|
||||||
|
|
||||||
|
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
|
||||||
|
|
||||||
|
### Add a new public method
|
||||||
|
|
||||||
|
The API tries to be as fluent as possible. Image processing concepts follow the naming conventions from _libvips_ and, to a lesser extent, _ImageMagick_.
|
||||||
|
|
||||||
|
Most methods have optional parameters and assume sensible defaults. Methods with mandatory parameters often have names like `doSomethingWith(X)`.
|
||||||
|
|
||||||
|
Please ensure backwards compatibility where possible. Methods to modify previously default behaviour often have names like `withoutOptionY()` or `withExtraZ()`.
|
||||||
|
|
||||||
|
Feel free to create a [new issue](https://github.com/lovell/sharp/issues/new) to gather feedback on a potential API change.
|
||||||
|
|
||||||
|
### Remove an existing public method
|
||||||
|
|
||||||
|
A method to be removed should be deprecated in the next major version then removed in the following major version.
|
||||||
|
|
||||||
|
By way of example, the [bilinearInterpolation method](https://github.com/lovell/sharp/blob/v0.6.0/index.js#L155) present in v0.5.0 was deprecated in v0.6.0 and removed in v0.7.0.
|
||||||
|
|
||||||
|
## Run the tests
|
||||||
|
|
||||||
|
### Functional tests and static code analysis
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory leak tests
|
||||||
|
|
||||||
|
Requires _valgrind_.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd sharp/test/leak
|
||||||
|
./leak.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Finally
|
||||||
|
|
||||||
|
Please feel free to ask any questions via a [new issue](https://github.com/lovell/sharp/issues/new) or contact me by [e-mail](https://github.com/lovell/sharp/blob/master/package.json#L4).
|
||||||
74
README.md
74
README.md
@@ -3,6 +3,7 @@
|
|||||||
* [Installation](https://github.com/lovell/sharp#installation)
|
* [Installation](https://github.com/lovell/sharp#installation)
|
||||||
* [Usage examples](https://github.com/lovell/sharp#usage-examples)
|
* [Usage examples](https://github.com/lovell/sharp#usage-examples)
|
||||||
* [API](https://github.com/lovell/sharp#api)
|
* [API](https://github.com/lovell/sharp#api)
|
||||||
|
* [Contributing](https://github.com/lovell/sharp#contributing)
|
||||||
* [Testing](https://github.com/lovell/sharp#testing)
|
* [Testing](https://github.com/lovell/sharp#testing)
|
||||||
* [Performance](https://github.com/lovell/sharp#performance)
|
* [Performance](https://github.com/lovell/sharp#performance)
|
||||||
* [Thanks](https://github.com/lovell/sharp#thanks)
|
* [Thanks](https://github.com/lovell/sharp#thanks)
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
The typical use case for this high speed Node.js module is to convert large images of many formats to smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
The typical use case for this high speed Node.js module is to convert large images of many formats to smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
|
||||||
|
|
||||||
This module supports reading and writing JPEG, PNG and WebP images to and from Streams, Buffer objects and the filesystem.
|
This module supports reading and writing JPEG, PNG and WebP images to and from Streams, Buffer objects and the filesystem.
|
||||||
It also supports reading images of many other types from the filesystem via libmagick++ or libgraphicsmagick++ if present.
|
It also supports reading images of many other types from the filesystem via libmagick or libgraphicsmagick if present.
|
||||||
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
||||||
|
|
||||||
Only small regions of uncompressed image data are held in memory and processed at a time, taking full advantage of multiple CPU cores and L1/L2/L3 cache. Resizing an image is typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings.
|
Only small regions of uncompressed image data are held in memory and processed at a time, taking full advantage of multiple CPU cores and L1/L2/L3 cache. Resizing an image is typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings.
|
||||||
@@ -81,9 +82,11 @@ The _gettext_ dependency of _libvips_ [can lead](https://github.com/lovell/sharp
|
|||||||
|
|
||||||
docker pull marcbachmann/libvips
|
docker pull marcbachmann/libvips
|
||||||
|
|
||||||
### gulp.js
|
### Build tools
|
||||||
|
|
||||||
[Eugeny Vlasenko](https://github.com/mahnunchik) maintains [gulp-responsive](https://www.npmjs.org/package/gulp-responsive) and [Mohammad Prabowo](https://github.com/rizalp) maintains [gulp-sharp](https://www.npmjs.org/package/gulp-sharp).
|
* [gulp-responsive](https://www.npmjs.com/package/gulp-responsive)
|
||||||
|
* [gulp-sharp](https://www.npmjs.com/package/gulp-sharp)
|
||||||
|
* [grunt-sharp](https://www.npmjs.com/package/grunt-sharp)
|
||||||
|
|
||||||
## Usage examples
|
## Usage examples
|
||||||
|
|
||||||
@@ -110,7 +113,7 @@ readableStream.pipe(transformer).pipe(writableStream);
|
|||||||
```javascript
|
```javascript
|
||||||
var image = sharp(inputJpg);
|
var image = sharp(inputJpg);
|
||||||
image.metadata(function(err, metadata) {
|
image.metadata(function(err, metadata) {
|
||||||
image.resize(metadata.width / 2).webp().toBuffer(function(err, outputBuffer, info) {
|
image.resize(Math.floor(metadata.width / 2)).webp().toBuffer(function(err, outputBuffer, info) {
|
||||||
// outputBuffer contains a WebP image half the width and height of the original JPEG
|
// outputBuffer contains a WebP image half the width and height of the original JPEG
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -253,15 +256,21 @@ A Promises/A+ promise is returned when `callback` is not provided.
|
|||||||
|
|
||||||
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
||||||
|
|
||||||
|
#### limitInputPixels(pixels)
|
||||||
|
|
||||||
|
Do not process input images where the number of pixels (width * height) exceeds this limit.
|
||||||
|
|
||||||
|
`pixels` is the integral Number of pixels, with a value between 1 and the default 268402689 (0x3FFF * 0x3FFF).
|
||||||
|
|
||||||
### Image transformation options
|
### Image transformation options
|
||||||
|
|
||||||
#### resize(width, [height])
|
#### resize(width, [height])
|
||||||
|
|
||||||
Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified.
|
Scale output to `width` x `height`. By default, the resized image is cropped to the exact size specified.
|
||||||
|
|
||||||
`width` is the Number of pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
|
`width` is the integral Number of 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.
|
||||||
|
|
||||||
`height` is the Number of pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
|
`height` is the integral Number of pixels high the resultant image should be, between 1 and 16383. Use `null` or `undefined` to auto-scale the height to match the width.
|
||||||
|
|
||||||
#### extract(top, left, width, height)
|
#### extract(top, left, width, height)
|
||||||
|
|
||||||
@@ -315,6 +324,8 @@ Rotate the output image by either an explicit angle or auto-orient based on the
|
|||||||
|
|
||||||
Use this method without `angle` to determine the angle from EXIF data. Mirroring is supported and may infer the use of a `flip` operation.
|
Use this method without `angle` to determine the angle from EXIF data. Mirroring is supported and may infer the use of a `flip` operation.
|
||||||
|
|
||||||
|
Method order is important when both rotating and extracting regions, for example `rotate(x).extract(y)` will produce a different result to `extract(y).rotate(x)`.
|
||||||
|
|
||||||
#### flip()
|
#### flip()
|
||||||
|
|
||||||
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
|
Flip the image about the vertical Y axis. This always occurs after rotation, if any.
|
||||||
@@ -392,6 +403,18 @@ Use PNG format for the output image.
|
|||||||
|
|
||||||
Use WebP format for the output image.
|
Use WebP format for the output image.
|
||||||
|
|
||||||
|
#### raw()
|
||||||
|
|
||||||
|
_Requires libvips 7.42.0+_
|
||||||
|
|
||||||
|
Provide raw, uncompressed uint8 (unsigned char) image data for Buffer and Stream based output.
|
||||||
|
|
||||||
|
The number of channels depends on the input image and selected options.
|
||||||
|
|
||||||
|
* 1 channel for images converted to `greyscale()`, with each byte representing one pixel.
|
||||||
|
* 3 channels for colour images without alpha transparency, with bytes ordered \[red, green, blue, red, green, blue, etc.\]).
|
||||||
|
* 4 channels for colour images with alpha transparency, with bytes ordered \[red, green, blue, alpha, red, green, blue, alpha, etc.\].
|
||||||
|
|
||||||
#### quality(quality)
|
#### quality(quality)
|
||||||
|
|
||||||
The output quality to use for lossy JPEG, WebP and TIFF output formats. The default quality is `80`.
|
The output quality to use for lossy JPEG, WebP and TIFF output formats. The default quality is `80`.
|
||||||
@@ -429,7 +452,7 @@ An advanced setting to disable adaptive row filtering for the lossless PNG outpu
|
|||||||
`callback`, if present, is called with two arguments `(err, info)` where:
|
`callback`, if present, is called with two arguments `(err, info)` where:
|
||||||
|
|
||||||
* `err` contains an error message, if any.
|
* `err` contains an error message, if any.
|
||||||
* `info` contains the output image `format`, `width` and `height`.
|
* `info` contains the output image `format`, `size` (bytes), `width` and `height`.
|
||||||
|
|
||||||
A Promises/A+ promise is returned when `callback` is not provided.
|
A Promises/A+ promise is returned when `callback` is not provided.
|
||||||
|
|
||||||
@@ -441,7 +464,7 @@ Write image data to a Buffer, the format of which will match the input image by
|
|||||||
|
|
||||||
* `err` is an error message, if any.
|
* `err` is an error message, if any.
|
||||||
* `buffer` is the output image data.
|
* `buffer` is the output image data.
|
||||||
* `info` contains the output image `format`, `width` and `height`.
|
* `info` contains the output image `format`, `size` (bytes), `width` and `height`.
|
||||||
|
|
||||||
A Promises/A+ promise is returned when `callback` is not provided.
|
A Promises/A+ promise is returned when `callback` is not provided.
|
||||||
|
|
||||||
@@ -464,7 +487,7 @@ sharp.cache(50, 200); // { current: 49, high: 99, memory: 50, items: 200}
|
|||||||
|
|
||||||
#### sharp.concurrency([threads])
|
#### sharp.concurrency([threads])
|
||||||
|
|
||||||
`threads`, if provided, is the Number of threads _libvips'_ should create for image processing. The default value is the number of CPU cores. A value of `0` will reset to this default.
|
`threads`, if provided, is the Number of threads _libvips'_ should create for processing each image. The default value is the number of CPU cores. A value of `0` will reset to this default.
|
||||||
|
|
||||||
This method always returns the current concurrency.
|
This method always returns the current concurrency.
|
||||||
|
|
||||||
@@ -474,6 +497,8 @@ sharp.concurrency(2); // 2
|
|||||||
sharp.concurrency(0); // 4
|
sharp.concurrency(0); // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The maximum number of images that can be processed in parallel is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
|
||||||
|
|
||||||
#### sharp.counters()
|
#### sharp.counters()
|
||||||
|
|
||||||
Provides access to internal task counters.
|
Provides access to internal task counters.
|
||||||
@@ -485,6 +510,10 @@ Provides access to internal task counters.
|
|||||||
var counters = sharp.counters(); // { queue: 2, process: 4 }
|
var counters = sharp.counters(); // { queue: 2, process: 4 }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
A [guide for contributors](https://github.com/lovell/sharp/blob/master/CONTRIBUTING.md) covers reporting bugs, requesting features and submitting code changes.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
### Functional tests
|
### Functional tests
|
||||||
@@ -501,29 +530,6 @@ var counters = sharp.counters(); // { queue: 2, process: 4 }
|
|||||||
|
|
||||||
[](https://snap-ci.com/lovell/sharp/branch/master)
|
[](https://snap-ci.com/lovell/sharp/branch/master)
|
||||||
|
|
||||||
#### It worked on my machine
|
|
||||||
|
|
||||||
```
|
|
||||||
npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Memory leak tests
|
|
||||||
|
|
||||||
```
|
|
||||||
cd sharp/test/leak
|
|
||||||
./leak.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Requires _valgrind_:
|
|
||||||
|
|
||||||
```
|
|
||||||
brew install valgrind
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo apt-get install -qq valgrind
|
|
||||||
```
|
|
||||||
|
|
||||||
### Benchmark tests
|
### Benchmark tests
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -540,7 +546,7 @@ brew install graphicsmagick
|
|||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo apt-get install -qq imagemagick graphicsmagick libmagick++-dev
|
sudo apt-get install -qq imagemagick graphicsmagick libmagickcore-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -610,7 +616,7 @@ Thank you!
|
|||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
Copyright 2013, 2014 Lovell Fuller and contributors.
|
Copyright 2013, 2014, 2015 Lovell Fuller and contributors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
57
index.js
57
index.js
@@ -11,6 +11,12 @@ var BluebirdPromise = require('bluebird');
|
|||||||
var sharp = require('./build/Release/sharp');
|
var sharp = require('./build/Release/sharp');
|
||||||
var libvipsVersion = sharp.libvipsVersion();
|
var libvipsVersion = sharp.libvipsVersion();
|
||||||
|
|
||||||
|
var maximum = {
|
||||||
|
width: 0x3FFF,
|
||||||
|
height: 0x3FFF,
|
||||||
|
pixels: Math.pow(0x3FFF, 2)
|
||||||
|
};
|
||||||
|
|
||||||
var Sharp = function(input) {
|
var Sharp = function(input) {
|
||||||
if (!(this instanceof Sharp)) {
|
if (!(this instanceof Sharp)) {
|
||||||
return new Sharp(input);
|
return new Sharp(input);
|
||||||
@@ -18,8 +24,10 @@ var Sharp = function(input) {
|
|||||||
stream.Duplex.call(this);
|
stream.Duplex.call(this);
|
||||||
this.options = {
|
this.options = {
|
||||||
// input options
|
// input options
|
||||||
|
bufferIn: null,
|
||||||
streamIn: false,
|
streamIn: false,
|
||||||
sequentialRead: false,
|
sequentialRead: false,
|
||||||
|
limitInputPixels: maximum.pixels,
|
||||||
// ICC profiles
|
// ICC profiles
|
||||||
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
|
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
|
||||||
// resize options
|
// resize options
|
||||||
@@ -36,6 +44,7 @@ var Sharp = function(input) {
|
|||||||
canvas: 'c',
|
canvas: 'c',
|
||||||
gravity: 0,
|
gravity: 0,
|
||||||
angle: 0,
|
angle: 0,
|
||||||
|
rotateBeforePreExtract: false,
|
||||||
flip: false,
|
flip: false,
|
||||||
flop: false,
|
flop: false,
|
||||||
withoutEnlargement: false,
|
withoutEnlargement: false,
|
||||||
@@ -95,16 +104,16 @@ Sharp.prototype._write = function(chunk, encoding, callback) {
|
|||||||
/*jslint unused: false */
|
/*jslint unused: false */
|
||||||
if (this.options.streamIn) {
|
if (this.options.streamIn) {
|
||||||
if (typeof chunk === 'object' && chunk instanceof Buffer) {
|
if (typeof chunk === 'object' && chunk instanceof Buffer) {
|
||||||
if (typeof this.options.bufferIn === 'undefined') {
|
if (this.options.bufferIn instanceof Buffer) {
|
||||||
// Create new Buffer
|
|
||||||
this.options.bufferIn = new Buffer(chunk.length);
|
|
||||||
chunk.copy(this.options.bufferIn);
|
|
||||||
} else {
|
|
||||||
// Append to existing Buffer
|
// Append to existing Buffer
|
||||||
this.options.bufferIn = Buffer.concat(
|
this.options.bufferIn = Buffer.concat(
|
||||||
[this.options.bufferIn, chunk],
|
[this.options.bufferIn, chunk],
|
||||||
this.options.bufferIn.length + chunk.length
|
this.options.bufferIn.length + chunk.length
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
// Create new Buffer
|
||||||
|
this.options.bufferIn = new Buffer(chunk.length);
|
||||||
|
chunk.copy(this.options.bufferIn);
|
||||||
}
|
}
|
||||||
callback();
|
callback();
|
||||||
} else {
|
} else {
|
||||||
@@ -135,6 +144,10 @@ Sharp.prototype.extract = function(topOffset, leftOffset, width, height) {
|
|||||||
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) {
|
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) {
|
||||||
this.options[name + suffix] = values[index];
|
this.options[name + suffix] = values[index];
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
// Ensure existing rotation occurs before pre-resize extraction
|
||||||
|
if (suffix === 'Pre' && this.options.angle !== 0) {
|
||||||
|
this.options.rotateBeforePreExtract = true;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -340,10 +353,10 @@ Sharp.prototype.compressionLevel = function(compressionLevel) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Disable the use of adaptive row filtering for PNG output - requires libvips 7.41.0+
|
Disable the use of adaptive row filtering for PNG output - requires libvips 7.42.0+
|
||||||
*/
|
*/
|
||||||
Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
|
Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
|
||||||
if (semver.gte(libvipsVersion, '7.41.0')) {
|
if (semver.gte(libvipsVersion, '7.42.0')) {
|
||||||
this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
|
this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
|
||||||
} else {
|
} else {
|
||||||
console.error('withoutAdaptiveFiltering requires libvips 7.41.0+');
|
console.error('withoutAdaptiveFiltering requires libvips 7.41.0+');
|
||||||
@@ -360,24 +373,37 @@ Sharp.prototype.resize = function(width, height) {
|
|||||||
if (!width) {
|
if (!width) {
|
||||||
this.options.width = -1;
|
this.options.width = -1;
|
||||||
} else {
|
} else {
|
||||||
if (typeof width === 'number' && !Number.isNaN(width)) {
|
if (typeof width === 'number' && !Number.isNaN(width) && width % 1 === 0 && width > 0 && width <= maximum.width) {
|
||||||
this.options.width = width;
|
this.options.width = width;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid width ' + width);
|
throw new Error('Invalid width (1 to ' + maximum.width + ') ' + width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!height) {
|
if (!height) {
|
||||||
this.options.height = -1;
|
this.options.height = -1;
|
||||||
} else {
|
} else {
|
||||||
if (typeof height === 'number' && !Number.isNaN(height)) {
|
if (typeof height === 'number' && !Number.isNaN(height) && height % 1 === 0 && height > 0 && height <= maximum.height) {
|
||||||
this.options.height = height;
|
this.options.height = height;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid height ' + height);
|
throw new Error('Invalid height (1 to ' + maximum.height + ') ' + height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Limit the total number of pixels for input images
|
||||||
|
Assumes the image dimensions contained in the file header can be trusted
|
||||||
|
*/
|
||||||
|
Sharp.prototype.limitInputPixels = function(limit) {
|
||||||
|
if (typeof limit === 'number' && !Number.isNaN(limit) && limit % 1 === 0 && limit > 0 && limit <= maximum.pixels) {
|
||||||
|
this.options.limitInputPixels = limit;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid pixel limit (1 to ' + maximum.pixels + ') ' + limit);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Write output image data to a file
|
Write output image data to a file
|
||||||
*/
|
*/
|
||||||
@@ -424,6 +450,15 @@ Sharp.prototype.webp = function() {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Sharp.prototype.raw = function() {
|
||||||
|
if (semver.gte(libvipsVersion, '7.42.0')) {
|
||||||
|
this.options.output = '__raw';
|
||||||
|
} else {
|
||||||
|
console.error('Raw output requires libvips 7.42.0+');
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Used by a Writable Stream to notify that it is ready for data
|
Used by a Writable Stream to notify that it is ready for data
|
||||||
*/
|
*/
|
||||||
|
|||||||
17
package.json
17
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"version": "0.8.2",
|
"version": "0.9.0",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Pierre Inglebert <pierre.inglebert@gmail.com>",
|
"Pierre Inglebert <pierre.inglebert@gmail.com>",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
],
|
],
|
||||||
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
|
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=10000 ./test/unit/*.js"
|
"test": "VIPS_WARNING=0 node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- --slow=5000 --timeout=10000 ./test/unit/*.js"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -34,16 +34,17 @@
|
|||||||
"vips"
|
"vips"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bluebird": "^2.3.11",
|
"bluebird": "^2.8.2",
|
||||||
"color": "^0.7.1",
|
"color": "^0.7.3",
|
||||||
"nan": "^1.4.1",
|
"nan": "^1.6.1",
|
||||||
"semver": "^4.1.0"
|
"semver": "^4.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^2.0.1",
|
"mocha": "^2.1.0",
|
||||||
"mocha-jshint": "^0.0.9",
|
"mocha-jshint": "^0.0.9",
|
||||||
"istanbul": "^0.3.5",
|
"istanbul": "^0.3.5",
|
||||||
"coveralls": "^2.11.2"
|
"coveralls": "^2.11.2",
|
||||||
|
"node-cpplint": "^0.4.0"
|
||||||
},
|
},
|
||||||
"license": "Apache 2.0",
|
"license": "Apache 2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
vips_version_minimum=7.40.0
|
vips_version_minimum=7.40.0
|
||||||
vips_version_latest_major=7.42
|
vips_version_latest_major=7.42
|
||||||
vips_version_latest_minor=0
|
vips_version_latest_minor=1
|
||||||
|
|
||||||
install_libvips_from_source() {
|
install_libvips_from_source() {
|
||||||
echo "Compiling libvips $vips_version_latest_major.$vips_version_latest_minor from source"
|
echo "Compiling libvips $vips_version_latest_major.$vips_version_latest_minor from source"
|
||||||
@@ -95,7 +95,7 @@ case $(uname -s) in
|
|||||||
trusty|utopic|qiana|rebecca)
|
trusty|utopic|qiana|rebecca)
|
||||||
# Ubuntu 14, Mint 17
|
# Ubuntu 14, Mint 17
|
||||||
echo "Installing libvips dependencies via apt-get"
|
echo "Installing libvips dependencies via apt-get"
|
||||||
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev liblcms2-dev libxml2-dev swig libmagickwand-dev curl
|
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff5-dev libexif-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
|
||||||
install_libvips_from_source
|
install_libvips_from_source
|
||||||
;;
|
;;
|
||||||
precise|wheezy|maya)
|
precise|wheezy|maya)
|
||||||
@@ -103,7 +103,7 @@ case $(uname -s) in
|
|||||||
echo "Installing libvips dependencies via apt-get"
|
echo "Installing libvips dependencies via apt-get"
|
||||||
add-apt-repository -y ppa:lyrasis/precise-backports
|
add-apt-repository -y ppa:lyrasis/precise-backports
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev liblcms2-dev libxml2-dev swig libmagickwand-dev curl
|
apt-get install -y automake build-essential gobject-introspection gtk-doc-tools libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff4-dev libexif-dev liblcms2-dev libxml2-dev swig libmagickcore-dev curl
|
||||||
install_libvips_from_source
|
install_libvips_from_source
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
|||||||
@@ -162,4 +162,4 @@ namespace sharp {
|
|||||||
return window_size;
|
return window_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace sharp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef SHARP_COMMON_H
|
#ifndef SRC_COMMON_H_
|
||||||
#define SHARP_COMMON_H
|
#define SRC_COMMON_H_
|
||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
@@ -66,6 +66,6 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
int InterpolatorWindowSize(char const *name);
|
int InterpolatorWindowSize(char const *name);
|
||||||
|
|
||||||
} // namespace
|
} // namespace sharp
|
||||||
|
|
||||||
#endif
|
#endif // SRC_COMMON_H_
|
||||||
|
|||||||
@@ -6,8 +6,23 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
|
|
||||||
using namespace v8;
|
using v8::Handle;
|
||||||
using namespace sharp;
|
using v8::Local;
|
||||||
|
using v8::Value;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Number;
|
||||||
|
using v8::String;
|
||||||
|
using v8::Boolean;
|
||||||
|
using v8::Function;
|
||||||
|
using v8::Exception;
|
||||||
|
|
||||||
|
using sharp::ImageType;
|
||||||
|
using sharp::DetermineImageType;
|
||||||
|
using sharp::InitImage;
|
||||||
|
using sharp::HasProfile;
|
||||||
|
using sharp::HasAlpha;
|
||||||
|
using sharp::ExifOrientation;
|
||||||
|
using sharp::counterQueue;
|
||||||
|
|
||||||
struct MetadataBaton {
|
struct MetadataBaton {
|
||||||
// Input
|
// Input
|
||||||
@@ -47,6 +62,10 @@ class MetadataWorker : public NanAsyncWorker {
|
|||||||
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
imageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
||||||
if (imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
image = InitImage(imageType, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
|
image = InitImage(imageType, baton->bufferIn, baton->bufferInLength, VIPS_ACCESS_RANDOM);
|
||||||
|
if (image == NULL) {
|
||||||
|
(baton->err).append("Input buffer has corrupt header");
|
||||||
|
imageType = ImageType::UNKNOWN;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("Input buffer contains unsupported image format");
|
(baton->err).append("Input buffer contains unsupported image format");
|
||||||
}
|
}
|
||||||
@@ -55,8 +74,12 @@ class MetadataWorker : public NanAsyncWorker {
|
|||||||
imageType = DetermineImageType(baton->fileIn.c_str());
|
imageType = DetermineImageType(baton->fileIn.c_str());
|
||||||
if (imageType != ImageType::UNKNOWN) {
|
if (imageType != ImageType::UNKNOWN) {
|
||||||
image = InitImage(imageType, baton->fileIn.c_str(), VIPS_ACCESS_RANDOM);
|
image = InitImage(imageType, baton->fileIn.c_str(), VIPS_ACCESS_RANDOM);
|
||||||
|
if (image == NULL) {
|
||||||
|
(baton->err).append("Input file has corrupt header");
|
||||||
|
imageType = ImageType::UNKNOWN;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("File is of an unsupported image format");
|
(baton->err).append("Input file is of an unsupported image format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (image != NULL && imageType != ImageType::UNKNOWN) {
|
if (image != NULL && imageType != ImageType::UNKNOWN) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef SHARP_METADATA_H
|
#ifndef SRC_METADATA_H_
|
||||||
#define SHARP_METADATA_H
|
#define SRC_METADATA_H_
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
NAN_METHOD(metadata);
|
NAN_METHOD(metadata);
|
||||||
|
|
||||||
#endif
|
#endif // SRC_METADATA_H_
|
||||||
|
|||||||
174
src/resize.cc
174
src/resize.cc
@@ -10,8 +10,30 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "resize.h"
|
#include "resize.h"
|
||||||
|
|
||||||
using namespace v8;
|
using v8::Handle;
|
||||||
using namespace sharp;
|
using v8::Local;
|
||||||
|
using v8::Value;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::Integer;
|
||||||
|
using v8::Uint32;
|
||||||
|
using v8::String;
|
||||||
|
using v8::Array;
|
||||||
|
using v8::Function;
|
||||||
|
using v8::Exception;
|
||||||
|
|
||||||
|
using sharp::ImageType;
|
||||||
|
using sharp::DetermineImageType;
|
||||||
|
using sharp::InitImage;
|
||||||
|
using sharp::InterpolatorWindowSize;
|
||||||
|
using sharp::HasProfile;
|
||||||
|
using sharp::HasAlpha;
|
||||||
|
using sharp::ExifOrientation;
|
||||||
|
using sharp::IsJpeg;
|
||||||
|
using sharp::IsPng;
|
||||||
|
using sharp::IsWebp;
|
||||||
|
using sharp::IsTiff;
|
||||||
|
using sharp::counterProcess;
|
||||||
|
using sharp::counterQueue;
|
||||||
|
|
||||||
enum class Canvas {
|
enum class Canvas {
|
||||||
CROP,
|
CROP,
|
||||||
@@ -20,21 +42,22 @@ enum class Canvas {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum class Angle {
|
enum class Angle {
|
||||||
D0,
|
D0,
|
||||||
D90,
|
D90,
|
||||||
D180,
|
D180,
|
||||||
D270,
|
D270,
|
||||||
DLAST
|
DLAST
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResizeBaton {
|
struct ResizeBaton {
|
||||||
std::string fileIn;
|
std::string fileIn;
|
||||||
void* bufferIn;
|
char *bufferIn;
|
||||||
size_t bufferInLength;
|
size_t bufferInLength;
|
||||||
std::string iccProfilePath;
|
std::string iccProfilePath;
|
||||||
|
int limitInputPixels;
|
||||||
std::string output;
|
std::string output;
|
||||||
std::string outputFormat;
|
std::string outputFormat;
|
||||||
void* bufferOut;
|
void *bufferOut;
|
||||||
size_t bufferOutLength;
|
size_t bufferOutLength;
|
||||||
int topOffsetPre;
|
int topOffsetPre;
|
||||||
int leftOffsetPre;
|
int leftOffsetPre;
|
||||||
@@ -58,6 +81,7 @@ struct ResizeBaton {
|
|||||||
double gamma;
|
double gamma;
|
||||||
bool greyscale;
|
bool greyscale;
|
||||||
int angle;
|
int angle;
|
||||||
|
bool rotateBeforePreExtract;
|
||||||
bool flip;
|
bool flip;
|
||||||
bool flop;
|
bool flop;
|
||||||
bool progressive;
|
bool progressive;
|
||||||
@@ -71,6 +95,7 @@ struct ResizeBaton {
|
|||||||
|
|
||||||
ResizeBaton():
|
ResizeBaton():
|
||||||
bufferInLength(0),
|
bufferInLength(0),
|
||||||
|
limitInputPixels(0),
|
||||||
outputFormat(""),
|
outputFormat(""),
|
||||||
bufferOutLength(0),
|
bufferOutLength(0),
|
||||||
topOffsetPre(-1),
|
topOffsetPre(-1),
|
||||||
@@ -100,6 +125,16 @@ struct ResizeBaton {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Delete input char[] buffer and notify V8 of memory deallocation
|
||||||
|
Used as the callback function for the "postclose" signal
|
||||||
|
*/
|
||||||
|
static void DeleteBuffer(VipsObject *object, char *buffer) {
|
||||||
|
if (buffer != NULL) {
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ResizeWorker : public NanAsyncWorker {
|
class ResizeWorker : public NanAsyncWorker {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -110,6 +145,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
libuv worker
|
libuv worker
|
||||||
*/
|
*/
|
||||||
void Execute() {
|
void Execute() {
|
||||||
|
|
||||||
// Decrement queued task counter
|
// Decrement queued task counter
|
||||||
g_atomic_int_dec_and_test(&counterQueue);
|
g_atomic_int_dec_and_test(&counterQueue);
|
||||||
// Increment processing task counter
|
// Increment processing task counter
|
||||||
@@ -123,29 +159,68 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
|
|
||||||
// Input
|
// Input
|
||||||
ImageType inputImageType = ImageType::UNKNOWN;
|
ImageType inputImageType = ImageType::UNKNOWN;
|
||||||
VipsImage *image;
|
VipsImage *image = NULL;
|
||||||
if (baton->bufferInLength > 1) {
|
if (baton->bufferInLength > 1) {
|
||||||
// From buffer
|
// From buffer
|
||||||
inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
inputImageType = DetermineImageType(baton->bufferIn, baton->bufferInLength);
|
||||||
if (inputImageType != ImageType::UNKNOWN) {
|
if (inputImageType != ImageType::UNKNOWN) {
|
||||||
image = InitImage(inputImageType, baton->bufferIn, baton->bufferInLength, baton->accessMethod);
|
image = InitImage(inputImageType, baton->bufferIn, baton->bufferInLength, baton->accessMethod);
|
||||||
|
if (image != NULL) {
|
||||||
|
// Listen for "postclose" signal to delete input buffer
|
||||||
|
g_signal_connect(image, "postclose", G_CALLBACK(DeleteBuffer), baton->bufferIn);
|
||||||
|
} else {
|
||||||
|
// Could not read header data
|
||||||
|
(baton->err).append("Input buffer has corrupt header");
|
||||||
|
inputImageType = ImageType::UNKNOWN;
|
||||||
|
DeleteBuffer(NULL, baton->bufferIn);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("Input buffer contains unsupported image format");
|
(baton->err).append("Input buffer contains unsupported image format");
|
||||||
|
DeleteBuffer(NULL, baton->bufferIn);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// From file
|
// From file
|
||||||
inputImageType = DetermineImageType(baton->fileIn.c_str());
|
inputImageType = DetermineImageType(baton->fileIn.c_str());
|
||||||
if (inputImageType != ImageType::UNKNOWN) {
|
if (inputImageType != ImageType::UNKNOWN) {
|
||||||
image = InitImage(inputImageType, baton->fileIn.c_str(), baton->accessMethod);
|
image = InitImage(inputImageType, baton->fileIn.c_str(), baton->accessMethod);
|
||||||
|
if (image == NULL) {
|
||||||
|
(baton->err).append("Input file has corrupt header");
|
||||||
|
inputImageType = ImageType::UNKNOWN;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("File is of an unsupported image format");
|
(baton->err).append("Input file is of an unsupported image format");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (inputImageType == ImageType::UNKNOWN) {
|
if (image == NULL || inputImageType == ImageType::UNKNOWN) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, image);
|
vips_object_local(hook, image);
|
||||||
|
|
||||||
|
// Limit input images to a given number of pixels, where pixels = width * height
|
||||||
|
if (image->Xsize * image->Ysize > baton->limitInputPixels) {
|
||||||
|
(baton->err).append("Input image exceeds pixel limit");
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate angle of rotation
|
||||||
|
Angle rotation;
|
||||||
|
bool flip;
|
||||||
|
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image);
|
||||||
|
if (flip && !baton->flip) {
|
||||||
|
// Add flip operation due to EXIF mirroring
|
||||||
|
baton->flip = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate pre-extract
|
||||||
|
if (baton->rotateBeforePreExtract && rotation != Angle::D0) {
|
||||||
|
VipsImage *rotated;
|
||||||
|
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, rotated);
|
||||||
|
image = rotated;
|
||||||
|
}
|
||||||
|
|
||||||
// Pre extraction
|
// Pre extraction
|
||||||
if (baton->topOffsetPre != -1) {
|
if (baton->topOffsetPre != -1) {
|
||||||
VipsImage *extractedPre;
|
VipsImage *extractedPre;
|
||||||
@@ -156,24 +231,15 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
image = extractedPre;
|
image = extractedPre;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get input image width and height
|
// Get pre-resize image width and height
|
||||||
int inputWidth = image->Xsize;
|
int inputWidth = image->Xsize;
|
||||||
int inputHeight = image->Ysize;
|
int inputHeight = image->Ysize;
|
||||||
|
|
||||||
// Calculate angle of rotation, to be carried out later
|
|
||||||
Angle rotation;
|
|
||||||
bool flip;
|
|
||||||
std::tie(rotation, flip) = CalculateRotationAndFlip(baton->angle, image);
|
|
||||||
if (rotation == Angle::D90 || rotation == Angle::D270) {
|
if (rotation == Angle::D90 || rotation == Angle::D270) {
|
||||||
// Swap input output width and height when rotating by 90 or 270 degrees
|
// Swap input output width and height when rotating by 90 or 270 degrees
|
||||||
int swap = inputWidth;
|
int swap = inputWidth;
|
||||||
inputWidth = inputHeight;
|
inputWidth = inputHeight;
|
||||||
inputHeight = swap;
|
inputHeight = swap;
|
||||||
}
|
}
|
||||||
if (flip && !baton->flip) {
|
|
||||||
// Add flip operation due to EXIF mirroring
|
|
||||||
baton->flip = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get window size of interpolator, used for determining shrink vs affine
|
// Get window size of interpolator, used for determining shrink vs affine
|
||||||
int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.c_str());
|
int interpolatorWindowSize = InterpolatorWindowSize(baton->interpolator.c_str());
|
||||||
@@ -305,7 +371,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
if (vips_flatten(image, &flattened, "background", background, NULL)) {
|
if (vips_flatten(image, &flattened, "background", background, NULL)) {
|
||||||
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
};
|
}
|
||||||
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
||||||
vips_object_local(hook, flattened);
|
vips_object_local(hook, flattened);
|
||||||
image = flattened;
|
image = flattened;
|
||||||
@@ -392,7 +458,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rotate
|
// Rotate
|
||||||
if (rotation != Angle::D0) {
|
if (!baton->rotateBeforePreExtract && rotation != Angle::D0) {
|
||||||
VipsImage *rotated;
|
VipsImage *rotated;
|
||||||
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
|
if (vips_rot(image, &rotated, static_cast<VipsAngle>(rotation), NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
@@ -499,7 +565,9 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
// Post extraction
|
// Post extraction
|
||||||
if (baton->topOffsetPost != -1) {
|
if (baton->topOffsetPost != -1) {
|
||||||
VipsImage *extractedPost;
|
VipsImage *extractedPost;
|
||||||
if (vips_extract_area(image, &extractedPost, baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL)) {
|
if (vips_extract_area(image, &extractedPost,
|
||||||
|
baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL
|
||||||
|
)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, extractedPost);
|
vips_object_local(hook, extractedPost);
|
||||||
@@ -529,7 +597,6 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
vips_object_local(hook, gaussian);
|
vips_object_local(hook, gaussian);
|
||||||
// Apply Gaussian function
|
// Apply Gaussian function
|
||||||
VipsImage *blurred;
|
|
||||||
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
|
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
@@ -636,6 +703,35 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
baton->outputFormat = "webp";
|
baton->outputFormat = "webp";
|
||||||
|
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)
|
||||||
|
} else if (baton->output == "__raw") {
|
||||||
|
// Write raw, uncompressed image data to buffer
|
||||||
|
if (baton->greyscale) {
|
||||||
|
// Extract first band for greyscale image
|
||||||
|
VipsImage *grey;
|
||||||
|
if (vips_extract_band(image, &grey, 1, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, grey);
|
||||||
|
image = grey;
|
||||||
|
}
|
||||||
|
if (image->BandFmt != VIPS_FORMAT_UCHAR) {
|
||||||
|
// Cast pixels to uint8 (unsigned char)
|
||||||
|
VipsImage *uchar;
|
||||||
|
if (vips_cast(image, &uchar, VIPS_FORMAT_UCHAR, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, uchar);
|
||||||
|
image = uchar;
|
||||||
|
}
|
||||||
|
// Get raw image data
|
||||||
|
baton->bufferOut = vips_image_write_to_memory(image, &baton->bufferOutLength);
|
||||||
|
if (baton->bufferOut == NULL) {
|
||||||
|
(baton->err).append("Could not allocate enough memory for raw output");
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
baton->outputFormat = "raw";
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
bool outputJpeg = IsJpeg(baton->output);
|
bool outputJpeg = IsJpeg(baton->output);
|
||||||
bool outputPng = IsPng(baton->output);
|
bool outputPng = IsPng(baton->output);
|
||||||
@@ -650,7 +746,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
baton->outputFormat = "jpeg";
|
baton->outputFormat = "jpeg";
|
||||||
} else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) {
|
} else if (outputPng || (matchInput && inputImageType == ImageType::PNG)) {
|
||||||
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41)
|
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 42)
|
||||||
// Select PNG row filter
|
// Select PNG row filter
|
||||||
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
|
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
|
||||||
// Write PNG to file
|
// Write PNG to file
|
||||||
@@ -713,16 +809,22 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
// Info Object
|
// Info Object
|
||||||
Local<Object> info = NanNew<Object>();
|
Local<Object> info = NanNew<Object>();
|
||||||
info->Set(NanNew<String>("format"), NanNew<String>(baton->outputFormat));
|
info->Set(NanNew<String>("format"), NanNew<String>(baton->outputFormat));
|
||||||
info->Set(NanNew<String>("width"), NanNew<Number>(width));
|
info->Set(NanNew<String>("width"), NanNew<Integer>(width));
|
||||||
info->Set(NanNew<String>("height"), NanNew<Number>(height));
|
info->Set(NanNew<String>("height"), NanNew<Integer>(height));
|
||||||
|
|
||||||
if (baton->bufferOutLength > 0) {
|
if (baton->bufferOutLength > 0) {
|
||||||
// Buffer
|
// Copy data to new Buffer
|
||||||
argv[1] = NanNewBufferHandle(static_cast<char*>(baton->bufferOut), baton->bufferOutLength);
|
argv[1] = NanNewBufferHandle(static_cast<char*>(baton->bufferOut), baton->bufferOutLength);
|
||||||
|
// bufferOut was allocated via g_malloc
|
||||||
g_free(baton->bufferOut);
|
g_free(baton->bufferOut);
|
||||||
|
// Add buffer size to info
|
||||||
|
info->Set(NanNew<String>("size"), NanNew<Uint32>(static_cast<uint32_t>(baton->bufferOutLength)));
|
||||||
argv[2] = info;
|
argv[2] = info;
|
||||||
} else {
|
} else {
|
||||||
// File
|
// Add file size to info
|
||||||
|
struct stat st;
|
||||||
|
g_stat(baton->output.c_str(), &st);
|
||||||
|
info->Set(NanNew<String>("size"), NanNew<Uint32>(static_cast<uint32_t>(st.st_size)));
|
||||||
argv[1] = info;
|
argv[1] = info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -833,11 +935,16 @@ NAN_METHOD(resize) {
|
|||||||
// Input Buffer object
|
// Input Buffer object
|
||||||
if (options->Get(NanNew<String>("bufferIn"))->IsObject()) {
|
if (options->Get(NanNew<String>("bufferIn"))->IsObject()) {
|
||||||
Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject();
|
Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject();
|
||||||
|
// Take a copy of the input Buffer to avoid problems with V8 heap compaction
|
||||||
baton->bufferInLength = node::Buffer::Length(buffer);
|
baton->bufferInLength = node::Buffer::Length(buffer);
|
||||||
baton->bufferIn = node::Buffer::Data(buffer);
|
baton->bufferIn = new char[baton->bufferInLength];
|
||||||
|
memcpy(baton->bufferIn, node::Buffer::Data(buffer), baton->bufferInLength);
|
||||||
|
options->Set(NanNew<String>("bufferIn"), NanNull());
|
||||||
}
|
}
|
||||||
// ICC profile to use when input CMYK image has no embedded profile
|
// ICC profile to use when input CMYK image has no embedded profile
|
||||||
baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString());
|
baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString());
|
||||||
|
// Limit input images to a given number of pixels, where pixels = width * height
|
||||||
|
baton->limitInputPixels = options->Get(NanNew<String>("limitInputPixels"))->Int32Value();
|
||||||
// Extract image options
|
// Extract image options
|
||||||
baton->topOffsetPre = options->Get(NanNew<String>("topOffsetPre"))->Int32Value();
|
baton->topOffsetPre = options->Get(NanNew<String>("topOffsetPre"))->Int32Value();
|
||||||
baton->leftOffsetPre = options->Get(NanNew<String>("leftOffsetPre"))->Int32Value();
|
baton->leftOffsetPre = options->Get(NanNew<String>("leftOffsetPre"))->Int32Value();
|
||||||
@@ -877,6 +984,7 @@ NAN_METHOD(resize) {
|
|||||||
baton->gamma = options->Get(NanNew<String>("gamma"))->NumberValue();
|
baton->gamma = options->Get(NanNew<String>("gamma"))->NumberValue();
|
||||||
baton->greyscale = options->Get(NanNew<String>("greyscale"))->BooleanValue();
|
baton->greyscale = options->Get(NanNew<String>("greyscale"))->BooleanValue();
|
||||||
baton->angle = options->Get(NanNew<String>("angle"))->Int32Value();
|
baton->angle = options->Get(NanNew<String>("angle"))->Int32Value();
|
||||||
|
baton->rotateBeforePreExtract = options->Get(NanNew<String>("rotateBeforePreExtract"))->BooleanValue();
|
||||||
baton->flip = options->Get(NanNew<String>("flip"))->BooleanValue();
|
baton->flip = options->Get(NanNew<String>("flip"))->BooleanValue();
|
||||||
baton->flop = options->Get(NanNew<String>("flop"))->BooleanValue();
|
baton->flop = options->Get(NanNew<String>("flop"))->BooleanValue();
|
||||||
// Output options
|
// Output options
|
||||||
@@ -889,7 +997,7 @@ NAN_METHOD(resize) {
|
|||||||
baton->output = *String::Utf8Value(options->Get(NanNew<String>("output"))->ToString());
|
baton->output = *String::Utf8Value(options->Get(NanNew<String>("output"))->ToString());
|
||||||
|
|
||||||
// Join queue for worker thread
|
// Join queue for worker thread
|
||||||
NanCallback *callback = new NanCallback(args[1].As<v8::Function>());
|
NanCallback *callback = new NanCallback(args[1].As<Function>());
|
||||||
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
||||||
|
|
||||||
// Increment queued task counter
|
// Increment queued task counter
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef SHARP_RESIZE_H
|
#ifndef SRC_RESIZE_H_
|
||||||
#define SHARP_RESIZE_H
|
#define SRC_RESIZE_H_
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
NAN_METHOD(resize);
|
NAN_METHOD(resize);
|
||||||
|
|
||||||
#endif
|
#endif // SRC_RESIZE_H_
|
||||||
|
|||||||
10
src/sharp.cc
10
src/sharp.cc
@@ -8,17 +8,9 @@
|
|||||||
#include "resize.h"
|
#include "resize.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
using namespace v8;
|
extern "C" void init(v8::Handle<v8::Object> target) {
|
||||||
|
|
||||||
static void at_exit(void* arg) {
|
|
||||||
NanScope();
|
|
||||||
vips_shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void init(Handle<Object> target) {
|
|
||||||
NanScope();
|
NanScope();
|
||||||
vips_init("sharp");
|
vips_init("sharp");
|
||||||
node::AtExit(at_exit);
|
|
||||||
|
|
||||||
// Set libvips operation cache limits
|
// Set libvips operation cache limits
|
||||||
vips_cache_set_max_mem(100 * 1048576); // 100 MB
|
vips_cache_set_max_mem(100 * 1048576); // 100 MB
|
||||||
|
|||||||
@@ -6,8 +6,13 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
using namespace v8;
|
using v8::Local;
|
||||||
using namespace sharp;
|
using v8::Object;
|
||||||
|
using v8::Number;
|
||||||
|
using v8::String;
|
||||||
|
|
||||||
|
using sharp::counterQueue;
|
||||||
|
using sharp::counterProcess;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get and set cache memory and item limits
|
Get and set cache memory and item limits
|
||||||
@@ -70,6 +75,6 @@ NAN_METHOD(counters) {
|
|||||||
NAN_METHOD(libvipsVersion) {
|
NAN_METHOD(libvipsVersion) {
|
||||||
NanScope();
|
NanScope();
|
||||||
char version[9];
|
char version[9];
|
||||||
snprintf(version, 9, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
|
snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
|
||||||
NanReturnValue(NanNew<String>(version));
|
NanReturnValue(NanNew<String>(version));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef SHARP_UTILITIES_H
|
#ifndef SRC_UTILITIES_H_
|
||||||
#define SHARP_UTILITIES_H
|
#define SRC_UTILITIES_H_
|
||||||
|
|
||||||
#include "nan.h"
|
#include "nan.h"
|
||||||
|
|
||||||
@@ -8,4 +8,4 @@ NAN_METHOD(concurrency);
|
|||||||
NAN_METHOD(counters);
|
NAN_METHOD(counters);
|
||||||
NAN_METHOD(libvipsVersion);
|
NAN_METHOD(libvipsVersion);
|
||||||
|
|
||||||
#endif
|
#endif // SRC_UTILITIES_H_
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"imagemagick-native": "^1.6.0",
|
"imagemagick-native": "^1.6.0",
|
||||||
"gm": "^1.17.0",
|
"gm": "^1.17.0",
|
||||||
"async": "^0.9.0",
|
"async": "^0.9.0",
|
||||||
"semver": "^4.1.0",
|
"semver": "^4.2.0",
|
||||||
"benchmark": "^1.0.0"
|
"benchmark": "^1.0.0"
|
||||||
},
|
},
|
||||||
"license": "Apache 2.0",
|
"license": "Apache 2.0",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ var min = 320;
|
|||||||
var max = 960;
|
var max = 960;
|
||||||
|
|
||||||
var randomDimension = function() {
|
var randomDimension = function() {
|
||||||
return Math.random() * (max - min) + min;
|
return Math.ceil(Math.random() * (max - min) + min);
|
||||||
};
|
};
|
||||||
|
|
||||||
new Benchmark.Suite('random').add('imagemagick', {
|
new Benchmark.Suite('random').add('imagemagick', {
|
||||||
|
|||||||
BIN
test/fixtures/corrupt-header.jpg
vendored
Normal file
BIN
test/fixtures/corrupt-header.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@@ -14,6 +14,7 @@ module.exports = {
|
|||||||
inputJpgWithGammaHoliness: getPath('gamma_dalai_lama_gray.jpg'), // http://www.4p8.com/eric.brasseur/gamma.html
|
inputJpgWithGammaHoliness: getPath('gamma_dalai_lama_gray.jpg'), // http://www.4p8.com/eric.brasseur/gamma.html
|
||||||
inputJpgWithCmykProfile: getPath('Channel_digital_image_CMYK_color.jpg'), // http://en.wikipedia.org/wiki/File:Channel_digital_image_CMYK_color.jpg
|
inputJpgWithCmykProfile: getPath('Channel_digital_image_CMYK_color.jpg'), // http://en.wikipedia.org/wiki/File:Channel_digital_image_CMYK_color.jpg
|
||||||
inputJpgWithCmykNoProfile: getPath('Channel_digital_image_CMYK_color_no_profile.jpg'),
|
inputJpgWithCmykNoProfile: getPath('Channel_digital_image_CMYK_color_no_profile.jpg'),
|
||||||
|
inputJpgWithCorruptHeader: getPath('corrupt-header.jpg'),
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
46
test/unit/cpplint.js
Executable file
46
test/unit/cpplint.js
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var cpplint = require('node-cpplint/lib/');
|
||||||
|
|
||||||
|
describe('cpplint', function() {
|
||||||
|
|
||||||
|
// List C++ source files
|
||||||
|
fs.readdirSync(path.join(__dirname, '..', '..', 'src')).forEach(function (source) {
|
||||||
|
var file = path.join('src', source);
|
||||||
|
it(file, function(done) {
|
||||||
|
// Lint each source file
|
||||||
|
cpplint({
|
||||||
|
files: [file],
|
||||||
|
linelength: 140,
|
||||||
|
filters: {
|
||||||
|
legal: {
|
||||||
|
copyright: false
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
include: false,
|
||||||
|
include_order: false
|
||||||
|
},
|
||||||
|
whitespace: {
|
||||||
|
blank_line: false,
|
||||||
|
comments: false,
|
||||||
|
parens: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, function(err, report) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
var expected = {};
|
||||||
|
expected[file] = [];
|
||||||
|
assert.deepEqual(expected, report);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -92,4 +92,28 @@ describe('Partial image extraction', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Extract then rotate', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.extract(10, 10, 100, 100)
|
||||||
|
.rotate(90)
|
||||||
|
.toFile(fixtures.path('output.extract.extract-then-rotate.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Rotate then extract', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.rotate(90)
|
||||||
|
.extract(10, 10, 100, 100)
|
||||||
|
.toFile(fixtures.path('output.extract.rotate-then-extract.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
199
test/unit/io.js
199
test/unit/io.js
@@ -18,6 +18,7 @@ describe('Input/output', function() {
|
|||||||
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
|
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -35,6 +36,7 @@ describe('Input/output', function() {
|
|||||||
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
|
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -49,6 +51,7 @@ describe('Input/output', function() {
|
|||||||
var readable = fs.createReadStream(fixtures.inputJpg);
|
var readable = fs.createReadStream(fixtures.inputJpg);
|
||||||
var pipeline = sharp().resize(320, 240).toFile(fixtures.outputJpg, function(err, info) {
|
var pipeline = sharp().resize(320, 240).toFile(fixtures.outputJpg, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -63,6 +66,7 @@ describe('Input/output', function() {
|
|||||||
var pipeline = sharp().resize(320, 240).toBuffer(function(err, data, info) {
|
var pipeline = sharp().resize(320, 240).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -90,6 +94,7 @@ describe('Input/output', function() {
|
|||||||
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
|
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -137,6 +142,7 @@ describe('Input/output', function() {
|
|||||||
.toBuffer(function(err, data, info) {
|
.toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -151,6 +157,7 @@ describe('Input/output', function() {
|
|||||||
.toBuffer(function(err, data, info) {
|
.toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -221,6 +228,7 @@ describe('Input/output', function() {
|
|||||||
sharp(data).toBuffer(function(err, data, info) {
|
sharp(data).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -262,31 +270,50 @@ describe('Input/output', function() {
|
|||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
.png()
|
.png()
|
||||||
.progressive(false)
|
.progressive(false)
|
||||||
.toBuffer(function(err, nonProgressive, info) {
|
.toBuffer(function(err, nonProgressiveData, nonProgressiveInfo) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, nonProgressive.length > 0);
|
assert.strictEqual(true, nonProgressiveData.length > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual(nonProgressiveData.length, nonProgressiveInfo.size);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual('png', nonProgressiveInfo.format);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(320, nonProgressiveInfo.width);
|
||||||
sharp(nonProgressive)
|
assert.strictEqual(240, nonProgressiveInfo.height);
|
||||||
|
sharp(nonProgressiveData)
|
||||||
.progressive()
|
.progressive()
|
||||||
.toBuffer(function(err, progressive, info) {
|
.toBuffer(function(err, progressiveData, progressiveInfo) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, progressive.length > 0);
|
assert.strictEqual(true, progressiveData.length > 0);
|
||||||
assert.strictEqual(true, progressive.length > nonProgressive.length);
|
assert.strictEqual(progressiveData.length, progressiveInfo.size);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual(true, progressiveData.length > nonProgressiveData.length);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual('png', progressiveInfo.format);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(320, progressiveInfo.width);
|
||||||
|
assert.strictEqual(240, progressiveInfo.height);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('File input with corrupt header fails gracefully', function(done) {
|
||||||
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
|
.toBuffer(function(err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Buffer input with corrupt header fails gracefully', function(done) {
|
||||||
|
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
|
||||||
|
.toBuffer(function(err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Output filename without extension uses input format', function() {
|
describe('Output filename without extension uses input format', function() {
|
||||||
|
|
||||||
it('JPEG', function(done) {
|
it('JPEG', function(done) {
|
||||||
sharp(fixtures.inputJpg).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
sharp(fixtures.inputJpg).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
@@ -298,6 +325,7 @@ describe('Input/output', function() {
|
|||||||
it('PNG', function(done) {
|
it('PNG', function(done) {
|
||||||
sharp(fixtures.inputPng).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
sharp(fixtures.inputPng).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
@@ -309,6 +337,7 @@ describe('Input/output', function() {
|
|||||||
it('Transparent PNG', function(done) {
|
it('Transparent PNG', function(done) {
|
||||||
sharp(fixtures.inputPngWithTransparency).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
sharp(fixtures.inputPngWithTransparency).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
@@ -319,6 +348,7 @@ describe('Input/output', function() {
|
|||||||
it('WebP', function(done) {
|
it('WebP', function(done) {
|
||||||
sharp(fixtures.inputWebP).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
sharp(fixtures.inputWebP).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('webp', info.format);
|
assert.strictEqual('webp', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
@@ -330,6 +360,7 @@ describe('Input/output', function() {
|
|||||||
it('TIFF', function(done) {
|
it('TIFF', function(done) {
|
||||||
sharp(fixtures.inputTiff).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
sharp(fixtures.inputTiff).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('tiff', info.format);
|
assert.strictEqual('tiff', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
@@ -369,29 +400,31 @@ describe('Input/output', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (semver.gte(sharp.libvipsVersion(), '7.41.0')) {
|
if (semver.gte(sharp.libvipsVersion(), '7.42.0')) {
|
||||||
it('withoutAdaptiveFiltering generates smaller file [libvips ' + sharp.libvipsVersion() + '>=7.41.0]', function(done) {
|
it('withoutAdaptiveFiltering generates smaller file [libvips ' + sharp.libvipsVersion() + '>=7.42.0]', function(done) {
|
||||||
// First generate with adaptive filtering
|
// First generate with adaptive filtering
|
||||||
sharp(fixtures.inputPng)
|
sharp(fixtures.inputPng)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
.withoutAdaptiveFiltering(false)
|
.withoutAdaptiveFiltering(false)
|
||||||
.toBuffer(function(err, dataAdaptive, info) {
|
.toBuffer(function(err, adaptiveData, adaptiveInfo) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, dataAdaptive.length > 0);
|
assert.strictEqual(true, adaptiveData.length > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual(adaptiveData.length, adaptiveInfo.size);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual('png', adaptiveInfo.format);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(320, adaptiveInfo.width);
|
||||||
|
assert.strictEqual(240, adaptiveInfo.height);
|
||||||
// Then generate without
|
// Then generate without
|
||||||
sharp(fixtures.inputPng)
|
sharp(fixtures.inputPng)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
.withoutAdaptiveFiltering()
|
.withoutAdaptiveFiltering()
|
||||||
.toBuffer(function(err, dataWithoutAdaptive, info) {
|
.toBuffer(function(err, withoutAdaptiveData, withoutAdaptiveInfo) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, dataWithoutAdaptive.length > 0);
|
assert.strictEqual(true, withoutAdaptiveData.length > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual(withoutAdaptiveData.length, withoutAdaptiveInfo.size);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual('png', withoutAdaptiveInfo.format);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(320, withoutAdaptiveInfo.width);
|
||||||
assert.strictEqual(true, dataWithoutAdaptive.length < dataAdaptive.length);
|
assert.strictEqual(240, withoutAdaptiveInfo.height);
|
||||||
|
assert.strictEqual(true, withoutAdaptiveData.length < adaptiveData.length);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -406,6 +439,7 @@ describe('Input/output', function() {
|
|||||||
.png()
|
.png()
|
||||||
.toFile(fixtures.path('output.svg.png'), function(err, info) {
|
.toFile(fixtures.path('output.svg.png'), function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(100, info.width);
|
assert.strictEqual(100, info.width);
|
||||||
assert.strictEqual(100, info.height);
|
assert.strictEqual(100, info.height);
|
||||||
@@ -419,6 +453,7 @@ describe('Input/output', function() {
|
|||||||
.png()
|
.png()
|
||||||
.toFile(fixtures.path('output.psd.png'), function(err, info) {
|
.toFile(fixtures.path('output.psd.png'), function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -435,6 +470,7 @@ describe('Input/output', function() {
|
|||||||
.toBuffer(function(err, data, info) {
|
.toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
@@ -443,4 +479,117 @@ describe('Input/output', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (semver.gte(sharp.libvipsVersion(), '7.42.0')) {
|
||||||
|
describe('Ouput raw, uncompressed image data [libvips ' + sharp.libvipsVersion() + '>=7.42.0]', function() {
|
||||||
|
it('1 channel greyscale image', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.greyscale()
|
||||||
|
.resize(32, 24)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
assert.strictEqual(32 * 24 * 1, info.size);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('raw', info.format);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(24, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('3 channel colour image without transparency', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(32, 24)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
assert.strictEqual(32 * 24 * 3, info.size);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('raw', info.format);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(24, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('4 channel colour image with transparency', function(done) {
|
||||||
|
sharp(fixtures.inputPngWithTransparency)
|
||||||
|
.resize(32, 24)
|
||||||
|
.raw()
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
assert.strictEqual(32 * 24 * 4, info.size);
|
||||||
|
assert.strictEqual(data.length, info.size);
|
||||||
|
assert.strictEqual('raw', info.format);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(24, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Limit pixel count of input image', function() {
|
||||||
|
|
||||||
|
it('Invalid fails - negative', function(done) {
|
||||||
|
var isValid = false;
|
||||||
|
try {
|
||||||
|
sharp().limitInputPixels(-1);
|
||||||
|
isValid = true;
|
||||||
|
} catch (e) {}
|
||||||
|
assert(!isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid fails - float', function(done) {
|
||||||
|
var isValid = false;
|
||||||
|
try {
|
||||||
|
sharp().limitInputPixels(12.3);
|
||||||
|
isValid = true;
|
||||||
|
} catch (e) {}
|
||||||
|
assert(!isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid fails - huge', function(done) {
|
||||||
|
var isValid = false;
|
||||||
|
try {
|
||||||
|
sharp().limitInputPixels(Math.pow(0x3FFF, 2) + 1);
|
||||||
|
isValid = true;
|
||||||
|
} catch (e) {}
|
||||||
|
assert(!isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid fails - string', function(done) {
|
||||||
|
var isValid = false;
|
||||||
|
try {
|
||||||
|
sharp().limitInputPixels('fail');
|
||||||
|
isValid = true;
|
||||||
|
} catch (e) {}
|
||||||
|
assert(!isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Same size as input works', function(done) {
|
||||||
|
sharp(fixtures.inputJpg).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.limitInputPixels(metadata.width * metadata.height)
|
||||||
|
.toBuffer(function(err) {
|
||||||
|
assert.strictEqual(true, !err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Smaller than input fails', function(done) {
|
||||||
|
sharp(fixtures.inputJpg).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.limitInputPixels(metadata.width * metadata.height - 1)
|
||||||
|
.toBuffer(function(err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ describe('Image metadata', function() {
|
|||||||
assert.strictEqual(3, metadata.channels);
|
assert.strictEqual(3, metadata.channels);
|
||||||
assert.strictEqual(false, metadata.hasProfile);
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
image.resize(metadata.width / 2).toBuffer(function(err, data, info) {
|
image.resize(Math.floor(metadata.width / 2)).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(true, data.length > 0);
|
assert.strictEqual(true, data.length > 0);
|
||||||
assert.strictEqual(1362, info.width);
|
assert.strictEqual(1362, info.width);
|
||||||
@@ -216,4 +216,20 @@ describe('Image metadata', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('File input with corrupt header fails gracefully', function(done) {
|
||||||
|
sharp(fixtures.inputJpgWithCorruptHeader)
|
||||||
|
.metadata(function(err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Buffer input with corrupt header fails gracefully', function(done) {
|
||||||
|
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
|
||||||
|
.metadata(function(err) {
|
||||||
|
assert.strictEqual(true, !!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ describe('Resize dimensions', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Invalid width', function(done) {
|
it('Invalid width - NaN', function(done) {
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
try {
|
try {
|
||||||
sharp(fixtures.inputJpg).resize('spoons', 240);
|
sharp(fixtures.inputJpg).resize('spoons', 240);
|
||||||
@@ -75,7 +75,7 @@ describe('Resize dimensions', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Invalid height', function(done) {
|
it('Invalid height - NaN', function(done) {
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
try {
|
try {
|
||||||
sharp(fixtures.inputJpg).resize(320, 'spoons');
|
sharp(fixtures.inputJpg).resize(320, 'spoons');
|
||||||
@@ -86,6 +86,50 @@ describe('Resize dimensions', function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid width - float', function(done) {
|
||||||
|
var isValid = true;
|
||||||
|
try {
|
||||||
|
sharp(fixtures.inputJpg).resize(1.5, 240);
|
||||||
|
} catch (err) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
assert.strictEqual(false, isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid height - float', function(done) {
|
||||||
|
var isValid = true;
|
||||||
|
try {
|
||||||
|
sharp(fixtures.inputJpg).resize(320, 1.5);
|
||||||
|
} catch (err) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
assert.strictEqual(false, isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid width - too large', function(done) {
|
||||||
|
var isValid = true;
|
||||||
|
try {
|
||||||
|
sharp(fixtures.inputJpg).resize(0x4000, 240);
|
||||||
|
} catch (err) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
assert.strictEqual(false, isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid height - too large', function(done) {
|
||||||
|
var isValid = true;
|
||||||
|
try {
|
||||||
|
sharp(fixtures.inputJpg).resize(320, 0x4000);
|
||||||
|
} catch (err) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
assert.strictEqual(false, isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
it('TIFF embed known to cause rounding errors', function(done) {
|
it('TIFF embed known to cause rounding errors', function(done) {
|
||||||
sharp(fixtures.inputTiff).resize(240, 320).embed().jpeg().toBuffer(function(err, data, info) {
|
sharp(fixtures.inputTiff).resize(240, 320).embed().jpeg().toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
|||||||
Reference in New Issue
Block a user