From 377662fffcf5816f1cff7c17a2a4b1a64871f606 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Tue, 21 Jan 2014 22:48:33 +0000 Subject: [PATCH] Ensure gm perf tests actually resize. Prevent coredump when embeding pre-shrunk image within same dimensions. --- README.md | 50 +++++++++++++++++++++---------------------- src/sharp.cc | 59 +++++++++++++++++++++++++++++++-------------------- tests/perf.js | 10 ++++----- 3 files changed, 66 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index ac7bef8d..88f5e293 100755 --- a/README.md +++ b/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 diff --git a/src/sharp.cc b/src/sharp.cc index 9259a482..35ea65e0 100755 --- a/src/sharp.cc +++ b/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,17 +98,20 @@ void resize_async(uv_work_t *work) { int shrink_on_load = 1; if (inputImageType == JPEG) { if (shrink >= 8) { - residual = residual * shrink / 8; + factor = residual * shrink / 8; shrink_on_load = 8; - shrink = 1; + shrink = floor(factor); + residual = shrink / factor; } else if (shrink >= 4) { - residual = residual * shrink / 4; + factor = residual * shrink / 4; shrink_on_load = 4; - shrink = 1; + shrink = floor(factor); + residual = shrink / factor; } else if (shrink >= 2) { - residual = residual * shrink / 2; + factor = residual * shrink / 2; shrink_on_load = 2; - shrink = 1; + shrink = floor(factor); + residual = shrink / factor; } if (shrink_on_load > 1) { g_object_unref(in); @@ -138,26 +141,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 && affined->Xsize != baton->width && affined->Ysize != baton->height) { + // 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 +261,10 @@ Handle 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(); diff --git a/tests/perf.js b/tests/perf.js index c54115d6..4c5b8198 100755 --- a/tests/perf.js +++ b/tests/perf.js @@ -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 {