#52788 closed feature request (maybelater)
Add JPEG XL support.
| Reported by: | atjn | Owned by: | adamsilverstein |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | Media | Version: | |
| Severity: | normal | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: | performance |
Description
JPEG XL is a new image format that beats JPEG, PNG, WebP and AVIF in pretty much all categories. I believe it will be widely used on the internet in a few years.
Cloudinary has made an awesome overview that shows why JPEG XL is better than anything else. Please note that they helped make the format, so they could be biased, but I haven't found any issues with it.
First and foremost it would be great to support uploading it to media, but down the line I think it could be used for much more. Thumbnails of any other image type could be generated in JPEG XL, greatly improving performance. It might even make sense to support delivering any source image as a JPEG XL, no matter the original encoding.
Here are a few points I think are especially nice for WordPress:
- Encoding speed is better than WebP and AVIF, so there's no risk that users would have to upgrade their server to use it.
- It supports progressive decode which is huge for perceived performance, and could theoretically allow you to only produce one large thumbnail and then just not load the entire image when you need a smaller preview.
- It is expected to be widely supported, meaning users can use it as their main local editing format, they can download and directly view the images, and it will work in all browsers. This is not the case for WebP and AVIF, which has caused a lot of frustration among developers and end-users.
JPEG XL is, as of writing, not supported in any browsers, but will almost certainly be soon. Chromium has marked it as their highest priority. You can see the current status here: caniuse.com/jpegxl
The official JPEG XL page is here: jpeg.org/jpegxl
A free and open source encoder/decoder is in the works: gitlab.com/wg1/jpeg-xl
Change History (11)
#3
follow-up:
↓ 4
@
5 years ago
Having a quick look into this, it seems like neither of the imagick or gd support this codex. So until they support it, feels like it unlikely core can.
#4
in reply to: ↑ 3
@
5 years ago
Replying to spacedmonkey
Good point! As far as I can tell, it is already supported in imagick: https://imagemagick.org/script/formats.php#supported
If JPEG XL becomes widely supported in browsers, I would expect GD to add support, the same way they have done with WebP and AVIF.
#5
@
5 years ago
You can now enable experimental JPEG XL support in Chrome, Edge, and Firefox: https://caniuse.com/jpegxl
#6
@
5 years ago
Noting that JPEGXL support is being proposed for libgd in https://github.com/libgd/libgd/issues/699.
#7
@
4 years ago
- Milestone Future Release
- Resolution → maybelater
- Status assigned → closed
We're probably years away from widespread JPEG XL support in browsers as the majority of browsers do not support it natively. Once the browser and gd added support for JPEG XL then we can check if WordPress core allows it to not.
For now, mark it as maybelater. Feel free to open it if you think you have any solutions or feedback.
#9
@
4 years ago
Chrome, Firefox, Edge, Brave support JXL via developer flags / development builds. Native browser support is on the near horizon.
https://jpegxl.io/articles/browser-support/
This ticket was mentioned in PR #12006 on WordPress/wordpress-develop by @adamsilverstein.
3 weeks ago
#10
- Keywords has-patch has-unit-tests added
## Summary
- Register
image/jxlas an allowed upload MIME type. - Add
wp_is_jxl_file(), a magic-bytes check that recognizes both JXL flavors (naked codestream + ISOBMFF container). - Add
wp_filter_jxl_filetype_and_ext()onwp_check_filetype_and_extto restore the canonicalimage/jxlMIME when PHP'sfileinforeports the non-canonicalimage/x-jxlform. - Accept
original-jxlas a sideloadimage_size. The HEIC and JXL branches insideload_item()are collapsed since both write to$metadata['original']and are mutually exclusive. - Rename
wp_delete_attachment_heic_companion_file()→wp_delete_attachment_preserved_original_companion_file()since the samedelete_attachmenthook now covers both HEIC and JXL companions. Implementation unchanged; only the name and docblock are generalized. The test file is renamed alongside, and a JXL-companion deletion test is added.
## Why
JPEG XL is a modern image format that can losslessly transcode existing JPEGs at significantly smaller size. WordPress core does not recognize .jxl uploads today:
image/jxlis not in the defaultupload_mimeslist, so the upload is rejected before any conversion can happen.getimagesize()cannot identify JXL files. PHP'sfileinfoextension reports them asimage/x-jxl(not the registeredimage/jxl), sowp_check_filetype_and_ext()flags the upload as MIME-mismatched and rejects it.
When client-side media processing is enabled, the browser decodes the uploaded JXL to a JPEG using a lazy-loaded VIPS/WASM module and uploads the JPEG as the primary attachment. The original .jxl is preserved as a companion file under $metadata[https://github.com/WordPress/wordpress-develop/pull/11323 'original'], exactly like the HEIC companion-original pattern in #11323.
This PR is the server-only backport of WordPress/gutenberg#77584. The lazy WASM loader, VIPS worker, store actions, and editor UI ship through the normal Gutenberg → Core package sync.
## Base
Stacked on #11323 (HEIC canvas fallback) — this PR generalizes the HEIC cleanup function so it can serve both formats. Merge #11323 first, then rebase this branch onto trunk.
## Backport scope
### src/wp-includes/media.php
- New
wp_is_jxl_file( $file )— 12-byte magic-bytes check; recognizes both JXL flavors. - New
wp_add_jxl_upload_mimes( $mimes )— adds'jxl' => 'image/jxl'. - New
wp_filter_jxl_filetype_and_ext( $data, $file, $filename )— restoresimage/jxlwhen fileinfo returnedimage/x-jxl(or nothing) and the magic bytes confirm it. wp_delete_attachment_heic_companion_file()→wp_delete_attachment_preserved_original_companion_file(). Implementation identical; docblock generalized.
### src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php
register_routes()— addsoriginal-jxlto the sideloadimage_sizeenum.sideload_item()— collapses theoriginal-heicbranch withoriginal-jxl(both write to$metadata['original']).
### src/wp-includes/default-filters.php
add_filter( 'upload_mimes', 'wp_add_jxl_upload_mimes' );add_filter( 'wp_check_filetype_and_ext', 'wp_filter_jxl_filetype_and_ext', 10, 3 );delete_attachmentaction updated to call the renamed function.
## Security model
The companion-file cleanup carries the existing HEIC hardening unchanged:
wp_basename()always strips path components from the recorded metadata before joining.wp_delete_file_from_directory()requires the resolved path to be a regular file strictly insidewp_get_upload_dir()['basedir'].
The new magic-bytes check reads only the first 12 bytes via fopen( 'rb' ) / fread() and closes the handle immediately. It runs on the uploaded temp file before the file is moved into the uploads directory, so it never touches a user-controlled path.
The wp_filter_jxl_filetype_and_ext() filter is conservative: it only acts when core has already failed to identify the file (empty( $data['type'] )) AND the extension is .jxl AND the magic bytes match. A JPEG renamed to .jxl is not rescued.
## Test plan
- [ ]
vendor/bin/phpunit tests/phpunit/tests/media/wpJxlUpload.php— 10 new tests pass (magic-bytes for naked + ISOBMFF + JPEG + PNG + short file + missing file; upload_mimes filter; filetype filter happy path, already-recognized passthrough, and JXL-extension-with-wrong-magic rejection). - [ ]
vendor/bin/phpunit tests/phpunit/tests/media/wpDeleteAttachmentPreservedOriginalCompanionFile.php— existing HEIC tests still pass + new JXL companion deletion test passes. - [ ] Manual: with client-side media processing enabled in Chromium 137+, upload a real
.jxlfile. Confirm the JPEG derivative is the primary attachment and the.jxlis preserved on disk and recorded in_wp_attachment_metadata['original']. - [ ] Manual: rename a JPEG to
evil.jxland attempt to upload. Confirm the upload is rejected (magic bytes fail). - [ ] Manual: delete a JXL-derived attachment. Confirm both the JPEG and the
.jxlare removed. - [ ] Manual: delete a HEIC-derived attachment (regression check). Confirm the HEIC companion is still removed via the renamed hook.
## Related
- Upstream: WordPress/gutenberg#77584 (OPEN, currently CONFLICTING with trunk). Keep this PR in draft until the GB PR merges.
- Stacked on: #11323 — HEIC canvas fallback. This PR renames the function added there.
- Part of the 7.1 client-side media reintroduction: #11324.
@adamsilverstein commented on PR #12006:
3 weeks ago
#11
Marking as draft since we are punting JXL support for client side media for now, see https://github.com/WordPress/gutenberg/pull/77584
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
If you want to play around with it, I can recommend Squoosh. Just remember that the encoder is in early beta, so it will be slower and produce larger files than it should do.