Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JPEG XL Open/Read support via libjxl #7848

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8e0c5db
add tests
olokelo Mar 1, 2024
a57ebea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 2, 2024
23fb57d
minor fixes, linting corrections
olokelo Mar 2, 2024
2eb5987
Added type hints
radarhere Mar 6, 2024
37b58f3
Removed feature
radarhere Mar 6, 2024
eeaecb4
Merge pull request #1 from radarhere/jxl-support2
olokelo Mar 11, 2024
24b63ad
fix goto labels for clang
olokelo Mar 11, 2024
0b50410
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 11, 2024
f403672
Merge branch 'main' into jxl-support2
hugovk Mar 19, 2024
f6086d4
modify leak test
olokelo Mar 19, 2024
5320450
fix _jxl_decoder_count_frames
olokelo Mar 19, 2024
1b049ab
minor plugin code tweaks
olokelo Mar 19, 2024
6048520
add type hints
olokelo Mar 19, 2024
8fa280f
rename jxl -> jpegxl
olokelo Mar 19, 2024
58c37bf
add test case for seeking to the same frame
olokelo Mar 19, 2024
48bbc2e
flip cases in metadata test
olokelo Mar 19, 2024
443a352
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 19, 2024
62c58c2
fix some type hinting mistakes
olokelo Mar 19, 2024
8cab1c1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 19, 2024
e5003ff
change Optional to python 3.10+ syntax
olokelo Mar 19, 2024
fa5bfac
add more metadata test cases
olokelo Mar 20, 2024
0b71605
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 20, 2024
1f00fb8
add 16-bits grayscale support for jpeg xl images
olokelo May 18, 2024
08270a7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 18, 2024
9313587
Merge branch 'main' into jxl-support2
radarhere May 22, 2024
4256b2a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 22, 2024
8a1c03e
Merge branch 'main' into jxl-support2
radarhere Aug 14, 2024
13944d5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 14, 2024
bb06057
Merge branch 'main' into jxl-support2
radarhere Sep 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Tests/images/flower.jxl
Binary file not shown.
Binary file added Tests/images/flower2.jxl
Binary file not shown.
Binary file added Tests/images/hopper.jxl
Binary file not shown.
Binary file added Tests/images/hopper_jxl_bits.ppm
Binary file not shown.
Binary file added Tests/images/iss634.jxl
Binary file not shown.
Binary file added Tests/images/jxl/16bit_subcutaneous.cropped.jxl
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/jxl/traffic_light.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/images/jxl/traffic_light.jxl
Binary file not shown.
Binary file added Tests/images/transparent.jxl
Binary file not shown.
89 changes: 89 additions & 0 deletions Tests/test_file_jxl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from __future__ import annotations

import re

import pytest

from PIL import Image, JpegXlImagePlugin, features

from .helper import (
assert_image_similar_tofile,
skip_unless_feature,
)

try:
from PIL import _jpegxl

HAVE_JPEGXL = True

Check warning on line 17 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L17

Added line #L17 was not covered by tests
except ImportError:
HAVE_JPEGXL = False

# cjxl v0.9.2 41b8cdab
# hopper.jxl: cjxl hopper.png hopper.jxl -q 75 -e 8
# 16_bit_binary.jxl: cjxl 16_bit_binary.pgm 16_bit_binary.jxl -q 100 -e 9


class TestUnsupportedJpegXl:
def test_unsupported(self) -> None:
if HAVE_JPEGXL:
JpegXlImagePlugin.SUPPORTED = False

Check warning on line 29 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L29

Added line #L29 was not covered by tests

file_path = "Tests/images/hopper.jxl"
with pytest.raises(OSError):
with Image.open(file_path):
pass

if HAVE_JPEGXL:
JpegXlImagePlugin.SUPPORTED = True

Check warning on line 37 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L37

Added line #L37 was not covered by tests


@skip_unless_feature("jpegxl")
class TestFileJpegXl:
def setup_method(self) -> None:
self.rgb_mode = "RGB"
self.i16_mode = "I;16"

Check warning on line 44 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L43-L44

Added lines #L43 - L44 were not covered by tests

def test_version(self) -> None:
_jpegxl.JpegXlDecoderVersion()
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jpegxl"))

Check warning on line 48 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L47-L48

Added lines #L47 - L48 were not covered by tests

def test_read_rgb(self) -> None:
"""
Can we read a RGB mode Jpeg XL file without error?
Does it have the bits we expect?
"""

with Image.open("Tests/images/hopper.jxl") as image:
assert image.mode == self.rgb_mode
assert image.size == (128, 128)
assert image.format == "JPEG XL"
image.load()
image.getdata()

Check warning on line 61 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L56-L61

Added lines #L56 - L61 were not covered by tests

# generated with:
# djxl hopper.jxl hopper_jxl_bits.ppm
assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0)

Check warning on line 65 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L65

Added line #L65 was not covered by tests

def test_read_i16(self) -> None:
"""
Can we read 16-bit Grayscale Jpeg XL image?
"""

with Image.open("Tests/images/jxl/16bit_subcutaneous.cropped.jxl") as image:
assert image.mode == self.i16_mode
assert image.size == (128, 64)
assert image.format == "JPEG XL"
image.load()
image.getdata()

Check warning on line 77 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L72-L77

Added lines #L72 - L77 were not covered by tests

assert_image_similar_tofile(

Check warning on line 79 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L79

Added line #L79 was not covered by tests
image, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1.0
)

def test_JpegXlDecode_with_invalid_args(self) -> None:
"""
Calling decoder functions with no arguments should result in an error.
"""

with pytest.raises(TypeError):
_jpegxl.PILJpegXlDecoder()

Check warning on line 89 in Tests/test_file_jxl.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl.py#L88-L89

Added lines #L88 - L89 were not covered by tests
29 changes: 29 additions & 0 deletions Tests/test_file_jxl_alpha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from __future__ import annotations

import pytest

from PIL import Image

from .helper import assert_image_similar_tofile

_jpegxl = pytest.importorskip("PIL._jpegxl", reason="JPEG XL support not installed")


def test_read_rgba() -> None:

Check warning on line 12 in Tests/test_file_jxl_alpha.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_alpha.py#L12

Added line #L12 was not covered by tests
"""
Can we read an RGBA mode file without error?
Does it have the bits we expect?
"""

# Generated with `cjxl transparent.png transparent.jxl -q 100 -e 8`
file_path = "Tests/images/transparent.jxl"
with Image.open(file_path) as image:
assert image.mode == "RGBA"
assert image.size == (200, 150)
assert image.format == "JPEG XL"
image.load()
image.getdata()

Check warning on line 25 in Tests/test_file_jxl_alpha.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_alpha.py#L19-L25

Added lines #L19 - L25 were not covered by tests

image.tobytes()

Check warning on line 27 in Tests/test_file_jxl_alpha.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_alpha.py#L27

Added line #L27 was not covered by tests

assert_image_similar_tofile(image, "Tests/images/transparent.png", 1.0)

Check warning on line 29 in Tests/test_file_jxl_alpha.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_alpha.py#L29

Added line #L29 was not covered by tests
76 changes: 76 additions & 0 deletions Tests/test_file_jxl_animated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from __future__ import annotations

import pytest

from PIL import Image

from .helper import (
assert_image_equal,
skip_unless_feature,
)

pytestmark = [
skip_unless_feature("jpegxl"),
]


def test_n_frames() -> None:
"""Ensure that jxl format sets n_frames and is_animated attributes correctly."""

with Image.open("Tests/images/hopper.jxl") as im:
assert im.n_frames == 1
assert not im.is_animated

Check warning on line 22 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L20-L22

Added lines #L20 - L22 were not covered by tests

with Image.open("Tests/images/iss634.jxl") as im:
assert im.n_frames == 41
assert im.is_animated

Check warning on line 26 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L24-L26

Added lines #L24 - L26 were not covered by tests


def test_float_duration() -> None:

with Image.open("Tests/images/iss634.jxl") as im:
im.load()
assert im.info["duration"] == 70

Check warning on line 33 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L31-L33

Added lines #L31 - L33 were not covered by tests


def test_seeking() -> None:
"""
Open an animated jxl file, and then try seeking through frames in reverse-order,
verifying the durations are correct.
"""

with Image.open("Tests/images/jxl/traffic_light.jxl") as im1:
with Image.open("Tests/images/jxl/traffic_light.gif") as im2:
assert im1.n_frames == im2.n_frames
assert im1.is_animated

Check warning on line 45 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L42-L45

Added lines #L42 - L45 were not covered by tests

# Traverse frames in reverse, checking timestamps and durations
total_dur = 0
for frame in reversed(range(im1.n_frames)):
im1.seek(frame)
im1.load()
im2.seek(frame)
im2.load()

Check warning on line 53 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L48-L53

Added lines #L48 - L53 were not covered by tests

assert_image_equal(im1.convert("RGB"), im2.convert("RGB"))

Check warning on line 55 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L55

Added line #L55 was not covered by tests

total_dur += im1.info["duration"]
assert im1.info["duration"] == im2.info["duration"]
assert im1.info["timestamp"] == im1.info["timestamp"]
assert total_dur == 8000

Check warning on line 60 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L57-L60

Added lines #L57 - L60 were not covered by tests

assert im1.tell() == 0 and im2.tell() == 0

Check warning on line 62 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L62

Added line #L62 was not covered by tests

im1.seek(0)
im1.load()
im2.seek(0)
im2.load()

Check warning on line 67 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L64-L67

Added lines #L64 - L67 were not covered by tests


def test_seek_errors() -> None:
with Image.open("Tests/images/iss634.jxl") as im:
with pytest.raises(EOFError):
im.seek(-1)

Check warning on line 73 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L71-L73

Added lines #L71 - L73 were not covered by tests

with pytest.raises(EOFError):
im.seek(47)

Check warning on line 76 in Tests/test_file_jxl_animated.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_animated.py#L75-L76

Added lines #L75 - L76 were not covered by tests
99 changes: 99 additions & 0 deletions Tests/test_file_jxl_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from __future__ import annotations

from types import ModuleType

import pytest

from PIL import Image

from .helper import skip_unless_feature

pytestmark = [
skip_unless_feature("jpegxl"),
]

ElementTree: ModuleType | None
try:
from defusedxml import ElementTree
except ImportError:
ElementTree = None


# cjxl flower.jpg flower.jxl --lossless_jpeg=0 -q 75 -e 8

# >>> from PIL import Image
# >>> with Image.open('Tests/images/flower2.webp') as im:
# >>> with open('/tmp/xmp.xml', 'wb') as f:
# >>> f.write(im.info['xmp'])
# cjxl flower2.jpg flower2.jxl --lossless_jpeg=0 -q 75 -e 8 -x xmp=/tmp/xmp.xml


def test_read_exif_metadata() -> None:
file_path = "Tests/images/flower.jxl"
with Image.open(file_path) as image:
assert image.format == "JPEG XL"
exif_data = image.info.get("exif", None)
assert exif_data

Check warning on line 36 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L32-L36

Added lines #L32 - L36 were not covered by tests

exif = image._getexif()

Check warning on line 38 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L38

Added line #L38 was not covered by tests

# Camera make
assert exif[271] == "Canon"

Check warning on line 41 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L41

Added line #L41 was not covered by tests

with Image.open("Tests/images/flower.jpg") as jpeg_image:
expected_exif = jpeg_image.info["exif"]

Check warning on line 44 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L43-L44

Added lines #L43 - L44 were not covered by tests

# jpeg xl always returns exif without 'Exif\0\0' prefix
assert exif_data == expected_exif[6:]

Check warning on line 47 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L47

Added line #L47 was not covered by tests


def test_read_exif_metadata_without_prefix() -> None:
with Image.open("Tests/images/flower2.jxl") as im:

Check warning on line 51 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L51

Added line #L51 was not covered by tests
# Assert prefix is not present
assert im.info["exif"][:6] != b"Exif\x00\x00"

Check warning on line 53 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L53

Added line #L53 was not covered by tests

exif = im.getexif()
assert exif[305] == "Adobe Photoshop CS6 (Macintosh)"

Check warning on line 56 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L55-L56

Added lines #L55 - L56 were not covered by tests


def test_read_icc_profile() -> None:
file_path = "Tests/images/flower2.jxl"
with Image.open(file_path) as image:
assert image.format == "JPEG XL"
assert image.info.get("icc_profile", None)

Check warning on line 63 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L60-L63

Added lines #L60 - L63 were not covered by tests

icc = image.info["icc_profile"]

Check warning on line 65 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L65

Added line #L65 was not covered by tests

with Image.open("Tests/images/flower2.jxl") as jpeg_image:
expected_icc = jpeg_image.info["icc_profile"]

Check warning on line 68 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L67-L68

Added lines #L67 - L68 were not covered by tests

assert icc == expected_icc

Check warning on line 70 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L70

Added line #L70 was not covered by tests


def test_getxmp() -> None:
with Image.open("Tests/images/flower.jxl") as im:
assert "xmp" not in im.info
assert im.getxmp() == {}

Check warning on line 76 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L74-L76

Added lines #L74 - L76 were not covered by tests

with Image.open("Tests/images/flower2.jxl") as im:
if ElementTree:
assert (

Check warning on line 80 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L78-L80

Added lines #L78 - L80 were not covered by tests
im.getxmp()["xmpmeta"]["xmptk"]
== "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 "
)
else:
with pytest.warns(

Check warning on line 85 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L85

Added line #L85 was not covered by tests
UserWarning,
match="XMP data cannot be read without defusedxml dependency",
):
assert im.getxmp() == {}

Check warning on line 89 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L89

Added line #L89 was not covered by tests


def test_fix_exif_fail() -> None:
with Image.open("Tests/images/flower2.jxl") as image:
assert image._fix_exif(b"\0\0\0\0") is None

Check warning on line 94 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L93-L94

Added lines #L93 - L94 were not covered by tests


def test_read_exif_metadata_empty() -> None:
with Image.open("Tests/images/hopper.jxl") as image:
assert image._getexif() is None

Check warning on line 99 in Tests/test_file_jxl_metadata.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_file_jxl_metadata.py#L98-L99

Added lines #L98 - L99 were not covered by tests
25 changes: 25 additions & 0 deletions Tests/test_jxl_leaks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from __future__ import annotations

from io import BytesIO

from PIL import Image

from .helper import PillowLeakTestCase, skip_unless_feature

TEST_FILE = "Tests/images/hopper.jxl"


@skip_unless_feature("jpegxl")
class TestJpegXlLeaks(PillowLeakTestCase):
mem_limit = 6 * 1024 # kb
iterations = 1000

def test_leak_load(self) -> None:
with open(TEST_FILE, "rb") as f:
im_data = f.read()

Check warning on line 19 in Tests/test_jxl_leaks.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_jxl_leaks.py#L18-L19

Added lines #L18 - L19 were not covered by tests

def core() -> None:
with Image.open(BytesIO(im_data)) as im:
im.load()

Check warning on line 23 in Tests/test_jxl_leaks.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_jxl_leaks.py#L21-L23

Added lines #L21 - L23 were not covered by tests

self._test_leak(core)

Check warning on line 25 in Tests/test_jxl_leaks.py

View check run for this annotation

Codecov / codecov/patch

Tests/test_jxl_leaks.py#L25

Added line #L25 was not covered by tests
20 changes: 20 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ class ext_feature:
features = [
"zlib",
"jpeg",
"jpegxl",
"tiff",
"freetype",
"raqm",
Expand Down Expand Up @@ -735,6 +736,14 @@ def build_extensions(self) -> None:
feature.set("jpeg2000", "openjp2")
feature.set("openjpeg_version", ".".join(str(x) for x in best_version))

if feature.want("jpegxl"):
_dbg("Looking for jpegxl")
if _find_include_file(self, "jxl/encode.h") and _find_include_file(
self, "jxl/decode.h"
):
if _find_library_file(self, "jxl"):
feature.set("jpegxl", "jxl jxl_threads")

if feature.want("imagequant"):
_dbg("Looking for imagequant")
if _find_include_file(self, "libimagequant.h"):
Expand Down Expand Up @@ -818,6 +827,15 @@ def build_extensions(self) -> None:
# alternate Windows name.
feature.set("lcms", "lcms2_static")

if feature.get("jpegxl"):
# jxl and jxl_threads are required
libs = feature.get("jpegxl").split()
defs = []

self._update_extension("PIL._jpegxl", libs, defs)
else:
self._remove_extension("PIL._jpegxl")

if feature.want("webp"):
_dbg("Looking for webp")
if all(
Expand Down Expand Up @@ -967,6 +985,7 @@ def summary_report(self, feature: ext_feature) -> None:
(feature.get("freetype"), "FREETYPE2"),
(feature.get("raqm"), "RAQM (Text shaping)", raqm_extra_info),
(feature.get("lcms"), "LITTLECMS2"),
(feature.get("jpegxl"), "JPEG XL"),
(feature.get("webp"), "WEBP"),
(feature.get("xcb"), "XCB (X protocol)"),
]
Expand Down Expand Up @@ -1010,6 +1029,7 @@ def debug_build() -> bool:
Extension("PIL._imaging", files),
Extension("PIL._imagingft", ["src/_imagingft.c"]),
Extension("PIL._imagingcms", ["src/_imagingcms.c"]),
Extension("PIL._jpegxl", ["src/_jpegxl.c"]),
Extension("PIL._webp", ["src/_webp.c"]),
Extension("PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"]),
Extension("PIL._imagingmath", ["src/_imagingmath.c"]),
Expand Down
Loading
Loading