mirror of
https://github.com/lovell/sharp.git
synced 2026-02-05 06:06:18 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16551bc058 | ||
|
|
e9d196f696 | ||
|
|
2f97d04dfa | ||
|
|
e4ca8f44ec | ||
|
|
6b3dc1e350 | ||
|
|
7ffcdb79e0 | ||
|
|
10ce7c6693 |
@@ -7,11 +7,13 @@
|
|||||||
'<!@(PKG_CONFIG_PATH="/usr/lib/pkgconfig" pkg-config --libs vips)'
|
'<!@(PKG_CONFIG_PATH="/usr/lib/pkgconfig" pkg-config --libs vips)'
|
||||||
],
|
],
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
|
'/usr/local/include/glib-2.0',
|
||||||
|
'/usr/local/lib/glib-2.0/include',
|
||||||
'/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'
|
||||||
],
|
],
|
||||||
'cflags': ['-fexceptions', '-O3'],
|
'cflags': ['-fexceptions', '-pedantic', '-Wall', '-O3'],
|
||||||
'cflags_cc': ['-fexceptions', '-O3']
|
'cflags_cc': ['-fexceptions', '-pedantic', '-Wall', '-O3']
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"version": "0.1.1",
|
"version": "0.1.5",
|
||||||
"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": {
|
||||||
|
|||||||
96
src/sharp.cc
96
src/sharp.cc
@@ -98,46 +98,47 @@ void resize_async(uv_work_t *work) {
|
|||||||
int shrink_on_load = 1;
|
int shrink_on_load = 1;
|
||||||
if (inputImageType == JPEG) {
|
if (inputImageType == JPEG) {
|
||||||
if (shrink >= 8) {
|
if (shrink >= 8) {
|
||||||
factor = residual * shrink / 8;
|
factor = factor / 8;
|
||||||
shrink_on_load = 8;
|
shrink_on_load = 8;
|
||||||
shrink = floor(factor);
|
|
||||||
residual = shrink / factor;
|
|
||||||
} else if (shrink >= 4) {
|
} else if (shrink >= 4) {
|
||||||
factor = residual * shrink / 4;
|
factor = factor / 4;
|
||||||
shrink_on_load = 4;
|
shrink_on_load = 4;
|
||||||
shrink = floor(factor);
|
|
||||||
residual = shrink / factor;
|
|
||||||
} else if (shrink >= 2) {
|
} else if (shrink >= 2) {
|
||||||
factor = residual * shrink / 2;
|
factor = factor / 2;
|
||||||
shrink_on_load = 2;
|
shrink_on_load = 2;
|
||||||
shrink = floor(factor);
|
|
||||||
residual = shrink / factor;
|
|
||||||
}
|
|
||||||
if (shrink_on_load > 1) {
|
|
||||||
g_object_unref(in);
|
|
||||||
in = vips_image_new();
|
|
||||||
if (baton->buffer_in_len > 1) {
|
|
||||||
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "shrink", shrink_on_load, NULL)) {
|
|
||||||
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 *shrunk_on_load = vips_image_new();
|
||||||
|
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
|
||||||
|
if (baton->buffer_in_len > 1) {
|
||||||
|
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &shrunk_on_load, "shrink", shrink_on_load, NULL)) {
|
||||||
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (vips_jpegload((baton->file_in).c_str(), &shrunk_on_load, "shrink", shrink_on_load, NULL)) {
|
||||||
|
return resize_error(baton, in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vips_copy(in, &shrunk_on_load, NULL);
|
||||||
|
}
|
||||||
|
g_object_unref(in);
|
||||||
|
|
||||||
VipsImage *shrunk = vips_image_new();
|
VipsImage *shrunk = vips_image_new();
|
||||||
if (shrink > 1) {
|
if (shrink > 1) {
|
||||||
// Use vips_shrink with the integral reduction
|
// Use vips_shrink with the integral reduction
|
||||||
if (vips_shrink(in, &shrunk, shrink, shrink, NULL)) {
|
if (vips_shrink(shrunk_on_load, &shrunk, shrink, shrink, NULL)) {
|
||||||
return resize_error(baton, in);
|
return resize_error(baton, shrunk_on_load);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vips_copy(in, &shrunk, NULL);
|
vips_copy(shrunk_on_load, &shrunk, NULL);
|
||||||
}
|
}
|
||||||
g_object_unref(in);
|
g_object_unref(shrunk_on_load);
|
||||||
|
|
||||||
// Use vips_affine with the remaining float part using bilinear interpolation
|
// Use vips_affine with the remaining float part using bilinear interpolation
|
||||||
VipsImage *affined = vips_image_new();
|
VipsImage *affined = vips_image_new();
|
||||||
@@ -151,8 +152,8 @@ void resize_async(uv_work_t *work) {
|
|||||||
g_object_unref(shrunk);
|
g_object_unref(shrunk);
|
||||||
|
|
||||||
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 && affined->Xsize != baton->width && affined->Ysize != baton->height) {
|
if (baton->crop) {
|
||||||
// Crop
|
// Crop
|
||||||
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);
|
||||||
@@ -192,28 +193,28 @@ void resize_async(uv_work_t *work) {
|
|||||||
|
|
||||||
if (baton->file_out == "__jpeg") {
|
if (baton->file_out == "__jpeg") {
|
||||||
// Write JPEG to buffer
|
// 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)) {
|
if (vips_jpegsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
|
||||||
return resize_error(baton, canvased);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else if (baton->file_out == "__png") {
|
} else if (baton->file_out == "__png") {
|
||||||
// Write PNG to buffer
|
// Write PNG to buffer
|
||||||
if (vips_pngsave_buffer(canvased, &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", 6, "interlace", baton->progessive, NULL)) {
|
||||||
return resize_error(baton, canvased);
|
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(canvased, 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", 80, "optimize_coding", TRUE, "interlace", baton->progessive, NULL)) {
|
||||||
return resize_error(baton, canvased);
|
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(canvased, 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", 6, "interlace", baton->progessive, NULL)) {
|
||||||
return resize_error(baton, canvased);
|
return resize_error(baton, sharpened);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(baton->err).append("Unsupported output " + baton->file_out);
|
(baton->err).append("Unsupported output " + baton->file_out);
|
||||||
}
|
}
|
||||||
g_object_unref(canvased);
|
g_object_unref(sharpened);
|
||||||
vips_thread_shutdown();
|
vips_thread_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,10 +223,15 @@ void resize_async_after(uv_work_t *work, int status) {
|
|||||||
|
|
||||||
resize_baton *baton = static_cast<resize_baton*>(work->data);
|
resize_baton *baton = static_cast<resize_baton*>(work->data);
|
||||||
|
|
||||||
|
// Free temporary copy of input buffer
|
||||||
|
if (baton->buffer_in_len > 0) {
|
||||||
|
g_free(baton->buffer_in);
|
||||||
|
}
|
||||||
|
|
||||||
Handle<Value> argv[2] = { Null(), Null() };
|
Handle<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] = scope.Close(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 *slowBuffer = Buffer::New(baton->buffer_out_len);
|
Buffer *slowBuffer = Buffer::New(baton->buffer_out_len);
|
||||||
@@ -233,7 +239,7 @@ void resize_async_after(uv_work_t *work, int status) {
|
|||||||
Local<Object> globalObj = Context::GetCurrent()->Global();
|
Local<Object> globalObj = Context::GetCurrent()->Global();
|
||||||
Local<Function> bufferConstructor = Local<Function>::Cast(globalObj->Get(String::New("Buffer")));
|
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) };
|
Handle<Value> constructorArgs[3] = { slowBuffer->handle_, v8::Integer::New(baton->buffer_out_len), v8::Integer::New(0) };
|
||||||
argv[1] = bufferConstructor->NewInstance(3, constructorArgs);
|
argv[1] = scope.Close(bufferConstructor->NewInstance(3, constructorArgs));
|
||||||
g_free(baton->buffer_out);
|
g_free(baton->buffer_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,8 +256,12 @@ Handle<Value> resize(const Arguments& args) {
|
|||||||
baton->file_in = *String::Utf8Value(args[0]->ToString());
|
baton->file_in = *String::Utf8Value(args[0]->ToString());
|
||||||
if (args[1]->IsObject()) {
|
if (args[1]->IsObject()) {
|
||||||
Local<Object> buffer = args[1]->ToObject();
|
Local<Object> buffer = args[1]->ToObject();
|
||||||
baton->buffer_in = Buffer::Data(buffer);
|
// Take temporary copy of input buffer
|
||||||
baton->buffer_in_len = Buffer::Length(buffer);
|
if (Buffer::Length(buffer) > 0) {
|
||||||
|
baton->buffer_in_len = Buffer::Length(buffer);
|
||||||
|
baton->buffer_in = g_malloc(baton->buffer_in_len);
|
||||||
|
memcpy(baton->buffer_in, Buffer::Data(buffer), baton->buffer_in_len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
baton->file_out = *String::Utf8Value(args[2]->ToString());
|
baton->file_out = *String::Utf8Value(args[2]->ToString());
|
||||||
baton->width = args[3]->Int32Value();
|
baton->width = args[3]->Int32Value();
|
||||||
@@ -287,6 +297,6 @@ extern "C" void init(Handle<Object> target) {
|
|||||||
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)
|
||||||
|
|||||||
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() {});
|
||||||
Reference in New Issue
Block a user