Skip to content

Add JPEG XL Open/Read support via libjxl - #7848

Open
olokelo wants to merge 98 commits into
python-pillow:mainfrom
olokelo:jxl-support2
Open

olokelo wants to merge 98 commits into
python-pillow:mainfrom
olokelo:jxl-support2

Conversation

@olokelo

@olokelo olokelo commented Mar 2, 2024 •

Copy link
Copy Markdown

Helps #4247

This PR enables opening and reading JPEG XL images and animations.
Supported image modes are: RGB, RGBA, RGBa, L, LA, La.
A relatively recent libjxl version is needed to compile Pillow with libjxl support.
The main changes are the addition of _jxl.c and JxlImagePlugin.py.
I'm also the author of jxlpy so this PR was influenced by the work of contributors there. This PR is also largely based on WebPImagePlugin.py which had similar implementation.

Why?
JPEG XL has recently seen increased adoption especially in Apple ecosystem. A lot of users are requesting Pillow support for JPEG XL as their products use Pillow and need to be able to handle jxl files.

I'm open to suggestions and comments. I understand such change would need a lot of testing and probably changes. After all Pillow would need to become somewhat dependent on libjxl. Creating documentation will not be a big problem however I decided to wait for feedback from Pillow core developers.

Comment thread src/PIL/features.py Outdated
Comment thread Tests/test_jxl_leaks.py Outdated
Comment thread Tests/test_file_jxl_metadata.py Outdated
Comment thread src/PIL/JxlImagePlugin.py Outdated
@olokelo olokelo mentioned this pull request Mar 2, 2024
@brunoais

brunoais commented Mar 2, 2024 •

Copy link
Copy Markdown

May you please commit the images as LFS?

@olokelo

olokelo commented Mar 2, 2024 •

Copy link
Copy Markdown
Author

Do you mean those .jxl files under Tests/images I've committed? I'm not really sure how to do that.

@brunoais

brunoais commented Mar 3, 2024

Copy link
Copy Markdown

The explanation is outlined here: https://docs.github.com/en/repositories/working-with-files/managing-large-files/configuring-git-large-file-storage
Nevermind. I just noticed they are not putting the test images as lfs:
image

They already setup lfs and they only use for other larger stuff. Let it stay as you did.

@radarhere radarhere added the JPEG label Mar 11, 2024
@olokelo

olokelo commented Mar 11, 2024

Copy link
Copy Markdown
Author

Mac OS builds were failing because clang complained about goto labels being declared before variables in scope.
I also merged @radarhere's commits that remove the jxl feature which was causing troubles before.
Now it should be fine. Almost all checks pass except codecov.

Comment thread src/PIL/JxlImagePlugin.py Outdated
Comment thread src/PIL/__init__.py Outdated
Comment thread Tests/test_file_jxl_metadata.py
Comment thread Tests/test_jxl_leaks.py Outdated
Comment thread Tests/test_jxl_leaks.py Outdated
Comment thread Tests/test_jxl_leaks.py Outdated
Comment thread src/_jxl.c
Comment thread src/PIL/JxlImagePlugin.py Outdated
Comment thread src/PIL/JxlImagePlugin.py Outdated
Comment thread src/PIL/JxlImagePlugin.py Outdated
Comment thread src/PIL/JxlImagePlugin.py
@hugovk

This comment was marked as resolved.

@olokelo

This comment was marked as resolved.

Comment thread src/PIL/JxlImagePlugin.py Outdated
Comment thread src/PIL/JxlImagePlugin.py
@hugovk

hugovk commented Apr 26, 2026

Copy link
Copy Markdown
Member

It does increase the size significantly. The current total size of our wheels is 531.5mb. In this PR, it is 730.2mb.

This seems like too much. I know we have fewer wheels right now, but it will soon increase for 3.15+3.15t, and a ~37% increase is a lot. And it will be paid by everyone, including those not using JPEG XL.

I think we should consider some of the suggestions I made at #9101 (comment).

@h-vetinari

Copy link
Copy Markdown

I think we should consider some of the suggestions I made at #9101 (comment).

I think that's a very worthwhile initiative, though IMO that should be pursued independently; IOW, I wouldn't like to see jpegxl support held up over this.

@aclark4life

Copy link
Copy Markdown
Member

I think that's a very worthwhile initiative, though IMO that should be pursued independently; IOW, I wouldn't like to see jpegxl support held up over this.

We can try to decide on and implement some alternative to "including in the wheel" prior to the next release.

@hugovk

hugovk commented Apr 27, 2026

Copy link
Copy Markdown
Member

I think that's a very worthwhile initiative, though IMO that should be pursued independently; IOW, I wouldn't like to see jpegxl support held up over this.

The thing is, the current size increase is a blocker for me.

@hugovk

hugovk commented May 13, 2026

Copy link
Copy Markdown
Member

We've added some tooling (#9621) to show the size increase of wheels compared to the last release.

This PR makes the total wheel size 46.83% bigger, with some individual wheels 89% bigger.

Dist size comparison vs Pillow 12.2.0 on PyPI

File Size before Size now Change
sdist (.tar.gz) 47.0 MB 47.4 MB 🟡 +0.90%
cp310-cp310-macosx_10_10_x86_64.whl 5.4 MB 10.1 MB 🔴 +89.01%
cp310-cp310-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +59.67%
cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB 9.6 MB 🔴 +51.13%
cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB 11.4 MB 🔴 +60.83%
cp310-cp310-musllinux_1_2_aarch64.whl 6.5 MB 10.8 MB 🔴 +66.42%
cp310-cp310-musllinux_1_2_x86_64.whl 7.2 MB 12.6 MB 🔴 +75.32%
cp310-cp310-win32.whl 6.4 MB 6.9 MB 🔴 +7.61%
cp310-cp310-win_amd64.whl 7.1 MB 7.7 MB 🔴 +8.76%
cp310-cp310-win_arm64.whl 2.5 MB 2.9 MB 🔴 +19.60%
cp311-cp311-macosx_10_10_x86_64.whl 5.4 MB 10.1 MB 🔴 +89.01%
cp311-cp311-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +59.67%
cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB 9.7 MB 🔴 +51.03%
cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB 11.4 MB 🔴 +60.74%
cp311-cp311-musllinux_1_2_aarch64.whl 6.5 MB 10.8 MB 🔴 +66.32%
cp311-cp311-musllinux_1_2_x86_64.whl 7.2 MB 12.6 MB 🔴 +75.23%
cp311-cp311-win32.whl 6.4 MB 6.9 MB 🔴 +7.61%
cp311-cp311-win_amd64.whl 7.1 MB 7.7 MB 🔴 +8.76%
cp311-cp311-win_arm64.whl 2.5 MB 2.9 MB 🔴 +19.60%
cp312-cp312-macosx_10_13_x86_64.whl 5.3 MB 10.0 MB 🔴 +88.05%
cp312-cp312-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +59.68%
cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB 9.7 MB 🔴 +51.00%
cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB 11.4 MB 🔴 +60.68%
cp312-cp312-musllinux_1_2_aarch64.whl 6.5 MB 10.8 MB 🔴 +66.25%
cp312-cp312-musllinux_1_2_x86_64.whl 7.2 MB 12.6 MB 🔴 +75.14%
cp312-cp312-win32.whl 6.4 MB 6.9 MB 🔴 +7.60%
cp312-cp312-win_amd64.whl 7.1 MB 7.7 MB 🔴 +8.76%
cp312-cp312-win_arm64.whl 2.5 MB 2.9 MB 🔴 +19.60%
cp313-cp313-ios_13_0_arm64_iphoneos.whl 4.1 MB 4.1 MB 🟡 +0.08%
cp313-cp313-ios_13_0_arm64_iphonesimulator.whl 4.2 MB 4.2 MB 🟡 +0.08%
cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl 3.6 MB 3.6 MB 🟡 +0.11%
cp313-cp313-macosx_10_13_x86_64.whl 5.3 MB 10.0 MB 🔴 +88.05%
cp313-cp313-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +59.68%
cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB 9.7 MB 🔴 +51.03%
cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB 11.4 MB 🔴 +60.73%
cp313-cp313-musllinux_1_2_aarch64.whl 6.5 MB 10.8 MB 🔴 +66.29%
cp313-cp313-musllinux_1_2_x86_64.whl 7.2 MB 12.6 MB 🔴 +75.18%
cp313-cp313-win32.whl 6.4 MB 6.9 MB 🔴 +7.61%
cp313-cp313-win_amd64.whl 7.1 MB 7.7 MB 🔴 +8.76%
cp313-cp313-win_arm64.whl 2.5 MB 2.9 MB 🔴 +19.60%
cp313-cp313t-macosx_10_13_x86_64.whl 5.3 MB n/a n/a
cp313-cp313t-macosx_11_0_arm64.whl 4.7 MB n/a n/a
cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB n/a n/a
cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB n/a n/a
cp313-cp313t-musllinux_1_2_aarch64.whl 6.5 MB n/a n/a
cp313-cp313t-musllinux_1_2_x86_64.whl 7.2 MB n/a n/a
cp313-cp313t-win32.whl 6.4 MB n/a n/a
cp313-cp313t-win_amd64.whl 7.1 MB n/a n/a
cp313-cp313t-win_arm64.whl 2.5 MB n/a n/a
cp314-cp314-ios_13_0_arm64_iphoneos.whl 4.1 MB 4.1 MB 🟡 +0.08%
cp314-cp314-ios_13_0_arm64_iphonesimulator.whl 4.2 MB 4.2 MB 🟡 +0.08%
cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl 3.6 MB 3.6 MB 🟡 +0.10%
cp314-cp314-macosx_10_15_x86_64.whl 5.3 MB 10.0 MB 🔴 +88.02%
cp314-cp314-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +59.68%
cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB 9.7 MB 🔴 +51.02%
cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB 11.4 MB 🔴 +60.72%
cp314-cp314-musllinux_1_2_aarch64.whl 6.5 MB 10.8 MB 🔴 +66.28%
cp314-cp314-musllinux_1_2_x86_64.whl 7.2 MB 12.6 MB 🔴 +75.17%
cp314-cp314-win32.whl 6.5 MB 7.0 MB 🔴 +7.66%
cp314-cp314-win_amd64.whl 7.2 MB 7.9 MB 🔴 +8.85%
cp314-cp314-win_arm64.whl 2.6 MB 3.1 MB 🔴 +19.76%
cp314-cp314t-macosx_10_15_x86_64.whl 5.3 MB 10.0 MB 🔴 +87.99%
cp314-cp314t-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +59.64%
cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 6.3 MB n/a n/a
cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 8.1 MB n/a n/a
cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 6.4 MB 9.7 MB 🔴 +50.73%
cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 7.1 MB 11.4 MB 🔴 +60.43%
cp314-cp314t-musllinux_1_2_aarch64.whl 6.5 MB 10.9 MB 🔴 +65.95%
cp314-cp314t-musllinux_1_2_x86_64.whl 7.2 MB 12.7 MB 🔴 +74.87%
cp314-cp314t-win32.whl 6.5 MB 7.0 MB 🔴 +7.66%
cp314-cp314t-win_amd64.whl 7.2 MB 7.9 MB 🔴 +8.85%
cp314-cp314t-win_arm64.whl 2.6 MB 3.1 MB 🔴 +19.75%
pp311-pypy311_pp73-macosx_10_15_x86_64.whl 5.3 MB 9.9 MB 🔴 +88.65%
pp311-pypy311_pp73-macosx_11_0_arm64.whl 4.7 MB 7.5 MB 🔴 +60.13%
pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl 5.3 MB n/a n/a
pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl 7.0 MB n/a n/a
pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl 5.4 MB 8.6 MB 🔴 +60.69%
pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl 6.0 MB 10.3 MB 🔴 +71.43%
pp311-pypy311_pp73-win_amd64.whl 7.1 MB 7.7 MB 🔴 +8.76%
wheel average (65 wheels) 5.7 MB 8.4 MB 🔴 +46.83%
wheel total (65 wheels) 371.8 MB 545.9 MB 🔴 +46.83%
artifacts total (66 artifacts) 418.8 MB 593.3 MB 🔴 +41.68%

https://github.com/python-pillow/Pillow/actions/runs/25698618068/attempts/3#summary-75485821427

@j99ca

j99ca commented Sep 8, 2026

Copy link
Copy Markdown

Curious what the blockers for this PR are at this point? Both Chrome and Firefox are going to enable JXL support by default (no longer behind a flag) at the end of this month:

https://chromium-review.googlesource.com/c/chromium/src/+/8279712

https://bsky.app/profile/webdevs.firefox.com/post/3mttaddub7f2u

@radarhere

radarhere commented Sep 8, 2026 •

Copy link
Copy Markdown
Member
  1. As always in Pillow development, time. Members of the core Pillow team are doing this in an underpaid capacity in their spare time.
  2. Concern that this would increase the size of Pillow wheels. The alternative is implementing the strategy of Consider adopting an image-format-per-plugin model #9101 (comment)

@hugovk

hugovk commented Sep 8, 2026

Copy link
Copy Markdown
Member

It's good to hear major browsers are including it by default.

The main blocker (for me) is this PR makes wheels around 50% bigger, and some nearly twice the size (#7848 (comment), #7848 (comment)), which I think is too expensive to be paid for everyone right now, especially given how many wheels we produce, and that we hit the PyPI quota limit recently when adding AVIF without paying attention to the added size cost.

As mentioned, see #9101 (comment) for some suggestions I made, but there wasn't much interest in those. Happy to continue with any of those.

I plan to close this if no other suggestions come up to reduce wheel size. Sorry it's been open for so long, I appreciate all the work that went into this PR - thank you @olokelo and @radarhere. We (or at least I) only paid attention to size cost after the previous problems.

JPEG XL support is available via the plugin that influenced this PR:

And there's at least two more plugins:

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.