mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 05:36:18 +01:00
Add WebP 'exact' option for control over transparent pixels
This commit is contained in:
@@ -563,6 +563,7 @@ Use these WebP options for output image.
|
||||
| [options.delay] | <code>number</code> \| <code>Array.<number></code> | | delay(s) between animation frames (in milliseconds) |
|
||||
| [options.minSize] | <code>boolean</code> | <code>false</code> | prevent use of animation key frames to minimise file size (slow) |
|
||||
| [options.mixed] | <code>boolean</code> | <code>false</code> | allow mixture of lossy and lossless animation frames (slow) |
|
||||
| [options.exact] | <code>boolean</code> | <code>false</code> | preserve the colour data in transparent pixels |
|
||||
| [options.force] | <code>boolean</code> | <code>true</code> | force WebP output, otherwise attempt to use input format |
|
||||
|
||||
**Example**
|
||||
|
||||
@@ -23,3 +23,5 @@ slug: changelog/v0.35.0
|
||||
|
||||
* Add `toUint8Array` for output image as a `TypedArray` backed by a transferable `ArrayBuffer`.
|
||||
[#4355](https://github.com/lovell/sharp/issues/4355)
|
||||
|
||||
* Add WebP `exact` option for control over transparent pixel colour values.
|
||||
|
||||
@@ -350,6 +350,7 @@ const Sharp = function (input, options) {
|
||||
webpEffort: 4,
|
||||
webpMinSize: false,
|
||||
webpMixed: false,
|
||||
webpExact: false,
|
||||
gifBitdepth: 8,
|
||||
gifEffort: 7,
|
||||
gifDither: 1,
|
||||
|
||||
6
lib/index.d.ts
vendored
6
lib/index.d.ts
vendored
@@ -1394,11 +1394,13 @@ declare namespace sharp {
|
||||
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
|
||||
effort?: number | undefined;
|
||||
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
|
||||
minSize?: boolean;
|
||||
minSize?: boolean | undefined;
|
||||
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
|
||||
mixed?: boolean;
|
||||
mixed?: boolean | undefined;
|
||||
/** Preset options: one of default, photo, picture, drawing, icon, text (optional, default 'default') */
|
||||
preset?: keyof PresetEnum | undefined;
|
||||
/** Preserve the colour data in transparent pixels (optional, default false) */
|
||||
exact?: boolean | undefined;
|
||||
}
|
||||
|
||||
interface AvifOptions extends OutputOptions {
|
||||
|
||||
@@ -749,6 +749,7 @@ function png (options) {
|
||||
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
|
||||
* @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
|
||||
* @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
|
||||
* @param {boolean} [options.exact=false] - preserve the colour data in transparent pixels
|
||||
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
@@ -801,6 +802,9 @@ function webp (options) {
|
||||
if (is.defined(options.mixed)) {
|
||||
this._setBooleanOption('webpMixed', options.mixed);
|
||||
}
|
||||
if (is.defined(options.exact)) {
|
||||
this._setBooleanOption('webpExact', options.exact);
|
||||
}
|
||||
}
|
||||
trySetAnimationOptions(options, this.options);
|
||||
return this._updateFormatOut('webp', options);
|
||||
|
||||
@@ -965,6 +965,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("min_size", baton->webpMinSize)
|
||||
->set("mixed", baton->webpMixed)
|
||||
->set("exact", baton->webpExact)
|
||||
->set("alpha_q", baton->webpAlphaQuality)));
|
||||
baton->bufferOut = static_cast<char*>(area->data);
|
||||
baton->bufferOutLength = area->length;
|
||||
@@ -1176,6 +1177,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
->set("effort", baton->webpEffort)
|
||||
->set("min_size", baton->webpMinSize)
|
||||
->set("mixed", baton->webpMixed)
|
||||
->set("exact", baton->webpExact)
|
||||
->set("alpha_q", baton->webpAlphaQuality));
|
||||
baton->formatOut = "webp";
|
||||
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
|
||||
@@ -1486,6 +1488,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
{"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
|
||||
{"min_size", baton->webpMinSize ? "true" : "false"},
|
||||
{"mixed", baton->webpMixed ? "true" : "false"},
|
||||
{"exact", baton->webpExact ? "true" : "false"},
|
||||
{"effort", std::to_string(baton->webpEffort)}
|
||||
};
|
||||
suffix = AssembleSuffixString(".webp", options);
|
||||
@@ -1760,6 +1763,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
|
||||
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
|
||||
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
|
||||
baton->webpExact = sharp::AttrAsBool(options, "webpExact");
|
||||
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
|
||||
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
|
||||
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");
|
||||
|
||||
@@ -168,6 +168,7 @@ struct PipelineBaton {
|
||||
int webpEffort;
|
||||
bool webpMinSize;
|
||||
bool webpMixed;
|
||||
bool webpExact;
|
||||
int gifBitdepth;
|
||||
int gifEffort;
|
||||
double gifDither;
|
||||
@@ -347,6 +348,7 @@ struct PipelineBaton {
|
||||
webpEffort(4),
|
||||
webpMinSize(false),
|
||||
webpMixed(false),
|
||||
webpExact(false),
|
||||
gifBitdepth(8),
|
||||
gifEffort(7),
|
||||
gifDither(1.0),
|
||||
|
||||
@@ -548,8 +548,8 @@ sharp('input.tiff').jxl({ decodingTier: 4 }).toFile('out.jxl');
|
||||
sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
|
||||
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
|
||||
|
||||
// Support `minSize` and `mixed` webp options
|
||||
sharp('input.tiff').webp({ minSize: true, mixed: true }).toFile('out.gif');
|
||||
// Support webp options
|
||||
sharp('input.tiff').webp({ minSize: true, mixed: true, exact: true }).toFile('out.webp');
|
||||
|
||||
// 'failOn' input param
|
||||
sharp('input.tiff', { failOn: 'none' });
|
||||
|
||||
@@ -213,6 +213,33 @@ describe('WebP', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('valid exact', () => {
|
||||
assert.doesNotThrow(() => sharp().webp({ exact: true }));
|
||||
});
|
||||
|
||||
it('invalid exact throws', () => {
|
||||
assert.throws(
|
||||
() => sharp().webp({ exact: 'fail' }),
|
||||
/Expected boolean for webpExact but received fail of type string/
|
||||
);
|
||||
});
|
||||
|
||||
it('saving exact pixel colour values produces larger file size', async () => {
|
||||
const withExact = await
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.resize(8, 8)
|
||||
.webp({ exact: true, effort: 0 })
|
||||
.toBuffer();
|
||||
|
||||
const withoutExact = await
|
||||
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
|
||||
.resize(8, 8)
|
||||
.webp({ exact: false, effort: 0 })
|
||||
.toBuffer()
|
||||
|
||||
assert.strictEqual(true, withExact.length > withoutExact.length);
|
||||
});
|
||||
|
||||
it('invalid loop throws', () => {
|
||||
assert.throws(() => {
|
||||
sharp().webp({ loop: -1 });
|
||||
|
||||
Reference in New Issue
Block a user