Skip to content

Commit 0d872bd

Browse files
committed
Add WebP 'exact' option for control over transparent pixels
1 parent 1cf4b7f commit 0d872bd

File tree

9 files changed

+47
-4
lines changed

9 files changed

+47
-4
lines changed

docs/src/content/docs/api-output.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ Use these WebP options for output image.
563563
| [options.delay] | <code>number</code> \| <code>Array.&lt;number&gt;</code> | | delay(s) between animation frames (in milliseconds) |
564564
| [options.minSize] | <code>boolean</code> | <code>false</code> | prevent use of animation key frames to minimise file size (slow) |
565565
| [options.mixed] | <code>boolean</code> | <code>false</code> | allow mixture of lossy and lossless animation frames (slow) |
566+
| [options.exact] | <code>boolean</code> | <code>false</code> | preserve the colour data in transparent pixels |
566567
| [options.force] | <code>boolean</code> | <code>true</code> | force WebP output, otherwise attempt to use input format |
567568

568569
**Example**

docs/src/content/docs/changelog/v0.35.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ slug: changelog/v0.35.0
2323

2424
* Add `toUint8Array` for output image as a `TypedArray` backed by a transferable `ArrayBuffer`.
2525
[#4355](https://github.com/lovell/sharp/issues/4355)
26+
27+
* Add WebP `exact` option for control over transparent pixel colour values.

lib/constructor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ const Sharp = function (input, options) {
350350
webpEffort: 4,
351351
webpMinSize: false,
352352
webpMixed: false,
353+
webpExact: false,
353354
gifBitdepth: 8,
354355
gifEffort: 7,
355356
gifDither: 1,

lib/index.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,11 +1394,13 @@ declare namespace sharp {
13941394
/** Level of CPU effort to reduce file size, integer 0-6 (optional, default 4) */
13951395
effort?: number | undefined;
13961396
/** Prevent use of animation key frames to minimise file size (slow) (optional, default false) */
1397-
minSize?: boolean;
1397+
minSize?: boolean | undefined;
13981398
/** Allow mixture of lossy and lossless animation frames (slow) (optional, default false) */
1399-
mixed?: boolean;
1399+
mixed?: boolean | undefined;
14001400
/** Preset options: one of default, photo, picture, drawing, icon, text (optional, default 'default') */
14011401
preset?: keyof PresetEnum | undefined;
1402+
/** Preserve the colour data in transparent pixels (optional, default false) */
1403+
exact?: boolean | undefined;
14021404
}
14031405

14041406
interface AvifOptions extends OutputOptions {

lib/output.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,7 @@ function png (options) {
749749
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
750750
* @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
751751
* @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
752+
* @param {boolean} [options.exact=false] - preserve the colour data in transparent pixels
752753
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
753754
* @returns {Sharp}
754755
* @throws {Error} Invalid options
@@ -801,6 +802,9 @@ function webp (options) {
801802
if (is.defined(options.mixed)) {
802803
this._setBooleanOption('webpMixed', options.mixed);
803804
}
805+
if (is.defined(options.exact)) {
806+
this._setBooleanOption('webpExact', options.exact);
807+
}
804808
}
805809
trySetAnimationOptions(options, this.options);
806810
return this._updateFormatOut('webp', options);

src/pipeline.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ class PipelineWorker : public Napi::AsyncWorker {
965965
->set("effort", baton->webpEffort)
966966
->set("min_size", baton->webpMinSize)
967967
->set("mixed", baton->webpMixed)
968+
->set("exact", baton->webpExact)
968969
->set("alpha_q", baton->webpAlphaQuality)));
969970
baton->bufferOut = static_cast<char*>(area->data);
970971
baton->bufferOutLength = area->length;
@@ -1176,6 +1177,7 @@ class PipelineWorker : public Napi::AsyncWorker {
11761177
->set("effort", baton->webpEffort)
11771178
->set("min_size", baton->webpMinSize)
11781179
->set("mixed", baton->webpMixed)
1180+
->set("exact", baton->webpExact)
11791181
->set("alpha_q", baton->webpAlphaQuality));
11801182
baton->formatOut = "webp";
11811183
} else if (baton->formatOut == "gif" || (mightMatchInput && isGif) ||
@@ -1486,6 +1488,7 @@ class PipelineWorker : public Napi::AsyncWorker {
14861488
{"preset", vips_enum_nick(VIPS_TYPE_FOREIGN_WEBP_PRESET, baton->webpPreset)},
14871489
{"min_size", baton->webpMinSize ? "true" : "false"},
14881490
{"mixed", baton->webpMixed ? "true" : "false"},
1491+
{"exact", baton->webpExact ? "true" : "false"},
14891492
{"effort", std::to_string(baton->webpEffort)}
14901493
};
14911494
suffix = AssembleSuffixString(".webp", options);
@@ -1760,6 +1763,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
17601763
baton->webpEffort = sharp::AttrAsUint32(options, "webpEffort");
17611764
baton->webpMinSize = sharp::AttrAsBool(options, "webpMinSize");
17621765
baton->webpMixed = sharp::AttrAsBool(options, "webpMixed");
1766+
baton->webpExact = sharp::AttrAsBool(options, "webpExact");
17631767
baton->gifBitdepth = sharp::AttrAsUint32(options, "gifBitdepth");
17641768
baton->gifEffort = sharp::AttrAsUint32(options, "gifEffort");
17651769
baton->gifDither = sharp::AttrAsDouble(options, "gifDither");

src/pipeline.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ struct PipelineBaton {
168168
int webpEffort;
169169
bool webpMinSize;
170170
bool webpMixed;
171+
bool webpExact;
171172
int gifBitdepth;
172173
int gifEffort;
173174
double gifDither;
@@ -347,6 +348,7 @@ struct PipelineBaton {
347348
webpEffort(4),
348349
webpMinSize(false),
349350
webpMixed(false),
351+
webpExact(false),
350352
gifBitdepth(8),
351353
gifEffort(7),
352354
gifDither(1.0),

test/types/sharp.test-d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,8 +548,8 @@ sharp('input.tiff').jxl({ decodingTier: 4 }).toFile('out.jxl');
548548
sharp('input.tiff').jxl({ lossless: true }).toFile('out.jxl');
549549
sharp('input.tiff').jxl({ effort: 7 }).toFile('out.jxl');
550550

551-
// Support `minSize` and `mixed` webp options
552-
sharp('input.tiff').webp({ minSize: true, mixed: true }).toFile('out.gif');
551+
// Support webp options
552+
sharp('input.tiff').webp({ minSize: true, mixed: true, exact: true }).toFile('out.webp');
553553

554554
// 'failOn' input param
555555
sharp('input.tiff', { failOn: 'none' });

test/unit/webp.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,33 @@ describe('WebP', () => {
213213
);
214214
});
215215

216+
it('valid exact', () => {
217+
assert.doesNotThrow(() => sharp().webp({ exact: true }));
218+
});
219+
220+
it('invalid exact throws', () => {
221+
assert.throws(
222+
() => sharp().webp({ exact: 'fail' }),
223+
/Expected boolean for webpExact but received fail of type string/
224+
);
225+
});
226+
227+
it('saving exact pixel colour values produces larger file size', async () => {
228+
const withExact = await
229+
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
230+
.resize(8, 8)
231+
.webp({ exact: true, effort: 0 })
232+
.toBuffer();
233+
234+
const withoutExact = await
235+
sharp(fixtures.inputPngAlphaPremultiplicationSmall)
236+
.resize(8, 8)
237+
.webp({ exact: false, effort: 0 })
238+
.toBuffer()
239+
240+
assert.strictEqual(true, withExact.length > withoutExact.length);
241+
});
242+
216243
it('invalid loop throws', () => {
217244
assert.throws(() => {
218245
sharp().webp({ loop: -1 });

0 commit comments

Comments
 (0)