mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Expose libwebp smartSubsample and reductionEffort #1545
This commit is contained in:
parent
119d16cad3
commit
36e8a3da88
@ -185,6 +185,8 @@ Use these WebP options for output image.
|
||||
- `options.alphaQuality` **[Number][8]** quality of alpha layer, integer 0-100 (optional, default `100`)
|
||||
- `options.lossless` **[Boolean][6]** use lossless compression mode (optional, default `false`)
|
||||
- `options.nearLossless` **[Boolean][6]** use near_lossless compression mode (optional, default `false`)
|
||||
- `options.smartSubsample` **[Boolean][6]** use high quality chroma subsampling (optional, default `false`)
|
||||
- `options.reductionEffort` **[Number][8]** level of CPU effort to reduce file size, integer 0-6 (optional, default `4`)
|
||||
- `options.force` **[Boolean][6]** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
||||
|
||||
### Examples
|
||||
|
@ -11,6 +11,9 @@ Requires libvips v8.8.0.
|
||||
* Add experimental support for HEIF images. Requires libvips compiled with libheif.
|
||||
[#1105](https://github.com/lovell/sharp/issues/1105)
|
||||
|
||||
* Expose libwebp `smartSubsample` and `reductionEffort` options.
|
||||
[#1545](https://github.com/lovell/sharp/issues/1545)
|
||||
|
||||
* Add experimental support for Worker Threads.
|
||||
[#1558](https://github.com/lovell/sharp/issues/1558)
|
||||
|
||||
|
@ -9,6 +9,7 @@ const is = require('./is');
|
||||
require('./libvips').hasVendoredLibvips();
|
||||
|
||||
let sharp;
|
||||
/* istanbul ignore next */
|
||||
try {
|
||||
sharp = require('../build/Release/sharp.node');
|
||||
} catch (err) {
|
||||
@ -197,6 +198,8 @@ const Sharp = function (input, options) {
|
||||
webpAlphaQuality: 100,
|
||||
webpLossless: false,
|
||||
webpNearLossless: false,
|
||||
webpSmartSubsample: false,
|
||||
webpReductionEffort: 4,
|
||||
tiffQuality: 80,
|
||||
tiffCompression: 'jpeg',
|
||||
tiffPredictor: 'horizontal',
|
||||
|
@ -290,6 +290,8 @@ function png (options) {
|
||||
* @param {Number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
|
||||
* @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 {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
@ -299,14 +301,14 @@ function webp (options) {
|
||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||
this.options.webpQuality = options.quality;
|
||||
} else {
|
||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
||||
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||
}
|
||||
}
|
||||
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 new Error('Invalid webp alpha quality (integer, 0-100) ' + options.alphaQuality);
|
||||
throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
|
||||
}
|
||||
}
|
||||
if (is.object(options) && is.defined(options.lossless)) {
|
||||
@ -315,6 +317,16 @@ function webp (options) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
return this._updateFormatOut('webp', options);
|
||||
}
|
||||
|
||||
|
@ -760,6 +760,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
->set("Q", baton->webpQuality)
|
||||
->set("lossless", baton->webpLossless)
|
||||
->set("near_lossless", baton->webpNearLossless)
|
||||
->set("smart_subsample", baton->webpSmartSubsample)
|
||||
->set("reduction_effort", baton->webpReductionEffort)
|
||||
->set("alpha_q", baton->webpAlphaQuality)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
@ -884,6 +886,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
->set("Q", baton->webpQuality)
|
||||
->set("lossless", baton->webpLossless)
|
||||
->set("near_lossless", baton->webpNearLossless)
|
||||
->set("smart_subsample", baton->webpSmartSubsample)
|
||||
->set("reduction_effort", baton->webpReductionEffort)
|
||||
->set("alpha_q", baton->webpAlphaQuality));
|
||||
baton->formatOut = "webp";
|
||||
} else if (baton->formatOut == "tiff" || (mightMatchInput && isTiff) ||
|
||||
@ -938,7 +942,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
{"Q", std::to_string(baton->webpQuality)},
|
||||
{"alpha_q", std::to_string(baton->webpAlphaQuality)},
|
||||
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
||||
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"}
|
||||
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"},
|
||||
{"smart_subsample", baton->webpSmartSubsample ? "TRUE" : "FALSE"},
|
||||
{"reduction_effort", std::to_string(baton->webpReductionEffort)}
|
||||
};
|
||||
suffix = AssembleSuffixString(".webp", options);
|
||||
} else {
|
||||
@ -1345,6 +1351,8 @@ NAN_METHOD(pipeline) {
|
||||
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
||||
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
||||
baton->webpSmartSubsample = AttrTo<bool>(options, "webpSmartSubsample");
|
||||
baton->webpReductionEffort = AttrTo<uint32_t>(options, "webpReductionEffort");
|
||||
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
||||
baton->tiffPyramid = AttrTo<bool>(options, "tiffPyramid");
|
||||
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
|
||||
|
@ -138,6 +138,8 @@ struct PipelineBaton {
|
||||
int webpAlphaQuality;
|
||||
bool webpNearLossless;
|
||||
bool webpLossless;
|
||||
bool webpSmartSubsample;
|
||||
int webpReductionEffort;
|
||||
int tiffQuality;
|
||||
VipsForeignTiffCompression tiffCompression;
|
||||
VipsForeignTiffPredictor tiffPredictor;
|
||||
@ -241,6 +243,11 @@ struct PipelineBaton {
|
||||
pngColours(256),
|
||||
pngDither(1.0),
|
||||
webpQuality(80),
|
||||
webpAlphaQuality(100),
|
||||
webpNearLossless(false),
|
||||
webpLossless(false),
|
||||
webpSmartSubsample(false),
|
||||
webpReductionEffort(4),
|
||||
tiffQuality(80),
|
||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
||||
|
@ -75,4 +75,54 @@ describe('WebP', function () {
|
||||
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should produce a larger file size using smartSubsample', () =>
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.webp({ smartSubsample: false })
|
||||
.toBuffer()
|
||||
.then(withoutSmartSubsample => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.webp({ smartSubsample: true })
|
||||
.toBuffer()
|
||||
.then(withSmartSubsample => {
|
||||
assert.strictEqual(true, withSmartSubsample.length > withoutSmartSubsample.length);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
it('invalid smartSubsample throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ smartSubsample: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
it('should produce a smaller file size with increased reductionEffort', () =>
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.webp()
|
||||
.toBuffer()
|
||||
.then(reductionEffort4 => {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.webp({ reductionEffort: 6 })
|
||||
.toBuffer()
|
||||
.then(reductionEffort6 => {
|
||||
assert.strictEqual(true, reductionEffort4.length > reductionEffort6.length);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
it('invalid reductionEffort throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ reductionEffort: true });
|
||||
});
|
||||
});
|
||||
|
||||
it('out of range reductionEffort throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ reductionEffort: -1 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user