Skip to content

Vips: bump wasm-vips to 0.0.18 for high-bit-depth AVIF decoding#79179

Merged
adamsilverstein merged 3 commits into
trunkfrom
update/wasm-vips-0.0.18
Jun 19, 2026
Merged

Vips: bump wasm-vips to 0.0.18 for high-bit-depth AVIF decoding#79179
adamsilverstein merged 3 commits into
trunkfrom
update/wasm-vips-0.0.18

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jun 15, 2026

Copy link
Copy Markdown
Member

What?

Bumps wasm-vips from ^0.0.17 to ^0.0.18 in @wordpress/vips, enabling native decoding of 10- and 12-bit (high bit depth) AVIF images, and adds an integration test that decodes real high-bit-depth AVIF fixtures.

Closes #78889

Why?

Client-side media processing could not handle high-bit-depth / HDR AVIF uploads because the bundled wasm-vips build could not decode them. kleisauke/wasm-vips#118 ("Enable HDR AVIF support, allows bit depths of 10 and 12") fixes this upstream by linking a libaom built with CONFIG_AV1_HIGHBITDEPTH=1, and it shipped in [email protected].

This is the upstream-supported alternative to vendoring a custom build in #78894, which can now be closed. It drops a large checked-in custom WASM binary and the multi-threaded/Web-Worker model that build required; the stock wasm-vips build is single-threaded and structurally identical to 0.0.17 (same vips.wasm / vips-heif.wasm module files and package exports), so no source changes are needed beyond the version bump.

How?

  • Bump the wasm-vips dependency to ^0.0.18 and update the lock file.
  • Add an integration test (packages/vips/src/test/highbitdepth-avif.ts) plus 10-, 12-, and 8-bit AVIF fixtures. Unlike the other tests in this package, it does not mock wasm-vips, because high-bit-depth decoding happens entirely inside the WebAssembly module; it runs in the Node test environment so the package's Node build is used (no Web Worker).

Testing Instructions

Verified locally that the bump fixes the decode (10/12-bit gradient AVIFs generated with avifenc --depth 10/12):

Version 10-bit AVIF 12-bit AVIF
[email protected] (before) ❌ fails (error in tile decode errors) ❌ fails
[email protected] (this PR) ✅ decodes to a 16-bit (ushort / rgb16) image ✅ decodes

To run the new test:

npm run test:unit -- packages/vips/src/test/highbitdepth-avif.ts

All three vips suites pass (28 tests).

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @kleisauke, @gregbenz.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: kleisauke, gregbenz.

Co-authored-by: adamsilverstein <[email protected]>
Co-authored-by: swissspidy <[email protected]>
Co-authored-by: andrewserong <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

Size Change: -547 kB (-6.36%) ✅

Total Size: 8.05 MB

📦 View Changed
Filename Size Change
build/modules/vips/worker.min.js 4.24 MB -547 kB (-11.42%) 👏

compressed-size-action

@swissspidy swissspidy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code changes look sensible, haven't tested it though.

@adamsilverstein

Copy link
Copy Markdown
Member Author

@gregbenz this should fix the high bit AVIF handling, it pulls in the upstream update.

@kleisauke

Copy link
Copy Markdown
Contributor

Great!

Size Change: -547 kB (-6.37%) ✅

FWIW, this size reduction is likely understated, since it doesn't appear to include the Wasm binaries. See for example:
https://packagephobia.com/result?p=wasm-vips

@kleisauke

Copy link
Copy Markdown
Contributor

Ah, sorry, the Wasm files are inlined at build time as base64 data URLs, which don't compress well. Emscripten's -sSINGLE_FILE_BINARY_ENCODE option would probably(?) help with this.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Ah, sorry, the Wasm files are inlined at build time as base64 data URLs, which don't compress well. Emscripten's -sSINGLE_FILE_BINARY_ENCODE option would probably(?) help with this.

I'll give that a try!

@adamsilverstein

adamsilverstein commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Emscripten's -sSINGLE_FILE_BINARY_ENCODE option would probably(?) help with this.

Gave it a try — it's a solid win. 👍

One clarification on our setup first: we don't use Emscripten's SINGLE_FILE here. wasm-vips ships the .wasm binaries as separate files, and Gutenberg inlines them itself in packages/wp-build/lib/build.mjs (the wasmInlinePlugin), currently as data:application/wasm;base64,… data URLs. So the fix isn't an Emscripten flag for us — it's applying the same idea @kleisauke described (Emscripten's custom UTF-8 binary embedding instead of base64) inside our own inline plugin.

I measured both encodings on the 0.0.18 vips.wasm + vips-heif.wasm (the two binaries inlined into worker.min.js), using a faithful port of Emscripten's binary_encode(), then compressed the result:

Encoding gzip brotli
base64 (current) 4,122 kB 3,210 kB
binary (UTF-8) 3,377 kB 2,580 kB
saved −745 kB (−18.1%) −631 kB (−19.6%)

The bundle-size bot uses gzip, so the headline is ~745 kB (~18%) off the compressed worker.min.js, on top of this PR's reduction. (My base64 gzip estimate of 4.12 MB lines up with the bot's reported 4.24 MB, so the method checks out.)

This is unrelated to the AVIF bump, so I'll track it separately in #79187 rather than expand the scope here.

Implementation note for whoever picks it up: it's not a drop-in. The binary-encoded string isn't a fetchable data URL, so locateFile in packages/vips/src/index.ts would need to decode it to bytes and hand wasm-vips either wasmBinary (for the main module) or a Blob URL (for the vips-heif.wasm dynamic library).

Thanks for the pointer, @kleisauke!


Update: implemented in #79188, with a matching investigation into @kleisauke's Brotli note. Through a faithful reproduction of the full build pipeline the accurate reduction is ~13% gzip / ~16% brotli on worker.min.js (the ~18% above was a standalone payload estimate; the real file wraps the worker in a JSON.stringify layer). One tradeoff: uncompressed size grows ~16%. Details in #79187 / #79188.

wasm-vips 0.0.18 links a libaom built with CONFIG_AV1_HIGHBITDEPTH=1,
adding native decoding of 10- and 12-bit AVIF images
(kleisauke/wasm-vips#118). 0.0.17 fails on these files with "error in
tile" decode errors, which is why client-side media processing could
not handle high-bit-depth/HDR AVIF uploads.

Add an integration test that decodes real 10-, 12-, and 8-bit AVIF
fixtures through the actual wasm-vips build (it cannot be mocked, since
the bit-depth handling lives entirely in the WebAssembly module).
@adamsilverstein adamsilverstein force-pushed the update/wasm-vips-0.0.18 branch from 38ffc55 to a4a32a9 Compare June 18, 2026 07:28
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

Flaky tests detected in 5aa3128.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27751578645
📝 Reported issues:

wasm-vips 0.0.18 no longer hoists to the root node_modules (it installs
under packages/vips/node_modules). This broke two things:

- test/e2e/bin/gen-ultrahdr-fixture.mjs imported `wasm-vips` by bare
  specifier, which no longer resolves from its location at runtime
  (MODULE_NOT_FOUND) and also tripped ESLint's import/no-unresolved.
  Resolve it via createRequire anchored at packages/vips, mirroring
  test/performance/specs/media-processing.spec.js, so the generator
  actually runs again and needs no lint suppression.

- packages/vips/src/test/highbitdepth-avif.ts carried a now-unused
  @typescript-eslint/no-require-imports disable that the current lint
  config flags; remove it.
@adamsilverstein adamsilverstein force-pushed the update/wasm-vips-0.0.18 branch from fedb6c7 to 5aa3128 Compare June 18, 2026 09:56
@adamsilverstein adamsilverstein merged commit bed156d into trunk Jun 19, 2026
40 checks passed
@adamsilverstein adamsilverstein deleted the update/wasm-vips-0.0.18 branch June 19, 2026 10:00
@github-project-automation github-project-automation Bot moved this from 🔎 Needs Review to ✅ Done in WordPress 7.1 Editor Tasks Jun 19, 2026
@github-actions github-actions Bot added this to the Gutenberg 23.5 milestone Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Client Side Media Media processing in the browser with WASM [Type] Bug An existing feature does not function as intended

Projects

Development

Successfully merging this pull request may close these issues.

Client-side media: high-bit-depth (10/12-bit) AVIF images fail to upload (bundled wasm-vips libaom built without high-bit-depth support)

3 participants