From bcd82f48938f65c75cb7c1f692de47224e3f635f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Fri, 27 Feb 2015 13:43:37 +0100 Subject: [PATCH] feature: min --- README.md | 6 ++++++ index.js | 13 +++++++++---- package.json | 3 ++- src/resize.cc | 46 +++++++++++++++++++++++++++++++-------------- test/unit/resize.js | 33 ++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 38c7e6bc..dc9fb8d3 100755 --- a/README.md +++ b/README.md @@ -332,6 +332,12 @@ Preserving aspect ratio, resize the image to the maximum `width` or `height` spe Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`. +#### min() + +Preserving aspect ratio, resize the image to the minimum `width` or `height` specified. + +Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`. + #### background(rgba) Set the background for the `embed` and `flatten` operations. diff --git a/index.js b/index.js index 9e0eca33..147a0835 100755 --- a/index.js +++ b/index.js @@ -41,7 +41,7 @@ var Sharp = function(input) { heightPost: -1, width: -1, height: -1, - canvas: 'c', + canvas: 'crop', gravity: 0, angle: 0, rotateBeforePreExtract: false, @@ -136,7 +136,7 @@ Sharp.prototype._write = function(chunk, encoding, callback) { module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'south': 3, 'west': 4}; Sharp.prototype.crop = function(gravity) { - this.options.canvas = 'c'; + this.options.canvas = 'crop'; if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 4) { this.options.gravity = gravity; } else { @@ -176,12 +176,17 @@ Sharp.prototype.background = function(rgba) { }; Sharp.prototype.embed = function() { - this.options.canvas = 'e'; + this.options.canvas = 'embed'; return this; }; Sharp.prototype.max = function() { - this.options.canvas = 'm'; + this.options.canvas = 'max'; + return this; +}; + +Sharp.prototype.min = function() { + this.options.canvas = 'min'; return this; }; diff --git a/package.json b/package.json index d7b56034..959553fb 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "Amit Pitaru ", "Brandon Aaron ", "Andreas Lind ", - "Maurus Cuelenaere " + "Maurus Cuelenaere ", + "Linus Unnebäck " ], "description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library", "scripts": { diff --git a/src/resize.cc b/src/resize.cc index 380a2227..d7410a63 100755 --- a/src/resize.cc +++ b/src/resize.cc @@ -37,8 +37,9 @@ using sharp::counterQueue; enum class Canvas { CROP, + EMBED, MAX, - EMBED + MIN }; enum class Angle { @@ -252,14 +253,29 @@ class ResizeWorker : public NanAsyncWorker { // Fixed width and height double xfactor = static_cast(inputWidth) / static_cast(baton->width); double yfactor = static_cast(inputHeight) / static_cast(baton->height); - factor = (baton->canvas == Canvas::CROP) ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor); - // if max is set, we need to compute the real size of the thumb image - if (baton->canvas == Canvas::MAX) { - if (xfactor > yfactor) { - baton->height = round(static_cast(inputHeight) / xfactor); - } else { - baton->width = round(static_cast(inputWidth) / yfactor); - } + switch (baton->canvas) { + case Canvas::CROP: + factor = std::min(xfactor, yfactor); + break; + case Canvas::EMBED: + factor = std::max(xfactor, yfactor); + break; + case Canvas::MAX: + factor = std::max(xfactor, yfactor); + if (xfactor > yfactor) { + baton->height = round(static_cast(inputHeight) / xfactor); + } else { + baton->width = round(static_cast(inputWidth) / yfactor); + } + break; + case Canvas::MIN: + factor = std::min(xfactor, yfactor); + if (xfactor < yfactor) { + baton->height = round(static_cast(inputHeight) / xfactor); + } else { + baton->width = round(static_cast(inputWidth) / yfactor); + } + break; } } else if (baton->width > 0) { // Fixed width, auto height @@ -549,7 +565,7 @@ class ResizeWorker : public NanAsyncWorker { vips_object_local(hook, embedded); image = embedded; } else { - // Crop/max + // Crop/max/min int left; int top; std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity); @@ -963,12 +979,14 @@ NAN_METHOD(resize) { baton->height = options->Get(NanNew("height"))->Int32Value(); // Canvas option Local canvas = options->Get(NanNew("canvas"))->ToString(); - if (canvas->Equals(NanNew("c"))) { + if (canvas->Equals(NanNew("crop"))) { baton->canvas = Canvas::CROP; - } else if (canvas->Equals(NanNew("m"))) { - baton->canvas = Canvas::MAX; - } else if (canvas->Equals(NanNew("e"))) { + } else if (canvas->Equals(NanNew("embed"))) { baton->canvas = Canvas::EMBED; + } else if (canvas->Equals(NanNew("max"))) { + baton->canvas = Canvas::MAX; + } else if (canvas->Equals(NanNew("min"))) { + baton->canvas = Canvas::MIN; } // Background colour Local background = Local::Cast(options->Get(NanNew("background"))); diff --git a/test/unit/resize.js b/test/unit/resize.js index 405316b7..b87f8676 100755 --- a/test/unit/resize.js +++ b/test/unit/resize.js @@ -185,6 +185,39 @@ describe('Resize dimensions', function() { }); }); + it('Min width or height considering ratio (landscape)', function(done) { + sharp(fixtures.inputJpg).resize(320, 320).min().toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(392, info.width); + assert.strictEqual(320, info.height); + done(); + }); + }); + + it('Min width or height considering ratio (portrait)', function(done) { + sharp(fixtures.inputTiff).resize(320, 320).min().jpeg().toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(422, info.height); + done(); + }); + }); + + it('Provide only one dimension with min, should default to crop', function(done) { + sharp(fixtures.inputJpg).resize(320).min().toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(true, data.length > 0); + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(261, info.height); + done(); + }); + }); + it('Do not enlarge when input width is already less than output width', function(done) { sharp(fixtures.inputJpg) .resize(2800)