mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Expose WebP alpha quality, lossless and near-lossless output options (#685)
This commit is contained in:
parent
815d076b35
commit
a1b8efe721
@ -115,6 +115,9 @@ Use these WebP options for output image.
|
|||||||
|
|
||||||
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
|
- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** output options
|
||||||
- `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`)
|
- `options.quality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality, integer 1-100 (optional, default `80`)
|
||||||
|
- `options.alphaQuality` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)?** quality of alpha layer, integer 0-100 (optional, default `100`)
|
||||||
|
- `options.lossless` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use lossless compression mode (optional, default `false`)
|
||||||
|
- `options.nearLossless` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** use near_lossless compression mode (optional, default `false`)
|
||||||
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
- `options.force` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** force WebP output, otherwise attempt to use input format (optional, default `true`)
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,6 +145,9 @@ const Sharp = function (input, options) {
|
|||||||
pngCompressionLevel: 6,
|
pngCompressionLevel: 6,
|
||||||
pngAdaptiveFiltering: true,
|
pngAdaptiveFiltering: true,
|
||||||
webpQuality: 80,
|
webpQuality: 80,
|
||||||
|
webpAlphaQuality: 100,
|
||||||
|
webpLossless: false,
|
||||||
|
webpNearLossless: false,
|
||||||
tiffQuality: 80,
|
tiffQuality: 80,
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
tileOverlap: 0,
|
tileOverlap: 0,
|
||||||
|
@ -168,6 +168,9 @@ const png = function png (options) {
|
|||||||
* Use these WebP options for output image.
|
* Use these WebP options for output image.
|
||||||
* @param {Object} [options] - output options
|
* @param {Object} [options] - output options
|
||||||
* @param {Number} [options.quality=80] - quality, integer 1-100
|
* @param {Number} [options.quality=80] - quality, integer 1-100
|
||||||
|
* @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.force=true] - force WebP output, otherwise attempt to use input format
|
* @param {Boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
@ -180,6 +183,19 @@ const webp = function webp (options) {
|
|||||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (is.object(options) && is.defined(options.alphaQuality)) {
|
||||||
|
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 1, 100)) {
|
||||||
|
this.options.webpAlphaQuality = options.alphaQuality;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid webp alpha quality (integer, 1-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);
|
||||||
|
}
|
||||||
return this._updateFormatOut('webp', options);
|
return this._updateFormatOut('webp', options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -767,6 +767,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
VipsArea *area = VIPS_AREA(image.webpsave_buffer(VImage::option()
|
VipsArea *area = VIPS_AREA(image.webpsave_buffer(VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("Q", baton->webpQuality)
|
->set("Q", baton->webpQuality)
|
||||||
|
->set("lossless", baton->webpLossless)
|
||||||
|
->set("near_lossless", baton->webpNearLossless)
|
||||||
|
->set("alpha_q", baton->webpAlphaQuality)
|
||||||
));
|
));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
baton->bufferOutLength = area->length;
|
baton->bufferOutLength = area->length;
|
||||||
@ -844,6 +847,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.webpsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
->set("Q", baton->webpQuality)
|
->set("Q", baton->webpQuality)
|
||||||
|
->set("lossless", baton->webpLossless)
|
||||||
|
->set("near_lossless", baton->webpNearLossless)
|
||||||
|
->set("alpha_q", baton->webpAlphaQuality)
|
||||||
);
|
);
|
||||||
baton->formatOut = "webp";
|
baton->formatOut = "webp";
|
||||||
} else if (baton->formatOut == "tiff" || isTiff || (matchInput && inputImageType == ImageType::TIFF)) {
|
} else if (baton->formatOut == "tiff" || isTiff || (matchInput && inputImageType == ImageType::TIFF)) {
|
||||||
@ -870,7 +876,10 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
suffix = AssembleSuffixString(".png", options);
|
suffix = AssembleSuffixString(".png", options);
|
||||||
} else if (baton->tileFormat == "webp") {
|
} else if (baton->tileFormat == "webp") {
|
||||||
std::vector<std::pair<std::string, std::string>> options {
|
std::vector<std::pair<std::string, std::string>> options {
|
||||||
{"Q", std::to_string(baton->webpQuality)}
|
{"Q", std::to_string(baton->webpQuality)},
|
||||||
|
{"alpha_q", std::to_string(baton->webpAlphaQuality)},
|
||||||
|
{"lossless", baton->webpLossless ? "TRUE" : "FALSE"},
|
||||||
|
{"near_lossless", baton->webpNearLossless ? "TRUE" : "FALSE"}
|
||||||
};
|
};
|
||||||
suffix = AssembleSuffixString(".webp", options);
|
suffix = AssembleSuffixString(".webp", options);
|
||||||
} else {
|
} else {
|
||||||
@ -1209,6 +1218,9 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
baton->pngCompressionLevel = AttrTo<uint32_t>(options, "pngCompressionLevel");
|
||||||
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
|
baton->pngAdaptiveFiltering = AttrTo<bool>(options, "pngAdaptiveFiltering");
|
||||||
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
|
baton->webpQuality = AttrTo<uint32_t>(options, "webpQuality");
|
||||||
|
baton->webpAlphaQuality = AttrTo<uint32_t>(options, "webpAlphaQuality");
|
||||||
|
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||||
|
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
||||||
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
||||||
// Tile output
|
// Tile output
|
||||||
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
||||||
|
@ -84,6 +84,9 @@ struct PipelineBaton {
|
|||||||
int pngCompressionLevel;
|
int pngCompressionLevel;
|
||||||
bool pngAdaptiveFiltering;
|
bool pngAdaptiveFiltering;
|
||||||
int webpQuality;
|
int webpQuality;
|
||||||
|
int webpAlphaQuality;
|
||||||
|
bool webpNearLossless;
|
||||||
|
bool webpLossless;
|
||||||
int tiffQuality;
|
int tiffQuality;
|
||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
|
BIN
test/fixtures/expected/webp-alpha-80.webp
vendored
Normal file
BIN
test/fixtures/expected/webp-alpha-80.webp
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
BIN
test/fixtures/expected/webp-lossless.webp
vendored
Normal file
BIN
test/fixtures/expected/webp-lossless.webp
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 170 KiB |
BIN
test/fixtures/expected/webp-near-lossless-50.webp
vendored
Normal file
BIN
test/fixtures/expected/webp-near-lossless-50.webp
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
@ -372,6 +372,50 @@ describe('Input/output', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work for webp alpha quality', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({alphaQuality: 80})
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-alpha-80.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for webp lossless', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({lossless: true})
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual('webp', info.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-lossless.webp'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work for webp near-lossless', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({nearLossless: true, quality: 50})
|
||||||
|
.toBuffer(function (err50, data50, info50) {
|
||||||
|
if (err50) throw err50;
|
||||||
|
assert.strictEqual(true, data50.length > 0);
|
||||||
|
assert.strictEqual('webp', info50.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use near-lossless when both lossless and nearLossless are specified', function (done) {
|
||||||
|
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||||
|
.webp({nearLossless: true, quality: 50, lossless: true})
|
||||||
|
.toBuffer(function (err50, data50, info50) {
|
||||||
|
if (err50) throw err50;
|
||||||
|
assert.strictEqual(true, data50.length > 0);
|
||||||
|
assert.strictEqual('webp', info50.format);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('webp-near-lossless-50.webp'), data50, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('Invalid output format', function (done) {
|
it('Invalid output format', function (done) {
|
||||||
@ -735,6 +779,12 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Invalid WebP alpha quality throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().webp({ alphaQuality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Invalid TIFF quality throws error', function () {
|
it('Invalid TIFF quality throws error', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tiff({ quality: 101 });
|
sharp().tiff({ quality: 101 });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user