mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f97d04dfa | ||
|
|
e4ca8f44ec | ||
|
|
6b3dc1e350 | ||
|
|
7ffcdb79e0 | ||
|
|
10ce7c6693 | ||
|
|
ccd6012152 | ||
|
|
377662fffc |
50
README.md
50
README.md
@@ -9,9 +9,9 @@ _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.
|
||||
|
||||
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.
|
||||
The performance of JPEG resizing is typically 15x-25x faster than ImageMagick and GraphicsMagick, based mainly on the number of CPU cores available.
|
||||
|
||||
Performance is up to 18x faster than ImageMagick and up to 8x faster than GraphicsMagick, based mainly on the number of CPU cores available.
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -119,35 +119,35 @@ Test environment:
|
||||
|
||||
### JPEG
|
||||
|
||||
* imagemagick x 5.50 ops/sec ±0.48% (31 runs sampled)
|
||||
* gm-file-file x 11.19 ops/sec ±0.51% (57 runs sampled)
|
||||
* gm-file-buffer x 11.11 ops/sec ±0.42% (57 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)
|
||||
* imagemagick x 5.53 ops/sec ±0.62% (31 runs sampled)
|
||||
* gm-file-file x 4.10 ops/sec ±0.41% (25 runs sampled)
|
||||
* gm-file-buffer x 4.10 ops/sec ±0.36% (25 runs sampled)
|
||||
* epeg-file-file x 23.82 ops/sec ±0.18% (60 runs sampled)
|
||||
* epeg-file-buffer x 23.98 ops/sec ±0.16% (61 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-buffer-file x 20.76 ops/sec ±0.55% (54 runs sampled)
|
||||
* sharp-buffer-buffer x 20.90 ops/sec ±0.26% (54 runs sampled)
|
||||
* sharp-file-file x 91.78 ops/sec ±0.38% (88 runs sampled)
|
||||
* sharp-file-buffer x __93.05 ops/sec__ ±0.61% (76 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)
|
||||
* sharp-file-buffer-sharpen x 63.09 ops/sec ±5.58% (63 runs sampled)
|
||||
* sharp-file-buffer-progressive x 61.68 ops/sec ±0.53% (76 runs sampled)
|
||||
* sharp-file-buffer-sequentialRead x 60.66 ops/sec ±0.38% (75 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)
|
||||
* imagemagick x 4.27 ops/sec ±0.21% (25 runs sampled)
|
||||
* gm-file-file x 8.33 ops/sec ±0.19% (44 runs sampled)
|
||||
* gm-file-buffer x 7.45 ops/sec ±0.16% (40 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)
|
||||
* sharp-buffer-file x 4.94 ops/sec ±118.46% (26 runs sampled)
|
||||
* sharp-buffer-buffer x 12.59 ops/sec ±0.55% (64 runs sampled)
|
||||
* sharp-file-file x 44.06 ops/sec ±6.86% (75 runs sampled)
|
||||
* sharp-file-buffer x __46.29 ops/sec__ ±0.38% (76 runs sampled)
|
||||
|
||||
* sharp-file-buffer-sharpen x 38.86 ops/sec ±0.22% (65 runs sampled)
|
||||
* sharp-file-buffer-progressive x 46.35 ops/sec ±0.20% (76 runs sampled)
|
||||
* sharp-file-buffer-sequentialRead x 29.02 ops/sec ±0.62% (72 runs sampled)
|
||||
|
||||
## Licence
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
'<!@(PKG_CONFIG_PATH="/usr/lib/pkgconfig" pkg-config --libs vips)'
|
||||
],
|
||||
'include_dirs': [
|
||||
'/usr/local/include/glib-2.0',
|
||||
'/usr/local/lib/glib-2.0/include',
|
||||
'/usr/include/glib-2.0',
|
||||
'/usr/lib/glib-2.0/include',
|
||||
'/usr/lib/x86_64-linux-gnu/glib-2.0/include'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.3",
|
||||
"author": "Lovell Fuller",
|
||||
"description": "High performance module to resize JPEG and PNG images using the libvips image processing library",
|
||||
"scripts": {
|
||||
|
||||
58
src/sharp.cc
58
src/sharp.cc
@@ -18,7 +18,7 @@ struct resize_baton {
|
||||
int width;
|
||||
int height;
|
||||
bool crop;
|
||||
int embed;
|
||||
VipsExtend extend;
|
||||
bool sharpen;
|
||||
bool progessive;
|
||||
VipsAccess access_method;
|
||||
@@ -98,19 +98,21 @@ void resize_async(uv_work_t *work) {
|
||||
int shrink_on_load = 1;
|
||||
if (inputImageType == JPEG) {
|
||||
if (shrink >= 8) {
|
||||
residual = residual * shrink / 8;
|
||||
factor = factor / 8;
|
||||
shrink_on_load = 8;
|
||||
shrink = 1;
|
||||
} else if (shrink >= 4) {
|
||||
residual = residual * shrink / 4;
|
||||
factor = factor / 4;
|
||||
shrink_on_load = 4;
|
||||
shrink = 1;
|
||||
} else if (shrink >= 2) {
|
||||
residual = residual * shrink / 2;
|
||||
factor = factor / 2;
|
||||
shrink_on_load = 2;
|
||||
shrink = 1;
|
||||
}
|
||||
if (shrink_on_load > 1) {
|
||||
// Recalculate integral shrink and double residual
|
||||
factor = std::max(factor, 1.0);
|
||||
shrink = floor(factor);
|
||||
residual = shrink / factor;
|
||||
// Reload input using shrink-on-load
|
||||
g_object_unref(in);
|
||||
in = vips_image_new();
|
||||
if (baton->buffer_in_len > 1) {
|
||||
@@ -138,26 +140,36 @@ void resize_async(uv_work_t *work) {
|
||||
|
||||
// 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);
|
||||
if (residual > 0) {
|
||||
if (vips_affine(shrunk, &affined, residual, 0, 0, residual, "interpolate", vips_interpolate_bilinear_static(), NULL)) {
|
||||
return resize_error(baton, shrunk);
|
||||
}
|
||||
} else {
|
||||
vips_copy(shrunk, &affined, NULL);
|
||||
}
|
||||
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);
|
||||
if (affined->Xsize != baton->width || affined->Ysize != baton->height) {
|
||||
if (baton->crop) {
|
||||
// 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 {
|
||||
// Embed
|
||||
int left = (baton->width - affined->Xsize) / 2;
|
||||
int top = (baton->height - affined->Ysize) / 2;
|
||||
if (vips_embed(affined, &canvased, left, top, baton->width, baton->height, "extend", baton->extend, 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);
|
||||
}
|
||||
vips_copy(affined, &canvased, NULL);
|
||||
}
|
||||
g_object_unref(affined);
|
||||
|
||||
@@ -248,10 +260,10 @@ Handle<Value> resize(const Arguments& args) {
|
||||
baton->crop = true;
|
||||
} else if (canvas->Equals(String::NewSymbol("w"))) {
|
||||
baton->crop = false;
|
||||
baton->embed = 4;
|
||||
baton->extend = VIPS_EXTEND_WHITE;
|
||||
} else if (canvas->Equals(String::NewSymbol("b"))) {
|
||||
baton->crop = false;
|
||||
baton->embed = 0;
|
||||
baton->extend = VIPS_EXTEND_BLACK;
|
||||
}
|
||||
baton->sharpen = args[6]->BooleanValue();
|
||||
baton->progessive = args[7]->BooleanValue();
|
||||
|
||||
29
tests/parallel.js
Executable file
29
tests/parallel.js
Executable file
@@ -0,0 +1,29 @@
|
||||
var sharp = require("../index");
|
||||
var fs = require("fs");
|
||||
var assert = require("assert");
|
||||
var async = require("async");
|
||||
|
||||
var inputJpg = __dirname + "/2569067123_aca715a2ee_o.jpg"; // http://www.flickr.com/photos/grizdave/2569067123/
|
||||
var width = 720;
|
||||
var height = 480;
|
||||
|
||||
async.mapSeries([1, 1, 2, 4, 8, 16, 32, 64, 128], function(parallelism, next) {
|
||||
var start = new Date().getTime();
|
||||
async.times(parallelism,
|
||||
function(id, callback) {
|
||||
sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, function(err, buffer) {
|
||||
buffer = null;
|
||||
callback(err, new Date().getTime() - start);
|
||||
});
|
||||
},
|
||||
function(err, ids) {
|
||||
assert(!err);
|
||||
assert(ids.length === parallelism);
|
||||
var mean = ids.reduce(function(a, b) {
|
||||
return a + b;
|
||||
}) / ids.length;
|
||||
console.log(parallelism + " parallel calls: fastest=" + ids[0] + "ms slowest=" + ids[ids.length - 1] + "ms mean=" + mean + "ms");
|
||||
next();
|
||||
}
|
||||
);
|
||||
}, function() {});
|
||||
@@ -13,7 +13,7 @@ var outputJpg = __dirname + "/output.jpg";
|
||||
var inputPng = __dirname + "/50020484-00001.png"; // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||
var outputPng = __dirname + "/output.png";
|
||||
|
||||
var width = 640;
|
||||
var width = 720;
|
||||
var height = 480;
|
||||
|
||||
async.series({
|
||||
@@ -39,7 +39,7 @@ async.series({
|
||||
}).add("gm-file-file", {
|
||||
defer: true,
|
||||
fn: function(deferred) {
|
||||
gm(inputJpg).crop(width, height).quality(80).write(outputJpg, function (err) {
|
||||
gm(inputJpg).resize(width, height).quality(80).write(outputJpg, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -50,7 +50,7 @@ async.series({
|
||||
}).add("gm-file-buffer", {
|
||||
defer: true,
|
||||
fn: function(deferred) {
|
||||
gm(inputJpg).crop(width, height).quality(80).toBuffer(function (err, buffer) {
|
||||
gm(inputJpg).resize(width, height).quality(80).toBuffer(function (err, buffer) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -183,7 +183,7 @@ async.series({
|
||||
}).add("gm-file-file", {
|
||||
defer: true,
|
||||
fn: function(deferred) {
|
||||
gm(inputPng).crop(width, height).write(outputPng, function (err) {
|
||||
gm(inputPng).resize(width, height).write(outputPng, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
@@ -194,7 +194,7 @@ async.series({
|
||||
}).add("gm-file-buffer", {
|
||||
defer: true,
|
||||
fn: function(deferred) {
|
||||
gm(inputPng).crop(width, height).quality(80).toBuffer(function (err, buffer) {
|
||||
gm(inputPng).resize(width, height).quality(80).toBuffer(function (err, buffer) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user