mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4cc6a2db4 | ||
|
|
0acf865654 | ||
|
|
8460e50ee0 | ||
|
|
f57a0e3b00 | ||
|
|
02b6016390 | ||
|
|
4e01d63195 | ||
|
|
94b47508c0 | ||
|
|
328cda82c5 | ||
|
|
118b17aa2f | ||
|
|
b7c7fc22f3 |
37
README.md
37
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.
|
||||||
|
|
||||||
@@ -68,11 +70,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 +242,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 +328,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 +403,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 (ICC, EXIF, XMP) from the input image in the output image.
|
||||||
|
|
||||||
|
The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space.
|
||||||
|
|
||||||
#### compressionLevel(compressionLevel)
|
#### compressionLevel(compressionLevel)
|
||||||
|
|
||||||
@@ -404,9 +415,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
|
||||||
|
|
||||||
@@ -548,7 +559,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.
28
index.js
28
index.js
@@ -20,8 +20,8 @@ var Sharp = function(input) {
|
|||||||
// input options
|
// input options
|
||||||
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 +43,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,
|
||||||
@@ -208,21 +208,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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"version": "0.8.0",
|
"version": "0.8.1",
|
||||||
"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,16 +27,11 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -12,15 +12,15 @@
|
|||||||
# * Fedora 21, 22
|
# * Fedora 21, 22
|
||||||
|
|
||||||
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 +94,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 +102,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 +119,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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
104
src/resize.cc
104
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),
|
||||||
@@ -269,33 +269,26 @@ 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)) {
|
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
|
||||||
VipsImage *profile;
|
VipsImage *transformed;
|
||||||
// Use embedded profile
|
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "embedded", TRUE, NULL)) {
|
||||||
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, NULL)) {
|
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, profile);
|
vips_object_local(hook, transformed);
|
||||||
image = profile;
|
image = transformed;
|
||||||
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
|
} else if (image->Type == VIPS_INTERPRETATION_CMYK) {
|
||||||
VipsImage *profile;
|
// Convert to sRGB using default "USWebCoatedSWOP" CMYK profile
|
||||||
// CMYK with no embedded profile
|
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
|
||||||
if (vips_icc_import(image, &profile, "pcs", VIPS_PCS_XYZ, "input_profile", (baton->iccProfileCmyk).c_str(), 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, profile);
|
vips_object_local(hook, transformed);
|
||||||
image = profile;
|
image = transformed;
|
||||||
}
|
|
||||||
// Attempt to convert to sRGB colour space
|
|
||||||
VipsImage *colourspaced;
|
|
||||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
|
||||||
return Error(baton, hook);
|
|
||||||
}
|
|
||||||
vips_object_local(hook, colourspaced);
|
|
||||||
image = colourspaced;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten image to remove alpha channel
|
// Flatten image to remove alpha channel
|
||||||
@@ -364,22 +357,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) {
|
||||||
|
// Calculate standard deviation
|
||||||
|
double sigma = ((1.0 / residual) - 0.4) / 3.0;
|
||||||
|
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;
|
VipsImage *blurred;
|
||||||
if (vips_gaussblur(image, &blurred, floor(residual), NULL)) {
|
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, blurred);
|
vips_object_local(hook, blurred);
|
||||||
image = 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 +506,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 +521,15 @@ 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
|
||||||
|
VipsImage *blurred;
|
||||||
|
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -559,14 +571,22 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
image = gammaDecoded;
|
image = gammaDecoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to sRGB colour space, if not already
|
// Convert colour space to either sRGB or RGB-with-profile, if not already
|
||||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
||||||
VipsImage *colourspaced;
|
VipsImage *rgb;
|
||||||
if (vips_colourspace(image, &colourspaced, VIPS_INTERPRETATION_sRGB, NULL)) {
|
if (baton->withMetadata && HasProfile(image)) {
|
||||||
|
// Convert to device-dependent RGB using embedded profile of input
|
||||||
|
if (vips_icc_export(image, &rgb, NULL)) {
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
}
|
}
|
||||||
vips_object_local(hook, colourspaced);
|
} else {
|
||||||
image = colourspaced;
|
// Convert to device-independent sRGB
|
||||||
|
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vips_object_local(hook, rgb);
|
||||||
|
image = rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
#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 +610,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
|
||||||
@@ -814,7 +834,7 @@ NAN_METHOD(resize) {
|
|||||||
baton->bufferIn = node::Buffer::Data(buffer);
|
baton->bufferIn = node::Buffer::Data(buffer);
|
||||||
}
|
}
|
||||||
// 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 +867,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,14 +53,25 @@ 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)
|
||||||
|
.resize(width, height)
|
||||||
|
.filter(magickFilter)
|
||||||
|
.quality(80)
|
||||||
|
.write(fixtures.outputJpg, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@@ -66,7 +82,11 @@ async.series({
|
|||||||
}).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)
|
||||||
|
.resize(width, height)
|
||||||
|
.filter(magickFilter)
|
||||||
|
.quality(80)
|
||||||
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@@ -78,7 +98,11 @@ async.series({
|
|||||||
}).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)
|
||||||
|
.resize(width, height)
|
||||||
|
.filter(magickFilter)
|
||||||
|
.quality(80)
|
||||||
|
.write(fixtures.outputJpg, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@@ -89,7 +113,11 @@ async.series({
|
|||||||
}).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)
|
||||||
|
.resize(width, height)
|
||||||
|
.filter(magickFilter)
|
||||||
|
.quality(80)
|
||||||
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@@ -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,14 +405,18 @@ 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)
|
||||||
|
.resize(width, height)
|
||||||
|
.filter(magickFilter)
|
||||||
|
.write(fixtures.outputPng, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@@ -393,7 +427,10 @@ async.series({
|
|||||||
}).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)
|
||||||
|
.resize(width, height)
|
||||||
|
.filter(magickFilter)
|
||||||
|
.toBuffer(function (err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
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 |
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ 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)
|
||||||
@@ -401,7 +401,7 @@ describe('Input/output', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -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,6 +116,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();
|
||||||
});
|
});
|
||||||
@@ -122,6 +131,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 +149,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 +165,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,10 +178,14 @@ 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)
|
||||||
|
.resize(320, 240)
|
||||||
|
.withMetadata()
|
||||||
|
.toBuffer(function(err, buffer) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
sharp(buffer).metadata(function(err, metadata) {
|
sharp(buffer).metadata(function(err, metadata) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, metadata.hasProfile);
|
||||||
assert.strictEqual(8, metadata.orientation);
|
assert.strictEqual(8, metadata.orientation);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@@ -177,10 +193,14 @@ describe('Image metadata', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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)
|
||||||
|
.resize(320, 240)
|
||||||
|
.withMetadata(false)
|
||||||
|
.toBuffer(function(err, buffer) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
sharp(buffer).metadata(function(err, metadata) {
|
sharp(buffer).metadata(function(err, metadata) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
assert.strictEqual(false, metadata.hasProfile);
|
||||||
assert.strictEqual('undefined', typeof metadata.orientation);
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user