diff --git a/docs/api-channel.md b/docs/api-channel.md index 75d6776e..a079bc7e 100644 --- a/docs/api-channel.md +++ b/docs/api-channel.md @@ -42,7 +42,7 @@ Extract a single channel from a multi-channel image. ### Parameters -- `channel` **([number][1] \| [string][2])** zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively. +- `channel` **([number][1] \| [string][2])** zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`. ### Examples diff --git a/docs/changelog.md b/docs/changelog.md index 0144af23..aa393495 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -18,6 +18,9 @@ Requires libvips v8.9.1 Expose `levels` metadata for multi-level images. [#2222](https://github.com/lovell/sharp/issues/2222) +* Add support for named `alpha` channel to `extractChannel` operation. + [#2138](https://github.com/lovell/sharp/issues/2138) + ### v0.25.3 - 17th May 2020 * Ensure libvips is initialised only once, improves worker thread safety. diff --git a/lib/channel.js b/lib/channel.js index f256b3fc..f40e72b5 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -60,22 +60,19 @@ function ensureAlpha () { * // green.jpg is a greyscale image containing the green channel of the input * }); * - * @param {number|string} channel - zero-indexed band number to extract, or `red`, `green` or `blue` as alternative to `0`, `1` or `2` respectively. + * @param {number|string} channel - zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`. * @returns {Sharp} * @throws {Error} Invalid channel */ function extractChannel (channel) { - if (channel === 'red') { - channel = 0; - } else if (channel === 'green') { - channel = 1; - } else if (channel === 'blue') { - channel = 2; + const channelMap = { red: 0, green: 1, blue: 2, alpha: 3 }; + if (Object.keys(channelMap).includes(channel)) { + channel = channelMap[channel]; } if (is.integer(channel) && is.inRange(channel, 0, 4)) { this.options.extractChannel = channel; } else { - throw is.invalidParameterError('channel', 'integer or one of: red, green, blue', channel); + throw is.invalidParameterError('channel', 'integer or one of: red, green, blue, alpha', channel); } return this; } diff --git a/src/pipeline.cc b/src/pipeline.cc index 9faceb46..7830d6b0 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -645,8 +645,12 @@ class PipelineWorker : public Napi::AsyncWorker { // Extract an image channel (aka vips band) if (baton->extractChannel > -1) { if (baton->extractChannel >= image.bands()) { - (baton->err).append("Cannot extract channel from image. Too few channels in image."); - return Error(); + if (baton->extractChannel == 3 && sharp::HasAlpha(image)) { + baton->extractChannel = image.bands() - 1; + } else { + (baton->err).append("Cannot extract channel from image. Too few channels in image."); + return Error(); + } } VipsInterpretation const interpretation = sharp::Is16Bit(image.interpretation()) ? VIPS_INTERPRETATION_GREY16 diff --git a/test/fixtures/expected/extract-alpha-2-channel.png b/test/fixtures/expected/extract-alpha-2-channel.png new file mode 100644 index 00000000..bfcd30b0 Binary files /dev/null and b/test/fixtures/expected/extract-alpha-2-channel.png differ diff --git a/test/unit/extractChannel.js b/test/unit/extractChannel.js index 15a7c2a3..a89627d8 100644 --- a/test/unit/extractChannel.js +++ b/test/unit/extractChannel.js @@ -80,6 +80,19 @@ describe('Image channel extraction', function () { }); }); + it('Alpha from 2-channel input', function (done) { + const output = fixtures.path('output.extract-alpha-2-channel.png'); + sharp(fixtures.inputPngWithGreyAlpha) + .extractChannel('alpha') + .toColourspace('b-w') + .toFile(output, function (err, info) { + if (err) throw err; + assert.strictEqual(1, info.channels); + fixtures.assertMaxColourDistance(output, fixtures.expected('extract-alpha-2-channel.png')); + done(); + }); + }); + it('Invalid channel number', function () { assert.throws(function () { sharp(fixtures.inputJpg)