From 17ea70a102bd383cc08be7a39f093c7874846894 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sun, 7 Jun 2020 10:43:27 +0100 Subject: [PATCH] Add named 'alpha' channel to extractChannel op #2138 --- docs/api-channel.md | 2 +- docs/changelog.md | 3 +++ lib/channel.js | 13 +++++-------- src/pipeline.cc | 8 ++++++-- .../expected/extract-alpha-2-channel.png | Bin 0 -> 4707 bytes test/unit/extractChannel.js | 13 +++++++++++++ 6 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 test/fixtures/expected/extract-alpha-2-channel.png 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 0000000000000000000000000000000000000000..bfcd30b053d2fda3bae79b0104c3a40e39a4668e GIT binary patch literal 4707 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 F|34l<2?78B literal 0 HcmV?d00001 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)