mirror of
https://github.com/lovell/sharp.git
synced 2026-02-05 14:16:17 +01:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c96bd0d37 | ||
|
|
6b5f2028b7 | ||
|
|
276ba5228b | ||
|
|
ad7735a0a6 | ||
|
|
88edad3fae | ||
|
|
308d1971d8 | ||
|
|
6622045172 | ||
|
|
f68ba8ea57 | ||
|
|
2e427bb28a | ||
|
|
efc7504961 | ||
|
|
8118613fa0 | ||
|
|
eb6a221cee | ||
|
|
acdfe02502 | ||
|
|
2e106f8e2e | ||
|
|
10496881f1 | ||
|
|
e275f6f5dd | ||
|
|
d635c297a2 | ||
|
|
817c0a2a5a | ||
|
|
92fd34c627 | ||
|
|
43086cf134 | ||
|
|
e607bac31c | ||
|
|
f8338e7c4f | ||
|
|
8322b442e0 | ||
|
|
afc51df4d8 | ||
|
|
e3a70c1075 | ||
|
|
e3ee2b2976 | ||
|
|
cb285a6fb3 | ||
|
|
aed3ca63b3 | ||
|
|
cbcf5e0dcc | ||
|
|
59f5c2d31b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,5 +13,3 @@ results
|
|||||||
build
|
build
|
||||||
node_modules
|
node_modules
|
||||||
tests/fixtures/output.*
|
tests/fixtures/output.*
|
||||||
|
|
||||||
npm-debug.log
|
|
||||||
|
|||||||
18
.npmignore
Normal file
18
.npmignore
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
lib-cov
|
||||||
|
*.seed
|
||||||
|
*.log
|
||||||
|
*.csv
|
||||||
|
*.dat
|
||||||
|
*.out
|
||||||
|
*.pid
|
||||||
|
*.gz
|
||||||
|
|
||||||
|
pids
|
||||||
|
logs
|
||||||
|
results
|
||||||
|
build
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
.gitignore
|
||||||
|
tests
|
||||||
|
.travis.yml
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "0.10"
|
- "0.10"
|
||||||
|
- "0.11"
|
||||||
before_install:
|
before_install:
|
||||||
- sudo add-apt-repository ppa:lyrasis/precise-backports -y
|
- sudo add-apt-repository ppa:lyrasis/precise-backports -y
|
||||||
- sudo apt-get update -qq
|
- sudo apt-get update -qq
|
||||||
- sudo apt-get install -qq automake gobject-introspection gtk-doc-tools libfftw3-dev libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff4-dev liborc-0.4-dev libxml2-dev swig graphicsmagick libmagick++-dev
|
- sudo apt-get install -qq automake gobject-introspection gtk-doc-tools libfftw3-dev libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff4-dev libxml2-dev swig graphicsmagick libmagick++-dev
|
||||||
- git clone https://github.com/jcupitt/libvips.git
|
- git clone https://github.com/jcupitt/libvips.git
|
||||||
- cd libvips
|
- cd libvips
|
||||||
- git checkout 7.38
|
- git checkout 7.38
|
||||||
- ./bootstrap.sh
|
- ./bootstrap.sh
|
||||||
- ./configure --enable-debug=no --enable-cxx=no --without-python
|
- ./configure --enable-debug=no --enable-cxx=no --without-orc --without-python
|
||||||
- make
|
- make
|
||||||
- sudo make install
|
- sudo make install
|
||||||
- sudo ldconfig
|
- sudo ldconfig
|
||||||
|
|||||||
64
README.md
64
README.md
@@ -28,6 +28,8 @@ This module is powered by the blazingly fast [libvips](https://github.com/jcupit
|
|||||||
* Node.js v0.10+
|
* Node.js v0.10+
|
||||||
* [libvips](https://github.com/jcupitt/libvips) v7.38.5+
|
* [libvips](https://github.com/jcupitt/libvips) v7.38.5+
|
||||||
|
|
||||||
|
_libvips_ will take advantage of [liborc](http://code.entropywave.com/orc/) if present, however versions of _liborc_ prior to 0.4.19 suffer memory leaks.
|
||||||
|
|
||||||
### Install libvips on Mac OS
|
### Install libvips on Mac OS
|
||||||
|
|
||||||
brew install homebrew/science/vips --with-webp --with-graphicsmagick
|
brew install homebrew/science/vips --with-webp --with-graphicsmagick
|
||||||
@@ -36,24 +38,36 @@ The _gettext_ dependency of _libvips_ [can lead](https://github.com/lovell/sharp
|
|||||||
|
|
||||||
brew link gettext --force
|
brew link gettext --force
|
||||||
|
|
||||||
### Install libvips on Ubuntu/Debian Linux
|
### Install libvips on Ubuntu Linux
|
||||||
|
|
||||||
sudo apt-get install automake build-essential git gobject-introspection gtk-doc-tools libfftw3-dev libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff5-dev liborc-0.4-dev libxml2-dev swig
|
#### Ubuntu 14.x
|
||||||
|
|
||||||
|
sudo apt-get install libvips-dev
|
||||||
|
|
||||||
|
#### Ubuntu 13.x
|
||||||
|
|
||||||
|
Compiling from source is recommended:
|
||||||
|
|
||||||
|
sudo apt-get install automake build-essential git gobject-introspection gtk-doc-tools libfftw3-dev libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff5-dev libxml2-dev swig
|
||||||
git clone https://github.com/jcupitt/libvips.git
|
git clone https://github.com/jcupitt/libvips.git
|
||||||
cd libvips
|
cd libvips
|
||||||
git checkout 7.38
|
git checkout 7.38
|
||||||
./bootstrap.sh
|
./bootstrap.sh
|
||||||
./configure --enable-debug=no --enable-cxx=no --without-python
|
./configure --enable-debug=no --enable-cxx=no --without-python --without-orc
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|
||||||
Ubuntu 12.04 requires `libtiff4-dev` instead of `libtiff5-dev` and has [a bug](https://bugs.launchpad.net/ubuntu/+source/libwebp/+bug/1108731) in the libwebp package. Work around these problems by running these command first:
|
#### Ubuntu 12.x
|
||||||
|
|
||||||
|
Requires `libtiff4-dev` instead of `libtiff5-dev` and has [a bug](https://bugs.launchpad.net/ubuntu/+source/libwebp/+bug/1108731) in the libwebp package. Work around these problems by running these command first:
|
||||||
|
|
||||||
sudo add-apt-repository ppa:lyrasis/precise-backports
|
sudo add-apt-repository ppa:lyrasis/precise-backports
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install libtiff4-dev
|
sudo apt-get install libtiff4-dev
|
||||||
|
|
||||||
|
Then follow Ubuntu 13.x instructions.
|
||||||
|
|
||||||
## Usage examples
|
## Usage examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@@ -71,25 +85,25 @@ sharp('input.jpg').resize(300, 200).write('output.jpg', function(err) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp('input.jpg').resize(null, 200).progressive().toBuffer(function(err, buffer) {
|
sharp('input.jpg').resize(null, 200).progressive().toBuffer(function(err, outputBuffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// buffer contains progressive JPEG image data, 200 pixels high
|
// outputBuffer contains progressive JPEG image data, 200 pixels high
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp('input.png').resize(300).sharpen().webp(function(err, buffer) {
|
sharp('input.png').resize(300).sharpen().quality(90).webp(function(err, outputBuffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// buffer contains sharpened WebP image data (converted from PNG), 300 pixels wide
|
// outputBuffer contains 300 pixels wide, sharpened, 90% quality WebP image data
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp(buffer).resize(200, 300).embedWhite().write('output.tiff', function(err) {
|
sharp(inputBuffer).resize(200, 300).embedWhite().write('output.tiff', function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -99,15 +113,25 @@ sharp(buffer).resize(200, 300).embedWhite().write('output.tiff', function(err) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp('input.gif').resize(200, 300).embedBlack().webp(function(err, buffer) {
|
sharp('input.gif').resize(200, 300).embedBlack().webp(function(err, outputBuffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// buffer contains WebP image data of a 200 pixels wide and 300 pixels high image
|
// outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high
|
||||||
// containing a scaled version, embedded on a black canvas, of input.gif
|
// containing a scaled version, embedded on a black canvas, of input.gif
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharp(inputBuffer).resize(200, 200).max().jpeg(function(err, outputBuffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
// outputBuffer contains JPEG image data no wider than 200 pixels and no higher
|
||||||
|
// than 200 pixels regardless of the inputBuffer image dimensions
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### sharp(input)
|
### sharp(input)
|
||||||
@@ -129,6 +153,12 @@ Scale to `width` x `height`. By default, the resized image is cropped to the exa
|
|||||||
|
|
||||||
Crop the resized image to the exact size specified, the default behaviour.
|
Crop the resized image to the exact size specified, the default behaviour.
|
||||||
|
|
||||||
|
### max()
|
||||||
|
|
||||||
|
Preserving aspect ratio, resize the image to the maximum width or height specified.
|
||||||
|
|
||||||
|
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
|
||||||
|
|
||||||
### embedWhite()
|
### embedWhite()
|
||||||
|
|
||||||
Embed the resized image on a white background of the exact size specified.
|
Embed the resized image on a white background of the exact size specified.
|
||||||
@@ -145,6 +175,18 @@ Perform a mild sharpen of the resultant image. This typically reduces performanc
|
|||||||
|
|
||||||
Use progressive (interlace) scan for JPEG and PNG output. This typically reduces compression performance by 30% but results in an image that can be rendered sooner when decompressed.
|
Use progressive (interlace) scan for JPEG and PNG output. This typically reduces compression performance by 30% but results in an image that can be rendered sooner when decompressed.
|
||||||
|
|
||||||
|
### quality(quality)
|
||||||
|
|
||||||
|
The output quality to use for lossy JPEG, WebP and TIFF output formats. The default quality is `80`.
|
||||||
|
|
||||||
|
`quality` is a Number between 1 and 100.
|
||||||
|
|
||||||
|
### compressionLevel(compressionLevel)
|
||||||
|
|
||||||
|
An advanced setting for the _zlib_ compression level of the lossless PNG output format. The default level is `6`.
|
||||||
|
|
||||||
|
`compressionLevel` is a Number between -1 and 9.
|
||||||
|
|
||||||
### sequentialRead()
|
### sequentialRead()
|
||||||
|
|
||||||
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
'/usr/include/glib-2.0',
|
'/usr/include/glib-2.0',
|
||||||
'/usr/lib/glib-2.0/include',
|
'/usr/lib/glib-2.0/include',
|
||||||
'/usr/lib/x86_64-linux-gnu/glib-2.0/include',
|
'/usr/lib/x86_64-linux-gnu/glib-2.0/include',
|
||||||
|
'/usr/lib/i386-linux-gnu/glib-2.0/include',
|
||||||
'<!(node -e "require(\'nan\')")'
|
'<!(node -e "require(\'nan\')")'
|
||||||
],
|
],
|
||||||
'cflags': ['-fexceptions', '-pedantic', '-Wall', '-O3'],
|
'cflags': ['-fexceptions', '-pedantic', '-Wall', '-O3'],
|
||||||
|
|||||||
28
index.js
28
index.js
@@ -14,6 +14,8 @@ var Sharp = function(input) {
|
|||||||
sharpen: false,
|
sharpen: false,
|
||||||
progressive: false,
|
progressive: false,
|
||||||
sequentialRead: false,
|
sequentialRead: false,
|
||||||
|
quality: 80,
|
||||||
|
compressionLevel: 6,
|
||||||
output: '__jpeg'
|
output: '__jpeg'
|
||||||
};
|
};
|
||||||
if (typeof input === 'string') {
|
if (typeof input === 'string') {
|
||||||
@@ -42,6 +44,12 @@ Sharp.prototype.embedBlack = function() {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Sharp.prototype.max = function() {
|
||||||
|
this.options.canvas = 'm';
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Sharp.prototype.sharpen = function(sharpen) {
|
Sharp.prototype.sharpen = function(sharpen) {
|
||||||
this.options.sharpen = (typeof sharpen === 'boolean') ? sharpen : true;
|
this.options.sharpen = (typeof sharpen === 'boolean') ? sharpen : true;
|
||||||
return this;
|
return this;
|
||||||
@@ -57,6 +65,24 @@ Sharp.prototype.sequentialRead = function(sequentialRead) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Sharp.prototype.quality = function(quality) {
|
||||||
|
if (!Number.isNaN(quality) && quality >= 1 && quality <= 100) {
|
||||||
|
this.options.quality = quality;
|
||||||
|
} else {
|
||||||
|
throw 'Invalid quality (1 to 100) ' + quality;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Sharp.prototype.compressionLevel = function(compressionLevel) {
|
||||||
|
if (!Number.isNaN(compressionLevel) && compressionLevel >= -1 && compressionLevel <= 9) {
|
||||||
|
this.options.compressionLevel = compressionLevel;
|
||||||
|
} else {
|
||||||
|
throw 'Invalid compressionLevel (-1 to 9) ' + compressionLevel;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
Sharp.prototype.resize = function(width, height) {
|
Sharp.prototype.resize = function(width, height) {
|
||||||
if (!width) {
|
if (!width) {
|
||||||
this.options.width = -1;
|
this.options.width = -1;
|
||||||
@@ -115,6 +141,8 @@ Sharp.prototype._sharp = function(output, callback) {
|
|||||||
this.options.sharpen,
|
this.options.sharpen,
|
||||||
this.options.progressive,
|
this.options.progressive,
|
||||||
this.options.sequentialRead,
|
this.options.sequentialRead,
|
||||||
|
this.options.quality,
|
||||||
|
this.options.compressionLevel,
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"version": "0.3.0",
|
"version": "0.4.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>"
|
||||||
@@ -31,13 +31,13 @@
|
|||||||
"buffer"
|
"buffer"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nan": "^0.8.0"
|
"nan": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"imagemagick": "^0.1.3",
|
"imagemagick": "^0.1.3",
|
||||||
"imagemagick-native": "^0.2.9",
|
"imagemagick-native": "^1.0.0",
|
||||||
"gm": "^1.14.2",
|
"gm": "^1.16.0",
|
||||||
"async": "^0.6.2",
|
"async": "^0.9.0",
|
||||||
"benchmark": "^1.0.0"
|
"benchmark": "^1.0.0"
|
||||||
},
|
},
|
||||||
"license": "Apache 2.0",
|
"license": "Apache 2.0",
|
||||||
|
|||||||
96
src/sharp.cc
96
src/sharp.cc
@@ -20,13 +20,16 @@ struct resize_baton {
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
bool crop;
|
bool crop;
|
||||||
|
bool max;
|
||||||
VipsExtend extend;
|
VipsExtend extend;
|
||||||
bool sharpen;
|
bool sharpen;
|
||||||
bool progessive;
|
bool progressive;
|
||||||
VipsAccess access_method;
|
VipsAccess access_method;
|
||||||
|
int quality;
|
||||||
|
int compressionLevel;
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
resize_baton(): buffer_in_len(0), buffer_out_len(0) {}
|
resize_baton(): buffer_in_len(0), buffer_out_len(0), crop(false), max(false), sharpen(false), progressive(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -133,17 +136,25 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
double factor;
|
double factor;
|
||||||
if (baton->width > 0 && baton->height > 0) {
|
if (baton->width > 0 && baton->height > 0) {
|
||||||
// Fixed width and height
|
// Fixed width and height
|
||||||
double xfactor = (double)(in->Xsize) / (double)(baton->width);
|
double xfactor = static_cast<double>(in->Xsize) / static_cast<double>(baton->width);
|
||||||
double yfactor = (double)(in->Ysize) / (double)(baton->height);
|
double yfactor = static_cast<double>(in->Ysize) / static_cast<double>(baton->height);
|
||||||
factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
|
factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
|
||||||
|
// if max is set, we need to compute the real size of the thumb image
|
||||||
|
if (baton->max) {
|
||||||
|
if (xfactor > yfactor) {
|
||||||
|
baton->height = round(static_cast<double>(in->Ysize) / xfactor);
|
||||||
|
} else {
|
||||||
|
baton->width = round(static_cast<double>(in->Xsize) / yfactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (baton->width > 0) {
|
} else if (baton->width > 0) {
|
||||||
// Fixed width, auto height
|
// Fixed width, auto height
|
||||||
factor = (double)(in->Xsize) / (double)(baton->width);
|
factor = static_cast<double>(in->Xsize) / static_cast<double>(baton->width);
|
||||||
baton->height = floor((double)(in->Ysize) / factor);
|
baton->height = floor(static_cast<double>(in->Ysize) / factor);
|
||||||
} else if (baton->height > 0) {
|
} else if (baton->height > 0) {
|
||||||
// Fixed height, auto width
|
// Fixed height, auto width
|
||||||
factor = (double)(in->Ysize) / (double)(baton->height);
|
factor = static_cast<double>(in->Ysize) / static_cast<double>(baton->height);
|
||||||
baton->width = floor((double)(in->Xsize) / factor);
|
baton->width = floor(static_cast<double>(in->Xsize) / factor);
|
||||||
} else {
|
} else {
|
||||||
// Identity transform
|
// Identity transform
|
||||||
factor = 1;
|
factor = 1;
|
||||||
@@ -154,7 +165,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
if (shrink < 1) {
|
if (shrink < 1) {
|
||||||
shrink = 1;
|
shrink = 1;
|
||||||
}
|
}
|
||||||
double residual = shrink / (double)factor;
|
double residual = static_cast<double>(shrink) / factor;
|
||||||
|
|
||||||
// Try to use libjpeg shrink-on-load
|
// Try to use libjpeg shrink-on-load
|
||||||
int shrink_on_load = 1;
|
int shrink_on_load = 1;
|
||||||
@@ -197,6 +208,14 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
if (vips_shrink(shrunk_on_load, &shrunk, shrink, shrink, NULL)) {
|
if (vips_shrink(shrunk_on_load, &shrunk, shrink, shrink, NULL)) {
|
||||||
return resize_error(baton, shrunk_on_load);
|
return resize_error(baton, shrunk_on_load);
|
||||||
}
|
}
|
||||||
|
// Recalculate residual float based on dimensions of required vs shrunk images
|
||||||
|
double residualx = static_cast<double>(baton->width) / static_cast<double>(shrunk->Xsize);
|
||||||
|
double residualy = static_cast<double>(baton->height) / static_cast<double>(shrunk->Ysize);
|
||||||
|
if (baton->crop) {
|
||||||
|
residual = std::max(residualx, residualy);
|
||||||
|
} else {
|
||||||
|
residual = std::min(residualx, residualy);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vips_copy(shrunk_on_load, &shrunk, NULL);
|
vips_copy(shrunk_on_load, &shrunk, NULL);
|
||||||
}
|
}
|
||||||
@@ -217,7 +236,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
VipsImage *canvased = vips_image_new();
|
VipsImage *canvased = vips_image_new();
|
||||||
if (affined->Xsize != baton->width || affined->Ysize != baton->height) {
|
if (affined->Xsize != baton->width || affined->Ysize != baton->height) {
|
||||||
if (baton->crop) {
|
if (baton->crop) {
|
||||||
// Crop
|
// Crop/max
|
||||||
int width = std::min(affined->Xsize, baton->width);
|
int width = std::min(affined->Xsize, baton->width);
|
||||||
int height = std::min(affined->Ysize, baton->height);
|
int height = std::min(affined->Ysize, baton->height);
|
||||||
int left = (affined->Xsize - width + 1) / 2;
|
int left = (affined->Xsize - width + 1) / 2;
|
||||||
@@ -241,14 +260,16 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
// Mild sharpen
|
// Mild sharpen
|
||||||
VipsImage *sharpened = vips_image_new();
|
VipsImage *sharpened = vips_image_new();
|
||||||
if (baton->sharpen) {
|
if (baton->sharpen) {
|
||||||
INTMASK* sharpen = im_create_imaskv("sharpen", 3, 3,
|
VipsImage *sharpen = vips_image_new_matrixv(3, 3,
|
||||||
-1, -1, -1,
|
-1.0, -1.0, -1.0,
|
||||||
-1, 32, -1,
|
-1.0, 32.0, -1.0,
|
||||||
-1, -1, -1);
|
-1.0, -1.0, -1.0);
|
||||||
sharpen->scale = 24;
|
vips_image_set_double(sharpen, "scale", 24);
|
||||||
if (im_conv(canvased, sharpened, sharpen)) {
|
if (vips_conv(canvased, &sharpened, sharpen, NULL)) {
|
||||||
|
g_object_unref(sharpen);
|
||||||
return resize_error(baton, canvased);
|
return resize_error(baton, canvased);
|
||||||
}
|
}
|
||||||
|
g_object_unref(sharpen);
|
||||||
} else {
|
} else {
|
||||||
vips_copy(canvased, &sharpened, NULL);
|
vips_copy(canvased, &sharpened, NULL);
|
||||||
}
|
}
|
||||||
@@ -257,37 +278,37 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
// Output
|
// Output
|
||||||
if (baton->file_out == "__jpeg" || (baton->file_out == "__input" && inputImageType == JPEG)) {
|
if (baton->file_out == "__jpeg" || (baton->file_out == "__input" && inputImageType == JPEG)) {
|
||||||
// Write JPEG to buffer
|
// Write JPEG to buffer
|
||||||
if (vips_jpegsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
|
if (vips_jpegsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (baton->file_out == "__png" || (baton->file_out == "__input" && inputImageType == PNG)) {
|
} else if (baton->file_out == "__png" || (baton->file_out == "__input" && inputImageType == PNG)) {
|
||||||
// Write PNG to buffer
|
// Write PNG to buffer
|
||||||
if (vips_pngsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) {
|
if (vips_pngsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (baton->file_out == "__webp" || (baton->file_out == "__input" && inputImageType == WEBP)) {
|
} else if (baton->file_out == "__webp" || (baton->file_out == "__input" && inputImageType == WEBP)) {
|
||||||
// Write WEBP to buffer
|
// Write WEBP to buffer
|
||||||
if (vips_webpsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, NULL)) {
|
if (vips_webpsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", baton->quality, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (is_jpeg(baton->file_out)) {
|
} else if (is_jpeg(baton->file_out)) {
|
||||||
// Write JPEG to file
|
// Write JPEG to file
|
||||||
if (vips_jpegsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
|
if (vips_jpegsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (is_png(baton->file_out)) {
|
} else if (is_png(baton->file_out)) {
|
||||||
// Write PNG to file
|
// Write PNG to file
|
||||||
if (vips_pngsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) {
|
if (vips_pngsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (is_webp(baton->file_out)) {
|
} else if (is_webp(baton->file_out)) {
|
||||||
// Write WEBP to file
|
// Write WEBP to file
|
||||||
if (vips_webpsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "Q", 80, NULL)) {
|
if (vips_webpsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "Q", baton->quality, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (is_tiff(baton->file_out)) {
|
} else if (is_tiff(baton->file_out)) {
|
||||||
// Write TIFF to file
|
// Write TIFF to file
|
||||||
if (vips_tiffsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", 80, NULL)) {
|
if (vips_tiffsave(sharpened, baton->file_out.c_str(), "strip", TRUE, "compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, "Q", baton->quality, NULL)) {
|
||||||
return resize_error(baton, sharpened);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -300,10 +321,10 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
void HandleOKCallback () {
|
void HandleOKCallback () {
|
||||||
NanScope();
|
NanScope();
|
||||||
|
|
||||||
Handle<Value> argv[2] = { Null(), Null() };
|
Handle<Value> argv[2] = { NanNull(), NanNull() };
|
||||||
if (!baton->err.empty()) {
|
if (!baton->err.empty()) {
|
||||||
// Error
|
// Error
|
||||||
argv[0] = String::New(baton->err.data(), baton->err.size());
|
argv[0] = NanNew<String>(baton->err.data(), baton->err.size());
|
||||||
} else if (baton->buffer_out_len > 0) {
|
} else if (baton->buffer_out_len > 0) {
|
||||||
// Buffer
|
// Buffer
|
||||||
argv[1] = NanNewBufferHandle((char *)baton->buffer_out, baton->buffer_out_len);
|
argv[1] = NanNewBufferHandle((char *)baton->buffer_out, baton->buffer_out_len);
|
||||||
@@ -331,20 +352,23 @@ NAN_METHOD(resize) {
|
|||||||
baton->width = args[3]->Int32Value();
|
baton->width = args[3]->Int32Value();
|
||||||
baton->height = args[4]->Int32Value();
|
baton->height = args[4]->Int32Value();
|
||||||
Local<String> canvas = args[5]->ToString();
|
Local<String> canvas = args[5]->ToString();
|
||||||
if (canvas->Equals(String::NewSymbol("c"))) {
|
if (canvas->Equals(NanSymbol("c"))) {
|
||||||
baton->crop = true;
|
baton->crop = true;
|
||||||
} else if (canvas->Equals(String::NewSymbol("w"))) {
|
} else if (canvas->Equals(NanSymbol("w"))) {
|
||||||
baton->crop = false;
|
|
||||||
baton->extend = VIPS_EXTEND_WHITE;
|
baton->extend = VIPS_EXTEND_WHITE;
|
||||||
} else if (canvas->Equals(String::NewSymbol("b"))) {
|
} else if (canvas->Equals(NanSymbol("b"))) {
|
||||||
baton->crop = false;
|
|
||||||
baton->extend = VIPS_EXTEND_BLACK;
|
baton->extend = VIPS_EXTEND_BLACK;
|
||||||
|
} else if (canvas->Equals(NanSymbol("m"))) {
|
||||||
|
baton->crop = true;
|
||||||
|
baton->max = true;
|
||||||
}
|
}
|
||||||
baton->sharpen = args[6]->BooleanValue();
|
baton->sharpen = args[6]->BooleanValue();
|
||||||
baton->progessive = args[7]->BooleanValue();
|
baton->progressive = args[7]->BooleanValue();
|
||||||
baton->access_method = args[8]->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
baton->access_method = args[8]->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
||||||
|
baton->quality = args[9]->Int32Value();
|
||||||
|
baton->compressionLevel = args[10]->Int32Value();
|
||||||
|
|
||||||
NanCallback *callback = new NanCallback(args[9].As<v8::Function>());
|
NanCallback *callback = new NanCallback(args[11].As<v8::Function>());
|
||||||
|
|
||||||
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
||||||
NanReturnUndefined();
|
NanReturnUndefined();
|
||||||
@@ -359,10 +383,10 @@ NAN_METHOD(cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get cache statistics
|
// Get cache statistics
|
||||||
Local<Object> cache = Object::New();
|
Local<Object> cache = NanNew<Object>();
|
||||||
cache->Set(String::NewSymbol("current"), Number::New(vips_tracked_get_mem() / 1048576));
|
cache->Set(NanSymbol("current"), NanNew<Number>(vips_tracked_get_mem() / 1048576));
|
||||||
cache->Set(String::NewSymbol("high"), Number::New(vips_tracked_get_mem_highwater() / 1048576));
|
cache->Set(NanSymbol("high"), NanNew<Number>(vips_tracked_get_mem_highwater() / 1048576));
|
||||||
cache->Set(String::NewSymbol("limit"), Number::New(vips_cache_get_max_mem() / 1048576));
|
cache->Set(NanSymbol("limit"), NanNew<Number>(vips_cache_get_max_mem() / 1048576));
|
||||||
NanReturnValue(cache);
|
NanReturnValue(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ var fixturesPath = path.join(__dirname, "fixtures");
|
|||||||
var inputJpg = path.join(fixturesPath, "2569067123_aca715a2ee_o.jpg"); // http://www.flickr.com/photos/grizdave/2569067123/
|
var inputJpg = path.join(fixturesPath, "2569067123_aca715a2ee_o.jpg"); // http://www.flickr.com/photos/grizdave/2569067123/
|
||||||
var outputJpg = path.join(fixturesPath, "output.jpg");
|
var outputJpg = path.join(fixturesPath, "output.jpg");
|
||||||
|
|
||||||
|
var inputTiff = path.join(fixturesPath, "G31D.TIF"); // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
||||||
|
var outputTiff = path.join(fixturesPath, "output.tiff");
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
// Resize with exact crop
|
// Resize with exact crop
|
||||||
function(done) {
|
function(done) {
|
||||||
@@ -69,5 +72,78 @@ async.series([
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
// Quality
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(320, 240).quality(70).jpeg(function(err, buffer70) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(inputJpg).resize(320, 240).jpeg(function(err, buffer80) {
|
||||||
|
if (err) throw err;
|
||||||
|
sharp(inputJpg).resize(320, 240).quality(90).jpeg(function(err, buffer90) {
|
||||||
|
assert(buffer70.length < buffer80.length);
|
||||||
|
assert(buffer80.length < buffer90.length);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// TIFF with dimensions known to cause rounding errors
|
||||||
|
function(done) {
|
||||||
|
sharp(inputTiff).resize(240, 320).embedBlack().write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(240, features.width);
|
||||||
|
assert.strictEqual(320, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(done) {
|
||||||
|
sharp(inputTiff).resize(240, 320).write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(240, features.width);
|
||||||
|
assert.strictEqual(320, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Resize to max width or height considering ratio (landscape)
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(320, 320).max().write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, features.width);
|
||||||
|
assert.strictEqual(261, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Resize to max width or height considering ratio (portrait)
|
||||||
|
function(done) {
|
||||||
|
sharp(inputTiff).resize(320, 320).max().write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(243, features.width);
|
||||||
|
assert.strictEqual(320, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Attempt to resize to max but only provide one dimension, so should default to crop
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(320).max().write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(320, features.width);
|
||||||
|
assert.strictEqual(261, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user