diff --git a/docs/api.md b/docs/api.md index 5bcded52..0d872b52 100644 --- a/docs/api.md +++ b/docs/api.md @@ -298,24 +298,11 @@ sharp(input) }); ``` -#### extractChannel(channel) +#### trim([tolerance]) -Extract a channel from the image. The following channel names are equivalent: +Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel. - * Red: `0, 'red'` - * Green: `1, 'green'` - * Blue: `2, 'blue'` - -The result will be a single-channel grayscale image. - -```javascript -sharp(input) - .extractChannel('green') - .toFile('input_green.jpg',function(err, info) { - // info.channels === 1 - // input_green.jpg contains the green channel of the input image - }); -``` +* `tolerance`, if present, is an integral Number between 1 and 99 representing the percentage similarity, defaulting to 10. #### background(rgba) @@ -502,15 +489,30 @@ sharp('input.png') }); ``` +#### extractChannel(channel) + +Extract a single channel from a multi-channel image. + +* `channel` is a zero-indexed integral Number representing the band number to extract. `red`, `green` or `blue` are also accepted as an alternative to `0`, `1` or `2` respectively. + +```javascript +sharp(input) + .extractChannel('green') + .toFile('input_green.jpg', function(err, info) { + // info.channels === 1 + // input_green.jpg contains the green channel of the input image + }); +``` + #### bandbool(operation) -Perform a bitwise boolean operation on image color channels (bands in vips terminology). The result is a single channel grayscale image. Bandbool is performed at the end of the image processing pipeline, after gamma correction, colorspace conversion, normalization, and other operations. This makes it possible to create an image that contains the unaltered result of the boolean operation. Note that the alpha channel of the image is included in `bandbool` operations. All channels are cast to an integer type before the operation. `bandbool` takes no effect on single channel images. +Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image. -`operation` is a string containing the name of the bitwise operator to be appled to image color channels, which can be one of: +`operation` is a string containing the name of the bitwise operator to be appled to image channels, which can be one of: - * `and` performs a bitwise and operation, like the c-operator `&` - * `or` performs a bitwise or operation, like the c-operator `|` - * `eor` performs a bitwise exclusive or operation, like the c-operator `^` +* `and` performs a bitwise and operation, like the c-operator `&`. +* `or` performs a bitwise or operation, like the c-operator `|`. +* `eor` performs a bitwise exclusive or operation, like the c-operator `^`. ```javascript sharp('input.png') @@ -518,23 +520,23 @@ sharp('input.png') .toFile('output.png') ``` -In the above example if `input.png` is a 3 channel RGB image, `output.png` will be a 1 channel grayscale image where each pixel `P = R & G & B`. For example, if `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]` then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`. +In the above example if `input.png` is a 3 channel RGB image, `output.png` will be a 1 channel grayscale image where each pixel `P = R & G & B`. +For example, if `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]` then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`. #### boolean(image, operation) -Perform a bitwise boolean operation with `image`. +Perform a bitwise boolean operation with `image`, where `image` is one of the following: -`image` is one of the following. - -* Buffer contianing PNG, WebP, GIF or SVG image data, or +* Buffer containing PNG, WebP, GIF or SVG image data, or * String containing the path to an image file -This operation creates an output image where each pixel is the result of the selected bitwise boolean `operation`, between the corresponding pixels of the input images. The boolean operation can be one of the following: +This operation creates an output image where each pixel is the result of the selected bitwise boolean `operation` between the corresponding pixels of the input images. +The boolean operation can be one of the following: + +* `and` performs a bitwise and operation, like the c-operator `&`. +* `or` performs a bitwise or operation, like the c-operator `|`. +* `eor` performs a bitwise exclusive or operation, like the c-operator `^`. - * `and` performs a bitwise and operation, like the c-operator `&` - * `or` performs a bitwise or operation, like the c-operator `|` - * `eor` performs a bitwise exclusive or operation, like the c-operator `^` - ### Output #### toFile(path, [callback]) diff --git a/docs/changelog.md b/docs/changelog.md index a34fd4f4..96c2a76e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -50,6 +50,26 @@ Requires libvips v8.3.1 [#490](https://github.com/lovell/sharp/issues/490) [@jwagner](https://github.com/jwagner) +* Add trim operation to remove "boring" edges. + [#492](https://github.com/lovell/sharp/pull/492) + [@kleisauke](https://github.com/kleisauke) + +* Add bandbool feature for channel-wise boolean operations. + [#496](https://github.com/lovell/sharp/pull/496) + [@mhirsch](https://github.com/mhirsch) + +* Add extractChannel operation to extract a channel from an image. + [#497](https://github.com/lovell/sharp/pull/497) + [@mhirsch](https://github.com/mhirsch) + +* Add ability to read and write native libvips .v files. + [#500](https://github.com/lovell/sharp/pull/500) + [@mhirsch](https://github.com/mhirsch) + +* Add boolean feature for bitwise image operations. + [#501](https://github.com/lovell/sharp/pull/501) + [@mhirsch](https://github.com/mhirsch) + #### v0.15.0 - 21st May 2016 * Use libvips' new Lanczos 3 kernel as default for image reduction. diff --git a/package.json b/package.json index 61dd04c4..a27e3184 100644 --- a/package.json +++ b/package.json @@ -58,11 +58,11 @@ "vips" ], "dependencies": { - "bluebird": "^3.3.5", - "color": "^0.11.1", - "nan": "^2.2.1", - "semver": "^5.1.0", - "request": "^2.71.0", + "bluebird": "^3.4.1", + "color": "^0.11.3", + "nan": "^2.4.0", + "semver": "^5.2.0", + "request": "^2.73.0", "tar": "^2.2.1" }, "devDependencies": { @@ -71,11 +71,11 @@ "coveralls": "^2.11.9", "exif-reader": "^1.0.0", "icc": "^0.0.2", - "istanbul": "^0.4.3", + "istanbul": "^0.4.4", "mocha": "^2.5.3", "mocha-jshint": "^2.3.1", "node-cpplint": "^0.4.0", - "rimraf": "^2.5.2", + "rimraf": "^2.5.3", "unzip": "^0.1.11" }, "license": "Apache-2.0", diff --git a/test/unit/bandbool.js b/test/unit/bandbool.js index 725e4542..d1d9de0b 100644 --- a/test/unit/bandbool.js +++ b/test/unit/bandbool.js @@ -6,46 +6,34 @@ var sharp = require('../../index'); describe('Bandbool per-channel boolean operations', function() { - it('\'and\' Operation', function(done) { - sharp(fixtures.inputPngBooleanNoAlpha) - .bandbool('and') - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - assert.strictEqual(1, info.channels); - fixtures.assertSimilar(fixtures.expected('bandbool_and_result.png'), data, done); - }); - }); - - it('\'or\' Operation', function(done) { - sharp(fixtures.inputPngBooleanNoAlpha) - .bandbool(sharp.bool.or) - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - assert.strictEqual(1, info.channels); - fixtures.assertSimilar(fixtures.expected('bandbool_or_result.png'), data, done); - }); - }); - - it('\'eor\' Operation', function(done) { - sharp(fixtures.inputPngBooleanNoAlpha) - .bandbool('eor') - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(200, info.width); - assert.strictEqual(200, info.height); - assert.strictEqual(1, info.channels); - fixtures.assertSimilar(fixtures.expected('bandbool_eor_result.png'), data, done); - }); + [ + sharp.bool.and, + sharp.bool.or, + sharp.bool.eor + ] + .forEach(function(op) { + it(op + ' operation', function(done) { + sharp(fixtures.inputPngBooleanNoAlpha) + .bandbool(op) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(200, info.width); + assert.strictEqual(200, info.height); + assert.strictEqual(1, info.channels); + fixtures.assertSimilar(fixtures.expected('bandbool_' + op + '_result.png'), data, done); + }); + }); }); it('Invalid operation', function() { assert.throws(function() { - sharp(fixtures.inputPngBooleanNoAlpha) - .bandbool('fail'); + sharp().bandbool('fail'); + }); + }); + + it('Missing operation', function() { + assert.throws(function() { + sharp().bandbool(); }); }); }); diff --git a/test/unit/boolean.js b/test/unit/boolean.js index 7f601de2..27a63e38 100644 --- a/test/unit/boolean.js +++ b/test/unit/boolean.js @@ -7,97 +7,56 @@ var sharp = require('../../index'); describe('Boolean operation between two images', function() { - it('\'and\' Operation, file', function(done) { - sharp(fixtures.inputJpg) //fixtures.inputJpg - .resize(320,240) - .boolean(fixtures.inputJpgBooleanTest, 'and') - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(320, info.width); - assert.strictEqual(240, info.height); - fixtures.assertSimilar(fixtures.expected('boolean_and_result.jpg'), data, done); - }); - }); + var inputJpgBooleanTestBuffer = fs.readFileSync(fixtures.inputJpgBooleanTest); - it('\'and\' Operation, buffer', function(done) { - sharp(fixtures.inputJpg) //fixtures.inputJpg - .resize(320,240) - .boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), sharp.bool.and) - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(320, info.width); - assert.strictEqual(240, info.height); - fixtures.assertSimilar(fixtures.expected('boolean_and_result.jpg'), data, done); - }); - }); + [ + sharp.bool.and, + sharp.bool.or, + sharp.bool.eor + ] + .forEach(function(op) { - it('\'or\' Operation, file', function(done) { - sharp(fixtures.inputJpg) - .resize(320,240) - .boolean(fixtures.inputJpgBooleanTest, sharp.bool.or) - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(320, info.width); - assert.strictEqual(240, info.height); - fixtures.assertSimilar(fixtures.expected('boolean_or_result.jpg'), data, done); - }); - }); + it(op + ' operation, file', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .boolean(fixtures.inputJpgBooleanTest, op) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + fixtures.assertSimilar(fixtures.expected('boolean_' + op + '_result.jpg'), data, done); + }); + }); - it('\'or\' Operation, buffer', function(done) { - sharp(fixtures.inputJpg) - .resize(320,240) - .boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), 'or') - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(320, info.width); - assert.strictEqual(240, info.height); - fixtures.assertSimilar(fixtures.expected('boolean_or_result.jpg'), data, done); - }); - }); + it(op + ' operation, buffer', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .boolean(inputJpgBooleanTestBuffer, op) + .toBuffer(function(err, data, info) { + if (err) throw err; + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + fixtures.assertSimilar(fixtures.expected('boolean_' + op + '_result.jpg'), data, done); + }); + }); - it('\'eor\' Operation, file', function(done) { - sharp(fixtures.inputJpg) - .resize(320,240) - .boolean(fixtures.inputJpgBooleanTest, 'eor') - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(320, info.width); - assert.strictEqual(240, info.height); - fixtures.assertSimilar(fixtures.expected('boolean_eor_result.jpg'), data, done); - }); - }); - - it('\'eor\' Operation, buffer', function(done) { - sharp(fixtures.inputJpg) - .resize(320,240) - .boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), sharp.bool.eor) - .toBuffer(function(err, data, info) { - if (err) throw err; - assert.strictEqual(320, info.width); - assert.strictEqual(240, info.height); - fixtures.assertSimilar(fixtures.expected('boolean_eor_result.jpg'), data, done); - }); }); it('Invalid operation', function() { assert.throws(function() { - sharp(fixtures.inputJpg) - .boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), 'fail'); + sharp().boolean(fixtures.inputJpgBooleanTest, 'fail'); }); }); it('Invalid operation, non-string', function() { assert.throws(function() { - sharp(fixtures.inputJpg) - .boolean(fs.readFileSync(fixtures.inputJpgBooleanTest), null); + sharp().boolean(fixtures.inputJpgBooleanTest, null); }); }); - if('Invalid buffer input', function() { + if('Invalid input', function() { assert.throws(function() { - sharp(fixtures.inputJpg) - .resize(320,240) - .boolean([],'eor'); + sharp().boolean([], 'eor'); }); }); });