mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Major rewrite and therefore API change. Despite the name, image sharpening is now optional, plus there are other new options. Can now handle buffers in and out. Doubled performance in certain cases. Closes #3. Closes #4.
This commit is contained in:
parent
be8f35d830
commit
d509458ba1
127
README.md
127
README.md
@ -1,4 +1,4 @@
|
|||||||
# sharp
|
# sharp
|
||||||
|
|
||||||
_adj_
|
_adj_
|
||||||
|
|
||||||
@ -9,22 +9,17 @@ _adj_
|
|||||||
|
|
||||||
The typical use case for this high speed Node.js module is to convert large JPEG and PNG images to smaller JPEG and PNG images of varying dimensions.
|
The typical use case for this high speed Node.js module is to convert large JPEG and PNG images to smaller JPEG and PNG images of varying dimensions.
|
||||||
|
|
||||||
It is somewhat opinionated in that it only deals with JPEG and PNG images, always obeys the requested dimensions by either cropping or embedding and insists on a mild sharpen of the resulting image.
|
Under the hood you'll find the blazingly fast [libvips](https://github.com/jcupitt/libvips) image processing library, originally created in 1989 at Birkbeck College and currently maintained by John Cupitt.
|
||||||
|
|
||||||
Under the hood you'll find the blazingly fast [libvips](https://github.com/jcupitt/libvips) image processing library, originally created in 1989 at Birkbeck College and currently maintained by the University of Southampton.
|
Performance is up to 18x faster than ImageMagick and up to 8x faster than GraphicsMagick, based mainly on the number of CPU cores available.
|
||||||
|
|
||||||
Performance is 12x-15x faster than ImageMagick and 4x-6x faster than GraphicsMagick, based mainly on the number of CPU cores available.
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
* Node.js v0.8+
|
* Node.js v0.8+
|
||||||
* node-gyp
|
* [libvips](https://github.com/jcupitt/libvips) v7.38+
|
||||||
* [libvips](https://github.com/jcupitt/libvips) v7.37+
|
|
||||||
|
|
||||||
For the sharpest results, please compile libvips from source.
|
For the sharpest results, please compile libvips from source.
|
||||||
|
|
||||||
If you prefer to run a stable, package-managed environment such as Ubuntu 12.04 LTS, [v0.0.3](https://github.com/lovell/sharp/tree/v0.0.3) will work with the libvips-dev package.
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
npm install sharp
|
npm install sharp
|
||||||
@ -33,12 +28,31 @@ If you prefer to run a stable, package-managed environment such as Ubuntu 12.04
|
|||||||
|
|
||||||
var sharp = require("sharp");
|
var sharp = require("sharp");
|
||||||
|
|
||||||
### crop(input, output, width, height, callback)
|
### resize(input, output, width, height, [options], callback)
|
||||||
|
|
||||||
Scale and crop to `width` x `height` calling `callback` when complete.
|
Scale and crop to `width` x `height` calling `callback` when complete.
|
||||||
|
|
||||||
|
`input` can either be a filename String or a Buffer. When using a filename libvips will `mmap` the file for improved performance.
|
||||||
|
|
||||||
|
`output` can either be a filename String or one of `sharp.buffer.jpeg` or `sharp.buffer.png` to pass a Buffer containing image data to `callback`.
|
||||||
|
|
||||||
|
`width` is the Number of pixels wide the resultant image should be.
|
||||||
|
|
||||||
|
`height` is the Number of pixels high the resultant image should be.
|
||||||
|
|
||||||
|
`options` is optional, and can contain one or more of:
|
||||||
|
|
||||||
|
* `canvas` can be one of `sharp.canvas.crop`, `sharp.canvas.embedWhite` or `sharp.canvas.embedBlack`. Defaults to `sharp.canvas.crop`.
|
||||||
|
* `sharpen` when set to true will perform a mild sharpen of the resultant image. This typically reduces performance by 30%.
|
||||||
|
* `progressive` when set will use progressive (interlace) scan for the output. This typically reduces performance by 30%.
|
||||||
|
* `sequentialRead` is an advanced setting that, when set, switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
||||||
|
|
||||||
|
`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant image data when a Buffer is requested.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp.crop("input.jpg", "output.jpg", 300, 200, function(err) {
|
sharp.resize("input.jpg", "output.jpg", 300, 200, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@ -48,73 +62,43 @@ sharp.crop("input.jpg", "output.jpg", 300, 200, function(err) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp.crop("input.jpg", sharp.buffer.jpeg, 300, 200, function(err, buffer) {
|
sharp.resize("input.jpg", sharp.buffer.jpeg, 300, 200, {progressive: true}, function(err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// buffer contains JPEG image data
|
// buffer contains progressive JPEG image data
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp.crop("input.jpg", sharp.buffer.png, 300, 200, function(err, buffer) {
|
sharp.resize("input.jpg", sharp.buffer.png, 300, 200, {sharpen: true}, function(err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// buffer contains PNG image data (converted from JPEG)
|
// buffer contains sharpened PNG image data (converted from JPEG)
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### embedWhite(input, output, width, height, callback)
|
|
||||||
|
|
||||||
Scale and embed to `width` x `height` using a white canvas calling `callback` when complete.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
sharp.embedWhite("input.jpg", "output.jpg", 200, 300, function(err) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
// output.jpg is a 200 pixels wide and 300 pixels high image
|
|
||||||
// containing a scaled version of input.png embedded on a white canvas
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp.embedWhite("input.jpg", sharp.buffer.jpeg, 200, 300, function(err, buffer) {
|
sharp.resize(buffer, "output.jpg", 200, 300, {canvas: sharp.canvas.embedWhite}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// buffer contains JPEG image data
|
// output.jpg is a 200 pixels wide and 300 pixels high image containing a scaled version
|
||||||
|
// of the image data contained in buffer embedded on a white canvas
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### embedBlack(input, output, width, height, callback)
|
|
||||||
|
|
||||||
Scale and embed to `width` x `height` using a black canvas calling `callback` when complete.
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
sharp.embedBlack("input.png", "output.png", 200, 300, function(err) {
|
sharp.resize("input.jpg", sharp.buffer.jpeg, 200, 300, {canvas: sharp.canvas.embedBlack}, function(err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
// output.png is a 200 pixels wide and 300 pixels high image
|
// buffer contains JPEG image data of a 200 pixels wide and 300 pixels high image
|
||||||
// containing a scaled version of input.png embedded on a black canvas
|
// containing a scaled version of input.png embedded on a black canvas
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Parameters common to all methods
|
|
||||||
|
|
||||||
#### input
|
|
||||||
|
|
||||||
String containing the filename to read from.
|
|
||||||
|
|
||||||
#### output
|
|
||||||
|
|
||||||
One of:
|
|
||||||
* String containing the filename to write to.
|
|
||||||
* `sharp.buffer.jpeg` to pass a Buffer containing JPEG image data to `callback`.
|
|
||||||
* `sharp.buffer.png` to pass a Buffer containing PNG image data to `callback`.
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
npm test
|
npm test
|
||||||
@ -129,20 +113,41 @@ Test environment:
|
|||||||
* libpng 1.6.6
|
* libpng 1.6.6
|
||||||
* zlib1g 1.2.7
|
* zlib1g 1.2.7
|
||||||
|
|
||||||
#### JPEG
|
`-file-buffer` indicates read from file and write to buffer, `-buffer-file` indicates read from buffer and write to file etc.
|
||||||
|
|
||||||
* imagemagick x 5.53 ops/sec ±0.55% (31 runs sampled)
|
`-sharpen`, `-progressive` etc. demonstrate the negative effect of options on performance.
|
||||||
* gm x 10.86 ops/sec ±0.43% (56 runs sampled)
|
|
||||||
* epeg x 28.07 ops/sec ±0.07% (70 runs sampled)
|
|
||||||
* sharp-file x 72.01 ops/sec ±7.19% (74 runs sampled)
|
|
||||||
* sharp-buffer x 75.73 ops/sec ±0.44% (75 runs sampled)
|
|
||||||
|
|
||||||
#### PNG
|
### JPEG
|
||||||
|
|
||||||
* imagemagick x 4.65 ops/sec ±0.37% (27 runs sampled)
|
* imagemagick x 5.50 ops/sec ±0.48% (31 runs sampled)
|
||||||
* gm x 21.65 ops/sec ±0.18% (56 runs sampled)
|
* gm-file-file x 11.19 ops/sec ±0.51% (57 runs sampled)
|
||||||
* sharp-file x 43.80 ops/sec ±6.81% (75 runs sampled)
|
* gm-file-buffer x 11.11 ops/sec ±0.42% (57 runs sampled)
|
||||||
* sharp-buffer x 45.67 ops/sec ±0.41% (75 runs sampled)
|
* epeg-file-file x 28.59 ops/sec ±0.09% (71 runs sampled)
|
||||||
|
* epeg-file-buffer x 28.67 ops/sec ±0.14% (71 runs sampled)
|
||||||
|
|
||||||
|
* sharp-buffer-file x 24.72 ops/sec ±0.42% (62 runs sampled)
|
||||||
|
* sharp-buffer-buffer x 24.24 ops/sec ±0.36% (61 runs sampled)
|
||||||
|
* sharp-file-file x 97.15 ops/sec ±0.44% (80 runs sampled)
|
||||||
|
* sharp-file-buffer x __98.51 ops/sec__ ±0.42% (80 runs sampled)
|
||||||
|
|
||||||
|
* sharp-file-buffer-sharpen x 56.99 ops/sec ±5.43% (57 runs sampled)
|
||||||
|
* sharp-file-buffer-progressive x 64.89 ops/sec ±0.42% (79 runs sampled)
|
||||||
|
* sharp-file-buffer-sequentialRead x 64.13 ops/sec ±0.40% (78 runs sampled)
|
||||||
|
|
||||||
|
### PNG
|
||||||
|
|
||||||
|
* imagemagick x 4.31 ops/sec ±0.27% (26 runs sampled)
|
||||||
|
* gm-file-file x 17.89 ops/sec ±0.21% (86 runs sampled)
|
||||||
|
* gm-file-buffer x 14.74 ops/sec ±0.15% (73 runs sampled)
|
||||||
|
|
||||||
|
* sharp-buffer-file x 4.97 ops/sec ±120.47% (26 runs sampled)
|
||||||
|
* sharp-buffer-buffer x 13.00 ops/sec ±0.53% (65 runs sampled)
|
||||||
|
* sharp-file-file x 53.00 ops/sec ±7.15% (88 runs sampled)
|
||||||
|
* sharp-file-buffer x __55.43 ops/sec__ ±0.65% (89 runs sampled)
|
||||||
|
|
||||||
|
* sharp-file-buffer-sharpen x 45.37 ops/sec ±0.38% (74 runs sampled)
|
||||||
|
* sharp-file-buffer-progressive x 55.49 ops/sec ±0.45% (89 runs sampled)
|
||||||
|
* sharp-file-buffer-sequentialRead x 32.27 ops/sec ±0.29% (79 runs sampled)
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
'/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'
|
||||||
],
|
],
|
||||||
'cflags': ['-fexceptions'],
|
'cflags': ['-fexceptions', '-O3'],
|
||||||
'cflags_cc': ['-fexceptions']
|
'cflags_cc': ['-fexceptions', '-O3']
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
56
index.js
56
index.js
@ -5,14 +5,60 @@ module.exports.buffer = {
|
|||||||
png: "__png"
|
png: "__png"
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.crop = function(input, output, width, height, callback) {
|
module.exports.canvas = {
|
||||||
sharp.resize(input, output, width, height, "c", callback);
|
crop: "c",
|
||||||
|
embedWhite: "w",
|
||||||
|
embedBlack: "b"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.resize = function(input, output, width, height, options, callback) {
|
||||||
|
"use strict";
|
||||||
|
if (typeof options === 'function') {
|
||||||
|
callback = options;
|
||||||
|
options = {};
|
||||||
|
} else {
|
||||||
|
options = options || {};
|
||||||
|
}
|
||||||
|
if (typeof input === 'string') {
|
||||||
|
options.inFile = input;
|
||||||
|
} else if (typeof input ==='object' && input instanceof Buffer) {
|
||||||
|
options.inBuffer = input;
|
||||||
|
} else {
|
||||||
|
callback("Unsupported input " + typeof input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!output || output.length === 0) {
|
||||||
|
callback("Invalid output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var outWidth = Number(width);
|
||||||
|
if (Number.isNaN(outWidth)) {
|
||||||
|
callback("Invalid width " + width);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var outHeight = Number(height);
|
||||||
|
if (Number.isNaN(outHeight)) {
|
||||||
|
callback("Invalid height " + height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var canvas = options.canvas || "c";
|
||||||
|
if (canvas.length !== 1 || "cwb".indexOf(canvas) === -1) {
|
||||||
|
callback("Invalid canvas " + canvas);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var sharpen = !!options.sharpen;
|
||||||
|
var progessive = !!options.progessive;
|
||||||
|
var sequentialRead = !!options.sequentialRead;
|
||||||
|
sharp.resize(options.inFile, options.inBuffer, output, width, height, canvas, sharpen, progessive, sequentialRead, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Deprecated v0.0.x methods */
|
||||||
|
module.exports.crop = function(input, output, width, height, sharpen, callback) {
|
||||||
|
sharp.resize(input, output, width, height, {canvas: "c", sharpen: true}, callback);
|
||||||
|
};
|
||||||
module.exports.embedWhite = function(input, output, width, height, callback) {
|
module.exports.embedWhite = function(input, output, width, height, callback) {
|
||||||
sharp.resize(input, output, width, height, "w", callback);
|
sharp.resize(input, output, width, height, {canvas: "w", sharpen: true}, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.embedBlack = function(input, output, width, height, callback) {
|
module.exports.embedBlack = function(input, output, width, height, callback) {
|
||||||
sharp.resize(input, output, width, height, "b", callback);
|
sharp.resize(input, output, width, height, {canvas: "b", sharpen: true}, callback);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"version": "0.0.9",
|
"version": "0.1.0",
|
||||||
"author": "Lovell Fuller",
|
"author": "Lovell Fuller",
|
||||||
"description": "High performance module to resize JPEG and PNG images using the libvips image processing library",
|
"description": "High performance module to resize JPEG and PNG images using the libvips image processing library",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
309
src/sharp.cc
309
src/sharp.cc
@ -2,58 +2,93 @@
|
|||||||
#include <node_buffer.h>
|
#include <node_buffer.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string.h>
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
|
|
||||||
using namespace v8;
|
using namespace v8;
|
||||||
using namespace node;
|
using namespace node;
|
||||||
|
|
||||||
struct ResizeBaton {
|
struct resize_baton {
|
||||||
std::string src;
|
std::string file_in;
|
||||||
std::string dst;
|
void* buffer_in;
|
||||||
|
size_t buffer_in_len;
|
||||||
|
std::string file_out;
|
||||||
void* buffer_out;
|
void* buffer_out;
|
||||||
size_t buffer_out_len;
|
size_t buffer_out_len;
|
||||||
int cols;
|
int width;
|
||||||
int rows;
|
int height;
|
||||||
bool crop;
|
bool crop;
|
||||||
int embed;
|
int embed;
|
||||||
|
bool sharpen;
|
||||||
|
bool progessive;
|
||||||
|
VipsAccess access_method;
|
||||||
std::string err;
|
std::string err;
|
||||||
Persistent<Function> callback;
|
Persistent<Function> callback;
|
||||||
|
|
||||||
ResizeBaton() : buffer_out_len(0) {}
|
resize_baton(): buffer_in_len(0), buffer_out_len(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool EndsWith(std::string const &str, std::string const &end) {
|
typedef enum {
|
||||||
|
JPEG,
|
||||||
|
PNG
|
||||||
|
} ImageType;
|
||||||
|
|
||||||
|
unsigned char MARKER_JPEG[] = {0xff, 0xd8};
|
||||||
|
unsigned char MARKER_PNG[] = {0x89, 0x50};
|
||||||
|
|
||||||
|
bool ends_with(std::string const &str, std::string const &end) {
|
||||||
return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);
|
return str.length() >= end.length() && 0 == str.compare(str.length() - end.length(), end.length(), end);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsJpeg(std::string const &str) {
|
bool is_jpeg(std::string const &str) {
|
||||||
return EndsWith(str, ".jpg") || EndsWith(str, ".jpeg");
|
return ends_with(str, ".jpg") || ends_with(str, ".jpeg");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPng(std::string const &str) {
|
bool is_png(std::string const &str) {
|
||||||
return EndsWith(str, ".png");
|
return ends_with(str, ".png");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResizeAsync(uv_work_t *work) {
|
void resize_error(resize_baton *baton, VipsImage *unref) {
|
||||||
ResizeBaton* baton = static_cast<ResizeBaton*>(work->data);
|
(baton->err).append(vips_error_buffer());
|
||||||
|
vips_error_clear();
|
||||||
|
g_object_unref(unref);
|
||||||
|
vips_thread_shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize_async(uv_work_t *work) {
|
||||||
|
resize_baton* baton = static_cast<resize_baton*>(work->data);
|
||||||
|
|
||||||
|
ImageType inputImageType = JPEG;
|
||||||
VipsImage *in = vips_image_new();
|
VipsImage *in = vips_image_new();
|
||||||
if (IsJpeg(baton->src)) {
|
if (baton->buffer_in_len > 1) {
|
||||||
vips_jpegload((baton->src).c_str(), &in, NULL);
|
if (memcmp(MARKER_JPEG, baton->buffer_in, 2) == 0) {
|
||||||
} else if (IsPng(baton->src)) {
|
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
|
||||||
vips_pngload((baton->src).c_str(), &in, NULL);
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
|
} else if(memcmp(MARKER_PNG, baton->buffer_in, 2) == 0) {
|
||||||
|
inputImageType = PNG;
|
||||||
|
if (vips_pngload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
|
||||||
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (is_jpeg(baton->file_in)) {
|
||||||
|
if (vips_jpegload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
||||||
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
|
} else if (is_png(baton->file_in)) {
|
||||||
|
inputImageType = PNG;
|
||||||
|
if (vips_pngload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
||||||
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("Unsupported input file type");
|
resize_error(baton, in);
|
||||||
return;
|
(baton->err).append("Unsupported input " + baton->file_in);
|
||||||
}
|
|
||||||
if (in == NULL) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double xfactor = static_cast<double>(in->Xsize) / std::max(baton->cols, 1);
|
double xfactor = static_cast<double>(in->Xsize) / std::max(baton->width, 1);
|
||||||
double yfactor = static_cast<double>(in->Ysize) / std::max(baton->rows, 1);
|
double yfactor = static_cast<double>(in->Ysize) / std::max(baton->height, 1);
|
||||||
double factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
|
double factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor);
|
||||||
factor = std::max(factor, 1.0);
|
factor = std::max(factor, 1.0);
|
||||||
int shrink = floor(factor);
|
int shrink = floor(factor);
|
||||||
@ -61,7 +96,7 @@ void ResizeAsync(uv_work_t *work) {
|
|||||||
|
|
||||||
// Try to use libjpeg shrink-on-load
|
// Try to use libjpeg shrink-on-load
|
||||||
int shrink_on_load = 1;
|
int shrink_on_load = 1;
|
||||||
if (IsJpeg(baton->src)) {
|
if (inputImageType == JPEG) {
|
||||||
if (shrink >= 8) {
|
if (shrink >= 8) {
|
||||||
residual = residual * shrink / 8;
|
residual = residual * shrink / 8;
|
||||||
shrink_on_load = 8;
|
shrink_on_load = 8;
|
||||||
@ -76,129 +111,117 @@ void ResizeAsync(uv_work_t *work) {
|
|||||||
shrink = 1;
|
shrink = 1;
|
||||||
}
|
}
|
||||||
if (shrink_on_load > 1) {
|
if (shrink_on_load > 1) {
|
||||||
if (vips_jpegload((baton->src).c_str(), &in, "shrink", shrink_on_load, NULL)) {
|
g_object_unref(in);
|
||||||
(baton->err).append(vips_error_buffer());
|
in = vips_image_new();
|
||||||
vips_error_clear();
|
if (baton->buffer_in_len > 1) {
|
||||||
g_object_unref(in);
|
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "shrink", shrink_on_load, NULL)) {
|
||||||
return;
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (vips_jpegload((baton->file_in).c_str(), &in, "shrink", shrink_on_load, NULL)) {
|
||||||
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VipsImage* img = in;
|
VipsImage *shrunk = vips_image_new();
|
||||||
VipsImage* t[4];
|
|
||||||
if (im_open_local_array(img, t, 4, "temp", "p")) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
g_object_unref(in);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shrink > 1) {
|
if (shrink > 1) {
|
||||||
// Use vips_shrink with the integral reduction
|
// Use vips_shrink with the integral reduction
|
||||||
if (vips_shrink(img, &t[0], shrink, shrink, NULL)) {
|
if (vips_shrink(in, &shrunk, shrink, shrink, NULL)) {
|
||||||
(baton->err).append(vips_error_buffer());
|
return resize_error(baton, in);
|
||||||
vips_error_clear();
|
|
||||||
g_object_unref(in);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t[0] = img;
|
vips_copy(in, &shrunk, NULL);
|
||||||
}
|
|
||||||
|
|
||||||
// Use vips_affine with the remaining float part using bilinear interpolation
|
|
||||||
if (vips_affine(t[0], &t[1], residual, 0, 0, residual, "interpolate", vips_interpolate_bilinear_static(), NULL)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
g_object_unref(in);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
img = t[1];
|
|
||||||
|
|
||||||
if (baton->crop) {
|
|
||||||
int width = std::min(img->Xsize, baton->cols);
|
|
||||||
int height = std::min(img->Ysize, baton->rows);
|
|
||||||
int left = (img->Xsize - width + 1) / 2;
|
|
||||||
int top = (img->Ysize - height + 1) / 2;
|
|
||||||
if (im_extract_area(img, t[2], left, top, width, height)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
g_object_unref(in);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
img = t[2];
|
|
||||||
} else {
|
|
||||||
int left = (baton->cols - img->Xsize) / 2;
|
|
||||||
int top = (baton->rows - img->Ysize) / 2;
|
|
||||||
if (im_embed(img, t[2], baton->embed, left, top, baton->cols, baton->rows)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
g_object_unref(in);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
img = t[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mild sharpen
|
|
||||||
INTMASK* sharpen = im_create_imaskv("sharpen", 3, 3,
|
|
||||||
-1, -1, -1,
|
|
||||||
-1, 32, -1,
|
|
||||||
-1, -1, -1);
|
|
||||||
sharpen->scale = 24;
|
|
||||||
if (im_conv(img, t[3], sharpen)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
g_object_unref(in);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
img = t[3];
|
|
||||||
|
|
||||||
if (baton->dst == "__jpeg") {
|
|
||||||
// Write JPEG to buffer
|
|
||||||
if (vips_jpegsave_buffer(img, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, "optimize_coding", TRUE, NULL)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
}
|
|
||||||
} else if (baton->dst == "__png") {
|
|
||||||
// Write PNG to buffer
|
|
||||||
if (vips_pngsave_buffer(img, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "compression", 6, "interlace", FALSE, NULL)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
}
|
|
||||||
} else if (EndsWith(baton->dst, ".jpg") || EndsWith(baton->dst, ".jpeg")) {
|
|
||||||
// Write JPEG to file
|
|
||||||
if (vips_foreign_save(img, baton->dst.c_str(), "strip", TRUE, "Q", 80, "optimize_coding", TRUE, NULL)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
}
|
|
||||||
} else if (EndsWith(baton->dst, ".png")) {
|
|
||||||
// Write PNG to file
|
|
||||||
if (vips_foreign_save(img, baton->dst.c_str(), "strip", TRUE, "compression", 6, "interlace", FALSE, NULL)) {
|
|
||||||
(baton->err).append(vips_error_buffer());
|
|
||||||
vips_error_clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(baton->err).append("Unsupported output file type");
|
|
||||||
}
|
}
|
||||||
g_object_unref(in);
|
g_object_unref(in);
|
||||||
|
|
||||||
|
// Use vips_affine with the remaining float part using bilinear interpolation
|
||||||
|
VipsImage *affined = vips_image_new();
|
||||||
|
if (vips_affine(shrunk, &affined, residual, 0, 0, residual, "interpolate", vips_interpolate_bilinear_static(), NULL)) {
|
||||||
|
return resize_error(baton, shrunk);
|
||||||
|
}
|
||||||
|
g_object_unref(shrunk);
|
||||||
|
|
||||||
|
VipsImage *canvased = vips_image_new();
|
||||||
|
if (baton->crop) {
|
||||||
|
int width = std::min(affined->Xsize, baton->width);
|
||||||
|
int height = std::min(affined->Ysize, baton->height);
|
||||||
|
int left = (affined->Xsize - width + 1) / 2;
|
||||||
|
int top = (affined->Ysize - height + 1) / 2;
|
||||||
|
if (vips_extract_area(affined, &canvased, left, top, width, height, NULL)) {
|
||||||
|
return resize_error(baton, affined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int left = (baton->width - affined->Xsize) / 2;
|
||||||
|
int top = (baton->height - affined->Ysize) / 2;
|
||||||
|
if (vips_embed(affined, &canvased, baton->embed, left, top, baton->width, baton->height, NULL)) {
|
||||||
|
return resize_error(baton, affined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_object_unref(affined);
|
||||||
|
|
||||||
|
// Mild sharpen
|
||||||
|
VipsImage *sharpened = vips_image_new();
|
||||||
|
if (baton->sharpen) {
|
||||||
|
INTMASK* sharpen = im_create_imaskv("sharpen", 3, 3,
|
||||||
|
-1, -1, -1,
|
||||||
|
-1, 32, -1,
|
||||||
|
-1, -1, -1);
|
||||||
|
sharpen->scale = 24;
|
||||||
|
if (im_conv(canvased, sharpened, sharpen)) {
|
||||||
|
return resize_error(baton, canvased);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vips_copy(canvased, &sharpened, NULL);
|
||||||
|
}
|
||||||
|
g_object_unref(canvased);
|
||||||
|
|
||||||
|
if (baton->file_out == "__jpeg") {
|
||||||
|
// Write JPEG to buffer
|
||||||
|
if (vips_jpegsave_buffer(canvased, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
|
||||||
|
return resize_error(baton, canvased);
|
||||||
|
}
|
||||||
|
} else if (baton->file_out == "__png") {
|
||||||
|
// Write PNG to buffer
|
||||||
|
if (vips_pngsave_buffer(canvased, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) {
|
||||||
|
return resize_error(baton, canvased);
|
||||||
|
}
|
||||||
|
} else if (is_jpeg(baton->file_out)) {
|
||||||
|
// Write JPEG to file
|
||||||
|
if (vips_jpegsave(canvased, baton->file_out.c_str(), "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
|
||||||
|
return resize_error(baton, canvased);
|
||||||
|
}
|
||||||
|
} else if (is_png(baton->file_out)) {
|
||||||
|
// Write PNG to file
|
||||||
|
if (vips_pngsave(canvased, baton->file_out.c_str(), "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) {
|
||||||
|
return resize_error(baton, canvased);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(baton->err).append("Unsupported output " + baton->file_out);
|
||||||
|
}
|
||||||
|
g_object_unref(canvased);
|
||||||
vips_thread_shutdown();
|
vips_thread_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResizeAsyncAfter(uv_work_t *work, int status) {
|
void resize_async_after(uv_work_t *work, int status) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
ResizeBaton *baton = static_cast<ResizeBaton*>(work->data);
|
resize_baton *baton = static_cast<resize_baton*>(work->data);
|
||||||
|
|
||||||
Local<Value> null = Local<Value>::New(Null());
|
Handle<Value> argv[2] = { Null(), Null() };
|
||||||
Local<Value> argv[2] = {null, null};
|
|
||||||
if (!baton->err.empty()) {
|
if (!baton->err.empty()) {
|
||||||
// Error
|
// Error
|
||||||
argv[0] = String::New(baton->err.data(), baton->err.size());
|
argv[0] = String::New(baton->err.data(), baton->err.size());
|
||||||
} else if (baton->buffer_out_len > 0) {
|
} else if (baton->buffer_out_len > 0) {
|
||||||
// Buffer
|
// Buffer
|
||||||
Buffer *buffer = Buffer::New((const char*)(baton->buffer_out), baton->buffer_out_len);
|
Buffer *slowBuffer = Buffer::New(baton->buffer_out_len);
|
||||||
argv[1] = Local<Object>::New(buffer->handle_);
|
memcpy(Buffer::Data(slowBuffer), baton->buffer_out, baton->buffer_out_len);
|
||||||
vips_free(baton->buffer_out);
|
Local<Object> globalObj = Context::GetCurrent()->Global();
|
||||||
|
Local<Function> bufferConstructor = Local<Function>::Cast(globalObj->Get(String::New("Buffer")));
|
||||||
|
Handle<Value> constructorArgs[3] = { slowBuffer->handle_, v8::Integer::New(baton->buffer_out_len), v8::Integer::New(0) };
|
||||||
|
argv[1] = bufferConstructor->NewInstance(3, constructorArgs);
|
||||||
|
g_free(baton->buffer_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
|
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
|
||||||
@ -207,15 +230,20 @@ void ResizeAsyncAfter(uv_work_t *work, int status) {
|
|||||||
delete work;
|
delete work;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<Value> Resize(const Arguments& args) {
|
Handle<Value> resize(const Arguments& args) {
|
||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
|
|
||||||
ResizeBaton *baton = new ResizeBaton;
|
resize_baton *baton = new resize_baton;
|
||||||
baton->src = *String::Utf8Value(args[0]->ToString());
|
baton->file_in = *String::Utf8Value(args[0]->ToString());
|
||||||
baton->dst = *String::Utf8Value(args[1]->ToString());
|
if (args[1]->IsObject()) {
|
||||||
baton->cols = args[2]->Int32Value();
|
Local<Object> buffer = args[1]->ToObject();
|
||||||
baton->rows = args[3]->Int32Value();
|
baton->buffer_in = Buffer::Data(buffer);
|
||||||
Local<String> canvas = args[4]->ToString();
|
baton->buffer_in_len = Buffer::Length(buffer);
|
||||||
|
}
|
||||||
|
baton->file_out = *String::Utf8Value(args[2]->ToString());
|
||||||
|
baton->width = args[3]->Int32Value();
|
||||||
|
baton->height = args[4]->Int32Value();
|
||||||
|
Local<String> canvas = args[5]->ToString();
|
||||||
if (canvas->Equals(String::NewSymbol("c"))) {
|
if (canvas->Equals(String::NewSymbol("c"))) {
|
||||||
baton->crop = true;
|
baton->crop = true;
|
||||||
} else if (canvas->Equals(String::NewSymbol("w"))) {
|
} else if (canvas->Equals(String::NewSymbol("w"))) {
|
||||||
@ -225,12 +253,15 @@ Handle<Value> Resize(const Arguments& args) {
|
|||||||
baton->crop = false;
|
baton->crop = false;
|
||||||
baton->embed = 0;
|
baton->embed = 0;
|
||||||
}
|
}
|
||||||
baton->callback = Persistent<Function>::New(Local<Function>::Cast(args[5]));
|
baton->sharpen = args[6]->BooleanValue();
|
||||||
|
baton->progessive = args[7]->BooleanValue();
|
||||||
|
baton->access_method = args[8]->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
||||||
|
baton->callback = Persistent<Function>::New(Local<Function>::Cast(args[9]));
|
||||||
|
|
||||||
uv_work_t *work = new uv_work_t;
|
uv_work_t *work = new uv_work_t;
|
||||||
work->data = baton;
|
work->data = baton;
|
||||||
uv_queue_work(uv_default_loop(), work, ResizeAsync, (uv_after_work_cb)ResizeAsyncAfter);
|
uv_queue_work(uv_default_loop(), work, resize_async, (uv_after_work_cb)resize_async_after);
|
||||||
return Undefined();
|
return scope.Close(Undefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void at_exit(void* arg) {
|
static void at_exit(void* arg) {
|
||||||
@ -242,7 +273,7 @@ extern "C" void init(Handle<Object> target) {
|
|||||||
HandleScope scope;
|
HandleScope scope;
|
||||||
vips_init("");
|
vips_init("");
|
||||||
AtExit(at_exit);
|
AtExit(at_exit);
|
||||||
NODE_SET_METHOD(target, "resize", Resize);
|
NODE_SET_METHOD(target, "resize", resize);
|
||||||
};
|
};
|
||||||
|
|
||||||
NODE_MODULE(sharp, init);
|
NODE_MODULE(sharp, init);
|
||||||
|
175
tests/perf.js
175
tests/perf.js
@ -1,4 +1,5 @@
|
|||||||
var sharp = require("../index");
|
var sharp = require("../index");
|
||||||
|
var fs = require("fs");
|
||||||
var imagemagick = require("imagemagick");
|
var imagemagick = require("imagemagick");
|
||||||
var gm = require("gm");
|
var gm = require("gm");
|
||||||
var epeg = require("epeg");
|
var epeg = require("epeg");
|
||||||
@ -17,6 +18,7 @@ var height = 480;
|
|||||||
|
|
||||||
async.series({
|
async.series({
|
||||||
jpeg: function(callback) {
|
jpeg: function(callback) {
|
||||||
|
var inputJpgBuffer = fs.readFileSync(inputJpg);
|
||||||
(new Benchmark.Suite("jpeg")).add("imagemagick", {
|
(new Benchmark.Suite("jpeg")).add("imagemagick", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
@ -34,7 +36,7 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).add("gm", {
|
}).add("gm-file-file", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(inputJpg).crop(width, height).quality(80).write(outputJpg, function (err) {
|
gm(inputJpg).crop(width, height).quality(80).write(outputJpg, function (err) {
|
||||||
@ -45,17 +47,37 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).add("epeg", {
|
}).add("gm-file-buffer", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
gm(inputJpg).crop(width, height).quality(80).toBuffer(function (err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("epeg-file-file", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
var image = new epeg.Image({path: inputJpg});
|
var image = new epeg.Image({path: inputJpg});
|
||||||
image.downsize(width, height, 80).saveTo(outputJpg);
|
image.downsize(width, height, 80).saveTo(outputJpg);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
}).add("sharp-file", {
|
}).add("epeg-file-buffer", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
sharp.crop(inputJpg, outputJpg, width, height, function(err) {
|
var image = new epeg.Image({path: inputJpg});
|
||||||
|
var buffer = image.downsize(width, height, 80).process();
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
}).add("sharp-buffer-file", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputJpgBuffer, outputJpg, width, height, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@ -63,10 +85,69 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).add("sharp-buffer", {
|
}).add("sharp-buffer-buffer", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
sharp.crop(inputJpg, sharp.buffer.jpeg, width, height, function(err, buffer) {
|
sharp.resize(inputJpgBuffer, sharp.buffer.jpeg, width, height, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-file", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputJpg, outputJpg, width, height, function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-sharpen", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, {sharpen: true}, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-progressive", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, {progressive: true}, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-sequentialRead", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, {sequentialRead: true}, function(err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@ -82,6 +163,7 @@ async.series({
|
|||||||
}).run();
|
}).run();
|
||||||
},
|
},
|
||||||
png: function(callback) {
|
png: function(callback) {
|
||||||
|
var inputPngBuffer = fs.readFileSync(inputPng);
|
||||||
(new Benchmark.Suite("png")).add("imagemagick", {
|
(new Benchmark.Suite("png")).add("imagemagick", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
@ -98,7 +180,7 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).add("gm", {
|
}).add("gm-file-file", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
gm(inputPng).crop(width, height).write(outputPng, function (err) {
|
gm(inputPng).crop(width, height).write(outputPng, function (err) {
|
||||||
@ -109,10 +191,22 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).add("sharp-file", {
|
}).add("gm-file-buffer", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
sharp.crop(inputPng, outputPng, width, height, function(err) {
|
gm(inputPng).crop(width, height).quality(80).toBuffer(function (err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-buffer-file", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputPngBuffer, outputPng, width, height, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
@ -120,10 +214,69 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).add("sharp-buffer", {
|
}).add("sharp-buffer-buffer", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
sharp.crop(inputPng, sharp.buffer.png, width, height, function(err, buffer) {
|
sharp.resize(inputPngBuffer, sharp.buffer.png, width, height, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-file", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputPng, outputPng, width, height, function(err) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputPng, sharp.buffer.png, width, height, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-sharpen", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputPng, sharp.buffer.png, width, height, {sharpen: true}, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-progressive", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputPng, sharp.buffer.png, width, height, {progressive: true}, function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-sequentialRead", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp.resize(inputPng, sharp.buffer.png, width, height, {sequentialRead: true}, function(err, buffer) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user