diff --git a/README.md b/README.md index ce45fd8d..2e046b49 100755 --- a/README.md +++ b/README.md @@ -79,51 +79,62 @@ var sharp = require('sharp'); ``` ```javascript -sharp('input.jpg').resize(300, 200).toFile('output.jpg', function(err) { +sharp('input.jpg').resize(300, 200).toFile('output.jpg', function(err, info) { if (err) { throw err; } // output.jpg is a 300 pixels wide and 200 pixels high image // containing a scaled and cropped version of input.jpg + + // info.width and info.height contain the final pixel dimensions of the resized image + // in this case they are the same as the input }); ``` ```javascript -sharp('input.jpg').rotate().resize(null, 200).progressive().toBuffer(function(err, outputBuffer) { +sharp('input.jpg').rotate().resize(null, 200).progressive().toBuffer(function(err, outputBuffer, info) { if (err) { throw err; } // outputBuffer contains 200px high progressive JPEG image data, auto-rotated using EXIF Orientation tag + // info.width and info.height contain the final pixel dimensions of the resized image }); ``` ```javascript -sharp('input.png').rotate(180).resize(300).sharpen().quality(90).webp().then(function(outputBuffer) { +sharp('input.png').rotate(180).resize(300).sharpen().quality(90).webp().then(function(outputBuffer, info) { // outputBuffer contains 300px wide, upside down, sharpened, 90% quality WebP image data + // info.width and info.height contain the final pixel dimensions of the resized image }); ``` ```javascript -sharp(inputBuffer).resize(200, 300).bicubicInterpolation().embedWhite().toFile('output.tiff').then(function() { +sharp(inputBuffer).resize(200, 300).bicubicInterpolation().embedWhite().toFile('output.tiff').then(function(info) { // output.tiff is a 200 pixels wide and 300 pixels high image containing a bicubic scaled // version, embedded on a white canvas, of the image data in buffer + + // info.width and info.height contain the final pixel dimensions of the resized image }); ``` ```javascript -sharp('input.gif').resize(200, 300).embedBlack().webp(function(err, outputBuffer) { +sharp('input.gif').resize(200, 300).embedBlack().webp(function(err, outputBuffer, info) { if (err) { throw err; } // outputBuffer contains WebP image data of a 200 pixels wide and 300 pixels high // containing a scaled version, embedded on a black canvas, of input.gif + + // info.width and info.height contain the final pixel dimensions of the resized image }); ``` ```javascript -sharp(inputBuffer).resize(200, 200).max().jpeg().then(function(outputBuffer) { +sharp(inputBuffer).resize(200, 200).max().jpeg().then(function(outputBuffer, info) { // outputBuffer contains JPEG image data no wider than 200 pixels and no higher // than 200 pixels regardless of the inputBuffer image dimensions + + // info.width and info.height contain the final pixel dimensions of the resized image }); ``` @@ -216,7 +227,7 @@ An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQU `filename` is a String containing the filename to write the image data to. The format is inferred from the extension, with JPEG, PNG, WebP and TIFF supported. -`callback`, if present, is called with a single argument `(err)` containing an error message, if any. +`callback`, if present, is called with two arguments `(err, info)` where `err` contains an error message, if any, and `info` contains the final resized image dimensions in its `width` and `height` properties. A Promises/A+ promise is returned when `callback` is not provided. @@ -224,7 +235,7 @@ A Promises/A+ promise is returned when `callback` is not provided. Write image data to a Buffer, the format of which will match the input image. JPEG, PNG and WebP are supported. -`callback`, if present, gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant image data. +`callback`, if present, gets three arguments `(err, buffer, info)` where `err` is an error message, if any, `buffer` is the resultant image data, and `info` contains the final resized image dimensions in its `width` and `height` properties. A Promises/A+ promise is returned when `callback` is not provided. @@ -232,7 +243,7 @@ A Promises/A+ promise is returned when `callback` is not provided. Write JPEG image data to a Buffer. -`callback`, if present, gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant JPEG image data. +`callback`, if present, gets three arguments `(err, buffer, info)` where `err` is an error message, if any, `buffer` is resultant JPEG image data, and `info` contains the final resized image dimensions in its `width` and `height` properties. A Promises/A+ promise is returned when `callback` is not provided. @@ -240,7 +251,7 @@ A Promises/A+ promise is returned when `callback` is not provided. Write PNG image data to a Buffer. -`callback`, if present, gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant PNG image data. +`callback`, if present, gets three arguments `(err, buffer, info)` where `err` is an error message, if any, `buffer` is resultant PNG image data, and `info` contains the final resized image dimensions in its `width` and `height` properties. A Promises/A+ promise is returned when `callback` is not provided. @@ -248,7 +259,7 @@ A Promises/A+ promise is returned when `callback` is not provided. Write WebP image data to a Buffer. -`callback`, if present, gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant WebP image data. +`callback`, if present, gets three arguments `(err, buffer, info)` where `err` is an error message, if any, `buffer` is resultant WebP image data, and `info` contains the final resized image dimensions in its `width` and `height` properties. A Promises/A+ promise is returned when `callback` is not provided. diff --git a/index.js b/index.js index f17ac939..e54ec7d6 100755 --- a/index.js +++ b/index.js @@ -222,11 +222,11 @@ Sharp.prototype._sharp = function(output, callback) { // I like promises var options = this.options; return new Promise(function(resolve, reject) { - sharp.resize(options, output, function(err, data) { + sharp.resize(options, output, function(err, data, info) { if (err) { reject(err); } else { - resolve(data); + resolve(data, info); } }); }); diff --git a/package.json b/package.json index a5fe6fbc..a97d9324 100755 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "author": "Lovell Fuller ", "contributors": [ "Pierre Inglebert ", - "Jonathan Ong " + "Jonathan Ong ", + "Chanon Sajjamanochai " ], "description": "High performance Node.js module to resize JPEG, PNG and WebP images using the libvips library", "scripts": { diff --git a/src/sharp.cc b/src/sharp.cc index 25a96602..5d27950a 100755 --- a/src/sharp.cc +++ b/src/sharp.cc @@ -431,17 +431,28 @@ class ResizeWorker : public NanAsyncWorker { void HandleOKCallback () { NanScope(); - Handle argv[2] = { NanNull(), NanNull() }; + Handle argv[3] = { NanNull(), NanNull(), NanNull() }; if (!baton->err.empty()) { // Error argv[0] = NanNew(baton->err.data(), baton->err.size()); - } else if (baton->buffer_out_len > 0) { - // Buffer - argv[1] = NanNewBufferHandle((char *)baton->buffer_out, baton->buffer_out_len); - g_free(baton->buffer_out); + } else { + // Info Object + Local info = NanNew(); + info->Set(NanNew("width"), NanNew(baton->width)); + info->Set(NanNew("height"), NanNew(baton->height)); + + if (baton->buffer_out_len > 0) { + // Buffer + argv[1] = NanNewBufferHandle((char *)baton->buffer_out, baton->buffer_out_len); + g_free(baton->buffer_out); + argv[2] = info; + } else { + // File + argv[1] = info; + } } delete baton; - callback->Call(2, argv); + callback->Call(3, argv); // Decrement queue length g_atomic_int_dec_and_test(&queue_length); } diff --git a/tests/unit.js b/tests/unit.js index 7ff9e414..5c125366 100755 --- a/tests/unit.js +++ b/tests/unit.js @@ -21,8 +21,10 @@ var inputJpgWithExif = path.join(fixturesPath, "Landscape_8.jpg"); // https://gi async.series([ // Resize with exact crop function(done) { - sharp(inputJpg).resize(320, 240).toFile(outputJpg, function(err) { + sharp(inputJpg).resize(320, 240).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -33,8 +35,10 @@ async.series([ }, // Resize to fixed width function(done) { - sharp(inputJpg).resize(320).toFile(outputJpg, function(err) { + sharp(inputJpg).resize(320).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(261, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -45,8 +49,10 @@ async.series([ }, // Resize to fixed height function(done) { - sharp(inputJpg).resize(null, 320).toFile(outputJpg, function(err) { + sharp(inputJpg).resize(null, 320).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(391, info.width); + assert.strictEqual(320, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(391, features.width); @@ -69,8 +75,10 @@ async.series([ }, // Upscale function(done) { - sharp(inputJpg).resize(3000).toFile(outputJpg, function(err) { + sharp(inputJpg).resize(3000).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(3000, info.width); + assert.strictEqual(2449, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(3000, features.width); @@ -118,8 +126,10 @@ async.series([ }, // Resize to max width or height considering ratio (landscape) function(done) { - sharp(inputJpg).resize(320, 320).max().toFile(outputJpg, function(err) { + sharp(inputJpg).resize(320, 320).max().toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(261, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -130,8 +140,10 @@ async.series([ }, // Resize to max width or height considering ratio (portrait) function(done) { - sharp(inputTiff).resize(320, 320).max().toFile(outputJpg, function(err) { + sharp(inputTiff).resize(320, 320).max().toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(243, info.width); + assert.strictEqual(320, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(243, features.width); @@ -142,8 +154,10 @@ async.series([ }, // Attempt to resize to max but only provide one dimension, so should default to crop function(done) { - sharp(inputJpg).resize(320).max().toFile(outputJpg, function(err) { + sharp(inputJpg).resize(320).max().toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(261, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -161,8 +175,10 @@ async.series([ }, // Rotate by 90 degrees, respecting output input size function(done) { - sharp(inputJpg).rotate(90).resize(320, 240).toFile(outputJpg, function(err) { + sharp(inputJpg).rotate(90).resize(320, 240).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -173,8 +189,10 @@ async.series([ }, // Input image has Orientation EXIF tag but do not rotate output function(done) { - sharp(inputJpgWithExif).resize(320).toFile(outputJpg, function(err) { + sharp(inputJpgWithExif).resize(320).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(426, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -185,8 +203,10 @@ async.series([ }, // Input image has Orientation EXIF tag value of 8 (270 degrees), auto-rotate function(done) { - sharp(inputJpgWithExif).rotate().resize(320).toFile(outputJpg, function(err) { + sharp(inputJpgWithExif).rotate().resize(320).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -197,8 +217,10 @@ async.series([ }, // Attempt to auto-rotate using image that has no EXIF function(done) { - sharp(inputJpg).rotate().resize(320).toFile(outputJpg, function(err) { + sharp(inputJpg).rotate().resize(320).toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(261, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); @@ -219,8 +241,10 @@ async.series([ }, // Do not enlarge the output if the input width is already less than the output width function(done) { - sharp(inputJpg).resize(2800).withoutEnlargement().toFile(outputJpg, function(err) { + sharp(inputJpg).resize(2800).withoutEnlargement().toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(2725, info.width); + assert.strictEqual(2225, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(2725, features.width); @@ -231,8 +255,10 @@ async.series([ }, // Do not enlarge the output if the input height is already less than the output height function(done) { - sharp(inputJpg).resize(null, 2300).withoutEnlargement().toFile(outputJpg, function(err) { + sharp(inputJpg).resize(null, 2300).withoutEnlargement().toFile(outputJpg, function(err, info) { if (err) throw err; + assert.strictEqual(2725, info.width); + assert.strictEqual(2225, info.height); imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(2725, features.width); @@ -243,7 +269,9 @@ async.series([ }, // Promises/A+ function(done) { - sharp(inputJpg).resize(320, 240).toFile(outputJpg).then(function() { + sharp(inputJpg).resize(320, 240).toFile(outputJpg).then(function(info) { + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); imagemagick.identify(outputJpg, function(err, features) { assert.strictEqual(320, features.width); assert.strictEqual(240, features.height);