mirror of
https://github.com/lovell/sharp.git
synced 2026-02-05 14:16:17 +01:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
850c2ecdd6 | ||
|
|
926c5603aa | ||
|
|
d3225fa193 | ||
|
|
f026a835fd | ||
|
|
47241db789 | ||
|
|
34a9970bd9 | ||
|
|
57203f841a | ||
|
|
bd20bd1881 | ||
|
|
60f1fda7ee | ||
|
|
ea1013f6ec | ||
|
|
247b607afd | ||
|
|
a56102a209 | ||
|
|
940b6f505f | ||
|
|
e1b5574c4a | ||
|
|
f4cc6a2db4 | ||
|
|
0acf865654 | ||
|
|
8460e50ee0 | ||
|
|
f57a0e3b00 | ||
|
|
02b6016390 | ||
|
|
4e01d63195 | ||
|
|
94b47508c0 | ||
|
|
328cda82c5 | ||
|
|
118b17aa2f | ||
|
|
b7c7fc22f3 |
46
README.md
46
README.md
@@ -10,13 +10,15 @@
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
The performance of JPEG resizing is typically 8x faster than ImageMagick and GraphicsMagick, based mainly on the number of CPU cores available.
|
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.
|
||||||
|
Colour spaces, embedded ICC profiles and alpha transparency channels are all handled correctly.
|
||||||
|
|
||||||
Memory usage is kept to a minimum, no child processes are spawned, everything remains non-blocking thanks to _libuv_ and Promises/A+ are supported.
|
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.
|
||||||
|
|
||||||
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.
|
Huffman tables are optimised when generating JPEG output images without having to use separate command line tools like [jpegoptim](https://github.com/tjko/jpegoptim) and [jpegtran](http://jpegclub.org/jpegtran/). PNG filtering can be disabled, which for diagrams and line art often produces the same result as [pngcrush](http://pmt.sourceforge.net/pngcrush/).
|
||||||
|
|
||||||
When generating JPEG output all metadata is removed and Huffman tables optimised without having to use separate command line tools like [jpegoptim](https://github.com/tjko/jpegoptim) and [jpegtran](http://jpegclub.org/jpegtran/).
|
Everything remains non-blocking thanks to _libuv_, no child processes are spawned and Promises/A+ are supported.
|
||||||
|
|
||||||
Anyone who has used the Node.js bindings for [GraphicsMagick](https://github.com/aheckmann/gm) will find the API similarly fluent.
|
Anyone who has used the Node.js bindings for [GraphicsMagick](https://github.com/aheckmann/gm) will find the API similarly fluent.
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ To install the most suitable version of libvips on the following Operating Syste
|
|||||||
* Red Hat Linux
|
* Red Hat Linux
|
||||||
* RHEL/Centos/Scientific 6, 7
|
* RHEL/Centos/Scientific 6, 7
|
||||||
* Fedora 21, 22
|
* Fedora 21, 22
|
||||||
|
* Amazon Linux 2014.09
|
||||||
|
|
||||||
run the following as a user with `sudo` access:
|
run the following as a user with `sudo` access:
|
||||||
|
|
||||||
@@ -68,11 +71,17 @@ The _gettext_ dependency of _libvips_ [can lead](https://github.com/lovell/sharp
|
|||||||
|
|
||||||
brew link gettext --force
|
brew link gettext --force
|
||||||
|
|
||||||
### Install libvips on Heroku
|
### Heroku
|
||||||
|
|
||||||
[Alessandro Tagliapietra](https://github.com/alex88) maintains an [Heroku buildpack for libvips](https://github.com/alex88/heroku-buildpack-vips) and its dependencies.
|
[Alessandro Tagliapietra](https://github.com/alex88) maintains an [Heroku buildpack for libvips](https://github.com/alex88/heroku-buildpack-vips) and its dependencies.
|
||||||
|
|
||||||
### Using with gulp.js
|
### Docker
|
||||||
|
|
||||||
|
[Marc Bachmann](https://github.com/marcbachmann) maintains a [Dockerfile for libvips](https://github.com/marcbachmann/dockerfile-libvips).
|
||||||
|
|
||||||
|
docker pull marcbachmann/libvips
|
||||||
|
|
||||||
|
### gulp.js
|
||||||
|
|
||||||
[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).
|
[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).
|
||||||
|
|
||||||
@@ -234,6 +243,7 @@ Fast access to image metadata without decoding any compressed image data.
|
|||||||
* `height`: Number of pixels high
|
* `height`: Number of pixels high
|
||||||
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522)
|
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L522)
|
||||||
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||||
|
* `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
|
||||||
* `orientation`: Number value of the EXIF Orientation header, if present
|
* `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
|
||||||
@@ -319,13 +329,13 @@ Do not enlarge the output image if the input image width *or* height are already
|
|||||||
|
|
||||||
This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensions of the image only if its width or height exceeds the geometry specification".
|
This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensions of the image only if its width or height exceeds the geometry specification".
|
||||||
|
|
||||||
#### blur([radius])
|
#### blur([sigma])
|
||||||
|
|
||||||
When used without parameters, performs a fast, mild blur of the output image. This typically reduces performance by 10%.
|
When used without parameters, performs a fast, mild blur of the output image. This typically reduces performance by 10%.
|
||||||
|
|
||||||
When a `radius` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 30%.
|
When a `sigma` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 25%.
|
||||||
|
|
||||||
* `radius`, if present, is an integral Number representing the approximate blur mask radius in pixels.
|
* `sigma`, if present, is a Number between 0.3 and 1000 representing the approximate blur radius in pixels.
|
||||||
|
|
||||||
#### sharpen([radius], [flat], [jagged])
|
#### sharpen([radius], [flat], [jagged])
|
||||||
|
|
||||||
@@ -394,7 +404,9 @@ Use progressive (interlace) scan for JPEG and PNG output. This typically reduces
|
|||||||
|
|
||||||
#### withMetadata()
|
#### withMetadata()
|
||||||
|
|
||||||
Include all metadata (ICC, EXIF, XMP) from the input image in the output image. The default behaviour is to strip all metadata.
|
Include all metadata (EXIF, XMP, IPTC) from the input image in the output image. This will also convert to and add the latest web-friendly v2 sRGB ICC profile.
|
||||||
|
|
||||||
|
The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space.
|
||||||
|
|
||||||
#### compressionLevel(compressionLevel)
|
#### compressionLevel(compressionLevel)
|
||||||
|
|
||||||
@@ -404,9 +416,9 @@ An advanced setting for the _zlib_ compression level of the lossless PNG output
|
|||||||
|
|
||||||
#### withoutAdaptiveFiltering()
|
#### withoutAdaptiveFiltering()
|
||||||
|
|
||||||
_Requires libvips 7.41.0+_
|
_Requires libvips 7.42.0+_
|
||||||
|
|
||||||
An advanced and experimental PNG output setting to disable adaptive row filtering.
|
An advanced setting to disable adaptive row filtering for the lossless PNG output format.
|
||||||
|
|
||||||
### Output methods
|
### Output methods
|
||||||
|
|
||||||
@@ -417,7 +429,7 @@ An advanced and experimental PNG output setting to disable adaptive row filterin
|
|||||||
`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.
|
||||||
|
|
||||||
@@ -429,7 +441,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.
|
||||||
|
|
||||||
@@ -452,7 +464,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.
|
||||||
|
|
||||||
@@ -462,6 +474,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.
|
||||||
@@ -548,7 +562,7 @@ sudo yum install -y --enablerepo=epel GraphicsMagick
|
|||||||
|
|
||||||
### The contenders
|
### The contenders
|
||||||
|
|
||||||
* [imagemagick-native](https://github.com/mash/node-imagemagick-native) v1.2.2 - Supports Buffers only and blocks main V8 thread whilst processing.
|
* [imagemagick-native](https://github.com/mash/node-imagemagick-native) v1.2.2 - Supports Buffers only
|
||||||
* [imagemagick](https://github.com/yourdeveloper/node-imagemagick) v0.1.3 - Supports filesystem only and "has been unmaintained for a long time".
|
* [imagemagick](https://github.com/yourdeveloper/node-imagemagick) v0.1.3 - Supports filesystem only and "has been unmaintained for a long time".
|
||||||
* [gm](https://github.com/aheckmann/gm) v1.16.0 - Fully featured wrapper around GraphicsMagick.
|
* [gm](https://github.com/aheckmann/gm) v1.16.0 - Fully featured wrapper around GraphicsMagick.
|
||||||
* sharp v0.6.2 - Caching within libvips disabled to ensure a fair comparison.
|
* sharp v0.6.2 - Caching within libvips disabled to ensure a fair comparison.
|
||||||
|
|||||||
BIN
icc/sRGB_IEC61966-2-1_black_scaled.icc
Normal file
BIN
icc/sRGB_IEC61966-2-1_black_scaled.icc
Normal file
Binary file not shown.
39
index.js
39
index.js
@@ -18,10 +18,11 @@ 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,
|
||||||
// ICC profile to use when input CMYK image has no embedded profile
|
// ICC profiles
|
||||||
iccProfileCmyk: path.join(__dirname, 'icc', 'USWebCoatedSWOP.icc'),
|
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
|
||||||
// resize options
|
// resize options
|
||||||
topOffsetPre: -1,
|
topOffsetPre: -1,
|
||||||
leftOffsetPre: -1,
|
leftOffsetPre: -1,
|
||||||
@@ -43,7 +44,7 @@ var Sharp = function(input) {
|
|||||||
// operations
|
// operations
|
||||||
background: [0, 0, 0, 255],
|
background: [0, 0, 0, 255],
|
||||||
flatten: false,
|
flatten: false,
|
||||||
blurRadius: 0,
|
blurSigma: 0,
|
||||||
sharpenRadius: 0,
|
sharpenRadius: 0,
|
||||||
sharpenFlat: 1,
|
sharpenFlat: 1,
|
||||||
sharpenJagged: 2,
|
sharpenJagged: 2,
|
||||||
@@ -95,16 +96,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 {
|
||||||
@@ -208,21 +209,21 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Blur the output image.
|
Blur the output image.
|
||||||
Call without a radius to use a fast, mild blur.
|
Call without a sigma to use a fast, mild blur.
|
||||||
Call with a radius to use a slower, more accurate Gaussian blur.
|
Call with a sigma to use a slower, more accurate Gaussian blur.
|
||||||
*/
|
*/
|
||||||
Sharp.prototype.blur = function(radius) {
|
Sharp.prototype.blur = function(sigma) {
|
||||||
if (typeof radius === 'undefined') {
|
if (typeof sigma === 'undefined') {
|
||||||
// No arguments: default to mild blur
|
// No arguments: default to mild blur
|
||||||
this.options.blurRadius = -1;
|
this.options.blurSigma = -1;
|
||||||
} else if (typeof radius === 'boolean') {
|
} else if (typeof sigma === 'boolean') {
|
||||||
// Boolean argument: apply mild blur?
|
// Boolean argument: apply mild blur?
|
||||||
this.options.blurRadius = radius ? -1 : 0;
|
this.options.blurSigma = sigma ? -1 : 0;
|
||||||
} else if (typeof radius === 'number' && !Number.isNaN(radius) && (radius % 1 === 0) && radius >= 1) {
|
} else if (typeof sigma === 'number' && !Number.isNaN(sigma) && sigma >= 0.3 && sigma <= 1000) {
|
||||||
// Numeric argument: specific radius
|
// Numeric argument: specific sigma
|
||||||
this.options.blurRadius = radius;
|
this.options.blurSigma = sigma;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid blur radius ' + radius + ' (expected integer >= 1)');
|
throw new Error('Invalid blur sigma (0.3 to 1000.0) ' + sigma);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"version": "0.8.0",
|
"version": "0.8.3",
|
||||||
"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>",
|
||||||
@@ -27,27 +27,22 @@
|
|||||||
"png",
|
"png",
|
||||||
"webp",
|
"webp",
|
||||||
"tiff",
|
"tiff",
|
||||||
"gif",
|
|
||||||
"resize",
|
"resize",
|
||||||
"thumbnail",
|
"thumbnail",
|
||||||
"sharpen",
|
|
||||||
"crop",
|
"crop",
|
||||||
"extract",
|
|
||||||
"embed",
|
|
||||||
"libvips",
|
"libvips",
|
||||||
"vips",
|
"vips"
|
||||||
"stream"
|
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bluebird": "^2.3.11",
|
"bluebird": "^2.3.11",
|
||||||
"color": "^0.7.1",
|
"color": "^0.7.3",
|
||||||
"nan": "^1.4.1",
|
"nan": "^1.4.1",
|
||||||
"semver": "^4.1.0"
|
"semver": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^2.0.1",
|
"mocha": "^2.0.1",
|
||||||
"mocha-jshint": "^0.0.9",
|
"mocha-jshint": "^0.0.9",
|
||||||
"istanbul": "^0.3.2",
|
"istanbul": "^0.3.5",
|
||||||
"coveralls": "^2.11.2"
|
"coveralls": "^2.11.2"
|
||||||
},
|
},
|
||||||
"license": "Apache 2.0",
|
"license": "Apache 2.0",
|
||||||
|
|||||||
@@ -10,17 +10,18 @@
|
|||||||
# * Red Hat Linux
|
# * Red Hat Linux
|
||||||
# * RHEL/Centos/Scientific 6, 7
|
# * RHEL/Centos/Scientific 6, 7
|
||||||
# * Fedora 21, 22
|
# * Fedora 21, 22
|
||||||
|
# * Amazon Linux 2014.09
|
||||||
|
|
||||||
vips_version_minimum=7.40.0
|
vips_version_minimum=7.40.0
|
||||||
vips_version_latest_major=7.40
|
vips_version_latest_major=7.42
|
||||||
vips_version_latest_minor=11
|
vips_version_latest_minor=0
|
||||||
|
|
||||||
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"
|
||||||
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major/vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
|
curl -O http://www.vips.ecs.soton.ac.uk/supported/$vips_version_latest_major/vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
|
||||||
tar zvxf vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
|
tar zvxf vips-$vips_version_latest_major.$vips_version_latest_minor.tar.gz
|
||||||
cd vips-$vips_version_latest_major.$vips_version_latest_minor
|
cd vips-$vips_version_latest_major.$vips_version_latest_minor
|
||||||
./configure --enable-debug=no --enable-docs=no --enable-cxx=yes --without-python --without-orc --without-fftw $1
|
./configure --enable-debug=no --enable-docs=no --enable-cxx=yes --without-python --without-orc --without-fftw --without-gsf $1
|
||||||
make
|
make
|
||||||
make install
|
make install
|
||||||
cd ..
|
cd ..
|
||||||
@@ -94,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 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 libmagickwand-dev curl
|
||||||
install_libvips_from_source
|
install_libvips_from_source
|
||||||
;;
|
;;
|
||||||
precise|wheezy|maya)
|
precise|wheezy|maya)
|
||||||
@@ -102,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 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 libmagickwand-dev curl
|
||||||
install_libvips_from_source
|
install_libvips_from_source
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -119,14 +120,14 @@ case $(uname -s) in
|
|||||||
# RHEL/CentOS 7
|
# RHEL/CentOS 7
|
||||||
echo "Installing libvips dependencies via yum"
|
echo "Installing libvips dependencies via yum"
|
||||||
yum groupinstall -y "Development Tools"
|
yum groupinstall -y "Development Tools"
|
||||||
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
|
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
|
||||||
install_libvips_from_source "--prefix=/usr"
|
install_libvips_from_source "--prefix=/usr"
|
||||||
;;
|
;;
|
||||||
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
|
"Red Hat Enterprise Linux release 6."*|"CentOS release 6."*|"Scientific Linux release 6."*)
|
||||||
# RHEL/CentOS 6
|
# RHEL/CentOS 6
|
||||||
echo "Installing libvips dependencies via yum"
|
echo "Installing libvips dependencies via yum"
|
||||||
yum groupinstall -y "Development Tools"
|
yum groupinstall -y "Development Tools"
|
||||||
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel ImageMagick-devel curl
|
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel curl
|
||||||
yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
|
yum install -y http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-2.el6.nux.noarch.rpm
|
||||||
yum install -y --enablerepo=nux-dextop gobject-introspection-devel
|
yum install -y --enablerepo=nux-dextop gobject-introspection-devel
|
||||||
yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
|
yum install -y http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
|
||||||
@@ -143,6 +144,19 @@ case $(uname -s) in
|
|||||||
sorry "$RELEASE"
|
sorry "$RELEASE"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
elif [ -f /etc/system-release ]; then
|
||||||
|
# Probably Amazon Linux
|
||||||
|
RELEASE=$(cat /etc/system-release)
|
||||||
|
case $RELEASE in
|
||||||
|
"Amazon Linux AMI release 2014.09")
|
||||||
|
# Amazon Linux
|
||||||
|
echo "Detected '$RELEASE'"
|
||||||
|
echo "Installing libvips dependencies via yum"
|
||||||
|
yum groupinstall -y "Development Tools"
|
||||||
|
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
|
||||||
|
install_libvips_from_source "--prefix=/usr"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
else
|
else
|
||||||
# Unsupported OS
|
# Unsupported OS
|
||||||
sorry "$(uname -a)"
|
sorry "$(uname -a)"
|
||||||
|
|||||||
@@ -117,6 +117,13 @@ namespace sharp {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Does this image have an embedded profile?
|
||||||
|
*/
|
||||||
|
bool HasProfile(VipsImage *image) {
|
||||||
|
return (vips_image_get_typeof(image, VIPS_META_ICC_NAME) > 0) ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have an alpha channel?
|
Does this image have an alpha channel?
|
||||||
Uses colour space interpretation with number of channels to guess this.
|
Uses colour space interpretation with number of channels to guess this.
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ namespace sharp {
|
|||||||
*/
|
*/
|
||||||
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access);
|
VipsImage* InitImage(ImageType imageType, char const *file, VipsAccess const access);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Does this image have an embedded profile?
|
||||||
|
*/
|
||||||
|
bool HasProfile(VipsImage *image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Does this image have an alpha channel?
|
Does this image have an alpha channel?
|
||||||
Uses colour space interpretation with number of channels to guess this.
|
Uses colour space interpretation with number of channels to guess this.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ struct MetadataBaton {
|
|||||||
int height;
|
int height;
|
||||||
std::string space;
|
std::string space;
|
||||||
int channels;
|
int channels;
|
||||||
|
bool hasProfile;
|
||||||
bool hasAlpha;
|
bool hasAlpha;
|
||||||
int orientation;
|
int orientation;
|
||||||
std::string err;
|
std::string err;
|
||||||
@@ -73,6 +74,7 @@ class MetadataWorker : public NanAsyncWorker {
|
|||||||
baton->height = image->Ysize;
|
baton->height = image->Ysize;
|
||||||
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
|
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
|
||||||
baton->channels = image->Bands;
|
baton->channels = image->Bands;
|
||||||
|
baton->hasProfile = HasProfile(image);
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = HasAlpha(image);
|
baton->hasAlpha = HasAlpha(image);
|
||||||
baton->orientation = ExifOrientation(image);
|
baton->orientation = ExifOrientation(image);
|
||||||
@@ -99,6 +101,7 @@ class MetadataWorker : public NanAsyncWorker {
|
|||||||
info->Set(NanNew<String>("height"), NanNew<Number>(baton->height));
|
info->Set(NanNew<String>("height"), NanNew<Number>(baton->height));
|
||||||
info->Set(NanNew<String>("space"), NanNew<String>(baton->space));
|
info->Set(NanNew<String>("space"), NanNew<String>(baton->space));
|
||||||
info->Set(NanNew<String>("channels"), NanNew<Number>(baton->channels));
|
info->Set(NanNew<String>("channels"), NanNew<Number>(baton->channels));
|
||||||
|
info->Set(NanNew<String>("hasProfile"), NanNew<Boolean>(baton->hasProfile));
|
||||||
info->Set(NanNew<String>("hasAlpha"), NanNew<Boolean>(baton->hasAlpha));
|
info->Set(NanNew<String>("hasAlpha"), NanNew<Boolean>(baton->hasAlpha));
|
||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
info->Set(NanNew<String>("orientation"), NanNew<Number>(baton->orientation));
|
info->Set(NanNew<String>("orientation"), NanNew<Number>(baton->orientation));
|
||||||
|
|||||||
141
src/resize.cc
141
src/resize.cc
@@ -31,7 +31,7 @@ struct ResizeBaton {
|
|||||||
std::string fileIn;
|
std::string fileIn;
|
||||||
void* bufferIn;
|
void* bufferIn;
|
||||||
size_t bufferInLength;
|
size_t bufferInLength;
|
||||||
std::string iccProfileCmyk;
|
std::string iccProfilePath;
|
||||||
std::string output;
|
std::string output;
|
||||||
std::string outputFormat;
|
std::string outputFormat;
|
||||||
void* bufferOut;
|
void* bufferOut;
|
||||||
@@ -51,7 +51,7 @@ struct ResizeBaton {
|
|||||||
std::string interpolator;
|
std::string interpolator;
|
||||||
double background[4];
|
double background[4];
|
||||||
bool flatten;
|
bool flatten;
|
||||||
int blurRadius;
|
double blurSigma;
|
||||||
int sharpenRadius;
|
int sharpenRadius;
|
||||||
double sharpenFlat;
|
double sharpenFlat;
|
||||||
double sharpenJagged;
|
double sharpenJagged;
|
||||||
@@ -78,7 +78,7 @@ struct ResizeBaton {
|
|||||||
canvas(Canvas::CROP),
|
canvas(Canvas::CROP),
|
||||||
gravity(0),
|
gravity(0),
|
||||||
flatten(false),
|
flatten(false),
|
||||||
blurRadius(0),
|
blurSigma(0.0),
|
||||||
sharpenRadius(0),
|
sharpenRadius(0),
|
||||||
sharpenFlat(1.0),
|
sharpenFlat(1.0),
|
||||||
sharpenJagged(2.0),
|
sharpenJagged(2.0),
|
||||||
@@ -115,6 +115,9 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
// Increment processing task counter
|
// Increment processing task counter
|
||||||
g_atomic_int_inc(&counterProcess);
|
g_atomic_int_inc(&counterProcess);
|
||||||
|
|
||||||
|
// Latest v2 sRGB ICC profile
|
||||||
|
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
|
||||||
|
|
||||||
// Hang image references from this hook object
|
// Hang image references from this hook object
|
||||||
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
|
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
|
||||||
|
|
||||||
@@ -269,33 +272,24 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
image = shrunkOnLoad;
|
image = shrunkOnLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle colour profile, if any, for non sRGB images
|
// Ensure we're using a device-independent colour space
|
||||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
if (HasProfile(image)) {
|
||||||
// Get the input colour profile
|
// Convert to sRGB using embedded profile
|
||||||
if (vips_image_get_typeof(image, VIPS_META_ICC_NAME)) {
|
VipsImage *transformed;
|
||||||
VipsImage *profile;
|
if (!vips_icc_transform(image, &transformed, srgbProfile.c_str(), "embedded", TRUE, NULL)) {
|
||||||
// Use embedded profile
|
// Embedded profile can fail, so only update references on success
|
||||||
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) {
|
vips_object_local(hook, transformed);
|
||||||
return Error(baton, hook);
|
image = transformed;
|
||||||
}
|
|
||||||
vips_object_local(hook, profile);
|
|
||||||
image = profile;
|
|
||||||
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
|
|
||||||
VipsImage *profile;
|
|
||||||
// CMYK with no embedded profile
|
|
||||||
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), NULL)) {
|
|
||||||
return Error(baton, hook);
|
|
||||||
}
|
|
||||||
vips_object_local(hook, profile);
|
|
||||||
image = profile;
|
|
||||||
}
|
}
|
||||||
// Attempt to convert to sRGB colour space
|
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
|
||||||
VipsImage *colourspaced;
|
// Convert to sRGB using default "USWebCoatedSWOP" CMYK profile
|
||||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc";
|
||||||
|
VipsImage *transformed;
|
||||||
|
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "input_profile", cmykProfile.c_str(), NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, colourspaced);
|
vips_object_local(hook, transformed);
|
||||||
image = colourspaced;
|
image = transformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten image to remove alpha channel
|
// Flatten image to remove alpha channel
|
||||||
@@ -364,22 +358,33 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use vips_affine with the remaining float part
|
// Use vips_affine with the remaining float part
|
||||||
if (residual != 0) {
|
if (residual != 0.0) {
|
||||||
// Apply variable blur radius of floor(residual) before large affine reductions
|
// Apply Gaussian blur before large affine reductions
|
||||||
if (residual >= 1) {
|
if (residual < 1.0) {
|
||||||
VipsImage *blurred;
|
// Calculate standard deviation
|
||||||
if (vips_gaussblur(image, &blurred, floor(residual), NULL)) {
|
double sigma = ((1.0 / residual) - 0.4) / 3.0;
|
||||||
return Error(baton, hook);
|
if (sigma >= 0.3) {
|
||||||
|
// Create Gaussian function for standard deviation
|
||||||
|
VipsImage *gaussian;
|
||||||
|
if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, gaussian);
|
||||||
|
// Apply Gaussian function
|
||||||
|
VipsImage *blurred;
|
||||||
|
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, blurred);
|
||||||
|
image = blurred;
|
||||||
}
|
}
|
||||||
vips_object_local(hook, blurred);
|
|
||||||
image = blurred;
|
|
||||||
}
|
}
|
||||||
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
|
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
|
||||||
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
|
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
|
||||||
vips_object_local(hook, interpolator);
|
vips_object_local(hook, interpolator);
|
||||||
// Perform affine transformation
|
// Perform affine transformation
|
||||||
VipsImage *affined;
|
VipsImage *affined;
|
||||||
if (vips_affine(image, &affined, residual, 0, 0, residual, "interpolate", interpolator, NULL)) {
|
if (vips_affine(image, &affined, residual, 0.0, 0.0, residual, "interpolate", interpolator, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, affined);
|
vips_object_local(hook, affined);
|
||||||
@@ -502,10 +507,10 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Blur
|
// Blur
|
||||||
if (baton->blurRadius != 0) {
|
if (baton->blurSigma != 0.0) {
|
||||||
VipsImage *blurred;
|
VipsImage *blurred;
|
||||||
if (baton->blurRadius == -1) {
|
if (baton->blurSigma < 0.0) {
|
||||||
// Fast, mild blur
|
// Fast, mild blur - averages neighbouring pixels
|
||||||
VipsImage *blur = vips_image_new_matrixv(3, 3,
|
VipsImage *blur = vips_image_new_matrixv(3, 3,
|
||||||
1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0,
|
||||||
@@ -517,7 +522,14 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Slower, accurate Gaussian blur
|
// Slower, accurate Gaussian blur
|
||||||
if (vips_gaussblur(image, &blurred, baton->blurRadius, NULL)) {
|
// Create Gaussian function for standard deviation
|
||||||
|
VipsImage *gaussian;
|
||||||
|
if (vips_gaussmat(&gaussian, baton->blurSigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, gaussian);
|
||||||
|
// Apply Gaussian function
|
||||||
|
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -559,14 +571,24 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
image = gammaDecoded;
|
image = gammaDecoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to sRGB colour space, if not already
|
// Convert image to sRGB, if not already
|
||||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
||||||
VipsImage *colourspaced;
|
// Switch intrepretation to sRGB
|
||||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
VipsImage *rgb;
|
||||||
|
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, colourspaced);
|
vips_object_local(hook, rgb);
|
||||||
image = colourspaced;
|
image = rgb;
|
||||||
|
// Tranform colours from embedded profile to sRGB profile
|
||||||
|
if (baton->withMetadata && HasProfile(image)) {
|
||||||
|
VipsImage *profiled;
|
||||||
|
if (vips_icc_transform(image, &profiled, srgbProfile.c_str(), "embedded", TRUE, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
vips_object_local(hook, profiled);
|
||||||
|
image = profiled;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
|
#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
|
||||||
@@ -590,7 +612,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
baton->outputFormat = "jpeg";
|
baton->outputFormat = "jpeg";
|
||||||
} else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == ImageType::PNG)) {
|
} else if (baton->output == "__png" || (baton->output == "__input" && 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 buffer
|
// Write PNG to buffer
|
||||||
@@ -672,6 +694,11 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
void HandleOKCallback () {
|
void HandleOKCallback () {
|
||||||
NanScope();
|
NanScope();
|
||||||
|
|
||||||
|
// Free input Buffer
|
||||||
|
if (baton->bufferInLength > 0) {
|
||||||
|
g_free(baton->bufferIn);
|
||||||
|
}
|
||||||
|
|
||||||
Handle<Value> argv[3] = { NanNull(), NanNull(), NanNull() };
|
Handle<Value> argv[3] = { NanNull(), NanNull(), NanNull() };
|
||||||
if (!baton->err.empty()) {
|
if (!baton->err.empty()) {
|
||||||
// Error
|
// Error
|
||||||
@@ -690,16 +717,21 @@ 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);
|
||||||
g_free(baton->bufferOut);
|
g_free(baton->bufferOut);
|
||||||
|
// Add buffer size to info
|
||||||
|
info->Set(NanNew<String>("size"), NanNew<Integer>(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<Integer>(st.st_size));
|
||||||
argv[1] = info;
|
argv[1] = info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -810,11 +842,14 @@ 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 = g_malloc(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->iccProfileCmyk = *String::Utf8Value(options->Get(NanNew<String>("iccProfileCmyk"))->ToString());
|
baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString());
|
||||||
// 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();
|
||||||
@@ -847,7 +882,7 @@ NAN_METHOD(resize) {
|
|||||||
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
||||||
// Operators
|
// Operators
|
||||||
baton->flatten = options->Get(NanNew<String>("flatten"))->BooleanValue();
|
baton->flatten = options->Get(NanNew<String>("flatten"))->BooleanValue();
|
||||||
baton->blurRadius = options->Get(NanNew<String>("blurRadius"))->Int32Value();
|
baton->blurSigma = options->Get(NanNew<String>("blurSigma"))->NumberValue();
|
||||||
baton->sharpenRadius = options->Get(NanNew<String>("sharpenRadius"))->Int32Value();
|
baton->sharpenRadius = options->Get(NanNew<String>("sharpenRadius"))->Int32Value();
|
||||||
baton->sharpenFlat = options->Get(NanNew<String>("sharpenFlat"))->NumberValue();
|
baton->sharpenFlat = options->Get(NanNew<String>("sharpenFlat"))->NumberValue();
|
||||||
baton->sharpenJagged = options->Get(NanNew<String>("sharpenJagged"))->NumberValue();
|
baton->sharpenJagged = options->Get(NanNew<String>("sharpenJagged"))->NumberValue();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"imagemagick": "^0.1.3",
|
"imagemagick": "^0.1.3",
|
||||||
"imagemagick-native": "^1.5.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.1.0",
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ var fixtures = require('../fixtures');
|
|||||||
var width = 720;
|
var width = 720;
|
||||||
var height = 480;
|
var height = 480;
|
||||||
|
|
||||||
|
// Approximately equivalent to fast bilinear
|
||||||
|
var magickFilter = 'Triangle';
|
||||||
|
|
||||||
// Disable libvips cache to ensure tests are as fair as they can be
|
// Disable libvips cache to ensure tests are as fair as they can be
|
||||||
sharp.cache(0);
|
sharp.cache(0);
|
||||||
|
|
||||||
@@ -31,7 +34,9 @@ async.series({
|
|||||||
dstPath: fixtures.outputJpg,
|
dstPath: fixtures.outputJpg,
|
||||||
quality: 0.8,
|
quality: 0.8,
|
||||||
width: width,
|
width: width,
|
||||||
height: height
|
height: height,
|
||||||
|
format: 'jpg',
|
||||||
|
filter: magickFilter
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -48,55 +53,78 @@ async.series({
|
|||||||
quality: 80,
|
quality: 80,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
format: 'JPEG'
|
format: 'JPEG',
|
||||||
|
filter: magickFilter
|
||||||
|
}, function (err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
deferred.resolve();
|
|
||||||
}
|
}
|
||||||
}).add('gm-buffer-file', {
|
}).add('gm-buffer-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(inputJpgBuffer).resize(width, height).quality(80).write(fixtures.outputJpg, function (err) {
|
gm(inputJpgBuffer)
|
||||||
if (err) {
|
.resize(width, height)
|
||||||
throw err;
|
.filter(magickFilter)
|
||||||
} else {
|
.quality(80)
|
||||||
deferred.resolve();
|
.write(fixtures.outputJpg, function (err) {
|
||||||
}
|
if (err) {
|
||||||
});
|
throw err;
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).add('gm-buffer-buffer', {
|
}).add('gm-buffer-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(inputJpgBuffer).resize(width, height).quality(80).toBuffer(function (err, buffer) {
|
gm(inputJpgBuffer)
|
||||||
if (err) {
|
.resize(width, height)
|
||||||
throw err;
|
.filter(magickFilter)
|
||||||
} else {
|
.quality(80)
|
||||||
assert.notStrictEqual(null, buffer);
|
.toBuffer(function (err, buffer) {
|
||||||
deferred.resolve();
|
if (err) {
|
||||||
}
|
throw err;
|
||||||
});
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).add('gm-file-file', {
|
}).add('gm-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(fixtures.inputJpg).resize(width, height).quality(80).write(fixtures.outputJpg, function (err) {
|
gm(fixtures.inputJpg)
|
||||||
if (err) {
|
.resize(width, height)
|
||||||
throw err;
|
.filter(magickFilter)
|
||||||
} else {
|
.quality(80)
|
||||||
deferred.resolve();
|
.write(fixtures.outputJpg, function (err) {
|
||||||
}
|
if (err) {
|
||||||
});
|
throw err;
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).add('gm-file-buffer', {
|
}).add('gm-file-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(fixtures.inputJpg).resize(width, height).quality(80).toBuffer(function (err, buffer) {
|
gm(fixtures.inputJpg)
|
||||||
if (err) {
|
.resize(width, height)
|
||||||
throw err;
|
.filter(magickFilter)
|
||||||
} else {
|
.quality(80)
|
||||||
assert.notStrictEqual(null, buffer);
|
.toBuffer(function (err, buffer) {
|
||||||
deferred.resolve();
|
if (err) {
|
||||||
}
|
throw err;
|
||||||
});
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).add('sharp-buffer-file', {
|
}).add('sharp-buffer-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
@@ -359,7 +387,9 @@ async.series({
|
|||||||
srcPath: fixtures.inputPng,
|
srcPath: fixtures.inputPng,
|
||||||
dstPath: fixtures.outputPng,
|
dstPath: fixtures.outputPng,
|
||||||
width: width,
|
width: width,
|
||||||
height: height
|
height: height,
|
||||||
|
format: 'jpg',
|
||||||
|
filter: magickFilter
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -375,32 +405,39 @@ async.series({
|
|||||||
srcData: inputPngBuffer,
|
srcData: inputPngBuffer,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
format: 'PNG'
|
format: 'PNG',
|
||||||
|
filter: magickFilter
|
||||||
});
|
});
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
}).add('gm-file-file', {
|
}).add('gm-file-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(fixtures.inputPng).resize(width, height).write(fixtures.outputPng, function (err) {
|
gm(fixtures.inputPng)
|
||||||
if (err) {
|
.resize(width, height)
|
||||||
throw err;
|
.filter(magickFilter)
|
||||||
} else {
|
.write(fixtures.outputPng, function (err) {
|
||||||
deferred.resolve();
|
if (err) {
|
||||||
}
|
throw err;
|
||||||
});
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).add('gm-file-buffer', {
|
}).add('gm-file-buffer', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(fixtures.inputPng).resize(width, height).quality(80).toBuffer(function (err, buffer) {
|
gm(fixtures.inputPng)
|
||||||
if (err) {
|
.resize(width, height)
|
||||||
throw err;
|
.filter(magickFilter)
|
||||||
} else {
|
.toBuffer(function (err, buffer) {
|
||||||
assert.notStrictEqual(null, buffer);
|
if (err) {
|
||||||
deferred.resolve();
|
throw err;
|
||||||
}
|
} else {
|
||||||
});
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).add('sharp-buffer-file', {
|
}).add('sharp-buffer-file', {
|
||||||
defer: true,
|
defer: true,
|
||||||
|
|||||||
BIN
test/fixtures/2569067123_aca715a2ee_o.jpg
vendored
BIN
test/fixtures/2569067123_aca715a2ee_o.jpg
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 813 KiB After Width: | Height: | Size: 810 KiB |
17
test/fixtures/Wikimedia-logo.svg
vendored
Normal file
17
test/fixtures/Wikimedia-logo.svg
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" standalone="yes"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
|
||||||
|
id="Wikimedia logo"
|
||||||
|
viewBox="-599 -599 1198 1198" width="1024" height="1024">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="mask">
|
||||||
|
<path d="M 47.5,-87.5 v 425 h -95 v -425 l -552,-552 v 1250 h 1199 v -1250 z" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#mask)">
|
||||||
|
<circle id="green parts" fill="#396" r="336.5"/>
|
||||||
|
<circle id="blue arc" fill="none" stroke="#069" r="480.25" stroke-width="135.5" />
|
||||||
|
</g>
|
||||||
|
<circle fill="#900" cy="-379.5" r="184.5" id="red circle"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 692 B |
BIN
test/fixtures/free-gearhead-pack.psd
vendored
Normal file
BIN
test/fixtures/free-gearhead-pack.psd
vendored
Normal file
Binary file not shown.
2
test/fixtures/index.js
vendored
2
test/fixtures/index.js
vendored
@@ -21,6 +21,8 @@ module.exports = {
|
|||||||
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
||||||
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
||||||
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
|
||||||
|
inputSvg: getPath('Wikimedia-logo.svg'), // http://commons.wikimedia.org/wiki/File:Wikimedia-logo.svg
|
||||||
|
inputPsd: getPath('free-gearhead-pack.psd'), // https://dribbble.com/shots/1624241-Free-Gearhead-Vector-Pack
|
||||||
|
|
||||||
outputJpg: getPath('output.jpg'),
|
outputJpg: getPath('output.jpg'),
|
||||||
outputPng: getPath('output.png'),
|
outputPng: getPath('output.png'),
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ describe('Blur', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('specific radius 100', function(done) {
|
it('specific radius 0.3', function(done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
.blur(100)
|
.blur(0.3)
|
||||||
.toFile(fixtures.path('output.blur-100.jpg'), function(err, info) {
|
.toFile(fixtures.path('output.blur-0.3.jpg'), function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
@@ -64,7 +64,7 @@ describe('Blur', function() {
|
|||||||
it('invalid radius', function(done) {
|
it('invalid radius', function(done) {
|
||||||
var isValid = true;
|
var isValid = true;
|
||||||
try {
|
try {
|
||||||
sharp(fixtures.inputJpg).blur(1.5);
|
sharp(fixtures.inputJpg).blur(0.1);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,21 +270,23 @@ 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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -287,6 +297,7 @@ describe('Input/output', 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 +309,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 +321,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 +332,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 +344,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);
|
||||||
@@ -370,28 +385,30 @@ describe('Input/output', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (semver.gte(sharp.libvipsVersion(), '7.41.0')) {
|
if (semver.gte(sharp.libvipsVersion(), '7.41.0')) {
|
||||||
it('withoutAdaptiveFiltering generates smaller file [libvips 7.41.0+]', function(done) {
|
it('withoutAdaptiveFiltering generates smaller file [libvips ' + sharp.libvipsVersion() + '>=7.41.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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -400,8 +417,36 @@ describe('Input/output', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Convert SVG to PNG', function(done) {
|
||||||
|
sharp(fixtures.inputSvg)
|
||||||
|
.resize(100, 100)
|
||||||
|
.png()
|
||||||
|
.toFile(fixtures.path('output.svg.png'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Convert PSD to PNG', function(done) {
|
||||||
|
sharp(fixtures.inputPsd)
|
||||||
|
.resize(320, 240)
|
||||||
|
.png()
|
||||||
|
.toFile(fixtures.path('output.psd.png'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (semver.gte(sharp.libvipsVersion(), '7.40.0')) {
|
if (semver.gte(sharp.libvipsVersion(), '7.40.0')) {
|
||||||
it('Load TIFF from Buffer [libvips 7.40.0+]', function(done) {
|
it('Load TIFF from Buffer [libvips ' + sharp.libvipsVersion() + '>=7.40.0]', function(done) {
|
||||||
var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff);
|
var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff);
|
||||||
sharp(inputTiffBuffer)
|
sharp(inputTiffBuffer)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
@@ -409,6 +454,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);
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ 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(false, metadata.hasProfile);
|
||||||
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
assert.strictEqual('undefined', typeof metadata.orientation);
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -31,6 +33,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(true, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
assert.strictEqual(8, metadata.orientation);
|
assert.strictEqual(8, metadata.orientation);
|
||||||
done();
|
done();
|
||||||
@@ -45,6 +48,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -58,6 +62,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -71,6 +76,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(true, metadata.hasAlpha);
|
assert.strictEqual(true, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -84,6 +90,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -96,6 +103,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -108,11 +116,21 @@ 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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Non-existent file in, Promise out', function(done) {
|
||||||
|
sharp('fail').metadata().then(function(metadata) {
|
||||||
|
throw new Error('Non-existent file');
|
||||||
|
}, function (err) {
|
||||||
|
assert.ok(!!err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Stream in, Promise out', function(done) {
|
it('Stream in, Promise out', function(done) {
|
||||||
var readable = fs.createReadStream(fixtures.inputJpg);
|
var readable = fs.createReadStream(fixtures.inputJpg);
|
||||||
var pipeline = sharp();
|
var pipeline = sharp();
|
||||||
@@ -122,6 +140,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
@@ -139,6 +158,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(false, metadata.hasProfile);
|
||||||
assert.strictEqual(false, metadata.hasAlpha);
|
assert.strictEqual(false, metadata.hasAlpha);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -154,6 +174,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(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(metadata.width / 2).toBuffer(function(err, data, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
@@ -166,25 +187,33 @@ describe('Image metadata', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Keep EXIF metadata after a resize', function(done) {
|
it('Keep EXIF metadata after a resize', function(done) {
|
||||||
sharp(fixtures.inputJpgWithExif).resize(320, 240).withMetadata().toBuffer(function(err, buffer) {
|
sharp(fixtures.inputJpgWithExif)
|
||||||
if (err) throw err;
|
.resize(320, 240)
|
||||||
sharp(buffer).metadata(function(err, metadata) {
|
.withMetadata()
|
||||||
|
.toBuffer(function(err, buffer) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual(8, metadata.orientation);
|
sharp(buffer).metadata(function(err, metadata) {
|
||||||
done();
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, metadata.hasProfile);
|
||||||
|
assert.strictEqual(8, metadata.orientation);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Remove EXIF metadata after a resize', function(done) {
|
it('Remove EXIF metadata after a resize', function(done) {
|
||||||
sharp(fixtures.inputJpgWithExif).resize(320, 240).withMetadata(false).toBuffer(function(err, buffer) {
|
sharp(fixtures.inputJpgWithExif)
|
||||||
if (err) throw err;
|
.resize(320, 240)
|
||||||
sharp(buffer).metadata(function(err, metadata) {
|
.withMetadata(false)
|
||||||
|
.toBuffer(function(err, buffer) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('undefined', typeof metadata.orientation);
|
sharp(buffer).metadata(function(err, metadata) {
|
||||||
done();
|
if (err) throw err;
|
||||||
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user