mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Standardise WebP effort option name, deprecate reductionEffort
This commit is contained in:
parent
72025051c5
commit
2b1f5cbe07
@ -282,7 +282,7 @@ Use these WebP options for output image.
|
||||
* `options.lossless` **[boolean][7]** use lossless compression mode (optional, default `false`)
|
||||
* `options.nearLossless` **[boolean][7]** use near_lossless compression mode (optional, default `false`)
|
||||
* `options.smartSubsample` **[boolean][7]** use high quality chroma subsampling (optional, default `false`)
|
||||
* `options.reductionEffort` **[number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
|
||||
* `options.effort` **[number][9]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
|
||||
* `options.pageHeight` **[number][9]?** page height for animated output
|
||||
* `options.loop` **[number][9]** number of animation iterations, use 0 for infinite animation (optional, default `0`)
|
||||
* `options.delay` **[Array][10]<[number][9]>?** list of delays between animation frames (in milliseconds)
|
||||
@ -300,7 +300,7 @@ const data = await sharp(input)
|
||||
```javascript
|
||||
// Optimise the file size of an animated WebP
|
||||
const outputWebp = await sharp(inputWebp, { animated: true })
|
||||
.webp({ reductionEffort: 6 })
|
||||
.webp({ effort: 6 })
|
||||
.toBuffer();
|
||||
```
|
||||
|
||||
|
@ -10,6 +10,8 @@ Requires libvips v8.12.0
|
||||
|
||||
* Reduce minimum Linux ARM64v8 glibc requirement to 2.17.
|
||||
|
||||
* Standardise WebP `effort` option name, deprecate `reductionEffort`.
|
||||
|
||||
* Expose control over CPU effort for palette-based PNG output.
|
||||
[#2541](https://github.com/lovell/sharp/issues/2541)
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -247,7 +247,7 @@ const Sharp = function (input, options) {
|
||||
webpLossless: false,
|
||||
webpNearLossless: false,
|
||||
webpSmartSubsample: false,
|
||||
webpReductionEffort: 4,
|
||||
webpEffort: 4,
|
||||
gifBitdepth: 8,
|
||||
gifEffort: 7,
|
||||
gifDither: 1,
|
||||
|
@ -448,7 +448,7 @@ function png (options) {
|
||||
* @example
|
||||
* // Optimise the file size of an animated WebP
|
||||
* const outputWebp = await sharp(inputWebp, { animated: true })
|
||||
* .webp({ reductionEffort: 6 })
|
||||
* .webp({ effort: 6 })
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {Object} [options] - output options
|
||||
@ -457,7 +457,7 @@ function png (options) {
|
||||
* @param {boolean} [options.lossless=false] - use lossless compression mode
|
||||
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
|
||||
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
|
||||
* @param {number} [options.reductionEffort=4] - level of CPU effort to reduce file size, integer 0-6
|
||||
* @param {number} [options.effort=4] - level of CPU effort to reduce file size, integer 0-6
|
||||
* @param {number} [options.pageHeight] - page height for animated output
|
||||
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
|
||||
* @param {number[]} [options.delay] - list of delays between animation frames (in milliseconds)
|
||||
@ -466,37 +466,39 @@ function png (options) {
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
function webp (options) {
|
||||
if (is.object(options) && is.defined(options.quality)) {
|
||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||
this.options.webpQuality = options.quality;
|
||||
} else {
|
||||
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||
if (is.object(options)) {
|
||||
if (is.defined(options.quality)) {
|
||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||
this.options.webpQuality = options.quality;
|
||||
} else {
|
||||
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.alphaQuality)) {
|
||||
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
|
||||
this.options.webpAlphaQuality = options.alphaQuality;
|
||||
} else {
|
||||
throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
|
||||
}
|
||||
}
|
||||
if (is.defined(options.lossless)) {
|
||||
this._setBooleanOption('webpLossless', options.lossless);
|
||||
}
|
||||
if (is.defined(options.nearLossless)) {
|
||||
this._setBooleanOption('webpNearLossless', options.nearLossless);
|
||||
}
|
||||
if (is.defined(options.smartSubsample)) {
|
||||
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
|
||||
}
|
||||
const effort = options.effort || options.reductionEffort;
|
||||
if (is.defined(effort)) {
|
||||
if (is.integer(effort) && is.inRange(effort, 0, 6)) {
|
||||
this.options.webpEffort = effort;
|
||||
} else {
|
||||
throw is.invalidParameterError('effort', 'integer between 0 and 6', effort);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is.object(options) && is.defined(options.alphaQuality)) {
|
||||
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
|
||||
this.options.webpAlphaQuality = options.alphaQuality;
|
||||
} else {
|
||||
throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
|
||||
}
|
||||
}
|
||||
if (is.object(options) && is.defined(options.lossless)) {
|
||||
this._setBooleanOption('webpLossless', options.lossless);
|
||||
}
|
||||
if (is.object(options) && is.defined(options.nearLossless)) {
|
||||
this._setBooleanOption('webpNearLossless', options.nearLossless);
|
||||
}
|
||||
if (is.object(options) && is.defined(options.smartSubsample)) {
|
||||
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
|
||||
}
|
||||
if (is.object(options) && is.defined(options.reductionEffort)) {
|
||||
if (is.integer(options.reductionEffort) && is.inRange(options.reductionEffort, 0, 6)) {
|
||||
this.options.webpReductionEffort = options.reductionEffort;
|
||||
} else {
|
||||
throw is.invalidParameterError('reductionEffort', 'integer between 0 and 6', options.reductionEffort);
|
||||
}
|
||||
}
|
||||
|
||||
trySetAnimationOptions(options, this.options);
|
||||
return this._updateFormatOut('webp', options);
|
||||
}
|
||||
|
@ -842,7 +842,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("lossless", baton->webpLossless)
|
||||
->set("near_lossless", baton->webpNearLossless)
|
||||
->set("smart_subsample", baton->webpSmartSubsample)
|
||||
->set("reduction_effort", baton->webpReductionEffort)
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("alpha_q", baton->webpAlphaQuality)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
@ -1008,7 +1008,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("lossless", baton->webpLossless)
|
||||
->set("near_lossless", baton->webpNearLossless)
|
||||
->set("smart_subsample", baton->webpSmartSubsample)
|
||||
->set("reduction_effort", baton->webpReductionEffort)
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("alpha_q", baton->webpAlphaQuality));
|
||||
baton->formatOut = "webp";
|
||||
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
||||
@ -1078,7 +1078,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
||||
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
||||
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
||||
{"reduction_effort", std::to_string(baton->webpReductionEffort)}
|
||||
{"effort", std::to_string(baton->webpEffort)}
|
||||
};
|
||||
suffix = AssembleSuffixString(".webp", options);
|
||||
} else {
|
||||
@ -1485,7 +1485,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->webpLossless = sharp::AttrAsBool(options, "webpLossless");
|
||||
baton->webpNearLossless = sharp::AttrAsBool(options, "webpNearLossless");
|
||||
baton->webpSmartSubsample = sharp::AttrAsBool(options, "webpSmartSubsample");
|
||||
baton->webpReductionEffort = sharp::AttrAsUint32(options, "webpReductionEffort");
|
||||
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
||||
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
||||
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
||||
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
||||
|
@ -160,7 +160,7 @@ struct PipelineBaton {
|
||||
bool webpNearLossless;
|
||||
bool webpLossless;
|
||||
bool webpSmartSubsample;
|
||||
int webpReductionEffort;
|
||||
int webpEffort;
|
||||
int gifBitdepth;
|
||||
int gifEffort;
|
||||
double gifDither;
|
||||
@ -301,7 +301,7 @@ struct PipelineBaton {
|
||||
webpNearLossless(false),
|
||||
webpLossless(false),
|
||||
webpSmartSubsample(false),
|
||||
webpReductionEffort(4),
|
||||
webpEffort(4),
|
||||
tiffQuality(80),
|
||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
||||
|
@ -667,7 +667,7 @@ describe('Tile', function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.webp({
|
||||
quality: 1,
|
||||
reductionEffort: 0
|
||||
effort: 0
|
||||
})
|
||||
.tile({
|
||||
layout: 'google'
|
||||
|
@ -35,7 +35,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should work for webp alpha quality', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ alphaQuality: 80, reductionEffort: 0 })
|
||||
.webp({ alphaQuality: 80, effort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
@ -46,7 +46,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should work for webp lossless', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ lossless: true, reductionEffort: 0 })
|
||||
.webp({ lossless: true, effort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
@ -57,7 +57,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should work for webp near-lossless', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ nearLossless: true, quality: 50, reductionEffort: 0 })
|
||||
.webp({ nearLossless: true, quality: 50, effort: 0 })
|
||||
.toBuffer(function (err50, data50, info50) {
|
||||
if (err50) throw err50;
|
||||
assert.strictEqual(true, data50.length > 0);
|
||||
@ -68,7 +68,7 @@ describe('WebP', function () {
|
||||
|
||||
it('should use near-lossless when both lossless and nearLossless are specified', function (done) {
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.webp({ nearLossless: true, quality: 50, lossless: true, reductionEffort: 0 })
|
||||
.webp({ nearLossless: true, quality: 50, lossless: true, effort: 0 })
|
||||
.toBuffer(function (err50, data50, info50) {
|
||||
if (err50) throw err50;
|
||||
assert.strictEqual(true, data50.length > 0);
|
||||
@ -99,31 +99,37 @@ describe('WebP', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should produce a smaller file size with increased reductionEffort', () =>
|
||||
it('should produce a smaller file size with increased effort', () =>
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.webp()
|
||||
.toBuffer()
|
||||
.then(reductionEffort4 =>
|
||||
.then(effort4 =>
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.webp({ reductionEffort: 6 })
|
||||
.webp({ effort: 6 })
|
||||
.toBuffer()
|
||||
.then(reductionEffort6 => {
|
||||
assert.strictEqual(true, reductionEffort4.length > reductionEffort6.length);
|
||||
.then(effort6 => {
|
||||
assert.strictEqual(true, effort4.length > effort6.length);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
it('invalid reductionEffort throws', () => {
|
||||
it('invalid effort throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ effort: true });
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid reductionEffort (deprecated) throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ reductionEffort: true });
|
||||
});
|
||||
});
|
||||
|
||||
it('out of range reductionEffort throws', () => {
|
||||
it('out of range effort throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ reductionEffort: -1 });
|
||||
sharp().webp({ effort: -1 });
|
||||
});
|
||||
});
|
||||
|
||||
@ -189,7 +195,7 @@ describe('WebP', function () {
|
||||
it('should work with streams when only animated is set', function (done) {
|
||||
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||
.pipe(sharp({ animated: true }))
|
||||
.webp({ lossless: true, reductionEffort: 0 })
|
||||
.webp({ lossless: true, effort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
@ -201,7 +207,7 @@ describe('WebP', function () {
|
||||
it('should work with streams when only pages is set', function (done) {
|
||||
fs.createReadStream(fixtures.inputWebPAnimated)
|
||||
.pipe(sharp({ pages: -1 }))
|
||||
.webp({ lossless: true, reductionEffort: 0 })
|
||||
.webp({ lossless: true, effort: 0 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, data.length > 0);
|
||||
@ -213,12 +219,11 @@ describe('WebP', function () {
|
||||
it('should remove animation properties when loading single page', async () => {
|
||||
const data = await sharp(fixtures.inputGifAnimatedLoop3)
|
||||
.resize({ height: 570 })
|
||||
.webp({ reductionEffort: 0 })
|
||||
.webp({ effort: 0 })
|
||||
.toBuffer();
|
||||
const metadata = await sharp(data).metadata();
|
||||
const { size, ...metadata } = await sharp(data).metadata();
|
||||
assert.deepStrictEqual(metadata, {
|
||||
format: 'webp',
|
||||
size: 2580,
|
||||
width: 740,
|
||||
height: 570,
|
||||
space: 'srgb',
|
||||
|
Loading…
x
Reference in New Issue
Block a user