Compare commits

...

3 Commits

3 changed files with 55 additions and 45 deletions

View File

@@ -13,7 +13,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', '-O3'], 'cflags': ['-fexceptions', '-pedantic', '-Wall', '-O3'],
'cflags_cc': ['-fexceptions', '-O3'] 'cflags_cc': ['-fexceptions', '-pedantic', '-Wall', '-O3']
}] }]
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "sharp", "name": "sharp",
"version": "0.1.2", "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": {

View File

@@ -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;
}
}
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); shrink = floor(factor);
residual = shrink / factor; residual = shrink / factor;
} // Reload input using shrink-on-load
if (shrink_on_load > 1) {
g_object_unref(in);
in = vips_image_new();
if (baton->buffer_in_len > 1) { if (baton->buffer_in_len > 1) {
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "shrink", shrink_on_load, NULL)) { if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &shrunk_on_load, "shrink", shrink_on_load, NULL)) {
return resize_error(baton, in); return resize_error(baton, in);
} }
} else { } else {
if (vips_jpegload((baton->file_in).c_str(), &in, "shrink", shrink_on_load, NULL)) { if (vips_jpegload((baton->file_in).c_str(), &shrunk_on_load, "shrink", shrink_on_load, NULL)) {
return resize_error(baton, in); 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();
@@ -152,7 +153,7 @@ void resize_async(uv_work_t *work) {
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
if (Buffer::Length(buffer) > 0) {
baton->buffer_in_len = Buffer::Length(buffer); 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)