mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add removeAlpha op, removes alpha channel if any #1248
This commit is contained in:
parent
25bd2cea3e
commit
c14434f9e7
@ -1,5 +1,21 @@
|
|||||||
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
||||||
|
|
||||||
|
## removeAlpha
|
||||||
|
|
||||||
|
Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharp('rgba.png')
|
||||||
|
.removeAlpha()
|
||||||
|
.toFile('rgb.png', function(err, info) {
|
||||||
|
// rgb.png is a 3 channel image without an alpha channel
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
## extractChannel
|
## extractChannel
|
||||||
|
|
||||||
Extract a single channel from a multi-channel image.
|
Extract a single channel from a multi-channel image.
|
||||||
|
@ -79,6 +79,7 @@ A Promise is returned when `callback` is not provided.
|
|||||||
- `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
- `maxX` (x-coordinate of one of the pixel where the maximum lies)
|
||||||
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
|
||||||
- `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
|
- `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
|
||||||
|
- `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ Requires libvips v8.6.1.
|
|||||||
|
|
||||||
#### v0.20.6 - TBD
|
#### v0.20.6 - TBD
|
||||||
|
|
||||||
|
* Add removeAlpha operation to remove alpha channel, if any.
|
||||||
|
[#1248](https://github.com/lovell/sharp/issues/1248)
|
||||||
|
|
||||||
* Expose mozjpeg quant_table flag.
|
* Expose mozjpeg quant_table flag.
|
||||||
[#1285](https://github.com/lovell/sharp/pull/1285)
|
[#1285](https://github.com/lovell/sharp/pull/1285)
|
||||||
[@rexxars](https://github.com/rexxars)
|
[@rexxars](https://github.com/rexxars)
|
||||||
|
@ -12,6 +12,23 @@ const bool = {
|
|||||||
eor: 'eor'
|
eor: 'eor'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* sharp('rgba.png')
|
||||||
|
* .removeAlpha()
|
||||||
|
* .toFile('rgb.png', function(err, info) {
|
||||||
|
* // rgb.png is a 3 channel image without an alpha channel
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @returns {Sharp}
|
||||||
|
*/
|
||||||
|
function removeAlpha () {
|
||||||
|
this.options.removeAlpha = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a single channel from a multi-channel image.
|
* Extract a single channel from a multi-channel image.
|
||||||
*
|
*
|
||||||
@ -102,6 +119,7 @@ function bandbool (boolOp) {
|
|||||||
module.exports = function (Sharp) {
|
module.exports = function (Sharp) {
|
||||||
// Public instance functions
|
// Public instance functions
|
||||||
[
|
[
|
||||||
|
removeAlpha,
|
||||||
extractChannel,
|
extractChannel,
|
||||||
joinChannel,
|
joinChannel,
|
||||||
bandbool
|
bandbool
|
||||||
|
@ -171,6 +171,7 @@ const Sharp = function (input, options) {
|
|||||||
booleanFileIn: '',
|
booleanFileIn: '',
|
||||||
joinChannelIn: [],
|
joinChannelIn: [],
|
||||||
extractChannel: -1,
|
extractChannel: -1,
|
||||||
|
removeAlpha: false,
|
||||||
colourspace: 'srgb',
|
colourspace: 'srgb',
|
||||||
// overlay
|
// overlay
|
||||||
overlayGravity: 0,
|
overlayGravity: 0,
|
||||||
|
@ -28,6 +28,16 @@ using vips::VError;
|
|||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes alpha channel, if any.
|
||||||
|
*/
|
||||||
|
VImage RemoveAlpha(VImage image) {
|
||||||
|
if (HasAlpha(image)) {
|
||||||
|
image = image.extract_band(0, VImage::option()->set("n", image.bands() - 1));
|
||||||
|
}
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Composite overlayImage over image at given position
|
Composite overlayImage over image at given position
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after
|
||||||
@ -223,10 +233,8 @@ namespace sharp {
|
|||||||
VImage Gamma(VImage image, double const exponent) {
|
VImage Gamma(VImage image, double const exponent) {
|
||||||
if (HasAlpha(image)) {
|
if (HasAlpha(image)) {
|
||||||
// Separate alpha channel
|
// Separate alpha channel
|
||||||
VImage imageWithoutAlpha = image.extract_band(0,
|
|
||||||
VImage::option()->set("n", image.bands() - 1));
|
|
||||||
VImage alpha = image[image.bands() - 1];
|
VImage alpha = image[image.bands() - 1];
|
||||||
return imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
|
return RemoveAlpha(image).gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
|
||||||
} else {
|
} else {
|
||||||
return image.gamma(VImage::option()->set("exponent", exponent));
|
return image.gamma(VImage::option()->set("exponent", exponent));
|
||||||
}
|
}
|
||||||
@ -374,10 +382,8 @@ namespace sharp {
|
|||||||
VImage Linear(VImage image, double const a, double const b) {
|
VImage Linear(VImage image, double const a, double const b) {
|
||||||
if (HasAlpha(image)) {
|
if (HasAlpha(image)) {
|
||||||
// Separate alpha channel
|
// Separate alpha channel
|
||||||
VImage imageWithoutAlpha = image.extract_band(0,
|
|
||||||
VImage::option()->set("n", image.bands() - 1));
|
|
||||||
VImage alpha = image[image.bands() - 1];
|
VImage alpha = image[image.bands() - 1];
|
||||||
return imageWithoutAlpha.linear(a, b).bandjoin(alpha);
|
return RemoveAlpha(image).linear(a, b).bandjoin(alpha);
|
||||||
} else {
|
} else {
|
||||||
return image.linear(a, b);
|
return image.linear(a, b);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,11 @@ using vips::VImage;
|
|||||||
|
|
||||||
namespace sharp {
|
namespace sharp {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes alpha channel, if any.
|
||||||
|
*/
|
||||||
|
VImage RemoveAlpha(VImage image);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Alpha composite src over dst with given gravity.
|
Alpha composite src over dst with given gravity.
|
||||||
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
Assumes alpha channels are already premultiplied and will be unpremultiplied after.
|
||||||
|
@ -698,6 +698,12 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
.extract_band(baton->extractChannel)
|
.extract_band(baton->extractChannel)
|
||||||
.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));
|
.copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_B_W));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove alpha channel, if any
|
||||||
|
if (baton->removeAlpha) {
|
||||||
|
image = sharp::RemoveAlpha(image);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert image to sRGB, if not already
|
// Convert image to sRGB, if not already
|
||||||
if (sharp::Is16Bit(image.interpretation())) {
|
if (sharp::Is16Bit(image.interpretation())) {
|
||||||
image = image.cast(VIPS_FORMAT_USHORT);
|
image = image.cast(VIPS_FORMAT_USHORT);
|
||||||
@ -1235,6 +1241,7 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->extendLeft = AttrTo<int32_t>(options, "extendLeft");
|
baton->extendLeft = AttrTo<int32_t>(options, "extendLeft");
|
||||||
baton->extendRight = AttrTo<int32_t>(options, "extendRight");
|
baton->extendRight = AttrTo<int32_t>(options, "extendRight");
|
||||||
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
|
baton->extractChannel = AttrTo<int32_t>(options, "extractChannel");
|
||||||
|
baton->removeAlpha = AttrTo<bool>(options, "removeAlpha");
|
||||||
if (HasAttr(options, "boolean")) {
|
if (HasAttr(options, "boolean")) {
|
||||||
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
|
baton->boolean = CreateInputDescriptor(AttrAs<v8::Object>(options, "boolean"), buffersToPersist);
|
||||||
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
|
baton->booleanOp = sharp::GetBooleanOperation(AttrAsStr(options, "booleanOp"));
|
||||||
|
@ -131,6 +131,7 @@ struct PipelineBaton {
|
|||||||
VipsOperationBoolean booleanOp;
|
VipsOperationBoolean booleanOp;
|
||||||
VipsOperationBoolean bandBoolOp;
|
VipsOperationBoolean bandBoolOp;
|
||||||
int extractChannel;
|
int extractChannel;
|
||||||
|
bool removeAlpha;
|
||||||
VipsInterpretation colourspace;
|
VipsInterpretation colourspace;
|
||||||
int tileSize;
|
int tileSize;
|
||||||
int tileOverlap;
|
int tileOverlap;
|
||||||
@ -213,6 +214,7 @@ struct PipelineBaton {
|
|||||||
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
|
booleanOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||||
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
bandBoolOp(VIPS_OPERATION_BOOLEAN_LAST),
|
||||||
extractChannel(-1),
|
extractChannel(-1),
|
||||||
|
removeAlpha(false),
|
||||||
colourspace(VIPS_INTERPRETATION_LAST),
|
colourspace(VIPS_INTERPRETATION_LAST),
|
||||||
tileSize(256),
|
tileSize(256),
|
||||||
tileOverlap(0),
|
tileOverlap(0),
|
||||||
|
@ -81,35 +81,45 @@ describe('Alpha transparency', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Enlargement with non-nearest neighbor interpolation shouldn’t cause dark edges', function (done) {
|
it('Enlargement with non-nearest neighbor interpolation shouldn’t cause dark edges', function () {
|
||||||
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png';
|
const base = 'alpha-premultiply-enlargement-2048x1536-paper.png';
|
||||||
const actual = fixtures.path('output.' + base);
|
const actual = fixtures.path('output.' + base);
|
||||||
const expected = fixtures.expected(base);
|
const expected = fixtures.expected(base);
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
return sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
.resize(2048, 1536)
|
.resize(2048, 1536)
|
||||||
.toFile(actual, function (err) {
|
.toFile(actual)
|
||||||
if (err) {
|
.then(function () {
|
||||||
done(err);
|
fixtures.assertMaxColourDistance(actual, expected, 102);
|
||||||
} else {
|
|
||||||
fixtures.assertMaxColourDistance(actual, expected, 102);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Reduction with non-nearest neighbor interpolation shouldn’t cause dark edges', function (done) {
|
it('Reduction with non-nearest neighbor interpolation shouldn’t cause dark edges', function () {
|
||||||
const base = 'alpha-premultiply-reduction-1024x768-paper.png';
|
const base = 'alpha-premultiply-reduction-1024x768-paper.png';
|
||||||
const actual = fixtures.path('output.' + base);
|
const actual = fixtures.path('output.' + base);
|
||||||
const expected = fixtures.expected(base);
|
const expected = fixtures.expected(base);
|
||||||
sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
return sharp(fixtures.inputPngAlphaPremultiplicationLarge)
|
||||||
.resize(1024, 768)
|
.resize(1024, 768)
|
||||||
.toFile(actual, function (err) {
|
.toFile(actual)
|
||||||
if (err) {
|
.then(function () {
|
||||||
done(err);
|
fixtures.assertMaxColourDistance(actual, expected, 102);
|
||||||
} else {
|
|
||||||
fixtures.assertMaxColourDistance(actual, expected, 102);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Removes alpha from fixtures with transparency, ignores those without', function () {
|
||||||
|
return Promise.all([
|
||||||
|
fixtures.inputPngWithTransparency,
|
||||||
|
fixtures.inputPngWithTransparency16bit,
|
||||||
|
fixtures.inputWebPWithTransparency,
|
||||||
|
fixtures.inputJpg,
|
||||||
|
fixtures.inputPng,
|
||||||
|
fixtures.inputWebP
|
||||||
|
].map(function (input) {
|
||||||
|
return sharp(input)
|
||||||
|
.removeAlpha()
|
||||||
|
.toBuffer({ resolveWithObject: true })
|
||||||
|
.then(function (result) {
|
||||||
|
assert.strictEqual(3, result.info.channels);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user