diff --git a/README.md b/README.md index 8e9c6b1d..4e649b30 100755 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ _adj_ -1. clearly defined; distinct: a sharp photographic image. -2. quick, brisk, or spirited. -3. shrewd or astute: a sharp bargainer. -4. (Informal.) very stylish: a sharp dresser; a sharp jacket. +1. clearly defined; distinct: a sharp photographic image. +2. quick, brisk, or spirited. +3. shrewd or astute: a sharp bargainer. +4. (Informal.) very stylish: a sharp dresser; a sharp jacket. The typical use case for this high speed Node.js module is to convert large JPEG, PNG, WebP and TIFF images to smaller images of varying dimensions. @@ -13,19 +13,20 @@ The performance of JPEG resizing is typically 15x-25x faster than ImageMagick an This module supports reading and writing images to and from both the filesystem and Buffer objects (TIFF is limited to filesystem only). Everything remains non-blocking thanks to _libuv_. -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. +Anyone who has used the Node.js bindings for [GraphicsMagick](https://github.com/aheckmann/gm) will find the API similarly expressive. + +This module is powered by 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 * Node.js v0.8+ * [libvips](https://github.com/jcupitt/libvips) v7.38.5+ -### Install libvips on Mac OS via homebrew +### Install libvips on Mac OS - brew tap homebrew/science - brew install vips + brew install homebrew/science/vips -### Install libvips on Ubuntu Linux +### Install libvips on Ubuntu/Debian Linux sudo apt-get install automake build-essential git gobject-introspection gtk-doc-tools libfftw3-dev libglib2.0-dev libjpeg-turbo8-dev libpng12-dev libwebp-dev libtiff5-dev liborc-0.4-dev libxml2-dev swig git clone https://github.com/jcupitt/libvips.git @@ -40,35 +41,14 @@ Under the hood you'll find the blazingly fast [libvips](https://github.com/jcupi npm install sharp -## Usage - - var sharp = require("sharp"); - -### resize(input, output, width, height, [options], callback) - -Scale and crop to `width` x `height` calling `callback` when complete. - -`input` can either be a filename String or a Buffer. - -`output` can either be a filename String or one of `sharp.buffer.jpeg`, `sharp.buffer.png` or `sharp.buffer.webp` to pass a Buffer containing JPEG, PNG or WebP image data to `callback`. - -`width` is the Number of pixels wide the resultant image should be. Use a value of -1 to auto-scale the width to match the height. - -`height` is the Number of pixels high the resultant image should be. Use a value of -1 to auto-scale the height to match the width. - -`options` is optional, and can contain one or more of: - -* `canvas` can be one of `sharp.canvas.crop`, `sharp.canvas.embedWhite` or `sharp.canvas.embedBlack`. Defaults to `sharp.canvas.crop`. -* `sharpen` when set to true will perform a mild sharpen of the resultant image. This typically reduces performance by 30%. -* `progressive` when set will use progressive (interlace) scan for the output. This typically reduces performance by 30%. -* `sequentialRead` is an advanced setting that, when set, switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems. - -`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant image data when a Buffer is requested. - -#### Examples +## Usage examples ```javascript -sharp.resize("input.jpg", "output.jpg", 300, 200, function(err) { + var sharp = require('sharp'); +``` + +```javascript +sharp('input.jpg').resize(300, 200).write('output.jpg', function(err) { if (err) { throw err; } @@ -78,7 +58,7 @@ sharp.resize("input.jpg", "output.jpg", 300, 200, function(err) { ``` ```javascript -sharp.resize("input.jpg", sharp.buffer.jpeg, -1, 200, {progressive: true}, function(err, buffer) { +sharp('input.jpg').resize(null, 200).progressive().toBuffer(function(err, buffer) { if (err) { throw err; } @@ -87,35 +67,105 @@ sharp.resize("input.jpg", sharp.buffer.jpeg, -1, 200, {progressive: true}, funct ``` ```javascript -sharp.resize("input.webp", sharp.buffer.png, 300, -1, {sharpen: true}, function(err, buffer) { +sharp('input.png').resize(300).sharpen().webp(function(err, buffer) { if (err) { throw err; } - // buffer contains sharpened PNG image data (converted from JPEG), 300 pixels wide + // buffer contains sharpened WebP image data (converted from PNG), 300 pixels wide }); ``` ```javascript -sharp.resize(buffer, "output.tiff", 200, 300, {canvas: sharp.canvas.embedWhite}, function(err) { +sharp(buffer).resize(200, 300).embedWhite().write('output.tiff', function(err) { if (err) { throw err; } - // output.tiff is a 200 pixels wide and 300 pixels high image containing a scaled version - // of the image data contained in buffer embedded on a white canvas + // output.tiff is a 200 pixels wide and 300 pixels high image containing a scaled + // version, embedded on a white canvas, of the image data in buffer }); ``` ```javascript -sharp.resize("input.jpg", sharp.buffer.webp, 200, 300, {canvas: sharp.canvas.embedBlack}, function(err, buffer) { +sharp('input.jpg').resize(200, 300).embedBlack().webp(function(err, buffer) { if (err) { throw err; } // buffer contains WebP image data of a 200 pixels wide and 300 pixels high image - // containing a scaled version of input.png embedded on a black canvas + // containing a scaled version, embedded on a black canvas, of input.png }); ``` -### cache([limit]) +## API + +### sharp(input) + +Constructor to which further methods are chained. + +`input` can either be a filename String or a Buffer. + +### resize(width, [height]) + +Scale to `width` x `height`. By default, the resized image is cropped to the exact size specified. + +`width` is the Number of pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height. + +`height` is the Number of pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width. + +### crop() + +Crop the resized image to the exact size specified, the default behaviour. + +### embedWhite() + +Embed the resized image on a white background of the exact size specified. + +### embedBlack() + +Embed the resized image on a black background of the exact size specified. + +### sharpen() + +Perform a mild sharpen of the resultant image. This typically reduces performance by 30%. + +### progressive() + +Use progressive (interlace) scan for the output. This typically reduces performance by 30%. + +### sequentialRead() + +An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems. + +### write(filename, callback) + +`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` is called with a single argument `(err)` containing an error message, if any. + +### jpeg(callback) + +Write JPEG image data to a Buffer. + +`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant JPEG image data. + +### png(callback) + +Write PNG image data to a Buffer. + +`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant PNG image data. + +### webp(callback) + +Write WebP image data to a Buffer. + +`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant WebP image data. + +### toBuffer(callback) + +Write image data to a Buffer, the format of which will match the input image. + +`callback` gets two arguments `(err, buffer)` where `err` is an error message, if any, and `buffer` is the resultant image data. + +### sharp.cache([limit]) If `limit` is provided, set the (soft) limit of _libvips_ working/cache memory to this value in MB. The default value is 100. diff --git a/index.js b/index.js index 55ed7f48..00e8067d 100755 --- a/index.js +++ b/index.js @@ -1,73 +1,128 @@ -var sharp = require("./build/Release/sharp"); +/*jslint node: true */ +'use strict'; -module.exports.buffer = { - jpeg: "__jpeg", - png: "__png", - webp: "__webp" -}; +var sharp = require('./build/Release/sharp'); -module.exports.canvas = { - crop: "c", - embedWhite: "w", - embedBlack: "b" -}; - -module.exports.resize = function(input, output, width, height, options, callback) { - "use strict"; - if (typeof options === 'function') { - callback = options; - options = {}; - } else { - options = options || {}; +var Sharp = function(input) { + if (!(this instanceof Sharp)) { + return new Sharp(input); } + this.options = { + width: -1, + height: -1, + canvas: 'c', + sharpen: false, + progressive: false, + sequentialRead: false, + output: '__jpeg' + }; if (typeof input === 'string') { - options.inFile = input; + this.options.inFile = input; } else if (typeof input ==='object' && input instanceof Buffer) { - options.inBuffer = input; + this.options.inBuffer = input; } else { - callback("Unsupported input " + typeof input); - return; + throw 'Unsupported input ' + typeof input; } + return this; +}; +module.exports = Sharp; + +Sharp.prototype.crop = function() { + this.options.canvas = 'c'; + return this; +}; + +Sharp.prototype.embedWhite = function() { + this.options.canvas = 'w'; + return this; +}; + +Sharp.prototype.embedBlack = function() { + this.options.canvas = 'b'; + return this; +}; + +Sharp.prototype.sharpen = function(sharpen) { + this.options.sharpen = (typeof sharpen === 'boolean') ? sharpen : true; + return this; +}; + +Sharp.prototype.progressive = function(progressive) { + this.options.progressive = (typeof progressive === 'boolean') ? progressive : true; + return this; +}; + +Sharp.prototype.sequentialRead = function(sequentialRead) { + this.options.sequentialRead = (typeof sequentialRead === 'boolean') ? sequentialRead : true; + return this; +}; + +Sharp.prototype.resize = function(width, height) { + if (!width) { + this.options.width = -1; + } else { + if (!Number.isNaN(width)) { + this.options.width = width; + } else { + throw 'Invalid width ' + width; + } + } + if (!height) { + this.options.height = -1; + } else { + if (!Number.isNaN(height)) { + this.options.height = height; + } else { + throw 'Invalid height ' + height; + } + } + return this; +}; + +Sharp.prototype.write = function(output, callback) { if (!output || output.length === 0) { - callback("Invalid output"); - return; + throw 'Invalid output'; + } else { + this._sharp(output, callback); } - var outWidth = Number(width); - if (Number.isNaN(outWidth)) { - callback("Invalid width " + width); - return; - } - var outHeight = Number(height); - if (Number.isNaN(outHeight)) { - callback("Invalid height " + height); - return; - } - var canvas = options.canvas || "c"; - if (canvas.length !== 1 || "cwb".indexOf(canvas) === -1) { - callback("Invalid canvas " + canvas); - return; - } - var sharpen = !!options.sharpen; - var progessive = !!options.progessive; - var sequentialRead = !!options.sequentialRead; - sharp.resize(options.inFile, options.inBuffer, output, outWidth, outHeight, canvas, sharpen, progessive, sequentialRead, callback); + return this; +}; + +Sharp.prototype.toBuffer = function(callback) { + return this._sharp('__input', callback); +}; + +Sharp.prototype.jpeg = function(callback) { + return this._sharp('__jpeg', callback); +}; + +Sharp.prototype.png = function(callback) { + return this._sharp('__png', callback); +}; + +Sharp.prototype.webp = function(callback) { + return this._sharp('__webp', callback); +}; + +Sharp.prototype._sharp = function(output, callback) { + sharp.resize( + this.options.inFile, + this.options.inBuffer, + output, + this.options.width, + this.options.height, + this.options.canvas, + this.options.sharpen, + this.options.progressive, + this.options.sequentialRead, + callback + ); + return this; }; module.exports.cache = function(limit) { - "use strict"; if (Number.isNaN(limit)) { limit = null; } return sharp.cache(limit); }; - -/* Deprecated v0.0.x methods */ -module.exports.crop = function(input, output, width, height, sharpen, callback) { - sharp.resize(input, output, width, height, {canvas: "c", sharpen: true}, callback); -}; -module.exports.embedWhite = function(input, output, width, height, callback) { - sharp.resize(input, output, width, height, {canvas: "w", sharpen: true}, callback); -}; -module.exports.embedBlack = function(input, output, width, height, callback) { - sharp.resize(input, output, width, height, {canvas: "b", sharpen: true}, callback); -}; diff --git a/package.json b/package.json index 67b5beab..966a0f7e 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sharp", - "version": "0.1.8", + "version": "0.2.0", "author": "Lovell Fuller", "description": "High performance module to resize JPEG, PNG, WebP and TIFF images using the libvips image processing library", "scripts": { diff --git a/src/sharp.cc b/src/sharp.cc index 74b8f52e..07db1e8c 100755 --- a/src/sharp.cc +++ b/src/sharp.cc @@ -70,6 +70,7 @@ void resize_error(resize_baton *baton, VipsImage *unref) { void resize_async(uv_work_t *work) { resize_baton* baton = static_cast(work->data); + // Input ImageType inputImageType = JPEG; VipsImage *in = vips_image_new(); if (baton->buffer_in_len > 1) { @@ -117,30 +118,32 @@ void resize_async(uv_work_t *work) { return; } - double xfactor = static_cast(in->Xsize) / std::max(baton->width, 1); - double yfactor = static_cast(in->Ysize) / std::max(baton->height, 1); + // Scaling calculations double factor; if (baton->width > 0 && baton->height > 0) { // Fixed width and height + double xfactor = (double)(in->Xsize) / (double)(baton->width); + double yfactor = (double)(in->Ysize) / (double)(baton->height); factor = baton->crop ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor); } else if (baton->width > 0) { // Fixed width, auto height - factor = xfactor; - baton->height = floor(in->Ysize * factor); + factor = (double)(in->Xsize) / (double)(baton->width); + baton->height = floor((double)(in->Ysize) / factor); } else if (baton->height > 0) { // Fixed height, auto width - factor = yfactor; - baton->width = floor(in->Xsize * factor); + factor = (double)(in->Ysize) / (double)(baton->height); + baton->width = floor((double)(in->Xsize) / factor); } else { // Identity transform factor = 1; baton->width = in->Xsize; baton->height = in->Ysize; } - - factor = std::max(factor, 1.0); int shrink = floor(factor); - double residual = shrink / factor; + if (shrink < 1) { + shrink = 1; + } + double residual = shrink / (double)factor; // Try to use libjpeg shrink-on-load int shrink_on_load = 1; @@ -190,7 +193,7 @@ void resize_async(uv_work_t *work) { // Use vips_affine with the remaining float part using bilinear interpolation VipsImage *affined = vips_image_new(); - if (residual > 0) { + if (residual != 0) { if (vips_affine(shrunk, &affined, residual, 0, 0, residual, "interpolate", vips_interpolate_bilinear_static(), NULL)) { return resize_error(baton, shrunk); } @@ -199,6 +202,7 @@ void resize_async(uv_work_t *work) { } g_object_unref(shrunk); + // Crop/embed VipsImage *canvased = vips_image_new(); if (affined->Xsize != baton->width || affined->Ysize != baton->height) { if (baton->crop) { @@ -239,17 +243,18 @@ void resize_async(uv_work_t *work) { } g_object_unref(canvased); - if (baton->file_out == "__jpeg") { + // Output + if (baton->file_out == "__jpeg" || (baton->file_out == "__input" && inputImageType == JPEG)) { // Write JPEG to buffer 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, sharpened); } - } else if (baton->file_out == "__png") { + } else if (baton->file_out == "__png" || (baton->file_out == "__input" && inputImageType == PNG)) { // Write PNG to buffer if (vips_pngsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "compression", 6, "interlace", baton->progessive, NULL)) { return resize_error(baton, sharpened); } - } else if (baton->file_out == "__webp") { + } else if (baton->file_out == "__webp" || (baton->file_out == "__input" && inputImageType == WEBP)) { // Write WEBP to buffer if (vips_webpsave_buffer(sharpened, &baton->buffer_out, &baton->buffer_out_len, "strip", TRUE, "Q", 80, NULL)) { return resize_error(baton, sharpened); diff --git a/tests/parallel.js b/tests/parallel.js index 2f3246bb..06843118 100755 --- a/tests/parallel.js +++ b/tests/parallel.js @@ -11,7 +11,7 @@ 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) { + sharp(inputJpg).resize(width, height).toBuffer(function(err, buffer) { buffer = null; callback(err, new Date().getTime() - start); }); diff --git a/tests/perf.js b/tests/perf.js index 5117175b..a3a2e700 100755 --- a/tests/perf.js +++ b/tests/perf.js @@ -83,7 +83,7 @@ async.series({ }).add("sharp-buffer-file", { defer: true, fn: function(deferred) { - sharp.resize(inputJpgBuffer, outputJpg, width, height, function(err) { + sharp(inputJpgBuffer).resize(width, height).write(outputJpg, function(err) { if (err) { throw err; } else { @@ -94,7 +94,7 @@ async.series({ }).add("sharp-buffer-buffer", { defer: true, fn: function(deferred) { - sharp.resize(inputJpgBuffer, sharp.buffer.jpeg, width, height, function(err, buffer) { + sharp(inputJpgBuffer).resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -106,7 +106,7 @@ async.series({ }).add("sharp-file-file", { defer: true, fn: function(deferred) { - sharp.resize(inputJpg, outputJpg, width, height, function(err) { + sharp(inputJpg).resize(width, height).write(outputJpg, function(err) { if (err) { throw err; } else { @@ -117,7 +117,7 @@ async.series({ }).add("sharp-file-buffer", { defer: true, fn: function(deferred) { - sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, function(err, buffer) { + sharp(inputJpg).resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -129,7 +129,7 @@ async.series({ }).add("sharp-file-buffer-sharpen", { defer: true, fn: function(deferred) { - sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, {sharpen: true}, function(err, buffer) { + sharp(inputJpg).resize(width, height).sharpen().toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -141,7 +141,7 @@ async.series({ }).add("sharp-file-buffer-progressive", { defer: true, fn: function(deferred) { - sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, {progressive: true}, function(err, buffer) { + sharp(inputJpg).resize(width, height).progressive().toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -153,7 +153,7 @@ async.series({ }).add("sharp-file-buffer-sequentialRead", { defer: true, fn: function(deferred) { - sharp.resize(inputJpg, sharp.buffer.jpeg, width, height, {sequentialRead: true}, function(err, buffer) { + sharp(inputJpg).resize(width, height).sequentialRead().toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -212,7 +212,7 @@ async.series({ }).add("sharp-buffer-file", { defer: true, fn: function(deferred) { - sharp.resize(inputPngBuffer, outputPng, width, height, function(err) { + sharp(inputPngBuffer).resize(width, height).write(outputPng, function(err) { if (err) { throw err; } else { @@ -223,7 +223,7 @@ async.series({ }).add("sharp-buffer-buffer", { defer: true, fn: function(deferred) { - sharp.resize(inputPngBuffer, sharp.buffer.png, width, height, function(err, buffer) { + sharp(inputPngBuffer).resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -235,7 +235,7 @@ async.series({ }).add("sharp-file-file", { defer: true, fn: function(deferred) { - sharp.resize(inputPng, outputPng, width, height, function(err) { + sharp(inputPng).resize(width, height).write(outputPng, function(err) { if (err) { throw err; } else { @@ -246,7 +246,7 @@ async.series({ }).add("sharp-file-buffer", { defer: true, fn: function(deferred) { - sharp.resize(inputPng, sharp.buffer.png, width, height, function(err, buffer) { + sharp(inputPng).resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -258,7 +258,7 @@ async.series({ }).add("sharp-file-buffer-sharpen", { defer: true, fn: function(deferred) { - sharp.resize(inputPng, sharp.buffer.png, width, height, {sharpen: true}, function(err, buffer) { + sharp(inputPng).resize(width, height).sharpen().toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -270,7 +270,7 @@ async.series({ }).add("sharp-file-buffer-progressive", { defer: true, fn: function(deferred) { - sharp.resize(inputPng, sharp.buffer.png, width, height, {progressive: true}, function(err, buffer) { + sharp(inputPng).resize(width, height).progressive().toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -282,7 +282,7 @@ async.series({ }).add("sharp-file-buffer-sequentialRead", { defer: true, fn: function(deferred) { - sharp.resize(inputPng, sharp.buffer.png, width, height, {sequentialRead: true}, function(err, buffer) { + sharp(inputPng).sequentialRead().resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -302,7 +302,7 @@ async.series({ (new Benchmark.Suite("webp")).add("sharp-buffer-file", { defer: true, fn: function(deferred) { - sharp.resize(inputWebpBuffer, outputWebp, width, height, function(err) { + sharp(inputWebpBuffer).resize(width, height).write(outputWebp, function(err) { if (err) { throw err; } else { @@ -313,7 +313,7 @@ async.series({ }).add("sharp-buffer-buffer", { defer: true, fn: function(deferred) { - sharp.resize(inputWebpBuffer, sharp.buffer.webp, width, height, function(err, buffer) { + sharp(inputWebpBuffer).resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -325,7 +325,7 @@ async.series({ }).add("sharp-file-file", { defer: true, fn: function(deferred) { - sharp.resize(inputWebp, outputWebp, width, height, function(err) { + sharp(inputWebp).resize(width, height).write(outputWebp, function(err) { if (err) { throw err; } else { @@ -336,7 +336,7 @@ async.series({ }).add("sharp-file-buffer", { defer: true, fn: function(deferred) { - sharp.resize(inputWebp, sharp.buffer.webp, width, height, function(err, buffer) { + sharp(inputWebp).resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -348,7 +348,7 @@ async.series({ }).add("sharp-file-buffer-sharpen", { defer: true, fn: function(deferred) { - sharp.resize(inputWebp, sharp.buffer.webp, width, height, {sharpen: true}, function(err, buffer) { + sharp(inputWebp).resize(width, height).sharpen().toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -360,7 +360,7 @@ async.series({ }).add("sharp-file-buffer-sequentialRead", { defer: true, fn: function(deferred) { - sharp.resize(inputWebp, sharp.buffer.webp, width, height, {sequentialRead: true}, function(err, buffer) { + sharp(inputWebp).sequentialRead().resize(width, height).toBuffer(function(err, buffer) { if (err) { throw err; } else { @@ -379,7 +379,7 @@ async.series({ (new Benchmark.Suite("tiff")).add("sharp-file-file", { defer: true, fn: function(deferred) { - sharp.resize(inputTiff, outputTiff, width, height, function(err) { + sharp(inputTiff).resize(width, height).write(outputTiff, function(err) { if (err) { throw err; } else { @@ -390,7 +390,7 @@ async.series({ }).add("sharp-file-file-sharpen", { defer: true, fn: function(deferred) { - sharp.resize(inputTiff, outputTiff, width, height, {sharpen: true}, function(err) { + sharp(inputTiff).resize(width, height).sharpen().write(outputTiff, function(err) { if (err) { throw err; } else { @@ -401,7 +401,7 @@ async.series({ }).add("sharp-file-file-sequentialRead", { defer: true, fn: function(deferred) { - sharp.resize(inputTiff, outputTiff, width, height, {sequentialRead: true}, function(err) { + sharp(inputTiff).sequentialRead().resize(width, height).write(outputTiff, function(err) { if (err) { throw err; } else { diff --git a/tests/random.js b/tests/random.js index f3e5b9ac..3e6bfda8 100755 --- a/tests/random.js +++ b/tests/random.js @@ -57,7 +57,7 @@ new Benchmark.Suite("random").add("imagemagick", { }).add("sharp", { defer: true, fn: function(deferred) { - sharp.resize(inputJpg, sharp.buffer.jpeg, randomDimension(), randomDimension(), function(err, buffer) { + sharp(inputJpg).resize(randomDimension(), randomDimension()).toBuffer(function(err, buffer) { if (err) { throw err; } else { diff --git a/tests/unit.js b/tests/unit.js index aef95a43..ea611ad9 100755 --- a/tests/unit.js +++ b/tests/unit.js @@ -7,8 +7,9 @@ var inputJpg = __dirname + "/2569067123_aca715a2ee_o.jpg"; // http://www.flickr. var outputJpg = __dirname + "/output.jpg"; async.series([ + // Resize with exact crop function(done) { - sharp.resize(inputJpg, outputJpg, 320, 240, function(err) { + sharp(inputJpg).resize(320, 240).write(outputJpg, function(err) { if (err) throw err; imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; @@ -18,30 +19,33 @@ async.series([ }); }); }, + // Resize to fixed width function(done) { - sharp.resize(inputJpg, outputJpg, 320, -1, function(err) { + sharp(inputJpg).resize(320).write(outputJpg, function(err) { if (err) throw err; imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; assert.strictEqual(320, features.width); - assert.strictEqual(262, features.height); + assert.strictEqual(261, features.height); done(); }); }); }, + // Resize to fixed height function(done) { - sharp.resize(inputJpg, outputJpg, -1, 320, function(err) { + sharp(inputJpg).resize(null, 320).write(outputJpg, function(err) { if (err) throw err; imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; - assert.strictEqual(392, features.width); + assert.strictEqual(391, features.width); assert.strictEqual(320, features.height); done(); }); }); }, + // Identity transform function(done) { - sharp.resize(inputJpg, outputJpg, -1, -1, function(err) { + sharp(inputJpg).write(outputJpg, function(err) { if (err) throw err; imagemagick.identify(outputJpg, function(err, features) { if (err) throw err; @@ -50,5 +54,17 @@ async.series([ done(); }); }); + }, + // Upscale + function(done) { + sharp(inputJpg).resize(3000).write(outputJpg, function(err) { + if (err) throw err; + imagemagick.identify(outputJpg, function(err, features) { + if (err) throw err; + assert.strictEqual(3000, features.width); + assert.strictEqual(2449, features.height); + done(); + }); + }); } ]);