From e069b46c1d22b4271688288c2a298157dcfde48b Mon Sep 17 00:00:00 2001 From: Eyal Arubas Date: Thu, 18 Dec 2014 16:32:11 +0800 Subject: [PATCH] #88 - image.cover --- README.md | 21 +++++++++++ examples/cover.js | 16 +++++++++ lib/BatchPrototypeInit.js | 9 +++++ lib/ImagePrototypeInit.js | 16 +++++++++ lib/defs.js | 18 +++++++++- tests/02.operations/119.cover.js | 61 ++++++++++++++++++++++++++++++++ tests/03.safety/00.locks.js | 7 ++++ tests/03.safety/01.releases.js | 7 ++++ tests/utils.js | 8 ++++- 9 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 examples/cover.js create mode 100644 tests/02.operations/119.cover.js diff --git a/README.md b/README.md index 36e241e8..c27ecfc3 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ 0. [Resize](#resize) 0. [Scale](#scale) 0. [Rotate](#rotate) + 0. [Cover](#cover) 0. [Crop](#crop) 0. [Blur](#blur) 0. [Sharpen](#sharpen) @@ -306,6 +307,26 @@ lwip.create(500, 500, 'yellow', function(err, image){ - `"lanczos"` 0. `callback {Function(err, image)}` +#### Cover + +Cover a canvas with the image. The image will be resized to the smallest +possible size such that both its dimensions are bigger than the canvas's +dimensions. Margins of the image exceeding the canvas will be discarded. + +`image.cover(width, height, inter, callback)` + +0. `width {Integer}`: Canvas' width in pixels. +0. `height {Integer}`: Canvas' height in pixels. +0. `inter {String}`: **Optional** interpolation method. Defaults to `"lanczos"`. + Possible values: + - `"nearest-neighbor"` + - `"moving-average"` + - `"linear"` + - `"grid"` + - `"cubic"` + - `"lanczos"` +0. `callback {Function(err, image)}` + #### Rotate `image.rotate(degs, color, callback)` diff --git a/examples/cover.js b/examples/cover.js new file mode 100644 index 00000000..26044e4e --- /dev/null +++ b/examples/cover.js @@ -0,0 +1,16 @@ +/** + * Example for using LWIP to cover a canvas with an image. + */ + +var path = require('path'), + lwip = require('../'); + +lwip.open('lena.jpg', function(err, image) { + if (err) return console.log(err); + image.cover(400,800,function(err, image){ + image.writeFile('lena_cover.jpg', function(err){ + if (err) return console.log(err); + console.log('done'); + }); + }); +}); diff --git a/lib/BatchPrototypeInit.js b/lib/BatchPrototypeInit.js index ab7a6939..4f83ec6f 100644 --- a/lib/BatchPrototypeInit.js +++ b/lib/BatchPrototypeInit.js @@ -14,6 +14,7 @@ // batch mode. scale: decree(defs.args.scale.slice(0, -1)), resize: decree(defs.args.resize.slice(0, -1)), + cover: decree(defs.args.cover.slice(0, -1)), rotate: decree(defs.args.rotate.slice(0, -1)), blur: decree(defs.args.blur.slice(0, -1)), hslaAdjust: decree(defs.args.hslaAdjust.slice(0, -1)), @@ -84,6 +85,14 @@ return this; }; + Batch.prototype.cover = function() { + var that = this; + judges.cover(arguments, function(width, height, inter) { + that.__addOp(that.__image.cover, [width, height, inter].filter(undefinedFilter)); + }); + return this; + }; + Batch.prototype.rotate = function() { var that = this; judges.rotate(arguments, function(degs, color) { diff --git a/lib/ImagePrototypeInit.js b/lib/ImagePrototypeInit.js index a0c2f048..bae350b8 100644 --- a/lib/ImagePrototypeInit.js +++ b/lib/ImagePrototypeInit.js @@ -13,6 +13,7 @@ var judges = { scale: decree(defs.args.scale), resize: decree(defs.args.resize), + cover: decree(defs.args.cover), rotate: decree(defs.args.rotate), blur: decree(defs.args.blur), hslaAdjust: decree(defs.args.hslaAdjust), @@ -118,6 +119,21 @@ ); }; + Image.prototype.cover = function() { + var that = this; + judges.cover( + arguments, + function(width, height, inter, callback) { + var s = Math.max(width / that.width(), height / that.height()); + that.scale(s, s, inter, function(err){ + if (err) return callback(err); + that.crop(width, height, callback); + }); + } + ); + }; + + Image.prototype.rotate = function() { this.__lock(); var that = this; diff --git a/lib/defs.js b/lib/defs.js index 57edfc82..f1529641 100644 --- a/lib/defs.js +++ b/lib/defs.js @@ -365,7 +365,23 @@ }, { name: 'callback', type: 'function' - }] + }], + cover: [{ + name: 'width', + type: 'p-number' + }, { + name: 'height', + type: 'p-number' + }, { + name: 'interpolation', + type: 'interpolation', + optional: true, + default: defaults.DEF_INTERPOLATION + }, { + name: 'callback', + type: 'function' + }], + }; })(); diff --git a/tests/02.operations/119.cover.js b/tests/02.operations/119.cover.js new file mode 100644 index 00000000..c69a40f7 --- /dev/null +++ b/tests/02.operations/119.cover.js @@ -0,0 +1,61 @@ +var join = require('path').join, + should = require('should'), + assert = require('assert'), + mkdirp = require('mkdirp'), + lwip = require('../../'), + imgs = require('../imgs'); + +var tmpDir = join(__dirname, '../results'), + basename = 'cover', + current; + +describe('lwip.cover', function() { + + var image; + + before(function(done) { + mkdirp(tmpDir, done); + }); + + beforeEach(function(done) { + lwip.open(imgs.png.rgb, function(err, img) { + image = img; + done(err); + }); + }); + + afterEach(function(done) { + image.writeFile(join(tmpDir, current.join('_') + '.jpg'), 'jpg', { + quality: 90 + }, done); + }); + + beforeEach(function(){ + current = [ basename ]; + }); + + describe('800X300, unspecified interpolation', function() { + it('image should have the correct size', function(done) { + current.push('800X300','unspecified_inter'); + image.cover(800, 300, function(err, im) { + if (err) return done(err); + assert(im.width() === 800); + assert(im.height() === 300); + done(); + }); + }); + }); + + describe('300X800, lanczos interpolation', function() { + it('image should have the correct size', function(done) { + current.push('300X800','lanczos'); + image.cover(300, 800, 'lanczos', function(err, im) { + if (err) return done(err); + assert(im.width() === 300); + assert(im.height() === 800); + done(); + }); + }); + }); + +}); diff --git a/tests/03.safety/00.locks.js b/tests/03.safety/00.locks.js index bb8a8df4..468d38f2 100644 --- a/tests/03.safety/00.locks.js +++ b/tests/03.safety/00.locks.js @@ -143,6 +143,13 @@ describe('simultaneous operations locks', function() { describe('image.paste lock', function() { it('should lock image', function() { image.paste.bind(image, 0, 0, tmpImage, function() {}).should.not.throwError(); + image.cover.bind(image, 100, 100, function() {}).should.throwError(); + }); + }); + + describe('image.cover lock', function() { + it('should lock image', function() { + image.cover.bind(image, 200, 300, function() {}).should.not.throwError(); image.resize.bind(image, 100, 100, function() {}).should.throwError(); }); }); diff --git a/tests/03.safety/01.releases.js b/tests/03.safety/01.releases.js index 455e49b5..f98c6f1a 100644 --- a/tests/03.safety/01.releases.js +++ b/tests/03.safety/01.releases.js @@ -143,6 +143,13 @@ describe('failed ops lock release', function() { describe('image.paste release', function() { it('should release image lock', function() { image.paste.bind(image, 'foo', 'foo', 'foo', function() {}).should.throwError(); + image.cover.bind(image, 100, 100, function() {}).should.not.throwError(); + }); + }); + + describe('image.cover release', function() { + it('should release image lock', function() { + image.cover.bind(image, 'foo', 'foo', function() {}).should.throwError(); image.resize.bind(image, 100, 100, function() {}).should.not.throwError(); }); }); diff --git a/tests/utils.js b/tests/utils.js index 2f9b44f0..c4f96ff6 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -6,7 +6,7 @@ module.exports = { function generateRandomBatch(batch, n) { var ops = []; for (var i = 0; i < n; i++) { - var r = Math.floor(Math.random() * 14); + var r = Math.floor(Math.random() * 15); switch (r) { case 0: var sd = Math.floor(Math.random() * 20); @@ -81,6 +81,12 @@ function generateRandomBatch(batch, n) { batch = batch.opacify(); ops.push('opc'); break; + case 14: + var w = Math.floor(Math.random() * 1000) + 10; + var h = Math.floor(Math.random() * 1000) + 10; + batch = batch.cover(w, h); + ops.push('cvr' + w + 'X' + h); + break; } } return ops;