From 8e0c5db1e07c96e5086c2a0ad2211ecf40f9a989 Mon Sep 17 00:00:00 2001 From: olokelo Date: Sat, 2 Mar 2024 00:56:38 +0100 Subject: [PATCH 01/66] add tests --- Tests/images/flower.jxl | Bin 0 -> 24663 bytes Tests/images/flower2.jxl | Bin 0 -> 16108 bytes Tests/images/hopper.jxl | Bin 0 -> 3409 bytes Tests/images/hopper_jxl_bits.ppm | Bin 0 -> 49167 bytes Tests/images/iss634.jxl | Bin 0 -> 230661 bytes Tests/images/jxl/traffic_light.gif | Bin 0 -> 1088 bytes Tests/images/jxl/traffic_light.jxl | Bin 0 -> 753 bytes Tests/images/transparent.jxl | Bin 0 -> 18104 bytes Tests/test_file_jxl.py | 72 ++++ Tests/test_file_jxl_alpha.py | 32 ++ Tests/test_file_jxl_animated.py | 70 ++++ Tests/test_file_jxl_metadata.py | 88 ++++ Tests/test_jxl_leaks.py | 26 ++ setup.py | 20 + src/PIL/JxlImagePlugin.py | 172 ++++++++ src/PIL/__init__.py | 1 + src/PIL/features.py | 3 + src/_jxl.c | 634 +++++++++++++++++++++++++++++ 18 files changed, 1118 insertions(+) create mode 100644 Tests/images/flower.jxl create mode 100644 Tests/images/flower2.jxl create mode 100644 Tests/images/hopper.jxl create mode 100644 Tests/images/hopper_jxl_bits.ppm create mode 100644 Tests/images/iss634.jxl create mode 100644 Tests/images/jxl/traffic_light.gif create mode 100644 Tests/images/jxl/traffic_light.jxl create mode 100644 Tests/images/transparent.jxl create mode 100644 Tests/test_file_jxl.py create mode 100644 Tests/test_file_jxl_alpha.py create mode 100644 Tests/test_file_jxl_animated.py create mode 100644 Tests/test_file_jxl_metadata.py create mode 100644 Tests/test_jxl_leaks.py create mode 100644 src/PIL/JxlImagePlugin.py create mode 100644 src/_jxl.c diff --git a/Tests/images/flower.jxl b/Tests/images/flower.jxl new file mode 100644 index 0000000000000000000000000000000000000000..aeb8ae79165907a83de683c0ef7938fb54331a5a GIT binary patch literal 24663 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 literal 0 HcmV?d00001 diff --git a/Tests/images/hopper.jxl b/Tests/images/hopper.jxl new file mode 100644 index 0000000000000000000000000000000000000000..d89d3c267fef793855eb3d7fb7bec3a10e999e83 GIT binary patch literal 3409 zcmV-X4X*P43SU4%<=!fa0Tm^=YaRfA3YAgD4PMUmeEFfnV%o-ahPK+au!Iwo-ReRs zCJw$AfP?pykdRQ)HP6fC4&a3}#F&aqL_|c#MnyzNW3_@}WMiaaRAa1StYfrdv}1H* ztYdUzv}0^zEMw#`*4egWv}0^ztYd6rlw#Dyn2hl<#$c7_&_v*b0MwLK1X`n+1nZBY zpq;Hxu32r!C9F}@pGCrh29ldDZQG6L3mVV*SK6g2bLR1P zkptv(lKi+GE$@#aShv(3dBIwAQg~-& ziq?bcA!89_Ad3P%i2jPu*KNtfR{aqWp&Z8<&M$jwyb1)`Zm)x99~KM&&`jI$v(Q|> zYjE1@dkWk5m^$-s+<%gfQD;hJX?2|sO(lM$RRT94#7|sBa{NPOTc(4 zC<9}JMh~_V|GcqeFv4U^2SR^burwyAaQ|hKN|YSm_l0$ya9x^DMT>{adZB?WB;RWQ zZH+5z@wfUMY$V9AZq{$ZfyuP--8ihX--T^$W+C-WOwW<~kgsMkP{jXC&Ek&H#E@O* zle!F06SqE0yV%esovNLLNx&UY_)CvrXyvucW=&u9ky;Fk!%=77rrm0VY4BhTcsCC+DXqfvsId zu_$37wi!?v%OOf+gw0)X>kT*24sSpHQFxamWxvTfg1D?^%wf(|C9dr#wqI{2KfEu2A20{^eZd{P6Wv$ z|8$S;y#NO*`iI2E5_$Dj@iMK91NHM=bGK%yvdWfH{OI^{aeHwa{p?f_euWeitUKU} zy!-s$_`kR^vTWOK+p3b>O3X}wsGjQq?kX9#{27Y%at`$vT~$en!!OaJYbu#uCldB`Ay>EF_c>h zTtVV;__rqO1XiFn1795kBw{z2I3u!mgx8BJ9dTkxU>%gJNG{~pq30}L=MjRGwu-Ig z8c#Ry>v{Bg#!WCzzb9%m2w2Hdb+O?^D!sKZNHPdKgkcNb{G-ZvO2cLJMk|IF7)Fue zCN|6Gv9S)t3*RBbZ zJ!|19EQZy#h`Aq78^n_@%|#FM;$o+KKQ2R;??ZJw7u6d44f_yI=GRpZ8mkX3&OvZS z*4eqyRP(O#pRa(R&`D1yv=FjZ1Zz`KmE_;{)}hr99zvp#J7p}pn(yGqk76|+)QDBURy6`5WN{&Nsbjgl5U z6~?NcbXW}&X+RQ#m``yO%fKrS|9;+iOznr=AJzx%$HGR>H%TZwxKtz_3KfOQBO5;# z?C5Cp3p0^7l51ishCU0LvI;g>Cm$qU59#q|*58od(MVR;*0Q7?2eCONpZ{wa`Ecx{ z z&l$LYMz?IHV<1 z#l~(aU@&TY5PdN(9@)d-R)8je#J%x>6NO=s#| zKX2SKs5t!qIFp|s>3wfoW0mdOceGrvGz1(no?DF04k(C1v=$wd64+kOf(|~KQ?b0y zo#B)#5S+$!d>TS_-4Nmf;E2-6tso1XE9$|Ak2)p3dKO&w>E5)yivQgsg{|-)n425> zx&-!cWIC|cUvx8p%HL}5H;iEdrQ3B0?=x(te28{{?K4(DaBcxN%IeD3-kQL2WC!m4^n`3+R3D1Lo`kwy8}b4}$JF zd1PD~X32_`aSJ?d6pEBgUKihg6Ltv6zANK8ZPYObNPuQZ1ZU^yY(p2%lF>4rHDdF|U^HAIXBmTO}CD1D;}R0HPkM_O(B^=hQJiyZYKRe`1w^DqbU8B&7i<5hadip1wdaXLz+t z1?xANg>5uBWkZL)Ma>|aF!HVVpD<4*lH?0)3O_{?hx)cPzPK=2#GBKvgmD5$o;nva z-E5h{(0w}yMT==&LenjS;fIKj(eJFJiJ?>rWm0m+cLUzsby5ASMDh@}OaO`aa-=k;d6nR^o5FueTqqns_yV?&iTZ(JCn6M(ODlSkT00>9Orc{K0 z!w;IQ=2fGnF7`hkl3S(Pr7QBGKT3xhBx4A$taIIz7+xDro*C90?~^}Z zJ+5uL=Y@)ooUpE%{_;q%(-~{{`%P7M$;z>BH6>yEzRKhh!d(B)1=ZiqBWIdLK~af7 zUVNE6ydN67ne(55R)nV#KUhe75u4_<n3Fqf7F>`%y;yc&5jXcEA`hVMgBAp!J)$MK{lDvz` z%cK$uStO|j$7rU%^E~B^jq-C?ci=KaAAavXdNJe}e-vB9Dp_B6E8I7Ba7}ev-d>y> z1a4^W7n8jEF1aQHmyYmm9sx(^prMos`(UE!<0Bvg2J8kM;ivNQ7p7!dpDPVkNUJmE zWDO2NnE)Nmes|6G-7c{h{{FAG;ph7mG};Uc+XaA({Z2rtP->%{vR1oY+9%Yq->HIdkl`>Cgby>1DL#rU;NjVW%Hi?ez9rr~IMj}H literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_jxl_bits.ppm b/Tests/images/hopper_jxl_bits.ppm new file mode 100644 index 0000000000000000000000000000000000000000..881aca33369b3bdb1e0ae8671777188d89734bc5 GIT binary patch literal 49167 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 LxjMIObL;;F0-tO! literal 0 HcmV?d00001 diff --git a/Tests/images/iss634.jxl b/Tests/images/iss634.jxl new file mode 100644 index 0000000000000000000000000000000000000000..99c2cf03633e45e2691ab7797ed571450be2cc32 GIT binary patch literal 230661 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 *!9>5#S+Q{KnhE&*G2$Y;CPbiXoiyX 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%0Dx~sgow9Po&i;n^U33q5Ahj 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 literal 0 HcmV?d00001 diff --git a/Tests/images/jxl/traffic_light.gif b/Tests/images/jxl/traffic_light.gif new file mode 100644 index 0000000000000000000000000000000000000000..4f7ecfdbcd70b79af1c397f5da836092759475ae GIT binary patch literal 1088 zcmZ?wbhEHbv|-3(_{z-ib&^F{S(*EefHx8nZzLsOsHi;A*1l(Me#^$@w2#lO$jCKG zNsH3cr{&~SR#sM3Rn^tiwY0RfwzhV6clY-8_V@Qsm@r|=lqu7uO`9=e#_ZX%7cXAC zbm`I+D^{#qw{F9R4VyM?+PZb?jvYJp?Afz_|Na994jeghqClA@D2R`V~cDDhewwLU_}m-Up3OrHnal0~U2cU@6EkhlBh^eONDxY*wN@txT< z{**ywW4?q@t-DTBXTDCGi$>Rk+?rnPPHs+iRu)!vPVOe{HiITEHs<9kR;BY9zM*vGv7QywQ|eZvm6zw5rP$5*B`9O z6AU-3T=w8BTZLhmS_SvZ2lMmP!elErKV9d{mkks9AampTn*-9^VG0it-7bVRnhUIX zqIe-dg3(4uJglT+qu0Df>0rs4$sc?7O<3`3;uj8nRjsN#uYHcGzv$lGT>Spnmetd>@9zuy&lILr@!_my+uvV4JX;NtxAe(BXSLax zpSpMY`?O`dE%VZM%)i|hW-XPUa%}0@WpUM3AG3~j$P35m>`s4kYunNXc5AA?$KBc& zzngVmbVb6W9lhpmanV2D+}`(en^_KPKnYKnjSK&SgQph1n7rb|jkptD+wVSE*Ym-a z=`_!M<4rXO{yya4y_0rs&V<@S0_?|ROeQ(AwuMe`R<^xjz?dL>rD1`9S;m6tY-TnV zEc{9rRGIk2#6Idev1O%xaBBD1v4K%VdWXX!A&V^nO>9C9ij8cFTN(~`Sv*_uxX zi^u&DER3KO#G1mu$e<&{pur%?AjQDIWZYA~fcu5~jF1awy|wQZRKKqEJRzBqF>lVP zZSS~`fAZOUan32_o)>>_XNKl2f5?|y6Vt{DHb)1TB!T8|1I-BmniJF0zw-23{>5{) z-0I$ZZ_jW3wnrY*o^>vJ1vTgTx8COq4t!^N%Kb^<(4z^7K6;bRuzc>UFyjr%Sa9n| z*7iH=zH{?+Z@q4CwdR}K{&Vk&ZY$WY;b36UjJd%LbvP5u;pRXK{YiHCq%+GtbM|^I Yzq0CU*IAf_$DeGvsU@(0m6gF70KfeNZ2$lO literal 0 HcmV?d00001 diff --git a/Tests/images/jxl/traffic_light.jxl b/Tests/images/jxl/traffic_light.jxl new file mode 100644 index 0000000000000000000000000000000000000000..c777e3bd618c3f38dbc4f9d90f53c383298d4f38 GIT binary patch literal 753 zcmVcNb?%Re7eUo0znAG{O)%_5#8K0Ma1=9713d4t`-l3(!+U z!)zA`5fRvgia@slAmD;AL1B0@koPEoBw-AS{0I>)!hqADwUt=`05U465fsu`2c-ZM zxYIB)`wyuMj^^OD7g|h7*7O-d2#^{uEXImwq%$I923Z=;_(C#|V))rtvv7D@qnb`f z(T_a|GCMAF!F1s{9nKhlFaTlnr@PW}A_EwaayWDpiv)KllTlphe_I+Z`9-`uB-}+r zB@{)IzG;m;Nu)iG4T&e1`weUQt1)@ z_eeb`p?34LCLwLFwnkuo9GiXUeV2>KvF}Q_Gngohd_-pSj1d|_U&%eVh)Q%wEs*F^ zd*a(K^kS+1l8FBZ8$lpQ7=hFlAT*Z!(jLl#aFsAVr7ixw%;JgxFR2lm^l=OM?;}#vtnYaf&i%}@xR6f;I(tg$%kei zF>x6t6O?Id`!FNqCQLdYIfWevkdOdH1{yMvWC8#X0ZSN*6(GX7ZH&Qr7xOM40zftT z27uu~9Yrws56}RJ=cMPESFXm2z86D7HhbnUl+bUrnM-spNbp|yuUG>3o^dK<2ndjn zU_k~{GSMsn00062H~~v2H>Tgq3JNABKwH!qKgJj*{{k`qoNHSE1WvHK$Ubp+)l&i> zhA9i+!=#zWxT*?34`v92)jU^0&MFn17bVPb9xP9fJ%`*>76(WI2#}BfMFuxAkz@e? zNC8VICDtJn6ih4@IKzlBSQ7~nV+@uPb1onQKut{tfFL^STR;^v;E9AHfNx$&5DB)o jMVhaxs#1l0U9PY@K%3gHekH1Qa0s$p-jOAdcQCpF0e&vy literal 0 HcmV?d00001 diff --git a/Tests/images/transparent.jxl b/Tests/images/transparent.jxl new file mode 100644 index 0000000000000000000000000000000000000000..cea19bb6cfa4f100f68ac04677fe0c11748410e3 GIT binary patch literal 18104 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 None: + if HAVE_JXL: + JxlImagePlugin.SUPPORTED = False + + file_path = "Tests/images/hopper.jxl" + with pytest.warns(UserWarning): + with pytest.raises(OSError): + with Image.open(file_path): + pass + + if HAVE_JXL: + JxlImagePlugin.SUPPORTED = True + +@skip_unless_feature("jxl") +class TestFileJxl: + def setup_method(self) -> None: + self.rgb_mode = "RGB" + + def test_version(self) -> None: + _jxl.JxlDecoderVersion() + assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jxl")) + + 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() + + # generated with: + # djxl hopper.jxl hopper_jxl_bits.ppm + assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0) + + def test_JxlDecode_with_invalid_args(self) -> None: + """ + Calling decoder functions with no arguments should result in an error. + """ + + with pytest.raises(TypeError): + _jxl.PILJxlDecoder() + + diff --git a/Tests/test_file_jxl_alpha.py b/Tests/test_file_jxl_alpha.py new file mode 100644 index 00000000000..710e3b68519 --- /dev/null +++ b/Tests/test_file_jxl_alpha.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import pytest + +from PIL import Image + +from .helper import ( + assert_image_similar_tofile +) + +_webp = pytest.importorskip("PIL._jxl", reason="JXL support not installed") + + +def test_read_rgba() -> None: + """ + 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() + + image.tobytes() + + assert_image_similar_tofile(image, "Tests/images/transparent.png", 1.0) + diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py new file mode 100644 index 00000000000..8a1b67e9f8b --- /dev/null +++ b/Tests/test_file_jxl_animated.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import pytest +from packaging.version import parse as parse_version + +from PIL import Image, features + +from .helper import ( + assert_image_equal, + skip_unless_feature, +) + +pytestmark = [ + skip_unless_feature("jxl"), +] + + +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 + + with Image.open("Tests/images/iss634.jxl") as im: + assert im.n_frames == 41 + assert im.is_animated + + +def test_float_duration() -> None: + + with Image.open("Tests/images/iss634.jxl") as im: + im.load() + assert im.info["duration"] == 70 + + +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 + + # 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() + + assert_image_equal(im1.convert('RGB'), im2.convert('RGB')) + + total_dur += im1.info["duration"] + assert im1.info["duration"] == im2.info["duration"] + assert im1.info["timestamp"] == im1.info["timestamp"] + assert total_dur == 8000 + + +def test_seek_errors() -> None: + with Image.open("Tests/images/iss634.jxl") as im: + with pytest.raises(EOFError): + im.seek(-1) + + with pytest.raises(EOFError): + im.seek(47) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py new file mode 100644 index 00000000000..8f99bd8f3da --- /dev/null +++ b/Tests/test_file_jxl_metadata.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from io import BytesIO +from pathlib import Path +from types import ModuleType + +import pytest + +from PIL import Image + +from .helper import mark_if_feature_version, skip_unless_feature + +pytestmark = [ + skip_unless_feature("jxl"), +] + +ElementTree: ModuleType | None +try: + from defusedxml import ElementTree +except ImportError: + ElementTree = None + + +# cjxl flower.jpg flower.jxl --lossless_jpeg=0 -q 75 -e 8 + +# python -c "from PIL import Image; im=Image.open('Tests/images/flower2.webp'); f=open('/tmp/xmp.xml', 'wb'); f.write(im.info['xmp']); f.close()" +# 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 + + exif = image._getexif() + + # Camera make + assert exif[271] == "Canon" + + with Image.open("Tests/images/flower.jpg") as jpeg_image: + expected_exif = jpeg_image.info["exif"] + + # jpeg xl always returns exif without 'Exif\0\0' prefix + assert exif_data == expected_exif[6:] + + +def test_read_exif_metadata_without_prefix() -> None: + with Image.open("Tests/images/flower2.jxl") as im: + # Assert prefix is not present + assert im.info["exif"][:6] != b"Exif\x00\x00" + + exif = im.getexif() + assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" + + +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) + + icc = image.info["icc_profile"] + + with Image.open("Tests/images/flower2.jxl") as jpeg_image: + expected_icc = jpeg_image.info["icc_profile"] + + assert icc == expected_icc + + +def test_getxmp() -> None: + with Image.open("Tests/images/flower.jxl") as im: + assert "xmp" not in im.info + assert im.getxmp() == {} + + with Image.open("Tests/images/flower2.jxl") as im: + if ElementTree is None: + with pytest.warns( + UserWarning, + match="XMP data cannot be read without defusedxml dependency", + ): + assert im.getxmp() == {} + else: + assert ( + im.getxmp()["xmpmeta"]["xmptk"] + == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " + ) + diff --git a/Tests/test_jxl_leaks.py b/Tests/test_jxl_leaks.py new file mode 100644 index 00000000000..5a18ab9a52f --- /dev/null +++ b/Tests/test_jxl_leaks.py @@ -0,0 +1,26 @@ +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("jxl") +class TestJxlLeaks(PillowLeakTestCase): + # TODO: lower the limit, I'm not sure what is correct limit since I have libjxl debug system-wide + mem_limit = 16 * 1024 # kb + iterations = 100 + + def test_leak_load(self) -> None: + with open(test_file, "rb") as f: + im_data = f.read() + + def core() -> None: + with Image.open(BytesIO(im_data)) as im: + im.load() + + self._test_leak(core) diff --git a/setup.py b/setup.py index 1bbd2c05cc9..1f3b1e57218 100644 --- a/setup.py +++ b/setup.py @@ -286,6 +286,7 @@ class feature: features = [ "zlib", "jpeg", + "jxl", "tiff", "freetype", "raqm", @@ -691,6 +692,14 @@ def build_extensions(self): feature.jpeg2000 = "openjp2" feature.openjpeg_version = ".".join(str(x) for x in best_version) + if feature.want("jxl"): + _dbg("Looking for jxl") + if _find_include_file(self, "jxl/encode.h") and _find_include_file( + self, "jxl/decode.h" + ): + if _find_library_file(self, "jxl"): + feature.jxl = "jxl jxl_threads" + if feature.want("imagequant"): _dbg("Looking for imagequant") if _find_include_file(self, "libimagequant.h"): @@ -774,6 +783,15 @@ def build_extensions(self): # alternate Windows name. feature.lcms = "lcms2_static" + if feature.jxl: + # jxl and jxl_threads are required + libs = feature.jxl.split() + defs = [] + + self._update_extension("PIL._jxl", libs, defs) + else: + self._remove_extension("PIL._jxl") + if feature.want("webp"): _dbg("Looking for webp") if _find_include_file(self, "webp/encode.h") and _find_include_file( @@ -934,6 +952,7 @@ def summary_report(self, feature): (feature.freetype, "FREETYPE2"), (feature.raqm, "RAQM (Text shaping)", raqm_extra_info), (feature.lcms, "LITTLECMS2"), + (feature.jxl, "JXL"), (feature.webp, "WEBP"), (feature.webpmux, "WEBPMUX"), (feature.xcb, "XCB (X protocol)"), @@ -978,6 +997,7 @@ def debug_build(): Extension("PIL._imaging", files), Extension("PIL._imagingft", ["src/_imagingft.c"]), Extension("PIL._imagingcms", ["src/_imagingcms.c"]), + Extension("PIL._jxl", ["src/_jxl.c"]), Extension("PIL._webp", ["src/_webp.c"]), Extension("PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"]), Extension("PIL._imagingmath", ["src/_imagingmath.c"]), diff --git a/src/PIL/JxlImagePlugin.py b/src/PIL/JxlImagePlugin.py new file mode 100644 index 00000000000..8226db09638 --- /dev/null +++ b/src/PIL/JxlImagePlugin.py @@ -0,0 +1,172 @@ +from io import BytesIO +from . import Image, ImageFile +import struct + +try: + from . import _jxl + + SUPPORTED = True +except ImportError: + SUPPORTED = False + + +## Future idea: +## it's not known how many frames does animated image have +## by default, _jxl_decoder_new will iterate over all frames without decoding them +## then libjxl decoder is rewinded and we're ready to decode frame by frame +## if OPEN_COUNTS_FRAMES is False, n_frames will be None until the last frame is decoded +## it only applies to animated jpeg xl images +#OPEN_COUNTS_FRAMES = True + +def _accept(prefix): + is_jxl = prefix[:2] == b'\xff\x0a' \ + or prefix[:12] == b'\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a' + if is_jxl and not SUPPORTED: + return "image file could not be identified because JXL support not installed" + return is_jxl + + +class JxlImageFile(ImageFile.ImageFile): + format = "JPEG XL" + format_description = "JPEG XL image" + __loaded = 0 + __logical_frame = 0 + + def _open(self): + self._decoder = _jxl.PILJxlDecoder(self.fp.read()) + + width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = self._decoder.get_info() + self._size = width, height + self.info["loop"] = n_loops + self.is_animated = has_anim + + self.n_frames = None + self._tps_dur_secs = 1 + if not self.is_animated: + self.n_frames = 1 + elif n_frames > 0: + self.n_frames = n_frames + self._tps_dur_secs = tps_num/tps_denom + # TODO: handle libjxl timecods + self.__timestamp = 0 + + self._mode = mode + self.rawmode = mode + self.tile = [] + + icc = self._decoder.get_icc() + exif = self._decoder.get_exif() + xmp = self._decoder.get_xmp() + if icc: self.info["icc_profile"] = icc + import traceback + try: + if exif: self.info["exif"] = self._fix_exif(exif) + except: + traceback.print_exc() + if xmp: self.info["xmp"] = xmp + + self._rewind() + + def _fix_exif(self, exif): + # jpeg xl does some weird shenanigans when storing exif + # it omits first 6 bytes of tiff header but adds 4 byte offset instead + if len(exif) <= 4: + return None + exif_start_offset = struct.unpack(">I", exif[:4])[0] + return exif[exif_start_offset+4:] + + def _getexif(self): + if "exif" not in self.info: + return None + return self.getexif()._get_merged_dict() + + def getxmp(self): + return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} + + def _get_next(self): + + # Get next frame + next_frame = self._decoder.get_next() + self.__physical_frame += 1 + + # this actually means EOF, errors are raised in _jxl + if next_frame is None: + msg = "failed to decode next frame in JXL file" + raise EOFError(msg) + + data, tps_duration, is_last = next_frame + if is_last and self.n_frames is None: + # libjxl said this frame is the last one + self.n_frames = self.__physical_frame + + # duration in miliseconds + duration = 1000 * tps_duration * (1/self._tps_dur_secs) + timestamp = self.__timestamp + self.__timestamp += duration + + return data, timestamp, duration, is_last + + def _rewind(self, hard=False): + if hard: + self._decoder.rewind() + self.__physical_frame = 0 + self.__loaded = -1 + self.__timestamp = 0 + + def _seek_check(self, frame): + # if image is not animated then only the 0th frame is available + if (not self.is_animated and frame != 0) or \ + (self.n_frames is not None and (frame >= self.n_frames or frame < 0)): + msg = "attempt to seek outside sequence" + raise EOFError(msg) + + return self.tell() != frame + + def _seek(self, frame): + #print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame)) + if frame == self.__physical_frame: + return # Nothing to do + if frame < self.__physical_frame: + # also rewind libjxl decoder instance + self._rewind(hard=True) + + while self.__physical_frame < frame: + self._get_next() # Advance to the requested frame + + def seek(self, frame): + if not self._seek_check(frame): + return + + # Set logical frame to requested position + self.__logical_frame = frame + + def load(self): + + if self.__loaded != self.__logical_frame: + self._seek(self.__logical_frame) + + data, timestamp, duration, is_last = self._get_next() + self.info["timestamp"] = timestamp + self.info["duration"] = duration + self.__loaded = self.__logical_frame + + # Set tile + if self.fp and self._exclusive_fp: + self.fp.close() + # this is horribly memory inefficient + # you need probably 2*(raw image plane) bytes of memory + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] + + return super().load() + + def load_seek(self, pos): + pass + + def tell(self): + return self.__logical_frame + + +Image.register_open(JxlImageFile.format, JxlImageFile, _accept) +Image.register_extension(JxlImageFile.format, ".jxl") +Image.register_mime(JxlImageFile.format, "image/jxl") \ No newline at end of file diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index 63a45769ba6..768315d3b25 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -47,6 +47,7 @@ "IptcImagePlugin", "JpegImagePlugin", "Jpeg2KImagePlugin", + "JxlImagePlugin", "McIdasImagePlugin", "MicImagePlugin", "MpegImagePlugin", diff --git a/src/PIL/features.py b/src/PIL/features.py index b14d6df13f2..abd1961a7ed 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -14,6 +14,7 @@ "tkinter": ("PIL._tkinter_finder", "tk_version"), "freetype2": ("PIL._imagingft", "freetype2_version"), "littlecms2": ("PIL._imagingcms", "littlecms_version"), + "jxl": ("PIL._jxl", "libjxl_version"), "webp": ("PIL._webp", "webpdecoder_version"), } @@ -124,6 +125,7 @@ def get_supported_codecs(): "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None), "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None), "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None), + "jxl": ("PIL._jxl", "HAVE_JXL", None), "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), @@ -268,6 +270,7 @@ def pilinfo(out=None, supported_formats=True): ("transp_webp", "WEBP Transparency"), ("webp_mux", "WEBPMUX"), ("webp_anim", "WEBP Animation"), + ("jxl", "JPEG XL") ("jpg", "JPEG"), ("jpg_2000", "OPENJPEG (JPEG2000)"), ("zlib", "ZLIB (PNG/ZIP)"), diff --git a/src/_jxl.c b/src/_jxl.c new file mode 100644 index 00000000000..5a0f8eeddb5 --- /dev/null +++ b/src/_jxl.c @@ -0,0 +1,634 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include "libImaging/Imaging.h" + +#include +#include +#include +#include + +#define _PIL_JXL_CHECK(call_name) if (decp->status != JXL_DEC_SUCCESS) { jxl_call_name = call_name; goto end; } + + +void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { + + pf->num_channels = bi->num_color_channels + bi->num_extra_channels; + + if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) { + pf->data_type = JXL_TYPE_FLOAT; // not yet supported + } else if (bi->bits_per_sample > 8) { + pf->data_type = JXL_TYPE_UINT16; // not yet supported + } else { + pf->data_type = JXL_TYPE_UINT8; + } + + // this *might* cause some issues on Big-Endian systems + // would be great to test it + pf->endianness = JXL_NATIVE_ENDIAN; + pf->align = 0; + +} + +// TODO: floating point mode +char* _pil_jxl_get_mode(const JxlBasicInfo *bi) { + + // PIL doesn't support high bit depth images + // it will throw an exception but that's for your own good + // you wouldn't want to see distorted image + if (bi->bits_per_sample != 8) return "uns"; + + // image has transparency + if (bi->alpha_bits > 0) { + if (bi->num_color_channels == 3) { + if (bi->alpha_premultiplied) return "RGBa"; + return "RGBA"; + } if (bi->num_color_channels == 1) { + if (bi->alpha_premultiplied) return "La"; + return "LA"; + } + } + + // image has no transparency + if (bi->num_color_channels == 3) return "RGB"; + if (bi->num_color_channels == 1) return "L"; + + // could not recognize mode + return NULL; + +} + +// Decoder type +typedef struct { + PyObject_HEAD JxlDecoder *decoder; + void *runner; + + uint8_t *jxl_data; // input jxl bitstream + Py_ssize_t jxl_data_len; // length of input jxl bitstream + + uint8_t *outbuf; + Py_ssize_t outbuf_len; + + uint8_t *jxl_icc; + Py_ssize_t jxl_icc_len; + uint8_t *jxl_exif; + Py_ssize_t jxl_exif_len; + uint8_t *jxl_xmp; + Py_ssize_t jxl_xmp_len; + + JxlDecoderStatus status; + JxlBasicInfo basic_info; + JxlPixelFormat pixel_format; + + Py_ssize_t n_frames; + + char *mode; +} PILJxlDecoderObject; + +static PyTypeObject PILJxlDecoder_Type; + +void +_jxl_decoder_dealloc(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + if (decp->jxl_data) { + free(decp->jxl_data); + decp->jxl_data = NULL; + decp->jxl_data_len = 0; + } + if (decp->outbuf) { + free(decp->outbuf); + decp->outbuf = NULL; + decp->outbuf_len = 0; + } + if (decp->jxl_icc) { + free(decp->jxl_icc); + decp->jxl_icc = NULL; + decp->jxl_icc_len = 0; + } + if (decp->jxl_exif) { + free(decp->jxl_exif); + decp->jxl_exif = NULL; + decp->jxl_exif_len = 0; + } + if (decp->jxl_xmp) { + free(decp->jxl_xmp); + decp->jxl_xmp = NULL; + decp->jxl_xmp_len = 0; + } + + if (decp->decoder) { + JxlDecoderDestroy(decp->decoder); + decp->decoder = NULL; + } + + if (decp->runner) { + JxlThreadParallelRunnerDestroy(decp->runner); + decp->runner = NULL; + } +} + +// sets input jxl bitstream loaded into jxl_data +// has to be called after every rewind +void _jxl_decoder_set_input(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + decp->status = JxlDecoderSetInput(decp->decoder, decp->jxl_data, + decp->jxl_data_len); + + // the input contains the whole jxl bitstream so it can be closed + JxlDecoderCloseInput(decp->decoder); + +} + +PyObject * +_jxl_decoder_rewind(PyObject *self) { + + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + JxlDecoderRewind(decp->decoder); + Py_RETURN_NONE; + +} + +void * +_jxl_decoder_count_frames(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + decp->n_frames = 0; + + // count all JXL_DEC_NEED_IMAGE_OUT_BUFFER events + while (decp->status != JXL_DEC_SUCCESS) { + //printf("fetch_frame_count status: %u\n", decp->status); + decp->status = JxlDecoderProcessInput(decp->decoder); + + if (decp->status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + if (JxlDecoderSkipCurrentFrame(decp->decoder) != JXL_DEC_SUCCESS) { + return false; + } + decp->n_frames++; + } + } + + _jxl_decoder_rewind((PyObject *)decp); + + return true; + +} + +PyObject * +_jxl_decoder_new(PyObject *self, PyObject *args) { + PyBytesObject *jxl_string; + + PILJxlDecoderObject *decp = NULL; + decp = PyObject_New(PILJxlDecoderObject, &PILJxlDecoder_Type); + decp->mode = NULL; + decp->jxl_data = NULL; + decp->jxl_data_len = 0; + decp->outbuf = NULL; + decp->outbuf_len = 0; + decp->jxl_icc = NULL; + decp->jxl_icc_len = 0; + decp->jxl_exif = NULL; + decp->jxl_exif_len = 0; + decp->jxl_xmp = NULL; + decp->jxl_xmp_len = 0; + decp->n_frames = 0; + + // used for printing more detailed error messages + char* jxl_call_name; + + // parse one argument which is a string with jxl data + if (!PyArg_ParseTuple(args, "S", &jxl_string)) { + return NULL; + } + + // this data needs to be copied to PILJxlDecoderObject + // so that input bitstream is preserved across calls + const uint8_t *_tmp_jxl_data; + Py_ssize_t _tmp_jxl_data_len; + + // convert jxl data string to C uint8_t pointer + PyBytes_AsStringAndSize((PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len); + + // here occurs this copying (inefficiency) + decp->jxl_data = malloc(_tmp_jxl_data_len); + memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len); + decp->jxl_data_len = _tmp_jxl_data_len; + + //printf("%zu\n", decp->jxl_data_len); + + size_t suggested_num_threads = JxlThreadParallelRunnerDefaultNumWorkerThreads(); + decp->runner = JxlThreadParallelRunnerCreate(NULL, suggested_num_threads); + decp->decoder = JxlDecoderCreate(NULL); + + decp->status = JxlDecoderSetParallelRunner(decp->decoder, + JxlThreadParallelRunner, decp->runner); + _PIL_JXL_CHECK("JxlDecoderSetParallelRunner") + + decp->status = JxlDecoderSubscribeEvents(decp->decoder, + JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING + | JXL_DEC_FRAME | JXL_DEC_BOX + | JXL_DEC_FULL_IMAGE + ); + _PIL_JXL_CHECK("JxlDecoderSubscribeEvents") + + // tell libjxl to decompress boxes (for example Exif is usually compressed) + decp->status = JxlDecoderSetDecompressBoxes(decp->decoder, JXL_TRUE); + _PIL_JXL_CHECK("JxlDecoderSetDecompressBoxes") + + _jxl_decoder_set_input((PyObject *)decp); + _PIL_JXL_CHECK("JxlDecoderSetInput") + + // decode everything up to the first frame + do { + + decp->status = JxlDecoderProcessInput(decp->decoder); + //printf("Status: %d\n", decp->status); + + decoder_loop_skip_process: + + // there was an error at JxlDecoderProcessInput stage + if (decp->status == JXL_DEC_ERROR) { + jxl_call_name = "JxlDecoderProcessInput"; + goto end; + } + + // got basic info + if (decp->status == JXL_DEC_BASIC_INFO) { + + decp->status = JxlDecoderGetBasicInfo(decp->decoder, + &decp->basic_info); + _PIL_JXL_CHECK("JxlDecoderGetBasicInfo"); + + _pil_jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); + if (decp->pixel_format.data_type != JXL_TYPE_UINT8) { + // only 8 bit integer value images are supported for now + PyErr_SetString(PyExc_NotImplementedError, + "unsupported pixel data type"); + goto end_with_custom_error; + } + decp->mode = _pil_jxl_get_mode(&decp->basic_info); + + continue; + } + + // got color encoding + if (decp->status == JXL_DEC_COLOR_ENCODING) { + + decp->status = JxlDecoderGetICCProfileSize(decp->decoder, + JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len); + _PIL_JXL_CHECK("JxlDecoderGetICCProfileSize"); + + decp->jxl_icc = malloc(decp->jxl_icc_len); + if (!decp->jxl_icc) { + PyErr_SetString(PyExc_OSError, "jxl_icc malloc failed"); + goto end_with_custom_error; + } + + decp->status = JxlDecoderGetColorAsICCProfile(decp->decoder, + JXL_COLOR_PROFILE_TARGET_DATA, + decp->jxl_icc, decp->jxl_icc_len); + _PIL_JXL_CHECK("JxlDecoderGetColorAsICCProfile"); + + continue; + } + + if (decp->status == JXL_DEC_BOX) { + + char btype[4]; + decp->status = JxlDecoderGetBoxType(decp->decoder, btype, JXL_TRUE); + _PIL_JXL_CHECK("JxlDecoderGetBoxType"); + + //printf("found box type: %c%c%c%c\n", btype[0], btype[1], btype[2], btype[3]); + + bool is_box_exif, is_box_xmp; + is_box_exif = !memcmp(btype, "Exif", 4); + is_box_xmp = !memcmp(btype, "xml ", 4); + if (!is_box_exif && !is_box_xmp) { + // not exif/xmp box so continue + continue; + } + + size_t cur_compr_box_size; + decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &cur_compr_box_size); + _PIL_JXL_CHECK("JxlDecoderGetBoxSizeRaw"); + //printf("Exif/xmp box size: %zu\n", cur_compr_box_size); + + uint8_t* final_jxl_buf = NULL; + Py_ssize_t final_jxl_buf_len = 0; + + // cur_box_size is actually compressed box size + // it will also serve as our chunk size + do { + uint8_t* _new_jxl_buf = realloc(final_jxl_buf, final_jxl_buf_len + cur_compr_box_size); + if (!_new_jxl_buf) { + PyErr_SetString(PyExc_OSError, "failed to allocate final_jxl_buf"); + goto end; + } + final_jxl_buf = _new_jxl_buf; + + decp->status = JxlDecoderSetBoxBuffer(decp->decoder, final_jxl_buf + final_jxl_buf_len, cur_compr_box_size); + _PIL_JXL_CHECK("JxlDecoderSetBoxBuffer"); + + decp->status = JxlDecoderProcessInput(decp->decoder); + + size_t remaining = JxlDecoderReleaseBoxBuffer(decp->decoder); + //printf("boxes status: %d, remaining: %zu\n", decp->status, remaining); + final_jxl_buf_len += (cur_compr_box_size - remaining); + } + while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); + + if (is_box_exif) { + decp->jxl_exif = final_jxl_buf; + decp->jxl_exif_len = final_jxl_buf_len; + } else { + decp->jxl_xmp = final_jxl_buf; + decp->jxl_xmp_len = final_jxl_buf_len; + } + + // dirty hack: skip first step of decoding loop since + // we already did it in do...while above + goto decoder_loop_skip_process; + + } + + } while (decp->status != JXL_DEC_FRAME); + + // couldn't determine Image mode or it is unsupported + if (!strcmp(decp->mode, "uns") || !decp->mode) { + PyErr_SetString(PyExc_NotImplementedError, "only 8-bit images are supported"); + goto end_with_custom_error; + } + + if (decp->basic_info.have_animation) { + // get frame count by iterating over image out events + if (!_jxl_decoder_count_frames((PyObject *)decp)) { + PyErr_SetString(PyExc_OSError, "something went wrong when counting frames"); + goto end_with_custom_error; + } + } + + return (PyObject *)decp; + //Py_RETURN_NONE; + + // on success we should never reach here + + end: + // set error message + char err_msg[128]; + snprintf(err_msg, 128, + "could not create decoder object. libjxl call: %s returned: %d", + jxl_call_name, decp->status); + PyErr_SetString(PyExc_OSError, err_msg); + + end_with_custom_error: + + // deallocate + _jxl_decoder_dealloc((PyObject *)decp); + PyObject_Del(decp); + + return NULL; +} + +PyObject * +_jxl_decoder_get_info(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + return Py_BuildValue( + "IIsiIIII", + decp->basic_info.xsize, + decp->basic_info.ysize, + decp->mode, + decp->basic_info.have_animation, + decp->basic_info.animation.tps_numerator, + decp->basic_info.animation.tps_denominator, + decp->basic_info.animation.num_loops, + decp->n_frames + ); +} + +PyObject * +_jxl_decoder_get_next(PyObject *self) { + + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PyObject *bytes; + PyObject *ret; + JxlFrameHeader fhdr = {}; + + char* jxl_call_name; + + // process events until next frame output is ready + while (decp->status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + decp->status = JxlDecoderProcessInput(decp->decoder); + + // every frame was decoded successfully + if (decp->status == JXL_DEC_SUCCESS) { + Py_RETURN_NONE; + } + + // this should only occur after rewind + if (decp->status == JXL_DEC_NEED_MORE_INPUT) { + _jxl_decoder_set_input((PyObject *)decp); + _PIL_JXL_CHECK("JxlDecoderSetInput") + continue; + } + + if (decp->status == JXL_DEC_FRAME) { + // decode frame header + decp->status = JxlDecoderGetFrameHeader(decp->decoder, &fhdr); + _PIL_JXL_CHECK("JxlDecoderGetFrameHeader"); + continue; + } + } + + size_t new_outbuf_len; + decp->status = JxlDecoderImageOutBufferSize(decp->decoder, &decp->pixel_format, &new_outbuf_len); + _PIL_JXL_CHECK("JxlDecoderImageOutBufferSize"); + + // only allocate memory when current buffer is too small + if (decp->outbuf_len < new_outbuf_len) { + + decp->outbuf_len = new_outbuf_len; + uint8_t* _new_outbuf = realloc(decp->outbuf, decp->outbuf_len); + if (!_new_outbuf) { + PyErr_SetString(PyExc_OSError, "failed to allocate outbuf"); + goto end_with_custom_error; + } + decp->outbuf = _new_outbuf; + + } + + decp->status = JxlDecoderSetImageOutBuffer(decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len); + _PIL_JXL_CHECK("JxlDecoderSetImageOutBuffer"); + + // decode image into output_buffer + decp->status = JxlDecoderProcessInput(decp->decoder); + + if (decp->status != JXL_DEC_FULL_IMAGE) { + PyErr_SetString(PyExc_OSError, "failed to read next frame"); + goto end_with_custom_error; + } + + bytes = PyBytes_FromStringAndSize( + (char *)(decp->outbuf), decp->outbuf_len); + + ret = Py_BuildValue("SIi", bytes, fhdr.duration, fhdr.is_last); + + Py_DECREF(bytes); + return ret; + + end: + // we also shouldn't reach here if frame read was ok + + // set error message + char err_msg[128]; + snprintf(err_msg, 128, + "could not read frame. libjxl call: %s returned: %d", + jxl_call_name, decp->status); + PyErr_SetString(PyExc_OSError, err_msg); + + end_with_custom_error: + + // no need to deallocate anything here + // user can just igonre error + + return NULL; + +} + +PyObject * +_jxl_decoder_get_icc(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + if (!decp->jxl_icc) Py_RETURN_NONE; + + return PyBytes_FromStringAndSize((const char *)decp->jxl_icc, decp->jxl_icc_len); +} + +PyObject * +_jxl_decoder_get_exif(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + if (!decp->jxl_exif) Py_RETURN_NONE; + + return PyBytes_FromStringAndSize((const char *)decp->jxl_exif, decp->jxl_exif_len); +} + +PyObject * +_jxl_decoder_get_xmp(PyObject *self) { + PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + + if (!decp->jxl_xmp) Py_RETURN_NONE; + + return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len); +} + +// PILJxlDecoder methods +static struct PyMethodDef _jxl_decoder_methods[] = { + {"get_info", (PyCFunction)_jxl_decoder_get_info, METH_NOARGS, "get_info"}, + {"get_next", (PyCFunction)_jxl_decoder_get_next, METH_NOARGS, "get_next"}, + {"get_icc", (PyCFunction)_jxl_decoder_get_icc, METH_NOARGS, "get_icc"}, + {"get_exif", (PyCFunction)_jxl_decoder_get_exif, METH_NOARGS, "get_exif"}, + {"get_xmp", (PyCFunction)_jxl_decoder_get_xmp, METH_NOARGS, "get_xmp"}, + {"rewind", (PyCFunction)_jxl_decoder_rewind, METH_NOARGS, "rewind"}, + {NULL, NULL} /* sentinel */ +}; + +// PILJxlDecoder type definition +static PyTypeObject PILJxlDecoder_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "PILJxlDecoder", /*tp_name */ + sizeof(PILJxlDecoderObject), /*tp_basicsize */ + 0, /*tp_itemsize */ + /* methods */ + (destructor)_jxl_decoder_dealloc, /*tp_dealloc*/ + 0, /*tp_vectorcall_offset*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_as_async*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _jxl_decoder_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ +}; + +// Return libjxl decoder version available as integer: +// MAJ*1_000_000 + MIN*1_000 + PATCH +PyObject * +JxlDecoderVersion_wrapper() { + return Py_BuildValue("i", JxlDecoderVersion()); +} + +// Version as string +const char * +JxlDecoderVersion_str(void) { + static char version[20]; + int version_number = JxlDecoderVersion(); + sprintf( + version, + "%d.%d.%d", + version_number / 1000000, + (version_number % 1000000) / 1000, + (version_number % 1000) + ); + return version; +} + +static PyMethodDef jxlMethods[] = { + {"JxlDecoderVersion", JxlDecoderVersion_wrapper, METH_NOARGS, "JxlVersion"}, + {"PILJxlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJxlDecoder"}, + {NULL, NULL} +}; + +static int +setup_module(PyObject *m) { + if (PyType_Ready(&PILJxlDecoder_Type) < 0) { + return -1; + } + + // TODO(oloke) ready object types? + PyObject *d = PyModule_GetDict(m); + + PyObject *v = PyUnicode_FromString(JxlDecoderVersion_str()); + PyDict_SetItemString(d, "libjxl_version", v ? v : Py_None); + Py_XDECREF(v); + + return 0; +} + +PyMODINIT_FUNC +PyInit__jxl(void) { + PyObject *m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_jxl", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + jxlMethods, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + if (setup_module(m) < 0) { + Py_DECREF(m); + return NULL; + } + + return m; +} \ No newline at end of file From a57ebeaaf4b66079ef556c67c87b6555c8514513 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 00:38:49 +0000 Subject: [PATCH 02/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_jxl.py | 10 +++--- Tests/test_file_jxl_alpha.py | 5 +-- Tests/test_file_jxl_animated.py | 9 +++--- Tests/test_file_jxl_metadata.py | 6 ++-- src/PIL/JxlImagePlugin.py | 55 ++++++++++++++++++++------------- src/PIL/features.py | 3 +- src/_jxl.c | 34 ++++++++++---------- 7 files changed, 64 insertions(+), 58 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 1ea2c8fcedb..bd2b1f0bcda 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -1,13 +1,13 @@ +from __future__ import annotations + import re + import pytest from PIL import Image, JxlImagePlugin, features from .helper import ( - assert_image_equal, - assert_image_similar, assert_image_similar_tofile, - hopper, skip_unless_feature, ) @@ -21,6 +21,7 @@ # cjxl v0.9.2 41b8cdab # hopper.jxl: cjxl hopper.png hopper.jxl -q 75 -e 8 + class TestUnsupportedJxl: def test_unsupported(self) -> None: if HAVE_JXL: @@ -35,6 +36,7 @@ def test_unsupported(self) -> None: if HAVE_JXL: JxlImagePlugin.SUPPORTED = True + @skip_unless_feature("jxl") class TestFileJxl: def setup_method(self) -> None: @@ -68,5 +70,3 @@ def test_JxlDecode_with_invalid_args(self) -> None: with pytest.raises(TypeError): _jxl.PILJxlDecoder() - - diff --git a/Tests/test_file_jxl_alpha.py b/Tests/test_file_jxl_alpha.py index 710e3b68519..d3f95062130 100644 --- a/Tests/test_file_jxl_alpha.py +++ b/Tests/test_file_jxl_alpha.py @@ -4,9 +4,7 @@ from PIL import Image -from .helper import ( - assert_image_similar_tofile -) +from .helper import assert_image_similar_tofile _webp = pytest.importorskip("PIL._jxl", reason="JXL support not installed") @@ -29,4 +27,3 @@ def test_read_rgba() -> None: image.tobytes() assert_image_similar_tofile(image, "Tests/images/transparent.png", 1.0) - diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index 8a1b67e9f8b..e39eabcb0c8 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -1,9 +1,8 @@ from __future__ import annotations import pytest -from packaging.version import parse as parse_version -from PIL import Image, features +from PIL import Image from .helper import ( assert_image_equal, @@ -52,9 +51,9 @@ def test_seeking() -> None: im1.load() im2.seek(frame) im2.load() - - assert_image_equal(im1.convert('RGB'), im2.convert('RGB')) - + + assert_image_equal(im1.convert("RGB"), im2.convert("RGB")) + total_dur += im1.info["duration"] assert im1.info["duration"] == im2.info["duration"] assert im1.info["timestamp"] == im1.info["timestamp"] diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index 8f99bd8f3da..0184d48140d 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -1,14 +1,12 @@ from __future__ import annotations -from io import BytesIO -from pathlib import Path from types import ModuleType import pytest from PIL import Image -from .helper import mark_if_feature_version, skip_unless_feature +from .helper import skip_unless_feature pytestmark = [ skip_unless_feature("jxl"), @@ -26,6 +24,7 @@ # python -c "from PIL import Image; im=Image.open('Tests/images/flower2.webp'); f=open('/tmp/xmp.xml', 'wb'); f.write(im.info['xmp']); f.close()" # 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: @@ -85,4 +84,3 @@ def test_getxmp() -> None: im.getxmp()["xmpmeta"]["xmptk"] == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " ) - diff --git a/src/PIL/JxlImagePlugin.py b/src/PIL/JxlImagePlugin.py index 8226db09638..1372c443f5e 100644 --- a/src/PIL/JxlImagePlugin.py +++ b/src/PIL/JxlImagePlugin.py @@ -1,6 +1,9 @@ +from __future__ import annotations + +import struct from io import BytesIO + from . import Image, ImageFile -import struct try: from . import _jxl @@ -16,11 +19,14 @@ ## then libjxl decoder is rewinded and we're ready to decode frame by frame ## if OPEN_COUNTS_FRAMES is False, n_frames will be None until the last frame is decoded ## it only applies to animated jpeg xl images -#OPEN_COUNTS_FRAMES = True +# OPEN_COUNTS_FRAMES = True + def _accept(prefix): - is_jxl = prefix[:2] == b'\xff\x0a' \ - or prefix[:12] == b'\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a' + is_jxl = ( + prefix[:2] == b"\xff\x0a" + or prefix[:12] == b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a" + ) if is_jxl and not SUPPORTED: return "image file could not be identified because JXL support not installed" return is_jxl @@ -35,7 +41,9 @@ class JxlImageFile(ImageFile.ImageFile): def _open(self): self._decoder = _jxl.PILJxlDecoder(self.fp.read()) - width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = self._decoder.get_info() + width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = ( + self._decoder.get_info() + ) self._size = width, height self.info["loop"] = n_loops self.is_animated = has_anim @@ -46,7 +54,7 @@ def _open(self): self.n_frames = 1 elif n_frames > 0: self.n_frames = n_frames - self._tps_dur_secs = tps_num/tps_denom + self._tps_dur_secs = tps_num / tps_denom # TODO: handle libjxl timecods self.__timestamp = 0 @@ -57,13 +65,17 @@ def _open(self): icc = self._decoder.get_icc() exif = self._decoder.get_exif() xmp = self._decoder.get_xmp() - if icc: self.info["icc_profile"] = icc + if icc: + self.info["icc_profile"] = icc import traceback + try: - if exif: self.info["exif"] = self._fix_exif(exif) + if exif: + self.info["exif"] = self._fix_exif(exif) except: traceback.print_exc() - if xmp: self.info["xmp"] = xmp + if xmp: + self.info["xmp"] = xmp self._rewind() @@ -73,7 +85,7 @@ def _fix_exif(self, exif): if len(exif) <= 4: return None exif_start_offset = struct.unpack(">I", exif[:4])[0] - return exif[exif_start_offset+4:] + return exif[exif_start_offset + 4 :] def _getexif(self): if "exif" not in self.info: @@ -82,7 +94,7 @@ def _getexif(self): def getxmp(self): return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} - + def _get_next(self): # Get next frame @@ -93,14 +105,14 @@ def _get_next(self): if next_frame is None: msg = "failed to decode next frame in JXL file" raise EOFError(msg) - + data, tps_duration, is_last = next_frame if is_last and self.n_frames is None: # libjxl said this frame is the last one self.n_frames = self.__physical_frame # duration in miliseconds - duration = 1000 * tps_duration * (1/self._tps_dur_secs) + duration = 1000 * tps_duration * (1 / self._tps_dur_secs) timestamp = self.__timestamp self.__timestamp += duration @@ -115,21 +127,22 @@ def _rewind(self, hard=False): def _seek_check(self, frame): # if image is not animated then only the 0th frame is available - if (not self.is_animated and frame != 0) or \ - (self.n_frames is not None and (frame >= self.n_frames or frame < 0)): + if (not self.is_animated and frame != 0) or ( + self.n_frames is not None and (frame >= self.n_frames or frame < 0) + ): msg = "attempt to seek outside sequence" raise EOFError(msg) return self.tell() != frame def _seek(self, frame): - #print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame)) + # print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame)) if frame == self.__physical_frame: return # Nothing to do if frame < self.__physical_frame: # also rewind libjxl decoder instance self._rewind(hard=True) - + while self.__physical_frame < frame: self._get_next() # Advance to the requested frame @@ -144,7 +157,7 @@ def load(self): if self.__loaded != self.__logical_frame: self._seek(self.__logical_frame) - + data, timestamp, duration, is_last = self._get_next() self.info["timestamp"] = timestamp self.info["duration"] = duration @@ -157,9 +170,9 @@ def load(self): # you need probably 2*(raw image plane) bytes of memory self.fp = BytesIO(data) self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] - + return super().load() - + def load_seek(self, pos): pass @@ -169,4 +182,4 @@ def tell(self): Image.register_open(JxlImageFile.format, JxlImageFile, _accept) Image.register_extension(JxlImageFile.format, ".jxl") -Image.register_mime(JxlImageFile.format, "image/jxl") \ No newline at end of file +Image.register_mime(JxlImageFile.format, "image/jxl") diff --git a/src/PIL/features.py b/src/PIL/features.py index abd1961a7ed..e735cb159dc 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -270,8 +270,7 @@ def pilinfo(out=None, supported_formats=True): ("transp_webp", "WEBP Transparency"), ("webp_mux", "WEBPMUX"), ("webp_anim", "WEBP Animation"), - ("jxl", "JPEG XL") - ("jpg", "JPEG"), + ("jxl", "JPEG XL")("jpg", "JPEG"), ("jpg_2000", "OPENJPEG (JPEG2000)"), ("zlib", "ZLIB (PNG/ZIP)"), ("libtiff", "LIBTIFF"), diff --git a/src/_jxl.c b/src/_jxl.c index 5a0f8eeddb5..d4b21208aff 100644 --- a/src/_jxl.c +++ b/src/_jxl.c @@ -12,9 +12,9 @@ void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { - + pf->num_channels = bi->num_color_channels + bi->num_extra_channels; - + if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) { pf->data_type = JXL_TYPE_FLOAT; // not yet supported } else if (bi->bits_per_sample > 8) { @@ -22,7 +22,7 @@ void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { } else { pf->data_type = JXL_TYPE_UINT8; } - + // this *might* cause some issues on Big-Endian systems // would be great to test it pf->endianness = JXL_NATIVE_ENDIAN; @@ -37,7 +37,7 @@ char* _pil_jxl_get_mode(const JxlBasicInfo *bi) { // it will throw an exception but that's for your own good // you wouldn't want to see distorted image if (bi->bits_per_sample != 8) return "uns"; - + // image has transparency if (bi->alpha_bits > 0) { if (bi->num_color_channels == 3) { @@ -62,7 +62,7 @@ char* _pil_jxl_get_mode(const JxlBasicInfo *bi) { typedef struct { PyObject_HEAD JxlDecoder *decoder; void *runner; - + uint8_t *jxl_data; // input jxl bitstream Py_ssize_t jxl_data_len; // length of input jxl bitstream @@ -75,13 +75,13 @@ typedef struct { Py_ssize_t jxl_exif_len; uint8_t *jxl_xmp; Py_ssize_t jxl_xmp_len; - + JxlDecoderStatus status; JxlBasicInfo basic_info; JxlPixelFormat pixel_format; Py_ssize_t n_frames; - + char *mode; } PILJxlDecoderObject; @@ -90,7 +90,7 @@ static PyTypeObject PILJxlDecoder_Type; void _jxl_decoder_dealloc(PyObject *self) { PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; - + if (decp->jxl_data) { free(decp->jxl_data); decp->jxl_data = NULL; @@ -209,7 +209,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // convert jxl data string to C uint8_t pointer PyBytes_AsStringAndSize((PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len); - + // here occurs this copying (inefficiency) decp->jxl_data = malloc(_tmp_jxl_data_len); memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len); @@ -222,7 +222,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->decoder = JxlDecoderCreate(NULL); decp->status = JxlDecoderSetParallelRunner(decp->decoder, - JxlThreadParallelRunner, decp->runner); + JxlThreadParallelRunner, decp->runner); _PIL_JXL_CHECK("JxlDecoderSetParallelRunner") decp->status = JxlDecoderSubscribeEvents(decp->decoder, @@ -241,7 +241,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // decode everything up to the first frame do { - + decp->status = JxlDecoderProcessInput(decp->decoder); //printf("Status: %d\n", decp->status); @@ -255,7 +255,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got basic info if (decp->status == JXL_DEC_BASIC_INFO) { - + decp->status = JxlDecoderGetBasicInfo(decp->decoder, &decp->basic_info); _PIL_JXL_CHECK("JxlDecoderGetBasicInfo"); @@ -274,7 +274,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got color encoding if (decp->status == JXL_DEC_COLOR_ENCODING) { - + decp->status = JxlDecoderGetICCProfileSize(decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len); _PIL_JXL_CHECK("JxlDecoderGetICCProfileSize"); @@ -337,7 +337,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { final_jxl_buf_len += (cur_compr_box_size - remaining); } while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); - + if (is_box_exif) { decp->jxl_exif = final_jxl_buf; decp->jxl_exif_len = final_jxl_buf_len; @@ -444,7 +444,7 @@ _jxl_decoder_get_next(PyObject *self) { size_t new_outbuf_len; decp->status = JxlDecoderImageOutBufferSize(decp->decoder, &decp->pixel_format, &new_outbuf_len); _PIL_JXL_CHECK("JxlDecoderImageOutBufferSize"); - + // only allocate memory when current buffer is too small if (decp->outbuf_len < new_outbuf_len) { @@ -455,7 +455,7 @@ _jxl_decoder_get_next(PyObject *self) { goto end_with_custom_error; } decp->outbuf = _new_outbuf; - + } decp->status = JxlDecoderSetImageOutBuffer(decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len); @@ -631,4 +631,4 @@ PyInit__jxl(void) { } return m; -} \ No newline at end of file +} From 23fb57dd266886cc767ffaf7c3082e88d3e8c59f Mon Sep 17 00:00:00 2001 From: olokelo Date: Sat, 2 Mar 2024 10:37:55 +0100 Subject: [PATCH 03/66] minor fixes, linting corrections --- Tests/test_file_jxl_metadata.py | 5 ++++- Tests/test_jxl_leaks.py | 3 ++- src/PIL/JxlImagePlugin.py | 9 ++------- src/PIL/features.py | 3 ++- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index 0184d48140d..7e4a02912ad 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -21,7 +21,10 @@ # cjxl flower.jpg flower.jxl --lossless_jpeg=0 -q 75 -e 8 -# python -c "from PIL import Image; im=Image.open('Tests/images/flower2.webp'); f=open('/tmp/xmp.xml', 'wb'); f.write(im.info['xmp']); f.close()" +# >>> 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 diff --git a/Tests/test_jxl_leaks.py b/Tests/test_jxl_leaks.py index 5a18ab9a52f..307cc61547d 100644 --- a/Tests/test_jxl_leaks.py +++ b/Tests/test_jxl_leaks.py @@ -11,7 +11,8 @@ @skip_unless_feature("jxl") class TestJxlLeaks(PillowLeakTestCase): - # TODO: lower the limit, I'm not sure what is correct limit since I have libjxl debug system-wide + # TODO: lower the limit, I'm not sure what is correct limit + # since I have libjxl debug system-wide mem_limit = 16 * 1024 # kb iterations = 100 diff --git a/src/PIL/JxlImagePlugin.py b/src/PIL/JxlImagePlugin.py index 1372c443f5e..b4304e9f0c7 100644 --- a/src/PIL/JxlImagePlugin.py +++ b/src/PIL/JxlImagePlugin.py @@ -67,13 +67,8 @@ def _open(self): xmp = self._decoder.get_xmp() if icc: self.info["icc_profile"] = icc - import traceback - - try: - if exif: - self.info["exif"] = self._fix_exif(exif) - except: - traceback.print_exc() + if exif: + self.info["exif"] = self._fix_exif(exif) if xmp: self.info["xmp"] = xmp diff --git a/src/PIL/features.py b/src/PIL/features.py index e735cb159dc..e8d428cac40 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -270,7 +270,8 @@ def pilinfo(out=None, supported_formats=True): ("transp_webp", "WEBP Transparency"), ("webp_mux", "WEBPMUX"), ("webp_anim", "WEBP Animation"), - ("jxl", "JPEG XL")("jpg", "JPEG"), + ("jxl", "JPEG XL"), + ("jpg", "JPEG"), ("jpg_2000", "OPENJPEG (JPEG2000)"), ("zlib", "ZLIB (PNG/ZIP)"), ("libtiff", "LIBTIFF"), From 2eb5987d1860294270bc58d4c81bdbed46626a92 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 6 Mar 2024 20:54:20 +1100 Subject: [PATCH 04/66] Added type hints --- src/PIL/_jxl.pyi | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/PIL/_jxl.pyi diff --git a/src/PIL/_jxl.pyi b/src/PIL/_jxl.pyi new file mode 100644 index 00000000000..b0235555dc5 --- /dev/null +++ b/src/PIL/_jxl.pyi @@ -0,0 +1,5 @@ +from __future__ import annotations + +from typing import Any + +def __getattr__(name: str) -> Any: ... From 37b58f340b56919d15095b8a475a5e44f9401ff2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 6 Mar 2024 20:55:14 +1100 Subject: [PATCH 05/66] Removed feature --- src/PIL/features.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/features.py b/src/PIL/features.py index e8d428cac40..028aa097429 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -125,7 +125,6 @@ def get_supported_codecs(): "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None), "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None), "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None), - "jxl": ("PIL._jxl", "HAVE_JXL", None), "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), From 24b63ad7df33187a4e1d997e2fe8b7a45636f9b3 Mon Sep 17 00:00:00 2001 From: olokelo Date: Mon, 11 Mar 2024 11:41:32 +0100 Subject: [PATCH 06/66] fix goto labels for clang --- src/_jxl.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/_jxl.c b/src/_jxl.c index d4b21208aff..2f0b4eec853 100644 --- a/src/_jxl.c +++ b/src/_jxl.c @@ -373,9 +373,10 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // on success we should never reach here - end: // set error message char err_msg[128]; + + end: snprintf(err_msg, 128, "could not create decoder object. libjxl call: %s returned: %d", jxl_call_name, decp->status); @@ -477,11 +478,12 @@ _jxl_decoder_get_next(PyObject *self) { Py_DECREF(bytes); return ret; - end: // we also shouldn't reach here if frame read was ok // set error message char err_msg[128]; + + end: snprintf(err_msg, 128, "could not read frame. libjxl call: %s returned: %d", jxl_call_name, decp->status); @@ -490,7 +492,7 @@ _jxl_decoder_get_next(PyObject *self) { end_with_custom_error: // no need to deallocate anything here - // user can just igonre error + // user can just ignore error return NULL; From 0b504109b6d89892d82f0552cd2df1dbe059b4c1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 10:44:29 +0000 Subject: [PATCH 07/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_jxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_jxl.c b/src/_jxl.c index 2f0b4eec853..c3b3a7bf45e 100644 --- a/src/_jxl.c +++ b/src/_jxl.c @@ -482,7 +482,7 @@ _jxl_decoder_get_next(PyObject *self) { // set error message char err_msg[128]; - + end: snprintf(err_msg, 128, "could not read frame. libjxl call: %s returned: %d", From f6086d40cc728f9c153b430e9566550fb5068912 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 13:00:50 +0100 Subject: [PATCH 08/66] modify leak test --- Tests/test_jxl_leaks.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Tests/test_jxl_leaks.py b/Tests/test_jxl_leaks.py index 307cc61547d..b8b0835cd12 100644 --- a/Tests/test_jxl_leaks.py +++ b/Tests/test_jxl_leaks.py @@ -6,18 +6,16 @@ from .helper import PillowLeakTestCase, skip_unless_feature -test_file = "Tests/images/hopper.jxl" +TEST_FILE = "Tests/images/hopper.jxl" @skip_unless_feature("jxl") class TestJxlLeaks(PillowLeakTestCase): - # TODO: lower the limit, I'm not sure what is correct limit - # since I have libjxl debug system-wide - mem_limit = 16 * 1024 # kb - iterations = 100 + mem_limit = 6 * 1024 # kb + iterations = 1000 def test_leak_load(self) -> None: - with open(test_file, "rb") as f: + with open(TEST_FILE, "rb") as f: im_data = f.read() def core() -> None: From 53204506791776bf1dc5d8150f9f171b14b76e71 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 13:03:08 +0100 Subject: [PATCH 09/66] fix _jxl_decoder_count_frames --- src/_jxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_jxl.c b/src/_jxl.c index c3b3a7bf45e..cf2fba3caad 100644 --- a/src/_jxl.c +++ b/src/_jxl.c @@ -150,7 +150,7 @@ _jxl_decoder_rewind(PyObject *self) { } -void * +bool _jxl_decoder_count_frames(PyObject *self) { PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; From 1b049ab4a83cfa94bb45c02a70de0bd2adbf8ec8 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 19:22:37 +0100 Subject: [PATCH 10/66] minor plugin code tweaks --- src/PIL/JxlImagePlugin.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/PIL/JxlImagePlugin.py b/src/PIL/JxlImagePlugin.py index b4304e9f0c7..873cc11609d 100644 --- a/src/PIL/JxlImagePlugin.py +++ b/src/PIL/JxlImagePlugin.py @@ -48,28 +48,26 @@ def _open(self): self.info["loop"] = n_loops self.is_animated = has_anim - self.n_frames = None self._tps_dur_secs = 1 - if not self.is_animated: - self.n_frames = 1 - elif n_frames > 0: - self.n_frames = n_frames - self._tps_dur_secs = tps_num / tps_denom - # TODO: handle libjxl timecods + self.n_frames = 1 + if self.is_animated: + self.n_frames = None + if n_frames > 0: + self.n_frames = n_frames + self._tps_dur_secs = tps_num / tps_denom + + # TODO: handle libjxl time codes self.__timestamp = 0 self._mode = mode self.rawmode = mode self.tile = [] - icc = self._decoder.get_icc() - exif = self._decoder.get_exif() - xmp = self._decoder.get_xmp() - if icc: + if icc := self._decoder.get_icc(): self.info["icc_profile"] = icc - if exif: + if exif := self._decoder.get_exif(): self.info["exif"] = self._fix_exif(exif) - if xmp: + if xmp := self._decoder.get_xmp(): self.info["xmp"] = xmp self._rewind() From 6048520fcf31f3b09c19273f5fad11eab702b611 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 20:33:24 +0100 Subject: [PATCH 11/66] add type hints --- src/PIL/JxlImagePlugin.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/PIL/JxlImagePlugin.py b/src/PIL/JxlImagePlugin.py index 873cc11609d..80278ae3336 100644 --- a/src/PIL/JxlImagePlugin.py +++ b/src/PIL/JxlImagePlugin.py @@ -22,13 +22,14 @@ # OPEN_COUNTS_FRAMES = True -def _accept(prefix): +def _accept(prefix: bytes) -> bool: is_jxl = ( prefix[:2] == b"\xff\x0a" or prefix[:12] == b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a" ) if is_jxl and not SUPPORTED: - return "image file could not be identified because JXL support not installed" + msg = "image file could not be identified because JXL support not installed" + raise SyntaxError(msg) return is_jxl @@ -38,7 +39,7 @@ class JxlImageFile(ImageFile.ImageFile): __loaded = 0 __logical_frame = 0 - def _open(self): + def _open(self) -> None: self._decoder = _jxl.PILJxlDecoder(self.fp.read()) width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = ( @@ -72,7 +73,7 @@ def _open(self): self._rewind() - def _fix_exif(self, exif): + def _fix_exif(self, exif: bytes) -> bytes: # jpeg xl does some weird shenanigans when storing exif # it omits first 6 bytes of tiff header but adds 4 byte offset instead if len(exif) <= 4: @@ -80,15 +81,15 @@ def _fix_exif(self, exif): exif_start_offset = struct.unpack(">I", exif[:4])[0] return exif[exif_start_offset + 4 :] - def _getexif(self): + def _getexif(self) -> dict[str, str]: if "exif" not in self.info: return None return self.getexif()._get_merged_dict() - def getxmp(self): + def getxmp(self) -> dict[str, str]: return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} - def _get_next(self): + def _get_next(self) -> tuple[bytes, float, float, bool]: # Get next frame next_frame = self._decoder.get_next() @@ -111,14 +112,14 @@ def _get_next(self): return data, timestamp, duration, is_last - def _rewind(self, hard=False): + def _rewind(self, hard: bool=False) -> None: if hard: self._decoder.rewind() self.__physical_frame = 0 self.__loaded = -1 self.__timestamp = 0 - def _seek_check(self, frame): + def _seek_check(self, frame: int) -> bool: # if image is not animated then only the 0th frame is available if (not self.is_animated and frame != 0) or ( self.n_frames is not None and (frame >= self.n_frames or frame < 0) @@ -128,7 +129,7 @@ def _seek_check(self, frame): return self.tell() != frame - def _seek(self, frame): + def _seek(self, frame: int) -> None: # print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame)) if frame == self.__physical_frame: return # Nothing to do @@ -139,7 +140,7 @@ def _seek(self, frame): while self.__physical_frame < frame: self._get_next() # Advance to the requested frame - def seek(self, frame): + def seek(self, frame: int) -> None: if not self._seek_check(frame): return @@ -166,10 +167,10 @@ def load(self): return super().load() - def load_seek(self, pos): + def load_seek(self, pos: int) -> None: pass - def tell(self): + def tell(self) -> int: return self.__logical_frame From 8fa280f6a55e58a77348769aa87af7936824f9af Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 22:17:35 +0100 Subject: [PATCH 12/66] rename jxl -> jpegxl --- Tests/test_file_jxl.py | 37 ++++++----- Tests/test_file_jxl_alpha.py | 2 +- Tests/test_file_jxl_animated.py | 2 +- Tests/test_file_jxl_metadata.py | 2 +- Tests/test_jxl_leaks.py | 4 +- setup.py | 20 +++--- ...JxlImagePlugin.py => JpegXlImagePlugin.py} | 12 ++-- src/PIL/__init__.py | 2 +- src/PIL/{_jxl.pyi => _jpegxl.pyi} | 0 src/PIL/features.py | 4 +- src/{_jxl.c => _jpegxl.c} | 62 +++++++++---------- 11 files changed, 73 insertions(+), 74 deletions(-) rename src/PIL/{JxlImagePlugin.py => JpegXlImagePlugin.py} (94%) rename src/PIL/{_jxl.pyi => _jpegxl.pyi} (100%) rename src/{_jxl.c => _jpegxl.c} (91%) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index bd2b1f0bcda..9b09371a998 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -4,7 +4,7 @@ import pytest -from PIL import Image, JxlImagePlugin, features +from PIL import Image, JpegXlImagePlugin, features from .helper import ( assert_image_similar_tofile, @@ -12,39 +12,38 @@ ) try: - from PIL import _jxl + from PIL import _jpegxl - HAVE_JXL = True + HAVE_JPEGXL = True except ImportError: - HAVE_JXL = False + HAVE_JPEGXL = False # cjxl v0.9.2 41b8cdab # hopper.jxl: cjxl hopper.png hopper.jxl -q 75 -e 8 -class TestUnsupportedJxl: +class TestUnsupportedJpegXl: def test_unsupported(self) -> None: - if HAVE_JXL: - JxlImagePlugin.SUPPORTED = False + if HAVE_JPEGXL: + JpegXlImagePlugin.SUPPORTED = False file_path = "Tests/images/hopper.jxl" - with pytest.warns(UserWarning): - with pytest.raises(OSError): - with Image.open(file_path): - pass + with pytest.raises(OSError): + with Image.open(file_path): + pass - if HAVE_JXL: - JxlImagePlugin.SUPPORTED = True + if HAVE_JPEGXL: + JpegXlImagePlugin.SUPPORTED = True -@skip_unless_feature("jxl") -class TestFileJxl: +@skip_unless_feature("jpegxl") +class TestFileJpegXl: def setup_method(self) -> None: self.rgb_mode = "RGB" def test_version(self) -> None: - _jxl.JxlDecoderVersion() - assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jxl")) + _jpegxl.JpegXlDecoderVersion() + assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jpegxl")) def test_read_rgb(self) -> None: """ @@ -63,10 +62,10 @@ def test_read_rgb(self) -> None: # djxl hopper.jxl hopper_jxl_bits.ppm assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0) - def test_JxlDecode_with_invalid_args(self) -> None: + def test_JpegXlDecode_with_invalid_args(self) -> None: """ Calling decoder functions with no arguments should result in an error. """ with pytest.raises(TypeError): - _jxl.PILJxlDecoder() + _jpegxl.PILJpegXlDecoder() diff --git a/Tests/test_file_jxl_alpha.py b/Tests/test_file_jxl_alpha.py index d3f95062130..8c3ab2b7111 100644 --- a/Tests/test_file_jxl_alpha.py +++ b/Tests/test_file_jxl_alpha.py @@ -6,7 +6,7 @@ from .helper import assert_image_similar_tofile -_webp = pytest.importorskip("PIL._jxl", reason="JXL support not installed") +_jpegxl = pytest.importorskip("PIL._jpegxl", reason="JPEG XL support not installed") def test_read_rgba() -> None: diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index e39eabcb0c8..e00c27639b6 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -10,7 +10,7 @@ ) pytestmark = [ - skip_unless_feature("jxl"), + skip_unless_feature("jpegxl"), ] diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index 7e4a02912ad..a7cd2c925fc 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -9,7 +9,7 @@ from .helper import skip_unless_feature pytestmark = [ - skip_unless_feature("jxl"), + skip_unless_feature("jpegxl"), ] ElementTree: ModuleType | None diff --git a/Tests/test_jxl_leaks.py b/Tests/test_jxl_leaks.py index b8b0835cd12..cec9f152894 100644 --- a/Tests/test_jxl_leaks.py +++ b/Tests/test_jxl_leaks.py @@ -9,8 +9,8 @@ TEST_FILE = "Tests/images/hopper.jxl" -@skip_unless_feature("jxl") -class TestJxlLeaks(PillowLeakTestCase): +@skip_unless_feature("jpegxl") +class TestJpegXlLeaks(PillowLeakTestCase): mem_limit = 6 * 1024 # kb iterations = 1000 diff --git a/setup.py b/setup.py index 7698339beb8..a5424f6c634 100644 --- a/setup.py +++ b/setup.py @@ -286,7 +286,7 @@ class feature: features = [ "zlib", "jpeg", - "jxl", + "jpegxl", "tiff", "freetype", "raqm", @@ -695,13 +695,13 @@ def build_extensions(self): feature.jpeg2000 = "openjp2" feature.openjpeg_version = ".".join(str(x) for x in best_version) - if feature.want("jxl"): - _dbg("Looking for jxl") + 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.jxl = "jxl jxl_threads" + feature.jpegxl = "jxl jxl_threads" if feature.want("imagequant"): _dbg("Looking for imagequant") @@ -786,14 +786,14 @@ def build_extensions(self): # alternate Windows name. feature.lcms = "lcms2_static" - if feature.jxl: + if feature.jpegxl: # jxl and jxl_threads are required - libs = feature.jxl.split() + libs = feature.jpegxl.split() defs = [] - self._update_extension("PIL._jxl", libs, defs) + self._update_extension("PIL._jpegxl", libs, defs) else: - self._remove_extension("PIL._jxl") + self._remove_extension("PIL._jpegxl") if feature.want("webp"): _dbg("Looking for webp") @@ -955,7 +955,7 @@ def summary_report(self, feature): (feature.freetype, "FREETYPE2"), (feature.raqm, "RAQM (Text shaping)", raqm_extra_info), (feature.lcms, "LITTLECMS2"), - (feature.jxl, "JXL"), + (feature.jpegxl, "JPEG XL"), (feature.webp, "WEBP"), (feature.webpmux, "WEBPMUX"), (feature.xcb, "XCB (X protocol)"), @@ -1000,7 +1000,7 @@ def debug_build(): Extension("PIL._imaging", files), Extension("PIL._imagingft", ["src/_imagingft.c"]), Extension("PIL._imagingcms", ["src/_imagingcms.c"]), - Extension("PIL._jxl", ["src/_jxl.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"]), diff --git a/src/PIL/JxlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py similarity index 94% rename from src/PIL/JxlImagePlugin.py rename to src/PIL/JpegXlImagePlugin.py index 80278ae3336..d14a2099ff5 100644 --- a/src/PIL/JxlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -6,7 +6,7 @@ from . import Image, ImageFile try: - from . import _jxl + from . import _jpegxl SUPPORTED = True except ImportError: @@ -33,14 +33,14 @@ def _accept(prefix: bytes) -> bool: return is_jxl -class JxlImageFile(ImageFile.ImageFile): +class JpegXlImageFile(ImageFile.ImageFile): format = "JPEG XL" format_description = "JPEG XL image" __loaded = 0 __logical_frame = 0 def _open(self) -> None: - self._decoder = _jxl.PILJxlDecoder(self.fp.read()) + self._decoder = _jpegxl.PILJpegXlDecoder(self.fp.read()) width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = ( self._decoder.get_info() @@ -174,6 +174,6 @@ def tell(self) -> int: return self.__logical_frame -Image.register_open(JxlImageFile.format, JxlImageFile, _accept) -Image.register_extension(JxlImageFile.format, ".jxl") -Image.register_mime(JxlImageFile.format, "image/jxl") +Image.register_open(JpegXlImageFile.format, JpegXlImageFile, _accept) +Image.register_extension(JpegXlImageFile.format, ".jxl") +Image.register_mime(JpegXlImageFile.format, "image/jxl") diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index 768315d3b25..09ea0f45747 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -47,7 +47,7 @@ "IptcImagePlugin", "JpegImagePlugin", "Jpeg2KImagePlugin", - "JxlImagePlugin", + "JpegXlImagePlugin", "McIdasImagePlugin", "MicImagePlugin", "MpegImagePlugin", diff --git a/src/PIL/_jxl.pyi b/src/PIL/_jpegxl.pyi similarity index 100% rename from src/PIL/_jxl.pyi rename to src/PIL/_jpegxl.pyi diff --git a/src/PIL/features.py b/src/PIL/features.py index 028aa097429..179e126f79b 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -14,7 +14,7 @@ "tkinter": ("PIL._tkinter_finder", "tk_version"), "freetype2": ("PIL._imagingft", "freetype2_version"), "littlecms2": ("PIL._imagingcms", "littlecms_version"), - "jxl": ("PIL._jxl", "libjxl_version"), + "jpegxl": ("PIL._jpegxl", "libjxl_version"), "webp": ("PIL._webp", "webpdecoder_version"), } @@ -269,7 +269,7 @@ def pilinfo(out=None, supported_formats=True): ("transp_webp", "WEBP Transparency"), ("webp_mux", "WEBPMUX"), ("webp_anim", "WEBP Animation"), - ("jxl", "JPEG XL"), + ("jpegxl", "JPEG XL"), ("jpg", "JPEG"), ("jpg_2000", "OPENJPEG (JPEG2000)"), ("zlib", "ZLIB (PNG/ZIP)"), diff --git a/src/_jxl.c b/src/_jpegxl.c similarity index 91% rename from src/_jxl.c rename to src/_jpegxl.c index cf2fba3caad..1905651c77f 100644 --- a/src/_jxl.c +++ b/src/_jpegxl.c @@ -83,13 +83,13 @@ typedef struct { Py_ssize_t n_frames; char *mode; -} PILJxlDecoderObject; +} PILJpegXlDecoderObject; -static PyTypeObject PILJxlDecoder_Type; +static PyTypeObject PILJpegXlDecoder_Type; void _jxl_decoder_dealloc(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; if (decp->jxl_data) { free(decp->jxl_data); @@ -131,7 +131,7 @@ _jxl_decoder_dealloc(PyObject *self) { // sets input jxl bitstream loaded into jxl_data // has to be called after every rewind void _jxl_decoder_set_input(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; decp->status = JxlDecoderSetInput(decp->decoder, decp->jxl_data, decp->jxl_data_len); @@ -144,7 +144,7 @@ void _jxl_decoder_set_input(PyObject *self) { PyObject * _jxl_decoder_rewind(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; JxlDecoderRewind(decp->decoder); Py_RETURN_NONE; @@ -152,7 +152,7 @@ _jxl_decoder_rewind(PyObject *self) { bool _jxl_decoder_count_frames(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; decp->n_frames = 0; @@ -179,8 +179,8 @@ PyObject * _jxl_decoder_new(PyObject *self, PyObject *args) { PyBytesObject *jxl_string; - PILJxlDecoderObject *decp = NULL; - decp = PyObject_New(PILJxlDecoderObject, &PILJxlDecoder_Type); + PILJpegXlDecoderObject *decp = NULL; + decp = PyObject_New(PILJpegXlDecoderObject, &PILJpegXlDecoder_Type); decp->mode = NULL; decp->jxl_data = NULL; decp->jxl_data_len = 0; @@ -202,7 +202,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { return NULL; } - // this data needs to be copied to PILJxlDecoderObject + // this data needs to be copied to PILJpegXlDecoderObject // so that input bitstream is preserved across calls const uint8_t *_tmp_jxl_data; Py_ssize_t _tmp_jxl_data_len; @@ -393,7 +393,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { PyObject * _jxl_decoder_get_info(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; return Py_BuildValue( "IIsiIIII", @@ -411,7 +411,7 @@ _jxl_decoder_get_info(PyObject *self) { PyObject * _jxl_decoder_get_next(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; PyObject *bytes; PyObject *ret; JxlFrameHeader fhdr = {}; @@ -500,7 +500,7 @@ _jxl_decoder_get_next(PyObject *self) { PyObject * _jxl_decoder_get_icc(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; if (!decp->jxl_icc) Py_RETURN_NONE; @@ -509,7 +509,7 @@ _jxl_decoder_get_icc(PyObject *self) { PyObject * _jxl_decoder_get_exif(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; if (!decp->jxl_exif) Py_RETURN_NONE; @@ -518,15 +518,15 @@ _jxl_decoder_get_exif(PyObject *self) { PyObject * _jxl_decoder_get_xmp(PyObject *self) { - PILJxlDecoderObject *decp = (PILJxlDecoderObject *)self; + PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; if (!decp->jxl_xmp) Py_RETURN_NONE; return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len); } -// PILJxlDecoder methods -static struct PyMethodDef _jxl_decoder_methods[] = { +// PILJpegXlDecoder methods +static struct PyMethodDef _jpegxl_decoder_methods[] = { {"get_info", (PyCFunction)_jxl_decoder_get_info, METH_NOARGS, "get_info"}, {"get_next", (PyCFunction)_jxl_decoder_get_next, METH_NOARGS, "get_next"}, {"get_icc", (PyCFunction)_jxl_decoder_get_icc, METH_NOARGS, "get_icc"}, @@ -536,10 +536,10 @@ static struct PyMethodDef _jxl_decoder_methods[] = { {NULL, NULL} /* sentinel */ }; -// PILJxlDecoder type definition -static PyTypeObject PILJxlDecoder_Type = { - PyVarObject_HEAD_INIT(NULL, 0) "PILJxlDecoder", /*tp_name */ - sizeof(PILJxlDecoderObject), /*tp_basicsize */ +// PILJpegXlDecoder type definition +static PyTypeObject PILJpegXlDecoder_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "PILJpegXlDecoder", /*tp_name */ + sizeof(PILJpegXlDecoderObject), /*tp_basicsize */ 0, /*tp_itemsize */ /* methods */ (destructor)_jxl_decoder_dealloc, /*tp_dealloc*/ @@ -565,7 +565,7 @@ static PyTypeObject PILJxlDecoder_Type = { 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ - _jxl_decoder_methods, /*tp_methods*/ + _jpegxl_decoder_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ }; @@ -573,13 +573,13 @@ static PyTypeObject PILJxlDecoder_Type = { // Return libjxl decoder version available as integer: // MAJ*1_000_000 + MIN*1_000 + PATCH PyObject * -JxlDecoderVersion_wrapper() { +JpegXlDecoderVersion_wrapper() { return Py_BuildValue("i", JxlDecoderVersion()); } // Version as string const char * -JxlDecoderVersion_str(void) { +JpegXlDecoderVersion_str(void) { static char version[20]; int version_number = JxlDecoderVersion(); sprintf( @@ -592,22 +592,22 @@ JxlDecoderVersion_str(void) { return version; } -static PyMethodDef jxlMethods[] = { - {"JxlDecoderVersion", JxlDecoderVersion_wrapper, METH_NOARGS, "JxlVersion"}, - {"PILJxlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJxlDecoder"}, +static PyMethodDef jpegxlMethods[] = { + {"JpegXlDecoderVersion", JpegXlDecoderVersion_wrapper, METH_NOARGS, "JpegXlVersion"}, + {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"}, {NULL, NULL} }; static int setup_module(PyObject *m) { - if (PyType_Ready(&PILJxlDecoder_Type) < 0) { + if (PyType_Ready(&PILJpegXlDecoder_Type) < 0) { return -1; } // TODO(oloke) ready object types? PyObject *d = PyModule_GetDict(m); - PyObject *v = PyUnicode_FromString(JxlDecoderVersion_str()); + PyObject *v = PyUnicode_FromString(JpegXlDecoderVersion_str()); PyDict_SetItemString(d, "libjxl_version", v ? v : Py_None); Py_XDECREF(v); @@ -615,15 +615,15 @@ setup_module(PyObject *m) { } PyMODINIT_FUNC -PyInit__jxl(void) { +PyInit__jpegxl(void) { PyObject *m; static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, - "_jxl", /* m_name */ + "_jpegxl", /* m_name */ NULL, /* m_doc */ -1, /* m_size */ - jxlMethods, /* m_methods */ + jpegxlMethods, /* m_methods */ }; m = PyModule_Create(&module_def); From 58c37bf7eb72f11d3566309595da15f6b433a405 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 22:25:19 +0100 Subject: [PATCH 13/66] add test case for seeking to the same frame --- Tests/test_file_jxl_animated.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index e00c27639b6..758fa79e2d8 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -59,6 +59,13 @@ def test_seeking() -> None: assert im1.info["timestamp"] == im1.info["timestamp"] assert total_dur == 8000 + assert im1.tell() == 0 and im2.tell() == 0 + + im1.seek(0) + im1.load() + im2.seek(0) + im2.load() + def test_seek_errors() -> None: with Image.open("Tests/images/iss634.jxl") as im: From 48bbc2e2f845d69d687044dfbc3eb7df4544e93e Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 22:27:51 +0100 Subject: [PATCH 14/66] flip cases in metadata test --- Tests/test_file_jxl_metadata.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index a7cd2c925fc..639624c2dc2 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -76,14 +76,14 @@ def test_getxmp() -> None: assert im.getxmp() == {} with Image.open("Tests/images/flower2.jxl") as im: - if ElementTree is None: + if ElementTree: + assert ( + im.getxmp()["xmpmeta"]["xmptk"] + == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " + ) + else: with pytest.warns( UserWarning, match="XMP data cannot be read without defusedxml dependency", ): assert im.getxmp() == {} - else: - assert ( - im.getxmp()["xmpmeta"]["xmptk"] - == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " - ) From 443a35235ab0767af5a6cccab804fbb439272515 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 21:30:09 +0000 Subject: [PATCH 15/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/JpegXlImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index d14a2099ff5..324c453ea83 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -56,7 +56,7 @@ def _open(self) -> None: if n_frames > 0: self.n_frames = n_frames self._tps_dur_secs = tps_num / tps_denom - + # TODO: handle libjxl time codes self.__timestamp = 0 @@ -112,7 +112,7 @@ def _get_next(self) -> tuple[bytes, float, float, bool]: return data, timestamp, duration, is_last - def _rewind(self, hard: bool=False) -> None: + def _rewind(self, hard: bool = False) -> None: if hard: self._decoder.rewind() self.__physical_frame = 0 From 62c58c2d001668789374fd2acfa1c8cdd0e472a5 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 22:39:12 +0100 Subject: [PATCH 16/66] fix some type hinting mistakes --- src/PIL/JpegXlImagePlugin.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 324c453ea83..7326ace46a2 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -50,13 +50,13 @@ def _open(self) -> None: self.is_animated = has_anim self._tps_dur_secs = 1 - self.n_frames = 1 + self.n_frames: Optional[int] = 1 if self.is_animated: self.n_frames = None if n_frames > 0: self.n_frames = n_frames self._tps_dur_secs = tps_num / tps_denom - + # TODO: handle libjxl time codes self.__timestamp = 0 @@ -73,7 +73,7 @@ def _open(self) -> None: self._rewind() - def _fix_exif(self, exif: bytes) -> bytes: + def _fix_exif(self, exif: bytes) -> Optional[bytes]: # jpeg xl does some weird shenanigans when storing exif # it omits first 6 bytes of tiff header but adds 4 byte offset instead if len(exif) <= 4: @@ -81,7 +81,7 @@ def _fix_exif(self, exif: bytes) -> bytes: exif_start_offset = struct.unpack(">I", exif[:4])[0] return exif[exif_start_offset + 4 :] - def _getexif(self) -> dict[str, str]: + def _getexif(self) -> Optional[dict[str, str]]: if "exif" not in self.info: return None return self.getexif()._get_merged_dict() @@ -112,7 +112,7 @@ def _get_next(self) -> tuple[bytes, float, float, bool]: return data, timestamp, duration, is_last - def _rewind(self, hard: bool = False) -> None: + def _rewind(self, hard: bool=False) -> None: if hard: self._decoder.rewind() self.__physical_frame = 0 From 8cab1c122c7af6326d88e1b3a434e786bb06f0c3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 21:40:09 +0000 Subject: [PATCH 17/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/JpegXlImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 7326ace46a2..e56445f58f4 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -56,7 +56,7 @@ def _open(self) -> None: if n_frames > 0: self.n_frames = n_frames self._tps_dur_secs = tps_num / tps_denom - + # TODO: handle libjxl time codes self.__timestamp = 0 @@ -112,7 +112,7 @@ def _get_next(self) -> tuple[bytes, float, float, bool]: return data, timestamp, duration, is_last - def _rewind(self, hard: bool=False) -> None: + def _rewind(self, hard: bool = False) -> None: if hard: self._decoder.rewind() self.__physical_frame = 0 From e5003ff2705213e4a8cfbefd3094a25f1ccb9805 Mon Sep 17 00:00:00 2001 From: olokelo Date: Tue, 19 Mar 2024 22:47:10 +0100 Subject: [PATCH 18/66] change Optional to python 3.10+ syntax --- src/PIL/JpegXlImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index e56445f58f4..4e475bf4a6f 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -50,7 +50,7 @@ def _open(self) -> None: self.is_animated = has_anim self._tps_dur_secs = 1 - self.n_frames: Optional[int] = 1 + self.n_frames: int | None = 1 if self.is_animated: self.n_frames = None if n_frames > 0: @@ -73,7 +73,7 @@ def _open(self) -> None: self._rewind() - def _fix_exif(self, exif: bytes) -> Optional[bytes]: + def _fix_exif(self, exif: bytes) -> bytes | None: # jpeg xl does some weird shenanigans when storing exif # it omits first 6 bytes of tiff header but adds 4 byte offset instead if len(exif) <= 4: @@ -81,7 +81,7 @@ def _fix_exif(self, exif: bytes) -> Optional[bytes]: exif_start_offset = struct.unpack(">I", exif[:4])[0] return exif[exif_start_offset + 4 :] - def _getexif(self) -> Optional[dict[str, str]]: + def _getexif(self) -> dict[str, str] | None: if "exif" not in self.info: return None return self.getexif()._get_merged_dict() From fa5bfacbd6c060f90ec283910ec271a51fdb7c28 Mon Sep 17 00:00:00 2001 From: olokelo Date: Wed, 20 Mar 2024 22:27:22 +0100 Subject: [PATCH 19/66] add more metadata test cases --- Tests/test_file_jxl_metadata.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index 639624c2dc2..b0eac6c6e71 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -87,3 +87,11 @@ def test_getxmp() -> None: match="XMP data cannot be read without defusedxml dependency", ): assert im.getxmp() == {} + +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 + +def test_read_exif_metadata_empty() -> None: + with Image.open("Tests/images/hopper.jxl") as image: + assert(image._getexif() is None) From 0b71605dbfe3bba59d7b67f0f7ebbbfbe0016b07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:30:58 +0000 Subject: [PATCH 20/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_jxl_metadata.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index b0eac6c6e71..f6be96c8062 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -88,10 +88,12 @@ def test_getxmp() -> None: ): assert im.getxmp() == {} + 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 + def test_read_exif_metadata_empty() -> None: with Image.open("Tests/images/hopper.jxl") as image: - assert(image._getexif() is None) + assert image._getexif() is None From 1f00fb8eefda7cbcddc051026f25ae34f9afaff4 Mon Sep 17 00:00:00 2001 From: olokelo Date: Sat, 18 May 2024 23:58:12 +0200 Subject: [PATCH 21/66] add 16-bits grayscale support for jpeg xl images --- Tests/images/jxl/16bit_subcutaneous.cropped.jxl | Bin 0 -> 9275 bytes Tests/images/jxl/16bit_subcutaneous.cropped.png | Bin 0 -> 12298 bytes Tests/test_file_jxl.py | 16 ++++++++++++++++ src/_jpegxl.c | 9 ++++++++- 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Tests/images/jxl/16bit_subcutaneous.cropped.jxl create mode 100644 Tests/images/jxl/16bit_subcutaneous.cropped.png diff --git a/Tests/images/jxl/16bit_subcutaneous.cropped.jxl b/Tests/images/jxl/16bit_subcutaneous.cropped.jxl new file mode 100644 index 0000000000000000000000000000000000000000..eae30759603bfbce6bfcb48ee203f85f9c41dc84 GIT binary patch literal 9275 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�vb%nHJv-hC!%S*~*_^L=Z@<+=M89F$Z!;Awpky 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 9b09371a998..f6173a57e80 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -20,6 +20,7 @@ # 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: @@ -40,6 +41,7 @@ def test_unsupported(self) -> None: class TestFileJpegXl: def setup_method(self) -> None: self.rgb_mode = "RGB" + self.i16_mode = "I;16" def test_version(self) -> None: _jpegxl.JpegXlDecoderVersion() @@ -62,6 +64,20 @@ def test_read_rgb(self) -> None: # djxl hopper.jxl hopper_jxl_bits.ppm assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0) + 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() + + assert_image_similar_tofile(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. diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 1905651c77f..471aa17ed7a 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -33,6 +33,11 @@ void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { // TODO: floating point mode char* _pil_jxl_get_mode(const JxlBasicInfo *bi) { + // 16-bit single channel images are supported + if (bi->bits_per_sample == 16 && bi->num_color_channels == 1 && + bi->alpha_bits == 0 && !bi->alpha_premultiplied + ) return "I;16"; + // PIL doesn't support high bit depth images // it will throw an exception but that's for your own good // you wouldn't want to see distorted image @@ -261,7 +266,9 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { _PIL_JXL_CHECK("JxlDecoderGetBasicInfo"); _pil_jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); - if (decp->pixel_format.data_type != JXL_TYPE_UINT8) { + if (decp->pixel_format.data_type != JXL_TYPE_UINT8 && + decp->pixel_format.data_type != JXL_TYPE_UINT16) { + // only 8 bit integer value images are supported for now PyErr_SetString(PyExc_NotImplementedError, "unsupported pixel data type"); From 08270a7b5ddec526f2b780b71172052dc952f17e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 18 May 2024 22:01:28 +0000 Subject: [PATCH 22/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_jxl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index f6173a57e80..c1b730a79e7 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -76,7 +76,9 @@ def test_read_i16(self) -> None: image.load() image.getdata() - assert_image_similar_tofile(image, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1.0) + assert_image_similar_tofile( + image, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1.0 + ) def test_JpegXlDecode_with_invalid_args(self) -> None: """ From 4256b2a8c5eee80bd37323017e3344c22a5c3146 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 10:09:11 +0000 Subject: [PATCH 23/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_jpegxl.c | 252 ++++++++++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 121 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 471aa17ed7a..2fcfb8ed975 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -8,15 +8,18 @@ #include #include -#define _PIL_JXL_CHECK(call_name) if (decp->status != JXL_DEC_SUCCESS) { jxl_call_name = call_name; goto end; } - - -void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { +#define _PIL_JXL_CHECK(call_name) \ + if (decp->status != JXL_DEC_SUCCESS) { \ + jxl_call_name = call_name; \ + goto end; \ + } +void +_pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { pf->num_channels = bi->num_color_channels + bi->num_extra_channels; if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) { - pf->data_type = JXL_TYPE_FLOAT; // not yet supported + pf->data_type = JXL_TYPE_FLOAT; // not yet supported } else if (bi->bits_per_sample > 8) { pf->data_type = JXL_TYPE_UINT16; // not yet supported } else { @@ -27,40 +30,44 @@ void _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { // would be great to test it pf->endianness = JXL_NATIVE_ENDIAN; pf->align = 0; - } // TODO: floating point mode -char* _pil_jxl_get_mode(const JxlBasicInfo *bi) { - +char * +_pil_jxl_get_mode(const JxlBasicInfo *bi) { // 16-bit single channel images are supported if (bi->bits_per_sample == 16 && bi->num_color_channels == 1 && - bi->alpha_bits == 0 && !bi->alpha_premultiplied - ) return "I;16"; + bi->alpha_bits == 0 && !bi->alpha_premultiplied) + return "I;16"; // PIL doesn't support high bit depth images // it will throw an exception but that's for your own good // you wouldn't want to see distorted image - if (bi->bits_per_sample != 8) return "uns"; + if (bi->bits_per_sample != 8) + return "uns"; // image has transparency if (bi->alpha_bits > 0) { if (bi->num_color_channels == 3) { - if (bi->alpha_premultiplied) return "RGBa"; + if (bi->alpha_premultiplied) + return "RGBa"; return "RGBA"; - } if (bi->num_color_channels == 1) { - if (bi->alpha_premultiplied) return "La"; + } + if (bi->num_color_channels == 1) { + if (bi->alpha_premultiplied) + return "La"; return "LA"; } } // image has no transparency - if (bi->num_color_channels == 3) return "RGB"; - if (bi->num_color_channels == 1) return "L"; + if (bi->num_color_channels == 3) + return "RGB"; + if (bi->num_color_channels == 1) + return "L"; // could not recognize mode return NULL; - } // Decoder type @@ -68,8 +75,8 @@ typedef struct { PyObject_HEAD JxlDecoder *decoder; void *runner; - uint8_t *jxl_data; // input jxl bitstream - Py_ssize_t jxl_data_len; // length of input jxl bitstream + uint8_t *jxl_data; // input jxl bitstream + Py_ssize_t jxl_data_len; // length of input jxl bitstream uint8_t *outbuf; Py_ssize_t outbuf_len; @@ -135,24 +142,22 @@ _jxl_decoder_dealloc(PyObject *self) { // sets input jxl bitstream loaded into jxl_data // has to be called after every rewind -void _jxl_decoder_set_input(PyObject *self) { +void +_jxl_decoder_set_input(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - decp->status = JxlDecoderSetInput(decp->decoder, decp->jxl_data, - decp->jxl_data_len); + decp->status = + JxlDecoderSetInput(decp->decoder, decp->jxl_data, decp->jxl_data_len); // the input contains the whole jxl bitstream so it can be closed JxlDecoderCloseInput(decp->decoder); - } PyObject * _jxl_decoder_rewind(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; JxlDecoderRewind(decp->decoder); Py_RETURN_NONE; - } bool @@ -163,7 +168,7 @@ _jxl_decoder_count_frames(PyObject *self) { // count all JXL_DEC_NEED_IMAGE_OUT_BUFFER events while (decp->status != JXL_DEC_SUCCESS) { - //printf("fetch_frame_count status: %u\n", decp->status); + // printf("fetch_frame_count status: %u\n", decp->status); decp->status = JxlDecoderProcessInput(decp->decoder); if (decp->status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { @@ -177,7 +182,6 @@ _jxl_decoder_count_frames(PyObject *self) { _jxl_decoder_rewind((PyObject *)decp); return true; - } PyObject * @@ -200,7 +204,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->n_frames = 0; // used for printing more detailed error messages - char* jxl_call_name; + char *jxl_call_name; // parse one argument which is a string with jxl data if (!PyArg_ParseTuple(args, "S", &jxl_string)) { @@ -213,28 +217,28 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { Py_ssize_t _tmp_jxl_data_len; // convert jxl data string to C uint8_t pointer - PyBytes_AsStringAndSize((PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len); + PyBytes_AsStringAndSize( + (PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len); // here occurs this copying (inefficiency) decp->jxl_data = malloc(_tmp_jxl_data_len); memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len); decp->jxl_data_len = _tmp_jxl_data_len; - //printf("%zu\n", decp->jxl_data_len); + // printf("%zu\n", decp->jxl_data_len); size_t suggested_num_threads = JxlThreadParallelRunnerDefaultNumWorkerThreads(); decp->runner = JxlThreadParallelRunnerCreate(NULL, suggested_num_threads); decp->decoder = JxlDecoderCreate(NULL); - decp->status = JxlDecoderSetParallelRunner(decp->decoder, - JxlThreadParallelRunner, decp->runner); + decp->status = JxlDecoderSetParallelRunner( + decp->decoder, JxlThreadParallelRunner, decp->runner); _PIL_JXL_CHECK("JxlDecoderSetParallelRunner") - decp->status = JxlDecoderSubscribeEvents(decp->decoder, - JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING - | JXL_DEC_FRAME | JXL_DEC_BOX - | JXL_DEC_FULL_IMAGE - ); + decp->status = JxlDecoderSubscribeEvents( + decp->decoder, + JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_BOX | + JXL_DEC_FULL_IMAGE); _PIL_JXL_CHECK("JxlDecoderSubscribeEvents") // tell libjxl to decompress boxes (for example Exif is usually compressed) @@ -246,11 +250,10 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // decode everything up to the first frame do { - decp->status = JxlDecoderProcessInput(decp->decoder); - //printf("Status: %d\n", decp->status); + // printf("Status: %d\n", decp->status); - decoder_loop_skip_process: +decoder_loop_skip_process: // there was an error at JxlDecoderProcessInput stage if (decp->status == JXL_DEC_ERROR) { @@ -260,18 +263,15 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got basic info if (decp->status == JXL_DEC_BASIC_INFO) { - - decp->status = JxlDecoderGetBasicInfo(decp->decoder, - &decp->basic_info); + decp->status = JxlDecoderGetBasicInfo(decp->decoder, &decp->basic_info); _PIL_JXL_CHECK("JxlDecoderGetBasicInfo"); _pil_jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); if (decp->pixel_format.data_type != JXL_TYPE_UINT8 && decp->pixel_format.data_type != JXL_TYPE_UINT16) { - // only 8 bit integer value images are supported for now - PyErr_SetString(PyExc_NotImplementedError, - "unsupported pixel data type"); + PyErr_SetString( + PyExc_NotImplementedError, "unsupported pixel data type"); goto end_with_custom_error; } decp->mode = _pil_jxl_get_mode(&decp->basic_info); @@ -281,9 +281,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got color encoding if (decp->status == JXL_DEC_COLOR_ENCODING) { - - decp->status = JxlDecoderGetICCProfileSize(decp->decoder, - JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len); + decp->status = JxlDecoderGetICCProfileSize( + decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len); _PIL_JXL_CHECK("JxlDecoderGetICCProfileSize"); decp->jxl_icc = malloc(decp->jxl_icc_len); @@ -292,21 +291,23 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { goto end_with_custom_error; } - decp->status = JxlDecoderGetColorAsICCProfile(decp->decoder, + decp->status = JxlDecoderGetColorAsICCProfile( + decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, - decp->jxl_icc, decp->jxl_icc_len); + decp->jxl_icc, + decp->jxl_icc_len); _PIL_JXL_CHECK("JxlDecoderGetColorAsICCProfile"); continue; } if (decp->status == JXL_DEC_BOX) { - char btype[4]; decp->status = JxlDecoderGetBoxType(decp->decoder, btype, JXL_TRUE); _PIL_JXL_CHECK("JxlDecoderGetBoxType"); - //printf("found box type: %c%c%c%c\n", btype[0], btype[1], btype[2], btype[3]); + // printf("found box type: %c%c%c%c\n", btype[0], btype[1], btype[2], + // btype[3]); bool is_box_exif, is_box_xmp; is_box_exif = !memcmp(btype, "Exif", 4); @@ -319,31 +320,35 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { size_t cur_compr_box_size; decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &cur_compr_box_size); _PIL_JXL_CHECK("JxlDecoderGetBoxSizeRaw"); - //printf("Exif/xmp box size: %zu\n", cur_compr_box_size); + // printf("Exif/xmp box size: %zu\n", cur_compr_box_size); - uint8_t* final_jxl_buf = NULL; + uint8_t *final_jxl_buf = NULL; Py_ssize_t final_jxl_buf_len = 0; // cur_box_size is actually compressed box size // it will also serve as our chunk size do { - uint8_t* _new_jxl_buf = realloc(final_jxl_buf, final_jxl_buf_len + cur_compr_box_size); + uint8_t *_new_jxl_buf = + realloc(final_jxl_buf, final_jxl_buf_len + cur_compr_box_size); if (!_new_jxl_buf) { PyErr_SetString(PyExc_OSError, "failed to allocate final_jxl_buf"); goto end; } final_jxl_buf = _new_jxl_buf; - decp->status = JxlDecoderSetBoxBuffer(decp->decoder, final_jxl_buf + final_jxl_buf_len, cur_compr_box_size); + decp->status = JxlDecoderSetBoxBuffer( + decp->decoder, + final_jxl_buf + final_jxl_buf_len, + cur_compr_box_size); _PIL_JXL_CHECK("JxlDecoderSetBoxBuffer"); decp->status = JxlDecoderProcessInput(decp->decoder); size_t remaining = JxlDecoderReleaseBoxBuffer(decp->decoder); - //printf("boxes status: %d, remaining: %zu\n", decp->status, remaining); + // printf("boxes status: %d, remaining: %zu\n", decp->status, + // remaining); final_jxl_buf_len += (cur_compr_box_size - remaining); - } - while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); + } while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); if (is_box_exif) { decp->jxl_exif = final_jxl_buf; @@ -356,7 +361,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // dirty hack: skip first step of decoding loop since // we already did it in do...while above goto decoder_loop_skip_process; - } } while (decp->status != JXL_DEC_FRAME); @@ -376,20 +380,23 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { } return (PyObject *)decp; - //Py_RETURN_NONE; + // Py_RETURN_NONE; // on success we should never reach here // set error message char err_msg[128]; - end: - snprintf(err_msg, 128, +end: + snprintf( + err_msg, + 128, "could not create decoder object. libjxl call: %s returned: %d", - jxl_call_name, decp->status); + jxl_call_name, + decp->status); PyErr_SetString(PyExc_OSError, err_msg); - end_with_custom_error: +end_with_custom_error: // deallocate _jxl_decoder_dealloc((PyObject *)decp); @@ -411,19 +418,17 @@ _jxl_decoder_get_info(PyObject *self) { decp->basic_info.animation.tps_numerator, decp->basic_info.animation.tps_denominator, decp->basic_info.animation.num_loops, - decp->n_frames - ); + decp->n_frames); } PyObject * _jxl_decoder_get_next(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; PyObject *bytes; PyObject *ret; JxlFrameHeader fhdr = {}; - char* jxl_call_name; + char *jxl_call_name; // process events until next frame output is ready while (decp->status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { @@ -450,23 +455,23 @@ _jxl_decoder_get_next(PyObject *self) { } size_t new_outbuf_len; - decp->status = JxlDecoderImageOutBufferSize(decp->decoder, &decp->pixel_format, &new_outbuf_len); + decp->status = JxlDecoderImageOutBufferSize( + decp->decoder, &decp->pixel_format, &new_outbuf_len); _PIL_JXL_CHECK("JxlDecoderImageOutBufferSize"); // only allocate memory when current buffer is too small if (decp->outbuf_len < new_outbuf_len) { - decp->outbuf_len = new_outbuf_len; - uint8_t* _new_outbuf = realloc(decp->outbuf, decp->outbuf_len); + uint8_t *_new_outbuf = realloc(decp->outbuf, decp->outbuf_len); if (!_new_outbuf) { PyErr_SetString(PyExc_OSError, "failed to allocate outbuf"); goto end_with_custom_error; } decp->outbuf = _new_outbuf; - } - decp->status = JxlDecoderSetImageOutBuffer(decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len); + decp->status = JxlDecoderSetImageOutBuffer( + decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len); _PIL_JXL_CHECK("JxlDecoderSetImageOutBuffer"); // decode image into output_buffer @@ -477,8 +482,7 @@ _jxl_decoder_get_next(PyObject *self) { goto end_with_custom_error; } - bytes = PyBytes_FromStringAndSize( - (char *)(decp->outbuf), decp->outbuf_len); + bytes = PyBytes_FromStringAndSize((char *)(decp->outbuf), decp->outbuf_len); ret = Py_BuildValue("SIi", bytes, fhdr.duration, fhdr.is_last); @@ -490,26 +494,29 @@ _jxl_decoder_get_next(PyObject *self) { // set error message char err_msg[128]; - end: - snprintf(err_msg, 128, +end: + snprintf( + err_msg, + 128, "could not read frame. libjxl call: %s returned: %d", - jxl_call_name, decp->status); + jxl_call_name, + decp->status); PyErr_SetString(PyExc_OSError, err_msg); - end_with_custom_error: +end_with_custom_error: // no need to deallocate anything here // user can just ignore error return NULL; - } PyObject * _jxl_decoder_get_icc(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - if (!decp->jxl_icc) Py_RETURN_NONE; + if (!decp->jxl_icc) + Py_RETURN_NONE; return PyBytes_FromStringAndSize((const char *)decp->jxl_icc, decp->jxl_icc_len); } @@ -518,7 +525,8 @@ PyObject * _jxl_decoder_get_exif(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - if (!decp->jxl_exif) Py_RETURN_NONE; + if (!decp->jxl_exif) + Py_RETURN_NONE; return PyBytes_FromStringAndSize((const char *)decp->jxl_exif, decp->jxl_exif_len); } @@ -527,7 +535,8 @@ PyObject * _jxl_decoder_get_xmp(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - if (!decp->jxl_xmp) Py_RETURN_NONE; + if (!decp->jxl_xmp) + Py_RETURN_NONE; return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len); } @@ -547,34 +556,34 @@ static struct PyMethodDef _jpegxl_decoder_methods[] = { static PyTypeObject PILJpegXlDecoder_Type = { PyVarObject_HEAD_INIT(NULL, 0) "PILJpegXlDecoder", /*tp_name */ sizeof(PILJpegXlDecoderObject), /*tp_basicsize */ - 0, /*tp_itemsize */ + 0, /*tp_itemsize */ /* methods */ - (destructor)_jxl_decoder_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - _jpegxl_decoder_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ + (destructor)_jxl_decoder_dealloc, /*tp_dealloc*/ + 0, /*tp_vectorcall_offset*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_as_async*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _jpegxl_decoder_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; // Return libjxl decoder version available as integer: @@ -594,16 +603,17 @@ JpegXlDecoderVersion_str(void) { "%d.%d.%d", version_number / 1000000, (version_number % 1000000) / 1000, - (version_number % 1000) - ); + (version_number % 1000)); return version; } static PyMethodDef jpegxlMethods[] = { - {"JpegXlDecoderVersion", JpegXlDecoderVersion_wrapper, METH_NOARGS, "JpegXlVersion"}, + {"JpegXlDecoderVersion", + JpegXlDecoderVersion_wrapper, + METH_NOARGS, + "JpegXlVersion"}, {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"}, - {NULL, NULL} -}; + {NULL, NULL}}; static int setup_module(PyObject *m) { @@ -627,10 +637,10 @@ PyInit__jpegxl(void) { static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, - "_jpegxl", /* m_name */ - NULL, /* m_doc */ - -1, /* m_size */ - jpegxlMethods, /* m_methods */ + "_jpegxl", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + jpegxlMethods, /* m_methods */ }; m = PyModule_Create(&module_def); From 13944d5c637993065d6fc25f0aa2d384339b8116 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 19:04:13 +0000 Subject: [PATCH 24/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_jpegxl.c | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 2fcfb8ed975..592db9adca6 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -218,7 +218,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // convert jxl data string to C uint8_t pointer PyBytes_AsStringAndSize( - (PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len); + (PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len + ); // here occurs this copying (inefficiency) decp->jxl_data = malloc(_tmp_jxl_data_len); @@ -232,13 +233,15 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->decoder = JxlDecoderCreate(NULL); decp->status = JxlDecoderSetParallelRunner( - decp->decoder, JxlThreadParallelRunner, decp->runner); + decp->decoder, JxlThreadParallelRunner, decp->runner + ); _PIL_JXL_CHECK("JxlDecoderSetParallelRunner") decp->status = JxlDecoderSubscribeEvents( decp->decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_BOX | - JXL_DEC_FULL_IMAGE); + JXL_DEC_FULL_IMAGE + ); _PIL_JXL_CHECK("JxlDecoderSubscribeEvents") // tell libjxl to decompress boxes (for example Exif is usually compressed) @@ -271,7 +274,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->pixel_format.data_type != JXL_TYPE_UINT16) { // only 8 bit integer value images are supported for now PyErr_SetString( - PyExc_NotImplementedError, "unsupported pixel data type"); + PyExc_NotImplementedError, "unsupported pixel data type" + ); goto end_with_custom_error; } decp->mode = _pil_jxl_get_mode(&decp->basic_info); @@ -282,7 +286,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got color encoding if (decp->status == JXL_DEC_COLOR_ENCODING) { decp->status = JxlDecoderGetICCProfileSize( - decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len); + decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len + ); _PIL_JXL_CHECK("JxlDecoderGetICCProfileSize"); decp->jxl_icc = malloc(decp->jxl_icc_len); @@ -295,7 +300,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, decp->jxl_icc, - decp->jxl_icc_len); + decp->jxl_icc_len + ); _PIL_JXL_CHECK("JxlDecoderGetColorAsICCProfile"); continue; @@ -337,9 +343,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { final_jxl_buf = _new_jxl_buf; decp->status = JxlDecoderSetBoxBuffer( - decp->decoder, - final_jxl_buf + final_jxl_buf_len, - cur_compr_box_size); + decp->decoder, final_jxl_buf + final_jxl_buf_len, cur_compr_box_size + ); _PIL_JXL_CHECK("JxlDecoderSetBoxBuffer"); decp->status = JxlDecoderProcessInput(decp->decoder); @@ -393,7 +398,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { 128, "could not create decoder object. libjxl call: %s returned: %d", jxl_call_name, - decp->status); + decp->status + ); PyErr_SetString(PyExc_OSError, err_msg); end_with_custom_error: @@ -418,7 +424,8 @@ _jxl_decoder_get_info(PyObject *self) { decp->basic_info.animation.tps_numerator, decp->basic_info.animation.tps_denominator, decp->basic_info.animation.num_loops, - decp->n_frames); + decp->n_frames + ); } PyObject * @@ -456,7 +463,8 @@ _jxl_decoder_get_next(PyObject *self) { size_t new_outbuf_len; decp->status = JxlDecoderImageOutBufferSize( - decp->decoder, &decp->pixel_format, &new_outbuf_len); + decp->decoder, &decp->pixel_format, &new_outbuf_len + ); _PIL_JXL_CHECK("JxlDecoderImageOutBufferSize"); // only allocate memory when current buffer is too small @@ -471,7 +479,8 @@ _jxl_decoder_get_next(PyObject *self) { } decp->status = JxlDecoderSetImageOutBuffer( - decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len); + decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len + ); _PIL_JXL_CHECK("JxlDecoderSetImageOutBuffer"); // decode image into output_buffer @@ -500,7 +509,8 @@ _jxl_decoder_get_next(PyObject *self) { 128, "could not read frame. libjxl call: %s returned: %d", jxl_call_name, - decp->status); + decp->status + ); PyErr_SetString(PyExc_OSError, err_msg); end_with_custom_error: @@ -603,17 +613,17 @@ JpegXlDecoderVersion_str(void) { "%d.%d.%d", version_number / 1000000, (version_number % 1000000) / 1000, - (version_number % 1000)); + (version_number % 1000) + ); return version; } static PyMethodDef jpegxlMethods[] = { - {"JpegXlDecoderVersion", - JpegXlDecoderVersion_wrapper, - METH_NOARGS, - "JpegXlVersion"}, + {"JpegXlDecoderVersion", JpegXlDecoderVersion_wrapper, METH_NOARGS, "JpegXlVersion" + }, {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"}, - {NULL, NULL}}; + {NULL, NULL} +}; static int setup_module(PyObject *m) { From ff269abd962df4ca1b9f9e4da604f1e91ee220a9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 04:12:40 +0000 Subject: [PATCH 25/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_jpegxl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 592db9adca6..4cb89e7e104 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -619,8 +619,10 @@ JpegXlDecoderVersion_str(void) { } static PyMethodDef jpegxlMethods[] = { - {"JpegXlDecoderVersion", JpegXlDecoderVersion_wrapper, METH_NOARGS, "JpegXlVersion" - }, + {"JpegXlDecoderVersion", + JpegXlDecoderVersion_wrapper, + METH_NOARGS, + "JpegXlVersion"}, {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"}, {NULL, NULL} }; From 661c0d8a350c24d6ab1aacd74a2d9e8246dcf343 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 26 Apr 2025 14:19:53 +1000 Subject: [PATCH 26/66] Lint fixes --- Tests/test_file_jxl_animated.py | 3 ++- src/PIL/JpegXlImagePlugin.py | 3 ++- src/PIL/_jpegxl.pyi | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index 758fa79e2d8..02aca5eb0e2 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -59,7 +59,8 @@ def test_seeking() -> None: assert im1.info["timestamp"] == im1.info["timestamp"] assert total_dur == 8000 - assert im1.tell() == 0 and im2.tell() == 0 + assert im1.tell() == 0 + assert im2.tell() == 0 im1.seek(0) im1.load() diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 4e475bf4a6f..32ef33f2894 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -2,6 +2,7 @@ import struct from io import BytesIO +from typing import Any from . import Image, ImageFile @@ -81,7 +82,7 @@ def _fix_exif(self, exif: bytes) -> bytes | None: exif_start_offset = struct.unpack(">I", exif[:4])[0] return exif[exif_start_offset + 4 :] - def _getexif(self) -> dict[str, str] | None: + def _getexif(self) -> dict[int, Any] | None: if "exif" not in self.info: return None return self.getexif()._get_merged_dict() diff --git a/src/PIL/_jpegxl.pyi b/src/PIL/_jpegxl.pyi index b0235555dc5..e27843e5338 100644 --- a/src/PIL/_jpegxl.pyi +++ b/src/PIL/_jpegxl.pyi @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any def __getattr__(name: str) -> Any: ... From ade1db08354d2cff899b6bc313b7541cece3f682 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Wed, 7 May 2025 14:59:34 +1000 Subject: [PATCH 27/66] Replace slice and comparison with startswith --- src/PIL/JpegXlImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 32ef33f2894..42539246185 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -25,8 +25,8 @@ def _accept(prefix: bytes) -> bool: is_jxl = ( - prefix[:2] == b"\xff\x0a" - or prefix[:12] == b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a" + prefix.startswith(b"\xff\x0a") + or prefix.startswith(b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a") ) if is_jxl and not SUPPORTED: msg = "image file could not be identified because JXL support not installed" From ceec3f92fc0e54d23d4063fa8d8a87a6f01122b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 04:59:58 +0000 Subject: [PATCH 28/66] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/JpegXlImagePlugin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 42539246185..8a33e5a8288 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -24,9 +24,8 @@ def _accept(prefix: bytes) -> bool: - is_jxl = ( - prefix.startswith(b"\xff\x0a") - or prefix.startswith(b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a") + is_jxl = prefix.startswith(b"\xff\x0a") or prefix.startswith( + b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a" ) if is_jxl and not SUPPORTED: msg = "image file could not be identified because JXL support not installed" From 5e0457a2b39a0538dd044e64336893273b88d3ac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 18:07:40 +1000 Subject: [PATCH 29/66] Removed getxmp() --- src/PIL/JpegXlImagePlugin.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 8a33e5a8288..84fc9361fe8 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -24,8 +24,8 @@ def _accept(prefix: bytes) -> bool: - is_jxl = prefix.startswith(b"\xff\x0a") or prefix.startswith( - b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a" + is_jxl = prefix.startswith( + (b"\xff\x0a", b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a") ) if is_jxl and not SUPPORTED: msg = "image file could not be identified because JXL support not installed" @@ -86,9 +86,6 @@ def _getexif(self) -> dict[int, Any] | None: return None return self.getexif()._get_merged_dict() - def getxmp(self) -> dict[str, str]: - return self._getxmp(self.info["xmp"]) if "xmp" in self.info else {} - def _get_next(self) -> tuple[bytes, float, float, bool]: # Get next frame From 92663180482504dd05110390886bb6c683609def Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 20:33:44 +1000 Subject: [PATCH 30/66] Do not set info["exif"] to None --- Tests/test_file_jxl_metadata.py | 49 +++++++++++++++++++++++++-------- src/PIL/JpegXlImagePlugin.py | 14 ++++------ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index f6be96c8062..8fc54064211 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -4,7 +4,7 @@ import pytest -from PIL import Image +from PIL import Image, JpegXlImagePlugin from .helper import skip_unless_feature @@ -73,25 +73,52 @@ def test_read_icc_profile() -> None: def test_getxmp() -> None: with Image.open("Tests/images/flower.jxl") as im: assert "xmp" not in im.info - assert im.getxmp() == {} + if ElementTree is None: + with pytest.warns( + UserWarning, + match="XMP data cannot be read without defusedxml dependency", + ): + xmp = im.getxmp() + else: + xmp = im.getxmp() + assert xmp == {} with Image.open("Tests/images/flower2.jxl") as im: - if ElementTree: - assert ( - im.getxmp()["xmpmeta"]["xmptk"] - == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " - ) - else: + if ElementTree is None: with pytest.warns( UserWarning, match="XMP data cannot be read without defusedxml dependency", ): assert im.getxmp() == {} + else: + assert "xmp" in im.info + assert ( + im.getxmp()["xmpmeta"]["xmptk"] + == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " + ) + +def test_4_byte_exif(monkeypatch: pytest.MonkeyPatch) -> None: + class _mock_jpegxl: + class PILJpegXlDecoder: + def __init__(self, b: bytes) -> None: + pass + def get_info(self) -> tuple[int, int, str, int, int, int, int, int]: + return (1, 1, "L", 0, 0, 0, 0, 0) -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 + def get_icc(self) -> None: + pass + + def get_exif(self) -> None: + return b"\0\0\0\0" + + def get_xmp(self) -> None: + pass + + monkeypatch.setattr(JpegXlImagePlugin, "_jpegxl", _mock_jpegxl) + + with Image.open("Tests/images/hopper.jxl") as image: + assert "exif" not in image.info def test_read_exif_metadata_empty() -> None: diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 84fc9361fe8..0a8c415b658 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -67,20 +67,16 @@ def _open(self) -> None: if icc := self._decoder.get_icc(): self.info["icc_profile"] = icc if exif := self._decoder.get_exif(): - self.info["exif"] = self._fix_exif(exif) + # jpeg xl does some weird shenanigans when storing exif + # it omits first 6 bytes of tiff header but adds 4 byte offset instead + if len(exif) > 4: + exif_start_offset = struct.unpack(">I", exif[:4])[0] + self.info["exif"] = exif[exif_start_offset + 4 :] if xmp := self._decoder.get_xmp(): self.info["xmp"] = xmp self._rewind() - def _fix_exif(self, exif: bytes) -> bytes | None: - # jpeg xl does some weird shenanigans when storing exif - # it omits first 6 bytes of tiff header but adds 4 byte offset instead - if len(exif) <= 4: - return None - exif_start_offset = struct.unpack(">I", exif[:4])[0] - return exif[exif_start_offset + 4 :] - def _getexif(self) -> dict[int, Any] | None: if "exif" not in self.info: return None From 29e4b558d7f98e6eb12f0dc5c86e265c9336a807 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 18:36:14 +1000 Subject: [PATCH 31/66] Do not add _getexif to new plugin --- Tests/test_file_jxl_metadata.py | 4 ++-- src/PIL/JpegXlImagePlugin.py | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index 8fc54064211..b1bcf6e7993 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -35,7 +35,7 @@ def test_read_exif_metadata() -> None: exif_data = image.info.get("exif", None) assert exif_data - exif = image._getexif() + exif = image.getexif() # Camera make assert exif[271] == "Canon" @@ -123,4 +123,4 @@ def get_xmp(self) -> None: def test_read_exif_metadata_empty() -> None: with Image.open("Tests/images/hopper.jxl") as image: - assert image._getexif() is None + assert image.getexif() == {} diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 0a8c415b658..d3192d0bd65 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -2,7 +2,6 @@ import struct from io import BytesIO -from typing import Any from . import Image, ImageFile @@ -77,11 +76,6 @@ def _open(self) -> None: self._rewind() - def _getexif(self) -> dict[int, Any] | None: - if "exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - def _get_next(self) -> tuple[bytes, float, float, bool]: # Get next frame From 3cd384886b09207715ffdcd8b5594e61be9b8161 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 18:13:24 +1000 Subject: [PATCH 32/66] Added type hint --- src/PIL/JpegXlImagePlugin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index d3192d0bd65..2e3928afb9a 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -134,8 +134,7 @@ def seek(self, frame: int) -> None: # Set logical frame to requested position self.__logical_frame = frame - def load(self): - + def load(self) -> Image.core.PixelAccess | None: if self.__loaded != self.__logical_frame: self._seek(self.__logical_frame) @@ -150,7 +149,7 @@ def load(self): # this is horribly memory inefficient # you need probably 2*(raw image plane) bytes of memory self.fp = BytesIO(data) - self.tile = [("raw", (0, 0) + self.size, 0, self.rawmode)] + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.rawmode)] return super().load() From aa6510fca292f172a12e8812eaf2f66322b50a91 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 18:14:31 +1000 Subject: [PATCH 33/66] Removed self.rawmode --- src/PIL/JpegXlImagePlugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 2e3928afb9a..e815f767426 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -60,7 +60,6 @@ def _open(self) -> None: self.__timestamp = 0 self._mode = mode - self.rawmode = mode self.tile = [] if icc := self._decoder.get_icc(): @@ -149,7 +148,7 @@ def load(self) -> Image.core.PixelAccess | None: # this is horribly memory inefficient # you need probably 2*(raw image plane) bytes of memory self.fp = BytesIO(data) - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.rawmode)] + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] return super().load() From 36640de607e4bcc66578334e208438a889653686 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 20:37:00 +1000 Subject: [PATCH 34/66] tile is already empty list --- Tests/test_file_jxl_metadata.py | 1 + src/PIL/JpegXlImagePlugin.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index b1bcf6e7993..d92a82e027a 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -97,6 +97,7 @@ def test_getxmp() -> None: == "Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 " ) + def test_4_byte_exif(monkeypatch: pytest.MonkeyPatch) -> None: class _mock_jpegxl: class PILJpegXlDecoder: diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index e815f767426..a89aefe0949 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -60,7 +60,6 @@ def _open(self) -> None: self.__timestamp = 0 self._mode = mode - self.tile = [] if icc := self._decoder.get_icc(): self.info["icc_profile"] = icc From 7125fe4096927a77d5e6ed278f94e387724f7a02 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 7 May 2025 23:29:54 +1000 Subject: [PATCH 35/66] Fixed type hints --- Tests/test_file_jxl.py | 4 +++- Tests/test_file_jxl_metadata.py | 20 +++++++++----------- setup.py | 19 +++++++++---------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index c1b730a79e7..96b6b0ea5e1 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -45,7 +45,9 @@ def setup_method(self) -> None: def test_version(self) -> None: _jpegxl.JpegXlDecoderVersion() - assert re.search(r"\d+\.\d+\.\d+$", features.version_module("jpegxl")) + version = features.version_module("jpegxl") + assert version is not None + assert re.search(r"\d+\.\d+\.\d+$", version) def test_read_rgb(self) -> None: """ diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index d92a82e027a..bf21be9933d 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -29,8 +29,7 @@ def test_read_exif_metadata() -> None: - file_path = "Tests/images/flower.jxl" - with Image.open(file_path) as image: + with Image.open("Tests/images/flower.jxl") as image: assert image.format == "JPEG XL" exif_data = image.info.get("exif", None) assert exif_data @@ -43,8 +42,8 @@ def test_read_exif_metadata() -> None: with Image.open("Tests/images/flower.jpg") as jpeg_image: expected_exif = jpeg_image.info["exif"] - # jpeg xl always returns exif without 'Exif\0\0' prefix - assert exif_data == expected_exif[6:] + # jpeg xl always returns exif without 'Exif\0\0' prefix + assert exif_data == expected_exif[6:] def test_read_exif_metadata_without_prefix() -> None: @@ -53,21 +52,20 @@ def test_read_exif_metadata_without_prefix() -> None: assert im.info["exif"][:6] != b"Exif\x00\x00" exif = im.getexif() - assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" + assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" def test_read_icc_profile() -> None: - file_path = "Tests/images/flower2.jxl" - with Image.open(file_path) as image: + with Image.open("Tests/images/flower2.jxl") as image: assert image.format == "JPEG XL" assert image.info.get("icc_profile", None) icc = image.info["icc_profile"] - with Image.open("Tests/images/flower2.jxl") as jpeg_image: - expected_icc = jpeg_image.info["icc_profile"] + with Image.open("Tests/images/flower2.jxl") as jpeg_image: + expected_icc = jpeg_image.info["icc_profile"] - assert icc == expected_icc + assert icc == expected_icc def test_getxmp() -> None: @@ -110,7 +108,7 @@ def get_info(self) -> tuple[int, int, str, int, int, int, int, int]: def get_icc(self) -> None: pass - def get_exif(self) -> None: + def get_exif(self) -> bytes: return b"\0\0\0\0" def get_xmp(self) -> None: diff --git a/setup.py b/setup.py index ac1900d8a5f..d8526e53eb5 100644 --- a/setup.py +++ b/setup.py @@ -753,7 +753,7 @@ def build_extensions(self) -> None: self, "jxl/decode.h" ): if _find_library_file(self, "jxl"): - feature.set("jpegxl", "jxl jxl_threads") + feature.set("jpegxl", "jxl") if feature.want("imagequant"): _dbg("Looking for imagequant") @@ -838,15 +838,6 @@ 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( @@ -970,6 +961,14 @@ def build_extensions(self) -> None: else: self._remove_extension("PIL._avif") + jpegxl = feature.get("jpegxl") + if isinstance(jpegxl, str): + # jxl and jxl_threads are required + libs = [jpegxl, jpegxl + "_threads"] + self._update_extension("PIL._jpegxl", libs) + else: + self._remove_extension("PIL._jpegxl") + tk_libs = ["psapi"] if sys.platform in ("win32", "cygwin") else [] self._update_extension("PIL._imagingtk", tk_libs) From 05aee33c5ea5ea2096c2cab17ca73cf3684db46b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 8 May 2025 00:02:02 +1000 Subject: [PATCH 36/66] Use monkeypatch --- Tests/test_file_jxl.py | 32 +++++++++----------------------- Tests/test_file_jxl_alpha.py | 23 ++++++++++------------- Tests/test_file_jxl_animated.py | 10 ++-------- Tests/test_file_jxl_metadata.py | 4 +--- src/PIL/JpegXlImagePlugin.py | 17 ++++++++++------- 5 files changed, 32 insertions(+), 54 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 96b6b0ea5e1..36a9c0aa246 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -6,17 +6,12 @@ from PIL import Image, JpegXlImagePlugin, features -from .helper import ( - assert_image_similar_tofile, - skip_unless_feature, -) +from .helper import assert_image_similar_tofile, skip_unless_feature try: from PIL import _jpegxl - - HAVE_JPEGXL = True except ImportError: - HAVE_JPEGXL = False + pass # cjxl v0.9.2 41b8cdab # hopper.jxl: cjxl hopper.png hopper.jxl -q 75 -e 8 @@ -24,25 +19,16 @@ class TestUnsupportedJpegXl: - def test_unsupported(self) -> None: - if HAVE_JPEGXL: - JpegXlImagePlugin.SUPPORTED = False + def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(JpegXlImagePlugin, "SUPPORTED", False) - file_path = "Tests/images/hopper.jxl" with pytest.raises(OSError): - with Image.open(file_path): + with Image.open("Tests/images/hopper.jxl"): pass - if HAVE_JPEGXL: - JpegXlImagePlugin.SUPPORTED = True - @skip_unless_feature("jpegxl") class TestFileJpegXl: - def setup_method(self) -> None: - self.rgb_mode = "RGB" - self.i16_mode = "I;16" - def test_version(self) -> None: _jpegxl.JpegXlDecoderVersion() version = features.version_module("jpegxl") @@ -56,7 +42,7 @@ def test_read_rgb(self) -> None: """ with Image.open("Tests/images/hopper.jxl") as image: - assert image.mode == self.rgb_mode + assert image.mode == "RGB" assert image.size == (128, 128) assert image.format == "JPEG XL" image.load() @@ -64,7 +50,7 @@ def test_read_rgb(self) -> None: # generated with: # djxl hopper.jxl hopper_jxl_bits.ppm - assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1.0) + assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1) def test_read_i16(self) -> None: """ @@ -72,14 +58,14 @@ def test_read_i16(self) -> None: """ with Image.open("Tests/images/jxl/16bit_subcutaneous.cropped.jxl") as image: - assert image.mode == self.i16_mode + assert image.mode == "I;16" assert image.size == (128, 64) assert image.format == "JPEG XL" image.load() image.getdata() assert_image_similar_tofile( - image, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1.0 + image, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1 ) def test_JpegXlDecode_with_invalid_args(self) -> None: diff --git a/Tests/test_file_jxl_alpha.py b/Tests/test_file_jxl_alpha.py index 8c3ab2b7111..b7cb1998f62 100644 --- a/Tests/test_file_jxl_alpha.py +++ b/Tests/test_file_jxl_alpha.py @@ -1,12 +1,10 @@ from __future__ import annotations -import pytest - from PIL import Image -from .helper import assert_image_similar_tofile +from .helper import assert_image_similar_tofile, skip_unless_feature -_jpegxl = pytest.importorskip("PIL._jpegxl", reason="JPEG XL support not installed") +pytestmark = [skip_unless_feature("jpegxl")] def test_read_rgba() -> None: @@ -16,14 +14,13 @@ def test_read_rgba() -> None: """ # 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() + with Image.open("Tests/images/transparent.jxl") as im: + assert im.mode == "RGBA" + assert im.size == (200, 150) + assert im.format == "JPEG XL" + im.load() + im.getdata() - image.tobytes() + im.tobytes() - assert_image_similar_tofile(image, "Tests/images/transparent.png", 1.0) + assert_image_similar_tofile(im, "Tests/images/transparent.png", 1) diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index 02aca5eb0e2..cd502b30a12 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -4,14 +4,9 @@ from PIL import Image -from .helper import ( - assert_image_equal, - skip_unless_feature, -) +from .helper import assert_image_equal, skip_unless_feature -pytestmark = [ - skip_unless_feature("jpegxl"), -] +pytestmark = [skip_unless_feature("jpegxl")] def test_n_frames() -> None: @@ -27,7 +22,6 @@ def test_n_frames() -> None: def test_float_duration() -> None: - with Image.open("Tests/images/iss634.jxl") as im: im.load() assert im.info["duration"] == 70 diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index bf21be9933d..d0b26b67214 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -8,9 +8,7 @@ from .helper import skip_unless_feature -pytestmark = [ - skip_unless_feature("jpegxl"), -] +pytestmark = [skip_unless_feature("jpegxl")] ElementTree: ModuleType | None try: diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index a89aefe0949..e69e3d1b121 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -41,12 +41,18 @@ class JpegXlImageFile(ImageFile.ImageFile): def _open(self) -> None: self._decoder = _jpegxl.PILJpegXlDecoder(self.fp.read()) - width, height, mode, has_anim, tps_num, tps_denom, n_loops, n_frames = ( - self._decoder.get_info() - ) + ( + width, + height, + self._mode, + self.is_animated, + tps_num, + tps_denom, + n_loops, + n_frames, + ) = self._decoder.get_info() self._size = width, height self.info["loop"] = n_loops - self.is_animated = has_anim self._tps_dur_secs = 1 self.n_frames: int | None = 1 @@ -59,8 +65,6 @@ def _open(self) -> None: # TODO: handle libjxl time codes self.__timestamp = 0 - self._mode = mode - if icc := self._decoder.get_icc(): self.info["icc_profile"] = icc if exif := self._decoder.get_exif(): @@ -115,7 +119,6 @@ def _seek_check(self, frame: int) -> bool: return self.tell() != frame def _seek(self, frame: int) -> None: - # print("_seek: phy: {}, fr: {}".format(self.__physical_frame, frame)) if frame == self.__physical_frame: return # Nothing to do if frame < self.__physical_frame: From 6c3f0b5ad2593ec63a749f65ddcf304b38f889a3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 May 2025 19:16:20 +1000 Subject: [PATCH 37/66] Use member names to initialize module --- Tests/test_file_jxl.py | 28 +++++++++++------------ Tests/test_file_jxl_metadata.py | 39 +++++++++++++++------------------ src/_jpegxl.c | 8 +++---- 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 36a9c0aa246..5f50018d61f 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -41,31 +41,31 @@ def test_read_rgb(self) -> None: Does it have the bits we expect? """ - with Image.open("Tests/images/hopper.jxl") as image: - assert image.mode == "RGB" - assert image.size == (128, 128) - assert image.format == "JPEG XL" - image.load() - image.getdata() + with Image.open("Tests/images/hopper.jxl") as im: + assert im.mode == "RGB" + assert im.size == (128, 128) + assert im.format == "JPEG XL" + im.load() + im.getdata() # generated with: # djxl hopper.jxl hopper_jxl_bits.ppm - assert_image_similar_tofile(image, "Tests/images/hopper_jxl_bits.ppm", 1) + assert_image_similar_tofile(im, "Tests/images/hopper_jxl_bits.ppm", 1) 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 == "I;16" - assert image.size == (128, 64) - assert image.format == "JPEG XL" - image.load() - image.getdata() + with Image.open("Tests/images/jxl/16bit_subcutaneous.cropped.jxl") as im: + assert im.mode == "I;16" + assert im.size == (128, 64) + assert im.format == "JPEG XL" + im.load() + im.getdata() assert_image_similar_tofile( - image, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1 + im, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1 ) def test_JpegXlDecode_with_invalid_args(self) -> None: diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index d0b26b67214..a11dfeefe25 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -27,21 +27,20 @@ def test_read_exif_metadata() -> None: - with Image.open("Tests/images/flower.jxl") as image: - assert image.format == "JPEG XL" - exif_data = image.info.get("exif", None) - assert exif_data + with Image.open("Tests/images/flower.jxl") as im: + assert im.format == "JPEG XL" + exif_data = im.info["exif"] - exif = image.getexif() + exif = im.getexif() # Camera make assert exif[271] == "Canon" - with Image.open("Tests/images/flower.jpg") as jpeg_image: - expected_exif = jpeg_image.info["exif"] + with Image.open("Tests/images/flower.jpg") as im_jpeg: + expected_exif = im_jpeg.info["exif"] - # jpeg xl always returns exif without 'Exif\0\0' prefix - assert exif_data == expected_exif[6:] + # jpeg xl always returns exif without 'Exif\0\0' prefix + assert exif_data == expected_exif[6:] def test_read_exif_metadata_without_prefix() -> None: @@ -50,18 +49,16 @@ def test_read_exif_metadata_without_prefix() -> None: assert im.info["exif"][:6] != b"Exif\x00\x00" exif = im.getexif() - assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" + assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" def test_read_icc_profile() -> None: - with Image.open("Tests/images/flower2.jxl") as image: - assert image.format == "JPEG XL" - assert image.info.get("icc_profile", None) - - icc = image.info["icc_profile"] + with Image.open("Tests/images/flower2.jxl") as im: + assert im.format == "JPEG XL" + icc = im.info["icc_profile"] - with Image.open("Tests/images/flower2.jxl") as jpeg_image: - expected_icc = jpeg_image.info["icc_profile"] + with Image.open("Tests/images/flower2.jxl") as im_jpeg: + expected_icc = im_jpeg.info["icc_profile"] assert icc == expected_icc @@ -114,10 +111,10 @@ def get_xmp(self) -> None: monkeypatch.setattr(JpegXlImagePlugin, "_jpegxl", _mock_jpegxl) - with Image.open("Tests/images/hopper.jxl") as image: - assert "exif" not in image.info + with Image.open("Tests/images/hopper.jxl") as im: + assert "exif" not in im.info def test_read_exif_metadata_empty() -> None: - with Image.open("Tests/images/hopper.jxl") as image: - assert image.getexif() == {} + with Image.open("Tests/images/hopper.jxl") as im: + assert im.getexif() == {} diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 4cb89e7e104..6196a9ea9e5 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -635,7 +635,6 @@ setup_module(PyObject *m) { // TODO(oloke) ready object types? PyObject *d = PyModule_GetDict(m); - PyObject *v = PyUnicode_FromString(JpegXlDecoderVersion_str()); PyDict_SetItemString(d, "libjxl_version", v ? v : Py_None); Py_XDECREF(v); @@ -649,10 +648,9 @@ PyInit__jpegxl(void) { static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, - "_jpegxl", /* m_name */ - NULL, /* m_doc */ - -1, /* m_size */ - jpegxlMethods, /* m_methods */ + .m_name = "_jpegxl", + .m_size = -1, + .m_methods = jpegxlMethods, }; m = PyModule_Create(&module_def); From 80e9963dcd445680138721cee78c65fed6a7fe0f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 May 2025 19:26:26 +1000 Subject: [PATCH 38/66] Use member names to initialize PyTypeObject --- src/_jpegxl.c | 72 ++++++++++++++++----------------------------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 6196a9ea9e5..3c10b91533d 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -37,34 +37,40 @@ char * _pil_jxl_get_mode(const JxlBasicInfo *bi) { // 16-bit single channel images are supported if (bi->bits_per_sample == 16 && bi->num_color_channels == 1 && - bi->alpha_bits == 0 && !bi->alpha_premultiplied) + bi->alpha_bits == 0 && !bi->alpha_premultiplied) { return "I;16"; + } // PIL doesn't support high bit depth images // it will throw an exception but that's for your own good // you wouldn't want to see distorted image - if (bi->bits_per_sample != 8) + if (bi->bits_per_sample != 8) { return "uns"; + } // image has transparency if (bi->alpha_bits > 0) { if (bi->num_color_channels == 3) { - if (bi->alpha_premultiplied) + if (bi->alpha_premultiplied) { return "RGBa"; + } return "RGBA"; } if (bi->num_color_channels == 1) { - if (bi->alpha_premultiplied) + if (bi->alpha_premultiplied) { return "La"; + } return "LA"; } } // image has no transparency - if (bi->num_color_channels == 3) + if (bi->num_color_channels == 3) { return "RGB"; - if (bi->num_color_channels == 1) + } + if (bi->num_color_channels == 1) { return "L"; + } // could not recognize mode return NULL; @@ -168,7 +174,6 @@ _jxl_decoder_count_frames(PyObject *self) { // count all JXL_DEC_NEED_IMAGE_OUT_BUFFER events while (decp->status != JXL_DEC_SUCCESS) { - // printf("fetch_frame_count status: %u\n", decp->status); decp->status = JxlDecoderProcessInput(decp->decoder); if (decp->status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { @@ -226,8 +231,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len); decp->jxl_data_len = _tmp_jxl_data_len; - // printf("%zu\n", decp->jxl_data_len); - size_t suggested_num_threads = JxlThreadParallelRunnerDefaultNumWorkerThreads(); decp->runner = JxlThreadParallelRunnerCreate(NULL, suggested_num_threads); decp->decoder = JxlDecoderCreate(NULL); @@ -254,7 +257,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // decode everything up to the first frame do { decp->status = JxlDecoderProcessInput(decp->decoder); - // printf("Status: %d\n", decp->status); decoder_loop_skip_process: @@ -312,9 +314,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderGetBoxType(decp->decoder, btype, JXL_TRUE); _PIL_JXL_CHECK("JxlDecoderGetBoxType"); - // printf("found box type: %c%c%c%c\n", btype[0], btype[1], btype[2], - // btype[3]); - bool is_box_exif, is_box_xmp; is_box_exif = !memcmp(btype, "Exif", 4); is_box_xmp = !memcmp(btype, "xml ", 4); @@ -326,7 +325,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { size_t cur_compr_box_size; decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &cur_compr_box_size); _PIL_JXL_CHECK("JxlDecoderGetBoxSizeRaw"); - // printf("Exif/xmp box size: %zu\n", cur_compr_box_size); uint8_t *final_jxl_buf = NULL; Py_ssize_t final_jxl_buf_len = 0; @@ -350,8 +348,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderProcessInput(decp->decoder); size_t remaining = JxlDecoderReleaseBoxBuffer(decp->decoder); - // printf("boxes status: %d, remaining: %zu\n", decp->status, - // remaining); final_jxl_buf_len += (cur_compr_box_size - remaining); } while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); @@ -385,7 +381,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { } return (PyObject *)decp; - // Py_RETURN_NONE; // on success we should never reach here @@ -525,8 +520,9 @@ PyObject * _jxl_decoder_get_icc(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - if (!decp->jxl_icc) + if (!decp->jxl_icc) { Py_RETURN_NONE; + } return PyBytes_FromStringAndSize((const char *)decp->jxl_icc, decp->jxl_icc_len); } @@ -535,8 +531,9 @@ PyObject * _jxl_decoder_get_exif(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - if (!decp->jxl_exif) + if (!decp->jxl_exif) { Py_RETURN_NONE; + } return PyBytes_FromStringAndSize((const char *)decp->jxl_exif, decp->jxl_exif_len); } @@ -545,8 +542,9 @@ PyObject * _jxl_decoder_get_xmp(PyObject *self) { PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; - if (!decp->jxl_xmp) + if (!decp->jxl_xmp) { Py_RETURN_NONE; + } return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len); } @@ -564,36 +562,10 @@ static struct PyMethodDef _jpegxl_decoder_methods[] = { // PILJpegXlDecoder type definition static PyTypeObject PILJpegXlDecoder_Type = { - PyVarObject_HEAD_INIT(NULL, 0) "PILJpegXlDecoder", /*tp_name */ - sizeof(PILJpegXlDecoderObject), /*tp_basicsize */ - 0, /*tp_itemsize */ - /* methods */ - (destructor)_jxl_decoder_dealloc, /*tp_dealloc*/ - 0, /*tp_vectorcall_offset*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - _jpegxl_decoder_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "PILJpegXlDecoder", + .tp_basicsize = sizeof(PILJpegXlDecoderObject), + .tp_dealloc = (destructor)_jxl_decoder_dealloc, + .tp_methods = _jpegxl_decoder_methods, }; // Return libjxl decoder version available as integer: From 29c1e4c56f571829096aeebaadac023980c1693f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 May 2025 19:59:01 +1000 Subject: [PATCH 39/66] Removed C method unused by Python --- Tests/test_file_jxl.py | 1 - src/_jpegxl.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 5f50018d61f..f214942c61e 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -30,7 +30,6 @@ def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None: @skip_unless_feature("jpegxl") class TestFileJpegXl: def test_version(self) -> None: - _jpegxl.JpegXlDecoderVersion() version = features.version_module("jpegxl") assert version is not None assert re.search(r"\d+\.\d+\.\d+$", version) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 3c10b91533d..750d563ca1b 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -568,13 +568,6 @@ static PyTypeObject PILJpegXlDecoder_Type = { .tp_methods = _jpegxl_decoder_methods, }; -// Return libjxl decoder version available as integer: -// MAJ*1_000_000 + MIN*1_000 + PATCH -PyObject * -JpegXlDecoderVersion_wrapper() { - return Py_BuildValue("i", JxlDecoderVersion()); -} - // Version as string const char * JpegXlDecoderVersion_str(void) { @@ -591,10 +584,6 @@ JpegXlDecoderVersion_str(void) { } static PyMethodDef jpegxlMethods[] = { - {"JpegXlDecoderVersion", - JpegXlDecoderVersion_wrapper, - METH_NOARGS, - "JpegXlVersion"}, {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"}, {NULL, NULL} }; From c8409e0969416e44e10008bcfa9bc7a2d347f99f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 9 May 2025 20:41:20 +1000 Subject: [PATCH 40/66] Simplified code --- Tests/test_file_jxl.py | 2 +- Tests/test_file_jxl_metadata.py | 6 +- src/PIL/JpegXlImagePlugin.py | 19 +---- src/_jpegxl.c | 127 +++++++++++++++++--------------- 4 files changed, 73 insertions(+), 81 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index f214942c61e..027d6b1c9e8 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -73,4 +73,4 @@ def test_JpegXlDecode_with_invalid_args(self) -> None: """ with pytest.raises(TypeError): - _jpegxl.PILJpegXlDecoder() + _jpegxl.JpegXlDecoder() diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index a11dfeefe25..eb996fb4106 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -93,12 +93,12 @@ def test_getxmp() -> None: def test_4_byte_exif(monkeypatch: pytest.MonkeyPatch) -> None: class _mock_jpegxl: - class PILJpegXlDecoder: + class JpegXlDecoder: def __init__(self, b: bytes) -> None: pass - def get_info(self) -> tuple[int, int, str, int, int, int, int, int]: - return (1, 1, "L", 0, 0, 0, 0, 0) + def get_info(self) -> tuple[tuple[int, int], str, int, int, int, int, int]: + return ((1, 1), "L", 0, 0, 0, 0, 0) def get_icc(self) -> None: pass diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index e69e3d1b121..58875294908 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -39,20 +39,17 @@ class JpegXlImageFile(ImageFile.ImageFile): __logical_frame = 0 def _open(self) -> None: - self._decoder = _jpegxl.PILJpegXlDecoder(self.fp.read()) + self._decoder = _jpegxl.JpegXlDecoder(self.fp.read()) ( - width, - height, + self._size, self._mode, self.is_animated, tps_num, tps_denom, - n_loops, + self.info["loop"], n_frames, ) = self._decoder.get_info() - self._size = width, height - self.info["loop"] = n_loops self._tps_dur_secs = 1 self.n_frames: int | None = 1 @@ -108,16 +105,6 @@ def _rewind(self, hard: bool = False) -> None: self.__loaded = -1 self.__timestamp = 0 - def _seek_check(self, frame: int) -> bool: - # if image is not animated then only the 0th frame is available - if (not self.is_animated and frame != 0) or ( - self.n_frames is not None and (frame >= self.n_frames or frame < 0) - ): - msg = "attempt to seek outside sequence" - raise EOFError(msg) - - return self.tell() != frame - def _seek(self, frame: int) -> None: if frame == self.__physical_frame: return # Nothing to do diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 750d563ca1b..6d8dcb928fc 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -8,14 +8,14 @@ #include #include -#define _PIL_JXL_CHECK(call_name) \ +#define _JXL_CHECK(call_name) \ if (decp->status != JXL_DEC_SUCCESS) { \ jxl_call_name = call_name; \ goto end; \ } void -_pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { +_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { pf->num_channels = bi->num_color_channels + bi->num_extra_channels; if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) { @@ -34,7 +34,7 @@ _pil_jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { // TODO: floating point mode char * -_pil_jxl_get_mode(const JxlBasicInfo *bi) { +_jxl_get_mode(const JxlBasicInfo *bi) { // 16-bit single channel images are supported if (bi->bits_per_sample == 16 && bi->num_color_channels == 1 && bi->alpha_bits == 0 && !bi->alpha_premultiplied) { @@ -45,7 +45,7 @@ _pil_jxl_get_mode(const JxlBasicInfo *bi) { // it will throw an exception but that's for your own good // you wouldn't want to see distorted image if (bi->bits_per_sample != 8) { - return "uns"; + return NULL; } // image has transparency @@ -101,13 +101,13 @@ typedef struct { Py_ssize_t n_frames; char *mode; -} PILJpegXlDecoderObject; +} JpegXlDecoderObject; -static PyTypeObject PILJpegXlDecoder_Type; +static PyTypeObject JpegXlDecoder_Type; void _jxl_decoder_dealloc(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; if (decp->jxl_data) { free(decp->jxl_data); @@ -150,7 +150,7 @@ _jxl_decoder_dealloc(PyObject *self) { // has to be called after every rewind void _jxl_decoder_set_input(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; decp->status = JxlDecoderSetInput(decp->decoder, decp->jxl_data, decp->jxl_data_len); @@ -161,14 +161,14 @@ _jxl_decoder_set_input(PyObject *self) { PyObject * _jxl_decoder_rewind(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; JxlDecoderRewind(decp->decoder); Py_RETURN_NONE; } bool _jxl_decoder_count_frames(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; decp->n_frames = 0; @@ -193,8 +193,8 @@ PyObject * _jxl_decoder_new(PyObject *self, PyObject *args) { PyBytesObject *jxl_string; - PILJpegXlDecoderObject *decp = NULL; - decp = PyObject_New(PILJpegXlDecoderObject, &PILJpegXlDecoder_Type); + JpegXlDecoderObject *decp = NULL; + decp = PyObject_New(JpegXlDecoderObject, &JpegXlDecoder_Type); decp->mode = NULL; decp->jxl_data = NULL; decp->jxl_data_len = 0; @@ -216,7 +216,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { return NULL; } - // this data needs to be copied to PILJpegXlDecoderObject + // this data needs to be copied to JpegXlDecoderObject // so that input bitstream is preserved across calls const uint8_t *_tmp_jxl_data; Py_ssize_t _tmp_jxl_data_len; @@ -238,21 +238,21 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderSetParallelRunner( decp->decoder, JxlThreadParallelRunner, decp->runner ); - _PIL_JXL_CHECK("JxlDecoderSetParallelRunner") + _JXL_CHECK("JxlDecoderSetParallelRunner") decp->status = JxlDecoderSubscribeEvents( decp->decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_BOX | JXL_DEC_FULL_IMAGE ); - _PIL_JXL_CHECK("JxlDecoderSubscribeEvents") + _JXL_CHECK("JxlDecoderSubscribeEvents") // tell libjxl to decompress boxes (for example Exif is usually compressed) decp->status = JxlDecoderSetDecompressBoxes(decp->decoder, JXL_TRUE); - _PIL_JXL_CHECK("JxlDecoderSetDecompressBoxes") + _JXL_CHECK("JxlDecoderSetDecompressBoxes") _jxl_decoder_set_input((PyObject *)decp); - _PIL_JXL_CHECK("JxlDecoderSetInput") + _JXL_CHECK("JxlDecoderSetInput") // decode everything up to the first frame do { @@ -269,9 +269,9 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got basic info if (decp->status == JXL_DEC_BASIC_INFO) { decp->status = JxlDecoderGetBasicInfo(decp->decoder, &decp->basic_info); - _PIL_JXL_CHECK("JxlDecoderGetBasicInfo"); + _JXL_CHECK("JxlDecoderGetBasicInfo"); - _pil_jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); + _jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); if (decp->pixel_format.data_type != JXL_TYPE_UINT8 && decp->pixel_format.data_type != JXL_TYPE_UINT16) { // only 8 bit integer value images are supported for now @@ -280,7 +280,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { ); goto end_with_custom_error; } - decp->mode = _pil_jxl_get_mode(&decp->basic_info); + decp->mode = _jxl_get_mode(&decp->basic_info); continue; } @@ -290,7 +290,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderGetICCProfileSize( decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len ); - _PIL_JXL_CHECK("JxlDecoderGetICCProfileSize"); + _JXL_CHECK("JxlDecoderGetICCProfileSize"); decp->jxl_icc = malloc(decp->jxl_icc_len); if (!decp->jxl_icc) { @@ -304,7 +304,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->jxl_icc, decp->jxl_icc_len ); - _PIL_JXL_CHECK("JxlDecoderGetColorAsICCProfile"); + _JXL_CHECK("JxlDecoderGetColorAsICCProfile"); continue; } @@ -312,11 +312,10 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { if (decp->status == JXL_DEC_BOX) { char btype[4]; decp->status = JxlDecoderGetBoxType(decp->decoder, btype, JXL_TRUE); - _PIL_JXL_CHECK("JxlDecoderGetBoxType"); + _JXL_CHECK("JxlDecoderGetBoxType"); - bool is_box_exif, is_box_xmp; - is_box_exif = !memcmp(btype, "Exif", 4); - is_box_xmp = !memcmp(btype, "xml ", 4); + bool is_box_exif = !memcmp(btype, "Exif", 4); + bool is_box_xmp = !memcmp(btype, "xml ", 4); if (!is_box_exif && !is_box_xmp) { // not exif/xmp box so continue continue; @@ -324,7 +323,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { size_t cur_compr_box_size; decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &cur_compr_box_size); - _PIL_JXL_CHECK("JxlDecoderGetBoxSizeRaw"); + _JXL_CHECK("JxlDecoderGetBoxSizeRaw"); uint8_t *final_jxl_buf = NULL; Py_ssize_t final_jxl_buf_len = 0; @@ -343,7 +342,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderSetBoxBuffer( decp->decoder, final_jxl_buf + final_jxl_buf_len, cur_compr_box_size ); - _PIL_JXL_CHECK("JxlDecoderSetBoxBuffer"); + _JXL_CHECK("JxlDecoderSetBoxBuffer"); decp->status = JxlDecoderProcessInput(decp->decoder); @@ -367,7 +366,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { } while (decp->status != JXL_DEC_FRAME); // couldn't determine Image mode or it is unsupported - if (!strcmp(decp->mode, "uns") || !decp->mode) { + if (!decp->mode) { PyErr_SetString(PyExc_NotImplementedError, "only 8-bit images are supported"); goto end_with_custom_error; } @@ -408,10 +407,10 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { PyObject * _jxl_decoder_get_info(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; return Py_BuildValue( - "IIsiIIII", + "(II)siIIII", decp->basic_info.xsize, decp->basic_info.ysize, decp->mode, @@ -425,7 +424,7 @@ _jxl_decoder_get_info(PyObject *self) { PyObject * _jxl_decoder_get_next(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; PyObject *bytes; PyObject *ret; JxlFrameHeader fhdr = {}; @@ -444,14 +443,14 @@ _jxl_decoder_get_next(PyObject *self) { // this should only occur after rewind if (decp->status == JXL_DEC_NEED_MORE_INPUT) { _jxl_decoder_set_input((PyObject *)decp); - _PIL_JXL_CHECK("JxlDecoderSetInput") + _JXL_CHECK("JxlDecoderSetInput") continue; } if (decp->status == JXL_DEC_FRAME) { // decode frame header decp->status = JxlDecoderGetFrameHeader(decp->decoder, &fhdr); - _PIL_JXL_CHECK("JxlDecoderGetFrameHeader"); + _JXL_CHECK("JxlDecoderGetFrameHeader"); continue; } } @@ -460,7 +459,7 @@ _jxl_decoder_get_next(PyObject *self) { decp->status = JxlDecoderImageOutBufferSize( decp->decoder, &decp->pixel_format, &new_outbuf_len ); - _PIL_JXL_CHECK("JxlDecoderImageOutBufferSize"); + _JXL_CHECK("JxlDecoderImageOutBufferSize"); // only allocate memory when current buffer is too small if (decp->outbuf_len < new_outbuf_len) { @@ -476,7 +475,7 @@ _jxl_decoder_get_next(PyObject *self) { decp->status = JxlDecoderSetImageOutBuffer( decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len ); - _PIL_JXL_CHECK("JxlDecoderSetImageOutBuffer"); + _JXL_CHECK("JxlDecoderSetImageOutBuffer"); // decode image into output_buffer decp->status = JxlDecoderProcessInput(decp->decoder); @@ -518,7 +517,7 @@ _jxl_decoder_get_next(PyObject *self) { PyObject * _jxl_decoder_get_icc(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; if (!decp->jxl_icc) { Py_RETURN_NONE; @@ -529,7 +528,7 @@ _jxl_decoder_get_icc(PyObject *self) { PyObject * _jxl_decoder_get_exif(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; if (!decp->jxl_exif) { Py_RETURN_NONE; @@ -540,7 +539,7 @@ _jxl_decoder_get_exif(PyObject *self) { PyObject * _jxl_decoder_get_xmp(PyObject *self) { - PILJpegXlDecoderObject *decp = (PILJpegXlDecoderObject *)self; + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; if (!decp->jxl_xmp) { Py_RETURN_NONE; @@ -549,7 +548,25 @@ _jxl_decoder_get_xmp(PyObject *self) { return PyBytes_FromStringAndSize((const char *)decp->jxl_xmp, decp->jxl_xmp_len); } -// PILJpegXlDecoder methods +// Version as string +const char * +JpegXlDecoderVersion_str(void) { + static char version[20]; + sprintf( + version, + "%d.%d.%d", + JPEGXL_MAJOR_VERSION, + JPEGXL_MINOR_VERSION, + JPEGXL_PATCH_VERSION + ); + return version; +} + +/* -------------------------------------------------------------------- */ +/* Type Definitions */ +/* -------------------------------------------------------------------- */ + +// JpegXlDecoder methods static struct PyMethodDef _jpegxl_decoder_methods[] = { {"get_info", (PyCFunction)_jxl_decoder_get_info, METH_NOARGS, "get_info"}, {"get_next", (PyCFunction)_jxl_decoder_get_next, METH_NOARGS, "get_next"}, @@ -560,37 +577,25 @@ static struct PyMethodDef _jpegxl_decoder_methods[] = { {NULL, NULL} /* sentinel */ }; -// PILJpegXlDecoder type definition -static PyTypeObject PILJpegXlDecoder_Type = { - PyVarObject_HEAD_INIT(NULL, 0).tp_name = "PILJpegXlDecoder", - .tp_basicsize = sizeof(PILJpegXlDecoderObject), +// JpegXlDecoder type definition +static PyTypeObject JpegXlDecoder_Type = { + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "JpegXlDecoder", + .tp_basicsize = sizeof(JpegXlDecoderObject), .tp_dealloc = (destructor)_jxl_decoder_dealloc, .tp_methods = _jpegxl_decoder_methods, }; -// Version as string -const char * -JpegXlDecoderVersion_str(void) { - static char version[20]; - int version_number = JxlDecoderVersion(); - sprintf( - version, - "%d.%d.%d", - version_number / 1000000, - (version_number % 1000000) / 1000, - (version_number % 1000) - ); - return version; -} +/* -------------------------------------------------------------------- */ +/* Module Setup */ +/* -------------------------------------------------------------------- */ static PyMethodDef jpegxlMethods[] = { - {"PILJpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "PILJpegXlDecoder"}, - {NULL, NULL} + {"JpegXlDecoder", _jxl_decoder_new, METH_VARARGS, "JpegXlDecoder"}, {NULL, NULL} }; static int setup_module(PyObject *m) { - if (PyType_Ready(&PILJpegXlDecoder_Type) < 0) { + if (PyType_Ready(&JpegXlDecoder_Type) < 0) { return -1; } From 61ce5c2a4caa0769809ad27f47d5a9e79b99fff0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 10 May 2025 17:46:59 +1000 Subject: [PATCH 41/66] is_animated should be a bool --- src/_jpegxl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 6d8dcb928fc..5672ce02689 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -410,11 +410,11 @@ _jxl_decoder_get_info(PyObject *self) { JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; return Py_BuildValue( - "(II)siIIII", + "(II)sOIIII", decp->basic_info.xsize, decp->basic_info.ysize, decp->mode, - decp->basic_info.have_animation, + decp->basic_info.have_animation ? Py_True : Py_False, decp->basic_info.animation.tps_numerator, decp->basic_info.animation.tps_denominator, decp->basic_info.animation.num_loops, From bf0cdb2a7f0721dcfde01e3df5c91948b2629dac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 12 May 2025 18:46:38 +1000 Subject: [PATCH 42/66] Test on Linux --- .ci/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index d065e7ab5c5..8c4bededccc 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -21,7 +21,7 @@ set -e if [[ $(uname) != CYGWIN* ]]; then sudo apt-get -qq install libfreetype6-dev liblcms2-dev libtiff-dev python3-tk\ - ghostscript libjpeg-turbo8-dev libopenjp2-7-dev\ + ghostscript libjpeg-turbo8-dev libjxl-dev libopenjp2-7-dev\ cmake meson imagemagick libharfbuzz-dev libfribidi-dev\ sway wl-clipboard libopenblas-dev nasm fi From 79f941dfa526486b83d9b14b7fa29253093a22b2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 12 May 2025 19:01:15 +1000 Subject: [PATCH 43/66] Added argument that was removed in 0.9.0 --- src/_jpegxl.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 5672ce02689..91df4052432 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -288,7 +288,12 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got color encoding if (decp->status == JXL_DEC_COLOR_ENCODING) { decp->status = JxlDecoderGetICCProfileSize( - decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len + decp->decoder, +#if JPEGXL_MINOR_VERSION < 9 + NULL, +#endif + JXL_COLOR_PROFILE_TARGET_DATA, + &decp->jxl_icc_len ); _JXL_CHECK("JxlDecoderGetICCProfileSize"); @@ -300,6 +305,9 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderGetColorAsICCProfile( decp->decoder, +#if JPEGXL_MINOR_VERSION < 9 + NULL, +#endif JXL_COLOR_PROFILE_TARGET_DATA, decp->jxl_icc, decp->jxl_icc_len From b5d64e8576431edc0dcb870fe934be66f7a45d06 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 15 Jun 2025 22:25:56 +1000 Subject: [PATCH 44/66] Removed specific leak check --- Tests/test_jxl_leaks.py | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 Tests/test_jxl_leaks.py diff --git a/Tests/test_jxl_leaks.py b/Tests/test_jxl_leaks.py deleted file mode 100644 index cec9f152894..00000000000 --- a/Tests/test_jxl_leaks.py +++ /dev/null @@ -1,25 +0,0 @@ -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() - - def core() -> None: - with Image.open(BytesIO(im_data)) as im: - im.load() - - self._test_leak(core) From ccca015977e421f808a9baeef250e8eda3add3cc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 25 Jun 2025 22:22:36 +1000 Subject: [PATCH 45/66] Use multi-phase initialization --- src/_jpegxl.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 91df4052432..e3396254b76 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -616,22 +616,22 @@ setup_module(PyObject *m) { return 0; } +static PyModuleDef_Slot slots[] = { + {Py_mod_exec, setup_module}, +#ifdef Py_GIL_DISABLED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif + {0, NULL} +}; + PyMODINIT_FUNC PyInit__jpegxl(void) { - PyObject *m; - static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, .m_name = "_jpegxl", - .m_size = -1, .m_methods = jpegxlMethods, + .m_slots = slots }; - m = PyModule_Create(&module_def); - if (setup_module(m) < 0) { - Py_DECREF(m); - return NULL; - } - - return m; + return PyModuleDef_Init(&module_def); } From 099048b874969bd3e6821b77aec5e4573c0c4d6c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 20 Nov 2025 08:14:15 +1100 Subject: [PATCH 46/66] Raise warning if image file identification fails due to lack of support --- Tests/test_file_jxl.py | 5 +++-- src/PIL/JpegXlImagePlugin.py | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 027d6b1c9e8..8816702e8b0 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -23,8 +23,9 @@ def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(JpegXlImagePlugin, "SUPPORTED", False) with pytest.raises(OSError): - with Image.open("Tests/images/hopper.jxl"): - pass + with pytest.warns(UserWarning, match="JXL support not installed"): + with Image.open("Tests/images/hopper.jxl"): + pass @skip_unless_feature("jpegxl") diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 58875294908..4f4807748c8 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -22,13 +22,12 @@ # OPEN_COUNTS_FRAMES = True -def _accept(prefix: bytes) -> bool: +def _accept(prefix: bytes) -> bool | str: is_jxl = prefix.startswith( (b"\xff\x0a", b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a") ) if is_jxl and not SUPPORTED: - msg = "image file could not be identified because JXL support not installed" - raise SyntaxError(msg) + return "image file could not be identified because JXL support not installed" return is_jxl From ff0d3b4ea2fb8294c84e8d99ccad599355cfc6dc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 20 Nov 2025 08:20:45 +1100 Subject: [PATCH 47/66] Fixed typo --- src/PIL/JpegXlImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 4f4807748c8..7a6cc274c10 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -90,7 +90,7 @@ def _get_next(self) -> tuple[bytes, float, float, bool]: # libjxl said this frame is the last one self.n_frames = self.__physical_frame - # duration in miliseconds + # duration in milliseconds duration = 1000 * tps_duration * (1 / self._tps_dur_secs) timestamp = self.__timestamp self.__timestamp += duration From 358150b83765aaafe88eedee9f85c9fa15f5ec8c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Nov 2025 17:33:06 +1100 Subject: [PATCH 48/66] libjxl < 0.9.0 is not supported. See https://github.com/libjxl/libjxl/issues/4400 --- .ci/install.sh | 2 +- docs/installation/building-from-source.rst | 8 ++++++-- docs/reference/features.rst | 1 + docs/reference/plugins.rst | 8 ++++++++ src/PIL/features.py | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index c4c49d6a1ca..2178c664626 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -18,7 +18,7 @@ aptget_update || aptget_update retry || aptget_update retry set -e sudo apt-get -qq install libfreetype6-dev liblcms2-dev libtiff-dev python3-tk\ - ghostscript libjpeg-turbo8-dev libjxl-dev libopenjp2-7-dev\ + ghostscript libjpeg-turbo8-dev libopenjp2-7-dev\ cmake meson imagemagick libharfbuzz-dev libfribidi-dev\ sway wl-clipboard libopenblas-dev nasm diff --git a/docs/installation/building-from-source.rst b/docs/installation/building-from-source.rst index 59c595742fe..7840e5c468a 100644 --- a/docs/installation/building-from-source.rst +++ b/docs/installation/building-from-source.rst @@ -97,6 +97,10 @@ Many of Pillow's features require external libraries: and decoder, such as rav1e and dav1d, or libaom, which both encodes and decodes AVIF images. +* **libjxl** provides support for the JPEG XL format. + + * Pillow requires libjxl version **0.9.0** or greater. + .. tab:: Linux If you didn't build Python from source, make sure you have Python's @@ -284,7 +288,7 @@ Build options ``-C tiff=disable``, ``-C freetype=disable``, ``-C raqm=disable``, ``-C lcms=disable``, ``-C webp=disable``, ``-C jpeg2000=disable``, ``-C imagequant=disable``, ``-C xcb=disable``, - ``-C avif=disable``. + ``-C avif=disable``, ``-C jpegxl=disable``. Disable building the corresponding feature even if the development libraries are present on the building machine. @@ -292,7 +296,7 @@ Build options ``-C tiff=enable``, ``-C freetype=enable``, ``-C raqm=enable``, ``-C lcms=enable``, ``-C webp=enable``, ``-C jpeg2000=enable``, ``-C imagequant=enable``, ``-C xcb=enable``, - ``-C avif=enable``. + ``-C avif=enable``, ``-C jpegxl=enable``. Require that the corresponding feature is built. The build will raise an exception if the libraries are not found. Tcl and Tk must be used together. diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 45067ba3587..01ae4582c02 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -22,6 +22,7 @@ Support for the following modules can be checked: * ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`. * ``webp``: WebP image support. * ``avif``: AVIF image support. +* ``jpegxl``: JPEG XL image support. .. autofunction:: PIL.features.check_module .. autofunction:: PIL.features.version_module diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index 243d4f353f5..b5e7b634622 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -169,6 +169,14 @@ Plugin reference :undoc-members: :show-inheritance: +:mod:`~PIL.JpegXlImagePlugin` module +------------------------------------ + +.. automodule:: PIL.JpegXlImagePlugin + :members: + :undoc-members: + :show-inheritance: + :mod:`~PIL.McIdasImagePlugin` module ------------------------------------ diff --git a/src/PIL/features.py b/src/PIL/features.py index 8064bc2a682..1bfa40020c5 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -15,9 +15,9 @@ "tkinter": ("PIL._tkinter_finder", "tk_version"), "freetype2": ("PIL._imagingft", "freetype2_version"), "littlecms2": ("PIL._imagingcms", "littlecms_version"), - "jpegxl": ("PIL._jpegxl", "libjxl_version"), "webp": ("PIL._webp", "webpdecoder_version"), "avif": ("PIL._avif", "libavif_version"), + "jpegxl": ("PIL._jpegxl", "libjxl_version"), } From fe612ea6e47dcb2c6c39737e2871b15b9fdeb75a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Nov 2025 17:40:40 +1100 Subject: [PATCH 49/66] Revert "Added argument that was removed in 0.9.0" This reverts commit 79f941dfa526486b83d9b14b7fa29253093a22b2. --- src/_jpegxl.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index e3396254b76..ca7fdf7f4f2 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -288,12 +288,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { // got color encoding if (decp->status == JXL_DEC_COLOR_ENCODING) { decp->status = JxlDecoderGetICCProfileSize( - decp->decoder, -#if JPEGXL_MINOR_VERSION < 9 - NULL, -#endif - JXL_COLOR_PROFILE_TARGET_DATA, - &decp->jxl_icc_len + decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len ); _JXL_CHECK("JxlDecoderGetICCProfileSize"); @@ -305,9 +300,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderGetColorAsICCProfile( decp->decoder, -#if JPEGXL_MINOR_VERSION < 9 - NULL, -#endif JXL_COLOR_PROFILE_TARGET_DATA, decp->jxl_icc, decp->jxl_icc_len From 66e5838080bbb7ac7fca27ad3b432758d3224f76 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Nov 2025 21:47:56 +1100 Subject: [PATCH 50/66] Test on MinGW --- .github/workflows/test-mingw.yml | 1 + docs/installation/building-from-source.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml index 5a83c16c329..323d33dd597 100644 --- a/.github/workflows/test-mingw.yml +++ b/.github/workflows/test-mingw.yml @@ -63,6 +63,7 @@ jobs: mingw-w64-x86_64-libavif \ mingw-w64-x86_64-libimagequant \ mingw-w64-x86_64-libjpeg-turbo \ + mingw-w64-x86_64-libjxl \ mingw-w64-x86_64-libraqm \ mingw-w64-x86_64-libtiff \ mingw-w64-x86_64-libwebp \ diff --git a/docs/installation/building-from-source.rst b/docs/installation/building-from-source.rst index 7840e5c468a..dc50c7b8a3e 100644 --- a/docs/installation/building-from-source.rst +++ b/docs/installation/building-from-source.rst @@ -206,6 +206,7 @@ Many of Pillow's features require external libraries: pacman -S \ mingw-w64-x86_64-libjpeg-turbo \ + mingw-w64-x86_64-libjxl \ mingw-w64-x86_64-zlib \ mingw-w64-x86_64-libtiff \ mingw-w64-x86_64-freetype \ From 9096240a632d18c01c5d558a3992d1b59772b192 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Nov 2025 23:50:33 +1100 Subject: [PATCH 51/66] Build on macOS and Linux wheels, except for manylinux2014 --- .github/workflows/wheels-dependencies.sh | 20 ++++++++++++++++++++ checks/check_wheel.py | 3 +++ 2 files changed, 23 insertions(+) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 226fcdb6af7..9759ccac596 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -102,6 +102,7 @@ HARFBUZZ_VERSION=12.2.0 LIBPNG_VERSION=1.6.50 JPEGTURBO_VERSION=3.1.2 OPENJPEG_VERSION=2.5.4 +JPEGXL_VERSION=0.11.1 XZ_VERSION=5.8.1 ZSTD_VERSION=1.5.7 TIFF_VERSION=4.7.1 @@ -173,6 +174,21 @@ function build_brotli { touch brotli-stamp } +function build_jpegxl { + if [ -e jpegxl-stamp ]; then return; fi + + local out_dir=$(fetch_unpack https://github.com/google/highway/archive/1.3.0.tar.gz) + (cd $out_dir \ + && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib $HOST_CMAKE_FLAGS . \ + && make install) + + local out_dir=$(fetch_unpack https://github.com/libjxl/libjxl/archive/v$JPEGXL_VERSION.tar.gz) + (cd $out_dir \ + && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib -DJPEGXL_ENABLE_SJPEG=OFF -DJPEGXL_ENABLE_SKCMS=OFF -DBUILD_TESTING=OFF $HOST_CMAKE_FLAGS . \ + && make install) + touch jpegxl-stamp +} + function build_harfbuzz { if [ -e harfbuzz-stamp ]; then return; fi python3 -m pip install meson ninja @@ -335,6 +351,10 @@ function build { # On iOS, there's no vendor-provided raqm, and we can't ship it due to # licensing, so there's no point building harfbuzz. build_harfbuzz + + if [[ "$MB_ML_VER" != 2014 ]]; then + build_jpegxl + fi fi } diff --git a/checks/check_wheel.py b/checks/check_wheel.py index f716c8498bb..9d5574630b7 100644 --- a/checks/check_wheel.py +++ b/checks/check_wheel.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import platform import sys @@ -25,6 +26,8 @@ def test_wheel_modules() -> None: elif sys.platform == "ios": # tkinter is not available on iOS expected_modules.remove("tkinter") + elif os.environ.get("AUDITWHEEL_POLICY") != "manylinux2014": + expected_modules.add("jpegxl") assert set(features.get_supported_modules()) == expected_modules From 762235cd56072cf76c0ceae52d6b854a097cc96b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Dec 2025 21:21:43 +1100 Subject: [PATCH 52/66] Simplified code --- Tests/test_file_jxl_alpha.py | 2 +- Tests/test_file_jxl_animated.py | 6 ++---- Tests/test_file_jxl_metadata.py | 8 ++++---- src/PIL/JpegXlImagePlugin.py | 11 ++++------- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Tests/test_file_jxl_alpha.py b/Tests/test_file_jxl_alpha.py index b7cb1998f62..a5fa1501913 100644 --- a/Tests/test_file_jxl_alpha.py +++ b/Tests/test_file_jxl_alpha.py @@ -4,7 +4,7 @@ from .helper import assert_image_similar_tofile, skip_unless_feature -pytestmark = [skip_unless_feature("jpegxl")] +pytestmark = skip_unless_feature("jpegxl") def test_read_rgba() -> None: diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index cd502b30a12..54bcae41e66 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -6,7 +6,7 @@ from .helper import assert_image_equal, skip_unless_feature -pytestmark = [skip_unless_feature("jpegxl")] +pytestmark = skip_unless_feature("jpegxl") def test_n_frames() -> None: @@ -27,7 +27,7 @@ def test_float_duration() -> None: assert im.info["duration"] == 70 -def test_seeking() -> None: +def test_seek() -> None: """ Open an animated jxl file, and then try seeking through frames in reverse-order, verifying the durations are correct. @@ -42,9 +42,7 @@ def test_seeking() -> None: total_dur = 0 for frame in reversed(range(im1.n_frames)): im1.seek(frame) - im1.load() im2.seek(frame) - im2.load() assert_image_equal(im1.convert("RGB"), im2.convert("RGB")) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index eb996fb4106..267ea7a1292 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -8,7 +8,7 @@ from .helper import skip_unless_feature -pytestmark = [skip_unless_feature("jpegxl")] +pytestmark = skip_unless_feature("jpegxl") ElementTree: ModuleType | None try: @@ -33,8 +33,8 @@ def test_read_exif_metadata() -> None: exif = im.getexif() - # Camera make - assert exif[271] == "Canon" + # Camera make + assert exif[271] == "Canon" with Image.open("Tests/images/flower.jpg") as im_jpeg: expected_exif = im_jpeg.info["exif"] @@ -49,7 +49,7 @@ def test_read_exif_metadata_without_prefix() -> None: assert im.info["exif"][:6] != b"Exif\x00\x00" exif = im.getexif() - assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" + assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" def test_read_icc_profile() -> None: diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 7a6cc274c10..d824449ea5b 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -14,7 +14,7 @@ ## Future idea: -## it's not known how many frames does animated image have +## it's not known how many frames an animated image has ## by default, _jxl_decoder_new will iterate over all frames without decoding them ## then libjxl decoder is rewinded and we're ready to decode frame by frame ## if OPEN_COUNTS_FRAMES is False, n_frames will be None until the last frame is decoded @@ -74,8 +74,7 @@ def _open(self) -> None: self._rewind() - def _get_next(self) -> tuple[bytes, float, float, bool]: - + def _get_next(self) -> tuple[bytes, float, float]: # Get next frame next_frame = self._decoder.get_next() self.__physical_frame += 1 @@ -95,7 +94,7 @@ def _get_next(self) -> tuple[bytes, float, float, bool]: timestamp = self.__timestamp self.__timestamp += duration - return data, timestamp, duration, is_last + return data, timestamp, duration def _rewind(self, hard: bool = False) -> None: if hard: @@ -125,9 +124,7 @@ def load(self) -> Image.core.PixelAccess | None: if self.__loaded != self.__logical_frame: self._seek(self.__logical_frame) - data, timestamp, duration, is_last = self._get_next() - self.info["timestamp"] = timestamp - self.info["duration"] = duration + data, self.info["timestamp"], self.info["duration"] = self._get_next() self.__loaded = self.__logical_frame # Set tile From 5e3dc407b40f11b33e507ef895fa9efec39ac036 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Dec 2025 23:06:23 +1100 Subject: [PATCH 53/66] Do not count frames on image open --- Tests/test_file_jxl_metadata.py | 4 +-- src/PIL/JpegXlImagePlugin.py | 49 ++++++++++++++------------------- src/_jpegxl.c | 49 ++++++++++++++------------------- 3 files changed, 42 insertions(+), 60 deletions(-) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index 267ea7a1292..aad4044a5f9 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -97,8 +97,8 @@ class JpegXlDecoder: def __init__(self, b: bytes) -> None: pass - def get_info(self) -> tuple[tuple[int, int], str, int, int, int, int, int]: - return ((1, 1), "L", 0, 0, 0, 0, 0) + def get_info(self) -> tuple[tuple[int, int], str, int, int, int, int]: + return ((1, 1), "L", 0, 0, 0, 0) def get_icc(self) -> None: pass diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index d824449ea5b..ef24e43bf71 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -13,15 +13,6 @@ SUPPORTED = False -## Future idea: -## it's not known how many frames an animated image has -## by default, _jxl_decoder_new will iterate over all frames without decoding them -## then libjxl decoder is rewinded and we're ready to decode frame by frame -## if OPEN_COUNTS_FRAMES is False, n_frames will be None until the last frame is decoded -## it only applies to animated jpeg xl images -# OPEN_COUNTS_FRAMES = True - - def _accept(prefix: bytes) -> bool | str: is_jxl = prefix.startswith( (b"\xff\x0a", b"\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a") @@ -34,8 +25,9 @@ def _accept(prefix: bytes) -> bool | str: class JpegXlImageFile(ImageFile.ImageFile): format = "JPEG XL" format_description = "JPEG XL image" - __loaded = 0 + __loaded = -1 __logical_frame = 0 + __physical_frame = 0 def _open(self) -> None: self._decoder = _jpegxl.JpegXlDecoder(self.fp.read()) @@ -47,16 +39,10 @@ def _open(self) -> None: tps_num, tps_denom, self.info["loop"], - n_frames, ) = self._decoder.get_info() - self._tps_dur_secs = 1 - self.n_frames: int | None = 1 - if self.is_animated: - self.n_frames = None - if n_frames > 0: - self.n_frames = n_frames - self._tps_dur_secs = tps_num / tps_denom + self._n_frames = None if self.is_animated else 1 + self._tps_dur_secs = tps_num / tps_denom if tps_denom != 0 else 1 # TODO: handle libjxl time codes self.__timestamp = 0 @@ -72,7 +58,14 @@ def _open(self) -> None: if xmp := self._decoder.get_xmp(): self.info["xmp"] = xmp - self._rewind() + @property + def n_frames(self) -> int: + if self._n_frames is None: + current = self.tell() + self._n_frames = current + self._decoder.get_frames_left() + self.seek(current) + + return self._n_frames def _get_next(self) -> tuple[bytes, float, float]: # Get next frame @@ -85,9 +78,9 @@ def _get_next(self) -> tuple[bytes, float, float]: raise EOFError(msg) data, tps_duration, is_last = next_frame - if is_last and self.n_frames is None: + if is_last and self._n_frames is None: # libjxl said this frame is the last one - self.n_frames = self.__physical_frame + self._n_frames = self.__physical_frame # duration in milliseconds duration = 1000 * tps_duration * (1 / self._tps_dur_secs) @@ -96,24 +89,22 @@ def _get_next(self) -> tuple[bytes, float, float]: return data, timestamp, duration - def _rewind(self, hard: bool = False) -> None: - if hard: - self._decoder.rewind() - self.__physical_frame = 0 - self.__loaded = -1 - self.__timestamp = 0 - def _seek(self, frame: int) -> None: if frame == self.__physical_frame: return # Nothing to do if frame < self.__physical_frame: # also rewind libjxl decoder instance - self._rewind(hard=True) + self._decoder.rewind() + self.__physical_frame = 0 + self.__loaded = -1 + self.__timestamp = 0 while self.__physical_frame < frame: self._get_next() # Advance to the requested frame def seek(self, frame: int) -> None: + if self._n_frames is None: + self.n_frames if not self._seek_check(frame): return diff --git a/src/_jpegxl.c b/src/_jpegxl.c index ca7fdf7f4f2..9f0d6aa750d 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -98,8 +98,6 @@ typedef struct { JxlBasicInfo basic_info; JxlPixelFormat pixel_format; - Py_ssize_t n_frames; - char *mode; } JpegXlDecoderObject; @@ -166,27 +164,26 @@ _jxl_decoder_rewind(PyObject *self) { Py_RETURN_NONE; } -bool -_jxl_decoder_count_frames(PyObject *self) { - JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; - - decp->n_frames = 0; +PyObject * +_jxl_decoder_get_frames_left(PyObject *self) { + int frames_left = 0; // count all JXL_DEC_NEED_IMAGE_OUT_BUFFER events + JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; while (decp->status != JXL_DEC_SUCCESS) { decp->status = JxlDecoderProcessInput(decp->decoder); if (decp->status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { if (JxlDecoderSkipCurrentFrame(decp->decoder) != JXL_DEC_SUCCESS) { - return false; + PyErr_SetString(PyExc_OSError, "Error when counting frames"); + break; } - decp->n_frames++; + frames_left++; } } + JxlDecoderRewind(decp->decoder); - _jxl_decoder_rewind((PyObject *)decp); - - return true; + return Py_BuildValue("i", frames_left); } PyObject * @@ -206,7 +203,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->jxl_exif_len = 0; decp->jxl_xmp = NULL; decp->jxl_xmp_len = 0; - decp->n_frames = 0; // used for printing more detailed error messages char *jxl_call_name; @@ -371,14 +367,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { goto end_with_custom_error; } - if (decp->basic_info.have_animation) { - // get frame count by iterating over image out events - if (!_jxl_decoder_count_frames((PyObject *)decp)) { - PyErr_SetString(PyExc_OSError, "something went wrong when counting frames"); - goto end_with_custom_error; - } - } - return (PyObject *)decp; // on success we should never reach here @@ -410,15 +398,14 @@ _jxl_decoder_get_info(PyObject *self) { JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; return Py_BuildValue( - "(II)sOIIII", + "(II)sOIII", decp->basic_info.xsize, decp->basic_info.ysize, decp->mode, decp->basic_info.have_animation ? Py_True : Py_False, decp->basic_info.animation.tps_numerator, decp->basic_info.animation.tps_denominator, - decp->basic_info.animation.num_loops, - decp->n_frames + decp->basic_info.animation.num_loops ); } @@ -432,6 +419,10 @@ _jxl_decoder_get_next(PyObject *self) { char *jxl_call_name; // process events until next frame output is ready + if (decp->status == JXL_DEC_FRAME) { + decp->status = JxlDecoderGetFrameHeader(decp->decoder, &fhdr); + _JXL_CHECK("JxlDecoderGetFrameHeader"); + } while (decp->status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { decp->status = JxlDecoderProcessInput(decp->decoder); @@ -444,14 +435,10 @@ _jxl_decoder_get_next(PyObject *self) { if (decp->status == JXL_DEC_NEED_MORE_INPUT) { _jxl_decoder_set_input((PyObject *)decp); _JXL_CHECK("JxlDecoderSetInput") - continue; - } - - if (decp->status == JXL_DEC_FRAME) { + } else if (decp->status == JXL_DEC_FRAME) { // decode frame header decp->status = JxlDecoderGetFrameHeader(decp->decoder, &fhdr); _JXL_CHECK("JxlDecoderGetFrameHeader"); - continue; } } @@ -573,6 +560,10 @@ static struct PyMethodDef _jpegxl_decoder_methods[] = { {"get_icc", (PyCFunction)_jxl_decoder_get_icc, METH_NOARGS, "get_icc"}, {"get_exif", (PyCFunction)_jxl_decoder_get_exif, METH_NOARGS, "get_exif"}, {"get_xmp", (PyCFunction)_jxl_decoder_get_xmp, METH_NOARGS, "get_xmp"}, + {"get_frames_left", + (PyCFunction)_jxl_decoder_get_frames_left, + METH_NOARGS, + "get_frames_left"}, {"rewind", (PyCFunction)_jxl_decoder_rewind, METH_NOARGS, "rewind"}, {NULL, NULL} /* sentinel */ }; From 612de5ae9182d0427c363869e9ee9e8a8a99591a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Dec 2025 21:32:50 +1100 Subject: [PATCH 54/66] Populate duration before load --- Tests/test_file_jxl.py | 17 ++++- Tests/test_file_jxl_alpha.py | 26 -------- Tests/test_file_jxl_animated.py | 15 +++-- Tests/test_file_jxl_metadata.py | 6 +- src/PIL/JpegXlImagePlugin.py | 78 +++++++++------------- src/_jpegxl.c | 110 ++++++++++++-------------------- 6 files changed, 100 insertions(+), 152 deletions(-) delete mode 100644 Tests/test_file_jxl_alpha.py diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index 8816702e8b0..be6c3568004 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -37,7 +37,7 @@ def test_version(self) -> None: def test_read_rgb(self) -> None: """ - Can we read a RGB mode Jpeg XL file without error? + Can we read an RGB mode JPEG XL file without error? Does it have the bits we expect? """ @@ -52,9 +52,22 @@ def test_read_rgb(self) -> None: # djxl hopper.jxl hopper_jxl_bits.ppm assert_image_similar_tofile(im, "Tests/images/hopper_jxl_bits.ppm", 1) + def test_read_rgba(self) -> None: + # Generated with `cjxl transparent.png transparent.jxl -q 100 -e 8` + with Image.open("Tests/images/transparent.jxl") as im: + assert im.mode == "RGBA" + assert im.size == (200, 150) + assert im.format == "JPEG XL" + im.load() + im.getdata() + + im.tobytes() + + assert_image_similar_tofile(im, "Tests/images/transparent.png", 1) + def test_read_i16(self) -> None: """ - Can we read 16-bit Grayscale Jpeg XL image? + Can we read 16-bit Grayscale JPEG XL image? """ with Image.open("Tests/images/jxl/16bit_subcutaneous.cropped.jxl") as im: diff --git a/Tests/test_file_jxl_alpha.py b/Tests/test_file_jxl_alpha.py deleted file mode 100644 index a5fa1501913..00000000000 --- a/Tests/test_file_jxl_alpha.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -from PIL import Image - -from .helper import assert_image_similar_tofile, skip_unless_feature - -pytestmark = skip_unless_feature("jpegxl") - - -def test_read_rgba() -> None: - """ - 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` - with Image.open("Tests/images/transparent.jxl") as im: - assert im.mode == "RGBA" - assert im.size == (200, 150) - assert im.format == "JPEG XL" - im.load() - im.getdata() - - im.tobytes() - - assert_image_similar_tofile(im, "Tests/images/transparent.png", 1) diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index 54bcae41e66..ebb342d3e78 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -21,10 +21,14 @@ def test_n_frames() -> None: assert im.is_animated -def test_float_duration() -> None: +def test_duration() -> None: with Image.open("Tests/images/iss634.jxl") as im: - im.load() assert im.info["duration"] == 70 + assert im.info["timestamp"] == 0 + + im.seek(2) + assert im.info["duration"] == 60 + assert im.info["timestamp"] == 140 def test_seek() -> None: @@ -62,8 +66,11 @@ def test_seek() -> None: def test_seek_errors() -> None: with Image.open("Tests/images/iss634.jxl") as im: - with pytest.raises(EOFError): + with pytest.raises(EOFError, match="attempt to seek outside sequence"): im.seek(-1) - with pytest.raises(EOFError): + im.seek(1) + with pytest.raises(EOFError, match="no more images in JPEG XL file"): im.seek(47) + + assert im.tell() == 1 diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index aad4044a5f9..d68d417917f 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -39,7 +39,7 @@ def test_read_exif_metadata() -> None: with Image.open("Tests/images/flower.jpg") as im_jpeg: expected_exif = im_jpeg.info["exif"] - # jpeg xl always returns exif without 'Exif\0\0' prefix + # JPEG XL always returns exif without 'Exif\0\0' prefix assert exif_data == expected_exif[6:] @@ -97,8 +97,8 @@ class JpegXlDecoder: def __init__(self, b: bytes) -> None: pass - def get_info(self) -> tuple[tuple[int, int], str, int, int, int, int]: - return ((1, 1), "L", 0, 0, 0, 0) + def get_info(self) -> tuple[tuple[int, int], str, int, int, int, int, int]: + return ((1, 1), "L", 0, 0, 0, 0, 0) def get_icc(self) -> None: pass diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index ef24e43bf71..9a541606884 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -25,9 +25,7 @@ def _accept(prefix: bytes) -> bool | str: class JpegXlImageFile(ImageFile.ImageFile): format = "JPEG XL" format_description = "JPEG XL image" - __loaded = -1 - __logical_frame = 0 - __physical_frame = 0 + __frame = 0 def _open(self) -> None: self._decoder = _jpegxl.JpegXlDecoder(self.fp.read()) @@ -39,24 +37,27 @@ def _open(self) -> None: tps_num, tps_denom, self.info["loop"], + tps_duration, ) = self._decoder.get_info() self._n_frames = None if self.is_animated else 1 self._tps_dur_secs = tps_num / tps_denom if tps_denom != 0 else 1 + self.info["duration"] = 1000 * tps_duration * (1 / self._tps_dur_secs) # TODO: handle libjxl time codes - self.__timestamp = 0 + self.info["timestamp"] = 0 if icc := self._decoder.get_icc(): self.info["icc_profile"] = icc if exif := self._decoder.get_exif(): - # jpeg xl does some weird shenanigans when storing exif + # JPEG XL does some weird shenanigans when storing exif # it omits first 6 bytes of tiff header but adds 4 byte offset instead if len(exif) > 4: exif_start_offset = struct.unpack(">I", exif[:4])[0] self.info["exif"] = exif[exif_start_offset + 4 :] if xmp := self._decoder.get_xmp(): self.info["xmp"] = xmp + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] @property def n_frames(self) -> int: @@ -67,64 +68,45 @@ def n_frames(self) -> int: return self._n_frames - def _get_next(self) -> tuple[bytes, float, float]: - # Get next frame - next_frame = self._decoder.get_next() - self.__physical_frame += 1 + def _get_next(self) -> bytes: + data, tps_duration, is_last = self._decoder.get_next() - # this actually means EOF, errors are raised in _jxl - if next_frame is None: - msg = "failed to decode next frame in JXL file" - raise EOFError(msg) - - data, tps_duration, is_last = next_frame if is_last and self._n_frames is None: - # libjxl said this frame is the last one - self._n_frames = self.__physical_frame + self._n_frames = self.__frame # duration in milliseconds - duration = 1000 * tps_duration * (1 / self._tps_dur_secs) - timestamp = self.__timestamp - self.__timestamp += duration - - return data, timestamp, duration + self.info["timestamp"] += self.info["duration"] + self.info["duration"] = 1000 * tps_duration * (1 / self._tps_dur_secs) - def _seek(self, frame: int) -> None: - if frame == self.__physical_frame: - return # Nothing to do - if frame < self.__physical_frame: - # also rewind libjxl decoder instance - self._decoder.rewind() - self.__physical_frame = 0 - self.__loaded = -1 - self.__timestamp = 0 - - while self.__physical_frame < frame: - self._get_next() # Advance to the requested frame + return data def seek(self, frame: int) -> None: - if self._n_frames is None: - self.n_frames if not self._seek_check(frame): return - # Set logical frame to requested position - self.__logical_frame = frame + if frame < self.__frame: + self.__frame = 0 + self._decoder.rewind() + self.info["timestamp"] = 0 - def load(self) -> Image.core.PixelAccess | None: - if self.__loaded != self.__logical_frame: - self._seek(self.__logical_frame) + last_frame = self.__frame + while self.__frame < frame: + self._get_next() + self.__frame += 1 + if self._n_frames is not None and self._n_frames < frame: + self.seek(last_frame) + msg = "no more images in JPEG XL file" + raise EOFError(msg) - data, self.info["timestamp"], self.info["duration"] = self._get_next() - self.__loaded = self.__logical_frame + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] + + def load(self) -> Image.core.PixelAccess | None: + if self.tile: + data = self._get_next() - # Set tile if self.fp and self._exclusive_fp: self.fp.close() - # this is horribly memory inefficient - # you need probably 2*(raw image plane) bytes of memory self.fp = BytesIO(data) - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] return super().load() @@ -132,7 +114,7 @@ def load_seek(self, pos: int) -> None: pass def tell(self) -> int: - return self.__logical_frame + return self.__frame Image.register_open(JpegXlImageFile.format, JpegXlImageFile, _accept) diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 9f0d6aa750d..b812061f6ab 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -18,10 +18,10 @@ void _jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { pf->num_channels = bi->num_color_channels + bi->num_extra_channels; - if (bi->exponent_bits_per_sample > 0 || bi->alpha_exponent_bits > 0) { - pf->data_type = JXL_TYPE_FLOAT; // not yet supported + if (bi->exponent_bits_per_sample) { + pf->data_type = JXL_TYPE_FLOAT; } else if (bi->bits_per_sample > 8) { - pf->data_type = JXL_TYPE_UINT16; // not yet supported + pf->data_type = JXL_TYPE_UINT16; } else { pf->data_type = JXL_TYPE_UINT8; } @@ -35,43 +35,38 @@ _jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { // TODO: floating point mode char * _jxl_get_mode(const JxlBasicInfo *bi) { - // 16-bit single channel images are supported - if (bi->bits_per_sample == 16 && bi->num_color_channels == 1 && - bi->alpha_bits == 0 && !bi->alpha_premultiplied) { - return "I;16"; - } - - // PIL doesn't support high bit depth images - // it will throw an exception but that's for your own good - // you wouldn't want to see distorted image - if (bi->bits_per_sample != 8) { - return NULL; + if (bi->num_color_channels == 1 && !bi->alpha_bits) { + if (bi->bits_per_sample == 16) { + return "I;16"; + } } - // image has transparency - if (bi->alpha_bits > 0) { - if (bi->num_color_channels == 3) { - if (bi->alpha_premultiplied) { - return "RGBa"; + if (bi->bits_per_sample == 8) { + // image has transparency + if (bi->alpha_bits) { + if (bi->num_color_channels == 3) { + if (bi->alpha_premultiplied) { + return "RGBa"; + } + return "RGBA"; } - return "RGBA"; - } - if (bi->num_color_channels == 1) { - if (bi->alpha_premultiplied) { - return "La"; + if (bi->num_color_channels == 1) { + if (bi->alpha_premultiplied) { + return "La"; + } + return "LA"; + } + } else { + // image has no transparency + if (bi->num_color_channels == 3) { + return "RGB"; + } + if (bi->num_color_channels == 1) { + return "L"; } - return "LA"; } } - // image has no transparency - if (bi->num_color_channels == 3) { - return "RGB"; - } - if (bi->num_color_channels == 1) { - return "L"; - } - // could not recognize mode return NULL; } @@ -85,10 +80,10 @@ typedef struct { Py_ssize_t jxl_data_len; // length of input jxl bitstream uint8_t *outbuf; - Py_ssize_t outbuf_len; + size_t outbuf_len; uint8_t *jxl_icc; - Py_ssize_t jxl_icc_len; + size_t jxl_icc_len; uint8_t *jxl_exif; Py_ssize_t jxl_exif_len; uint8_t *jxl_xmp; @@ -262,26 +257,16 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { goto end; } - // got basic info if (decp->status == JXL_DEC_BASIC_INFO) { decp->status = JxlDecoderGetBasicInfo(decp->decoder, &decp->basic_info); _JXL_CHECK("JxlDecoderGetBasicInfo"); _jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); - if (decp->pixel_format.data_type != JXL_TYPE_UINT8 && - decp->pixel_format.data_type != JXL_TYPE_UINT16) { - // only 8 bit integer value images are supported for now - PyErr_SetString( - PyExc_NotImplementedError, "unsupported pixel data type" - ); - goto end_with_custom_error; - } decp->mode = _jxl_get_mode(&decp->basic_info); continue; } - // got color encoding if (decp->status == JXL_DEC_COLOR_ENCODING) { decp->status = JxlDecoderGetICCProfileSize( decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len @@ -317,7 +302,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { continue; } - size_t cur_compr_box_size; + uint64_t cur_compr_box_size; decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &cur_compr_box_size); _JXL_CHECK("JxlDecoderGetBoxSizeRaw"); @@ -361,12 +346,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { } while (decp->status != JXL_DEC_FRAME); - // couldn't determine Image mode or it is unsupported - if (!decp->mode) { - PyErr_SetString(PyExc_NotImplementedError, "only 8-bit images are supported"); - goto end_with_custom_error; - } - return (PyObject *)decp; // on success we should never reach here @@ -396,16 +375,21 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { PyObject * _jxl_decoder_get_info(PyObject *self) { JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; - + JxlFrameHeader fhdr = {}; + if (JxlDecoderGetFrameHeader(decp->decoder, &fhdr) != JXL_DEC_SUCCESS) { + PyErr_SetString(PyExc_OSError, "Error determining duration"); + return NULL; + } return Py_BuildValue( - "(II)sOIII", + "(II)sOIIII", decp->basic_info.xsize, decp->basic_info.ysize, decp->mode, decp->basic_info.have_animation ? Py_True : Py_False, decp->basic_info.animation.tps_numerator, decp->basic_info.animation.tps_denominator, - decp->basic_info.animation.num_loops + decp->basic_info.animation.num_loops, + fhdr.duration ); } @@ -426,11 +410,6 @@ _jxl_decoder_get_next(PyObject *self) { while (decp->status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { decp->status = JxlDecoderProcessInput(decp->decoder); - // every frame was decoded successfully - if (decp->status == JXL_DEC_SUCCESS) { - Py_RETURN_NONE; - } - // this should only occur after rewind if (decp->status == JXL_DEC_NEED_MORE_INPUT) { _jxl_decoder_set_input((PyObject *)decp); @@ -454,7 +433,7 @@ _jxl_decoder_get_next(PyObject *self) { uint8_t *_new_outbuf = realloc(decp->outbuf, decp->outbuf_len); if (!_new_outbuf) { PyErr_SetString(PyExc_OSError, "failed to allocate outbuf"); - goto end_with_custom_error; + return NULL; } decp->outbuf = _new_outbuf; } @@ -469,7 +448,7 @@ _jxl_decoder_get_next(PyObject *self) { if (decp->status != JXL_DEC_FULL_IMAGE) { PyErr_SetString(PyExc_OSError, "failed to read next frame"); - goto end_with_custom_error; + return NULL; } bytes = PyBytes_FromStringAndSize((char *)(decp->outbuf), decp->outbuf_len); @@ -493,13 +472,6 @@ _jxl_decoder_get_next(PyObject *self) { decp->status ); PyErr_SetString(PyExc_OSError, err_msg); - -end_with_custom_error: - - // no need to deallocate anything here - // user can just ignore error - - return NULL; } PyObject * From e2105f3c7ee25518a908fbe6a14b4a292d7ce8b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Dec 2025 23:13:18 +1100 Subject: [PATCH 55/66] Improve wheel build speed --- .github/workflows/wheels-dependencies.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index 9759ccac596..ff58c0b374c 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -180,12 +180,12 @@ function build_jpegxl { local out_dir=$(fetch_unpack https://github.com/google/highway/archive/1.3.0.tar.gz) (cd $out_dir \ && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib $HOST_CMAKE_FLAGS . \ - && make install) + && make -j4 install) local out_dir=$(fetch_unpack https://github.com/libjxl/libjxl/archive/v$JPEGXL_VERSION.tar.gz) (cd $out_dir \ && cmake -DCMAKE_INSTALL_PREFIX=$BUILD_PREFIX -DCMAKE_INSTALL_LIBDIR=$BUILD_PREFIX/lib -DCMAKE_INSTALL_NAME_DIR=$BUILD_PREFIX/lib -DJPEGXL_ENABLE_SJPEG=OFF -DJPEGXL_ENABLE_SKCMS=OFF -DBUILD_TESTING=OFF $HOST_CMAKE_FLAGS . \ - && make install) + && make -j4 install) touch jpegxl-stamp } From 0b70b34f19dc8ec43da543f6d64426f1a8013a71 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 15 Dec 2025 20:49:18 +1100 Subject: [PATCH 56/66] Fix build on manylinux_2_28 x86_64 --- .ci/install.sh | 3 +++ .github/workflows/wheels-dependencies.sh | 25 +++++++++++----------- depends/install_libjxl.sh | 17 +++++++++++++++ docs/installation/building-from-source.rst | 4 +++- 4 files changed, 35 insertions(+), 14 deletions(-) create mode 100755 depends/install_libjxl.sh diff --git a/.ci/install.sh b/.ci/install.sh index 52b8214170c..ba6e753e23c 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -56,5 +56,8 @@ pushd depends && sudo ./install_raqm.sh && popd # libavif pushd depends && sudo ./install_libavif.sh && popd +# libjxl +pushd depends && sudo ./install_libjxl.sh && popd + # extra test images pushd depends && ./install_extra_test_images.sh && popd diff --git a/.github/workflows/wheels-dependencies.sh b/.github/workflows/wheels-dependencies.sh index ff58c0b374c..52b3804291d 100755 --- a/.github/workflows/wheels-dependencies.sh +++ b/.github/workflows/wheels-dependencies.sh @@ -321,19 +321,6 @@ function build { build_libpng build_lcms2 build_openjpeg - - webp_cflags="-O3 -DNDEBUG" - if [[ -n "$IS_MACOS" ]]; then - webp_cflags="$webp_cflags -Wl,-headerpad_max_install_names" - fi - webp_ldflags="" - if [[ -n "$IOS_SDK" ]]; then - webp_ldflags="$webp_ldflags -llzma -lz" - fi - CFLAGS="$CFLAGS $webp_cflags" LDFLAGS="$LDFLAGS $webp_ldflags" build_simple libwebp $LIBWEBP_VERSION \ - https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \ - --enable-libwebpmux --enable-libwebpdemux - build_brotli if [[ -n "$IS_MACOS" ]]; then @@ -356,6 +343,18 @@ function build { build_jpegxl fi fi + + webp_cflags="-O3 -DNDEBUG" + if [[ -n "$IS_MACOS" ]]; then + webp_cflags="$webp_cflags -Wl,-headerpad_max_install_names" + fi + webp_ldflags="" + if [[ -n "$IOS_SDK" ]]; then + webp_ldflags="$webp_ldflags -llzma -lz" + fi + CFLAGS="$CFLAGS $webp_cflags" LDFLAGS="$LDFLAGS $webp_ldflags" build_simple libwebp $LIBWEBP_VERSION \ + https://storage.googleapis.com/downloads.webmproject.org/releases/webp tar.gz \ + --enable-libwebpmux --enable-libwebpdemux } function create_meson_cross_config { diff --git a/depends/install_libjxl.sh b/depends/install_libjxl.sh new file mode 100755 index 00000000000..0b222a7e3a5 --- /dev/null +++ b/depends/install_libjxl.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +version=0.11.1 + +./download-and-extract.sh highway-1.3.0 https://github.com/google/highway/archive/1.3.0.tar.gz + +pushd highway-1.3.0 +cmake . +make -j4 install +popd + +./download-and-extract.sh libjxl-$version https://github.com/libjxl/libjxl/archive/v$version.tar.gz + +pushd libjxl-$version +cmake -DCMAKE_INSTALL_PREFIX=/usr -DJPEGXL_ENABLE_SJPEG=OFF -DJPEGXL_ENABLE_SKCMS=OFF -DBUILD_TESTING=OFF . +make -j4 install +popd diff --git a/docs/installation/building-from-source.rst b/docs/installation/building-from-source.rst index 971d961ba72..b9d0d172ac9 100644 --- a/docs/installation/building-from-source.rst +++ b/docs/installation/building-from-source.rst @@ -129,6 +129,8 @@ Many of Pillow's features require external libraries: To install libraqm, ``sudo apt-get install meson`` and then see ``depends/install_raqm.sh``. + To install libjxl, see ``depends/install_libjxl.sh``. + Build prerequisites for libavif on Ubuntu are installed with:: sudo apt-get install cmake ninja-build nasm @@ -166,7 +168,7 @@ Many of Pillow's features require external libraries: The easiest way to install external libraries is via `Homebrew `_. After you install Homebrew, run:: - brew install libavif libjpeg libraqm libtiff little-cms2 openjpeg webp + brew install jpeg-xl libavif libjpeg libraqm libtiff little-cms2 openjpeg webp If you would like to use libavif with more codecs than just aom, then instead of installing libavif through Homebrew directly, you can use From 52912c3d9f4ba2601b200561a2e430fd29dd4505 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 16 Dec 2025 19:52:55 +1100 Subject: [PATCH 57/66] Check for presence of jxl_threads --- setup.py | 5 +++-- src/_jpegxl.c | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index c952227566e..292f0f60393 100644 --- a/setup.py +++ b/setup.py @@ -783,7 +783,9 @@ def build_extensions(self) -> None: if _find_include_file(self, "jxl/encode.h") and _find_include_file( self, "jxl/decode.h" ): - if _find_library_file(self, "jxl"): + if _find_library_file(self, "jxl") and _find_library_file( + self, "jxl_threads" + ): feature.set("jpegxl", "jxl") if feature.want("imagequant"): @@ -1012,7 +1014,6 @@ def build_extensions(self) -> None: jpegxl = feature.get("jpegxl") if isinstance(jpegxl, str): - # jxl and jxl_threads are required libs = [jpegxl, jpegxl + "_threads"] self._update_extension("PIL._jpegxl", libs) else: diff --git a/src/_jpegxl.c b/src/_jpegxl.c index b812061f6ab..3bb3d7f2604 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -1,6 +1,5 @@ #define PY_SSIZE_T_CLEAN #include -#include #include "libImaging/Imaging.h" #include @@ -295,8 +294,8 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->status = JxlDecoderGetBoxType(decp->decoder, btype, JXL_TRUE); _JXL_CHECK("JxlDecoderGetBoxType"); - bool is_box_exif = !memcmp(btype, "Exif", 4); - bool is_box_xmp = !memcmp(btype, "xml ", 4); + int is_box_exif = !memcmp(btype, "Exif", 4); + int is_box_xmp = !memcmp(btype, "xml ", 4); if (!is_box_exif && !is_box_xmp) { // not exif/xmp box so continue continue; @@ -472,6 +471,7 @@ _jxl_decoder_get_next(PyObject *self) { decp->status ); PyErr_SetString(PyExc_OSError, err_msg); + return NULL; } PyObject * @@ -562,7 +562,6 @@ setup_module(PyObject *m) { return -1; } - // TODO(oloke) ready object types? PyObject *d = PyModule_GetDict(m); PyObject *v = PyUnicode_FromString(JpegXlDecoderVersion_str()); PyDict_SetItemString(d, "libjxl_version", v ? v : Py_None); From 7db30e0e9b1822087087c4e1cacb12a45f646e89 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Dec 2025 21:40:31 +1100 Subject: [PATCH 58/66] Removed TODO until sample image can be provided --- src/PIL/JpegXlImagePlugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 9a541606884..62a6f4fa512 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -43,8 +43,6 @@ def _open(self) -> None: self._n_frames = None if self.is_animated else 1 self._tps_dur_secs = tps_num / tps_denom if tps_denom != 0 else 1 self.info["duration"] = 1000 * tps_duration * (1 / self._tps_dur_secs) - - # TODO: handle libjxl time codes self.info["timestamp"] = 0 if icc := self._decoder.get_icc(): From daaf3b0b7bcb25f7e680e7a787682edf3312461c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Dec 2025 12:06:09 +1100 Subject: [PATCH 59/66] Added to list of read-only formats --- docs/handbook/image-file-formats.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 03ee96c0f00..0ac3978a209 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1569,6 +1569,31 @@ IPTC/NAA Pillow provides limited read support for IPTC/NAA newsphoto files. +JPEG XL +^^^^^^^ + +Pillow identifies and reads JPEG XL files. Requires libjxl version **0.9.0** or +greater. + +The :py:meth:`~PIL.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**duration** + The delay (in milliseconds) between each frame. + +**exif** + Raw EXIF data from the image. + +**icc_profile** + The ICC color profile for the image. + +**timestamp** + The time of the current frame. This is the sum of the duration of all previous + frames. + +**xmp** + Raw XMP data from the image. + MCIDAS ^^^^^^ From 96f0db3db9de0565970c37c4ff047853512ee5ab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Dec 2025 23:08:52 +1100 Subject: [PATCH 60/66] Added license and patents --- wheels/dependency_licenses/LIBJXL.txt | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 wheels/dependency_licenses/LIBJXL.txt diff --git a/wheels/dependency_licenses/LIBJXL.txt b/wheels/dependency_licenses/LIBJXL.txt new file mode 100644 index 00000000000..6f4e574e89b --- /dev/null +++ b/wheels/dependency_licenses/LIBJXL.txt @@ -0,0 +1,50 @@ +Copyright (c) the JPEG XL Project Authors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the JPEG XL project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of JPEG XL, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of JPEG XL. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of JPEG XL or any code incorporated within this +implementation of JPEG XL constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of JPEG XL +shall terminate as of the date such litigation is filed. From ffa84e56681c72300607ab97d0e23ad6bfa84961 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Dec 2025 19:18:01 +1100 Subject: [PATCH 61/66] Corrected ICC profile test --- Tests/test_file_jxl.py | 6 +- Tests/test_file_jxl_metadata.py | 12 +--- src/_jpegxl.c | 110 +++++++++++++------------------- 3 files changed, 49 insertions(+), 79 deletions(-) diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index be6c3568004..faefed00c1c 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -24,8 +24,7 @@ def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None: with pytest.raises(OSError): with pytest.warns(UserWarning, match="JXL support not installed"): - with Image.open("Tests/images/hopper.jxl"): - pass + Image.open("Tests/images/hopper.jxl") @skip_unless_feature("jpegxl") @@ -45,7 +44,6 @@ def test_read_rgb(self) -> None: assert im.mode == "RGB" assert im.size == (128, 128) assert im.format == "JPEG XL" - im.load() im.getdata() # generated with: @@ -58,7 +56,6 @@ def test_read_rgba(self) -> None: assert im.mode == "RGBA" assert im.size == (200, 150) assert im.format == "JPEG XL" - im.load() im.getdata() im.tobytes() @@ -74,7 +71,6 @@ def test_read_i16(self) -> None: assert im.mode == "I;16" assert im.size == (128, 64) assert im.format == "JPEG XL" - im.load() im.getdata() assert_image_similar_tofile( diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index d68d417917f..c8848351871 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -39,7 +39,7 @@ def test_read_exif_metadata() -> None: with Image.open("Tests/images/flower.jpg") as im_jpeg: expected_exif = im_jpeg.info["exif"] - # JPEG XL always returns exif without 'Exif\0\0' prefix + # JPEG XL always returns exif without "Exif\x00\x00" prefix assert exif_data == expected_exif[6:] @@ -53,14 +53,8 @@ def test_read_exif_metadata_without_prefix() -> None: def test_read_icc_profile() -> None: - with Image.open("Tests/images/flower2.jxl") as im: - assert im.format == "JPEG XL" - icc = im.info["icc_profile"] - - with Image.open("Tests/images/flower2.jxl") as im_jpeg: - expected_icc = im_jpeg.info["icc_profile"] - - assert icc == expected_icc + with Image.open("Tests/images/flower.jxl") as im: + assert "icc_profile" in im.info def test_getxmp() -> None: diff --git a/src/_jpegxl.c b/src/_jpegxl.c index 3bb3d7f2604..e9c6284d454 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -2,9 +2,7 @@ #include #include "libImaging/Imaging.h" -#include #include -#include #include #define _JXL_CHECK(call_name) \ @@ -25,13 +23,9 @@ _jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { pf->data_type = JXL_TYPE_UINT8; } - // this *might* cause some issues on Big-Endian systems - // would be great to test it - pf->endianness = JXL_NATIVE_ENDIAN; pf->align = 0; } -// TODO: floating point mode char * _jxl_get_mode(const JxlBasicInfo *bi) { if (bi->num_color_channels == 1 && !bi->alpha_bits) { @@ -41,19 +35,13 @@ _jxl_get_mode(const JxlBasicInfo *bi) { } if (bi->bits_per_sample == 8) { - // image has transparency if (bi->alpha_bits) { + // image has transparency if (bi->num_color_channels == 3) { - if (bi->alpha_premultiplied) { - return "RGBa"; - } - return "RGBA"; + return bi->alpha_premultiplied ? "RGBa" : "RGBA"; } if (bi->num_color_channels == 1) { - if (bi->alpha_premultiplied) { - return "La"; - } - return "LA"; + return bi->alpha_premultiplied ? "La" : "LA"; } } else { // image has no transparency @@ -78,8 +66,8 @@ typedef struct { uint8_t *jxl_data; // input jxl bitstream Py_ssize_t jxl_data_len; // length of input jxl bitstream - uint8_t *outbuf; - size_t outbuf_len; + uint8_t *output_buffer; + size_t output_buffer_len; uint8_t *jxl_icc; size_t jxl_icc_len; @@ -106,10 +94,10 @@ _jxl_decoder_dealloc(PyObject *self) { decp->jxl_data = NULL; decp->jxl_data_len = 0; } - if (decp->outbuf) { - free(decp->outbuf); - decp->outbuf = NULL; - decp->outbuf_len = 0; + if (decp->output_buffer) { + free(decp->output_buffer); + decp->output_buffer = NULL; + decp->output_buffer_len = 0; } if (decp->jxl_icc) { free(decp->jxl_icc); @@ -139,7 +127,6 @@ _jxl_decoder_dealloc(PyObject *self) { } // sets input jxl bitstream loaded into jxl_data -// has to be called after every rewind void _jxl_decoder_set_input(PyObject *self) { JpegXlDecoderObject *decp = (JpegXlDecoderObject *)self; @@ -184,28 +171,28 @@ PyObject * _jxl_decoder_new(PyObject *self, PyObject *args) { PyBytesObject *jxl_string; + // parse one argument which is a string with jxl data + if (!PyArg_ParseTuple(args, "O", &jxl_string)) { + return NULL; + } + JpegXlDecoderObject *decp = NULL; decp = PyObject_New(JpegXlDecoderObject, &JpegXlDecoder_Type); - decp->mode = NULL; decp->jxl_data = NULL; decp->jxl_data_len = 0; - decp->outbuf = NULL; - decp->outbuf_len = 0; + decp->output_buffer = NULL; + decp->output_buffer_len = 0; decp->jxl_icc = NULL; decp->jxl_icc_len = 0; decp->jxl_exif = NULL; decp->jxl_exif_len = 0; decp->jxl_xmp = NULL; decp->jxl_xmp_len = 0; + decp->mode = NULL; // used for printing more detailed error messages char *jxl_call_name; - // parse one argument which is a string with jxl data - if (!PyArg_ParseTuple(args, "S", &jxl_string)) { - return NULL; - } - // this data needs to be copied to JpegXlDecoderObject // so that input bitstream is preserved across calls const uint8_t *_tmp_jxl_data; @@ -216,7 +203,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { (PyObject *)jxl_string, (char **)&_tmp_jxl_data, &_tmp_jxl_data_len ); - // here occurs this copying (inefficiency) decp->jxl_data = malloc(_tmp_jxl_data_len); memcpy(decp->jxl_data, _tmp_jxl_data, _tmp_jxl_data_len); decp->jxl_data_len = _tmp_jxl_data_len; @@ -250,7 +236,6 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decoder_loop_skip_process: - // there was an error at JxlDecoderProcessInput stage if (decp->status == JXL_DEC_ERROR) { jxl_call_name = "JxlDecoderProcessInput"; goto end; @@ -262,11 +247,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { _jxl_get_pixel_format(&decp->pixel_format, &decp->basic_info); decp->mode = _jxl_get_mode(&decp->basic_info); - - continue; - } - - if (decp->status == JXL_DEC_COLOR_ENCODING) { + } else if (decp->status == JXL_DEC_COLOR_ENCODING) { decp->status = JxlDecoderGetICCProfileSize( decp->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &decp->jxl_icc_len ); @@ -285,34 +266,28 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { decp->jxl_icc_len ); _JXL_CHECK("JxlDecoderGetColorAsICCProfile"); - - continue; - } - - if (decp->status == JXL_DEC_BOX) { - char btype[4]; - decp->status = JxlDecoderGetBoxType(decp->decoder, btype, JXL_TRUE); + } else if (decp->status == JXL_DEC_BOX) { + char box_type[4]; + decp->status = JxlDecoderGetBoxType(decp->decoder, box_type, JXL_TRUE); _JXL_CHECK("JxlDecoderGetBoxType"); - int is_box_exif = !memcmp(btype, "Exif", 4); - int is_box_xmp = !memcmp(btype, "xml ", 4); + int is_box_exif = !memcmp(box_type, "Exif", 4); + int is_box_xmp = is_box_exif ? 0 : !memcmp(box_type, "xml ", 4); if (!is_box_exif && !is_box_xmp) { // not exif/xmp box so continue continue; } - uint64_t cur_compr_box_size; - decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &cur_compr_box_size); + uint64_t compressed_box_size; + decp->status = JxlDecoderGetBoxSizeRaw(decp->decoder, &compressed_box_size); _JXL_CHECK("JxlDecoderGetBoxSizeRaw"); uint8_t *final_jxl_buf = NULL; Py_ssize_t final_jxl_buf_len = 0; - // cur_box_size is actually compressed box size - // it will also serve as our chunk size do { uint8_t *_new_jxl_buf = - realloc(final_jxl_buf, final_jxl_buf_len + cur_compr_box_size); + realloc(final_jxl_buf, final_jxl_buf_len + compressed_box_size); if (!_new_jxl_buf) { PyErr_SetString(PyExc_OSError, "failed to allocate final_jxl_buf"); goto end; @@ -320,14 +295,16 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { final_jxl_buf = _new_jxl_buf; decp->status = JxlDecoderSetBoxBuffer( - decp->decoder, final_jxl_buf + final_jxl_buf_len, cur_compr_box_size + decp->decoder, + final_jxl_buf + final_jxl_buf_len, + compressed_box_size ); _JXL_CHECK("JxlDecoderSetBoxBuffer"); decp->status = JxlDecoderProcessInput(decp->decoder); size_t remaining = JxlDecoderReleaseBoxBuffer(decp->decoder); - final_jxl_buf_len += (cur_compr_box_size - remaining); + final_jxl_buf_len += compressed_box_size - remaining; } while (decp->status == JXL_DEC_BOX_NEED_MORE_OUTPUT); if (is_box_exif) { @@ -409,8 +386,8 @@ _jxl_decoder_get_next(PyObject *self) { while (decp->status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) { decp->status = JxlDecoderProcessInput(decp->decoder); - // this should only occur after rewind if (decp->status == JXL_DEC_NEED_MORE_INPUT) { + // this should only occur after rewind _jxl_decoder_set_input((PyObject *)decp); _JXL_CHECK("JxlDecoderSetInput") } else if (decp->status == JXL_DEC_FRAME) { @@ -420,29 +397,30 @@ _jxl_decoder_get_next(PyObject *self) { } } - size_t new_outbuf_len; + size_t new_output_buffer_len; decp->status = JxlDecoderImageOutBufferSize( - decp->decoder, &decp->pixel_format, &new_outbuf_len + decp->decoder, &decp->pixel_format, &new_output_buffer_len ); _JXL_CHECK("JxlDecoderImageOutBufferSize"); // only allocate memory when current buffer is too small - if (decp->outbuf_len < new_outbuf_len) { - decp->outbuf_len = new_outbuf_len; - uint8_t *_new_outbuf = realloc(decp->outbuf, decp->outbuf_len); - if (!_new_outbuf) { - PyErr_SetString(PyExc_OSError, "failed to allocate outbuf"); + if (decp->output_buffer_len < new_output_buffer_len) { + decp->output_buffer_len = new_output_buffer_len; + uint8_t *new_output_buffer = + realloc(decp->output_buffer, decp->output_buffer_len); + if (!new_output_buffer) { + PyErr_SetString(PyExc_OSError, "failed to allocate buffer"); return NULL; } - decp->outbuf = _new_outbuf; + decp->output_buffer = new_output_buffer; } decp->status = JxlDecoderSetImageOutBuffer( - decp->decoder, &decp->pixel_format, decp->outbuf, decp->outbuf_len + decp->decoder, &decp->pixel_format, decp->output_buffer, decp->output_buffer_len ); _JXL_CHECK("JxlDecoderSetImageOutBuffer"); - // decode image into output_buffer + // decode image into output buffer decp->status = JxlDecoderProcessInput(decp->decoder); if (decp->status != JXL_DEC_FULL_IMAGE) { @@ -450,7 +428,9 @@ _jxl_decoder_get_next(PyObject *self) { return NULL; } - bytes = PyBytes_FromStringAndSize((char *)(decp->outbuf), decp->outbuf_len); + bytes = PyBytes_FromStringAndSize( + (char *)(decp->output_buffer), decp->output_buffer_len + ); ret = Py_BuildValue("SIi", bytes, fhdr.duration, fhdr.is_last); From e24b3ebaab164a3dbd7b22263c328136c10554ee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Dec 2025 23:28:35 +1100 Subject: [PATCH 62/66] Added support for 1 mode images --- Tests/images/hopper.jxl | Bin 3409 -> 0 bytes Tests/images/hopper_jxl_bits.ppm | Bin 49167 -> 0 bytes Tests/images/{ => jxl}/flower.jxl | Bin Tests/images/{ => jxl}/flower2.jxl | Bin Tests/images/jxl/hopper.jxl | Bin 0 -> 5998 bytes Tests/images/jxl/hopper_bw_500.jxl | Bin 0 -> 92423 bytes Tests/images/jxl/hopper_gray.jxl | Bin 0 -> 5202 bytes Tests/images/{ => jxl}/iss634.jxl | Bin Tests/images/{ => jxl}/transparent.jxl | Bin Tests/images/jxl/unknown_mode.jxl | Bin 0 -> 205 bytes Tests/test_file_jxl.py | 70 +++++++++---------------- Tests/test_file_jxl_animated.py | 14 ++--- Tests/test_file_jxl_metadata.py | 14 ++--- src/PIL/JpegXlImagePlugin.py | 3 +- src/_jpegxl.c | 18 ++----- src/libImaging/Unpack.c | 9 ++++ 16 files changed, 54 insertions(+), 74 deletions(-) delete mode 100644 Tests/images/hopper.jxl delete mode 100644 Tests/images/hopper_jxl_bits.ppm rename Tests/images/{ => jxl}/flower.jxl (100%) rename Tests/images/{ => jxl}/flower2.jxl (100%) create mode 100644 Tests/images/jxl/hopper.jxl create mode 100644 Tests/images/jxl/hopper_bw_500.jxl create mode 100644 Tests/images/jxl/hopper_gray.jxl rename Tests/images/{ => jxl}/iss634.jxl (100%) rename Tests/images/{ => jxl}/transparent.jxl (100%) create mode 100644 Tests/images/jxl/unknown_mode.jxl diff --git a/Tests/images/hopper.jxl b/Tests/images/hopper.jxl deleted file mode 100644 index d89d3c267fef793855eb3d7fb7bec3a10e999e83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3409 zcmV-X4X*P43SU4%<=!fa0Tm^=YaRfA3YAgD4PMUmeEFfnV%o-ahPK+au!Iwo-ReRs zCJw$AfP?pykdRQ)HP6fC4&a3}#F&aqL_|c#MnyzNW3_@}WMiaaRAa1StYfrdv}1H* ztYdUzv}0^zEMw#`*4egWv}0^ztYd6rlw#Dyn2hl<#$c7_&_v*b0MwLK1X`n+1nZBY zpq;Hxu32r!C9F}@pGCrh29ldDZQG6L3mVV*SK6g2bLR1P zkptv(lKi+GE$@#aShv(3dBIwAQg~-& ziq?bcA!89_Ad3P%i2jPu*KNtfR{aqWp&Z8<&M$jwyb1)`Zm)x99~KM&&`jI$v(Q|> zYjE1@dkWk5m^$-s+<%gfQD;hJX?2|sO(lM$RRT94#7|sBa{NPOTc(4 zC<9}JMh~_V|GcqeFv4U^2SR^burwyAaQ|hKN|YSm_l0$ya9x^DMT>{adZB?WB;RWQ zZH+5z@wfUMY$V9AZq{$ZfyuP--8ihX--T^$W+C-WOwW<~kgsMkP{jXC&Ek&H#E@O* zle!F06SqE0yV%esovNLLNx&UY_)CvrXyvucW=&u9ky;Fk!%=77rrm0VY4BhTcsCC+DXqfvsId zu_$37wi!?v%OOf+gw0)X>kT*24sSpHQFxamWxvTfg1D?^%wf(|C9dr#wqI{2KfEu2A20{^eZd{P6Wv$ z|8$S;y#NO*`iI2E5_$Dj@iMK91NHM=bGK%yvdWfH{OI^{aeHwa{p?f_euWeitUKU} zy!-s$_`kR^vTWOK+p3b>O3X}wsGjQq?kX9#{27Y%at`$vT~$en!!OaJYbu#uCldB`Ay>EF_c>h zTtVV;__rqO1XiFn1795kBw{z2I3u!mgx8BJ9dTkxU>%gJNG{~pq30}L=MjRGwu-Ig z8c#Ry>v{Bg#!WCzzb9%m2w2Hdb+O?^D!sKZNHPdKgkcNb{G-ZvO2cLJMk|IF7)Fue zCN|6Gv9S)t3*RBbZ zJ!|19EQZy#h`Aq78^n_@%|#FM;$o+KKQ2R;??ZJw7u6d44f_yI=GRpZ8mkX3&OvZS z*4eqyRP(O#pRa(R&`D1yv=FjZ1Zz`KmE_;{)}hr99zvp#J7p}pn(yGqk76|+)QDBURy6`5WN{&Nsbjgl5U z6~?NcbXW}&X+RQ#m``yO%fKrS|9;+iOznr=AJzx%$HGR>H%TZwxKtz_3KfOQBO5;# z?C5Cp3p0^7l51ishCU0LvI;g>Cm$qU59#q|*58od(MVR;*0Q7?2eCONpZ{wa`Ecx{ z z&l$LYMz?IHV<1 z#l~(aU@&TY5PdN(9@)d-R)8je#J%x>6NO=s#| zKX2SKs5t!qIFp|s>3wfoW0mdOceGrvGz1(no?DF04k(C1v=$wd64+kOf(|~KQ?b0y zo#B)#5S+$!d>TS_-4Nmf;E2-6tso1XE9$|Ak2)p3dKO&w>E5)yivQgsg{|-)n425> zx&-!cWIC|cUvx8p%HL}5H;iEdrQ3B0?=x(te28{{?K4(DaBcxN%IeD3-kQL2WC!m4^n`3+R3D1Lo`kwy8}b4}$JF zd1PD~X32_`aSJ?d6pEBgUKihg6Ltv6zANK8ZPYObNPuQZ1ZU^yY(p2%lF>4rHDdF|U^HAIXBmTO}CD1D;}R0HPkM_O(B^=hQJiyZYKRe`1w^DqbU8B&7i<5hadip1wdaXLz+t z1?xANg>5uBWkZL)Ma>|aF!HVVpD<4*lH?0)3O_{?hx)cPzPK=2#GBKvgmD5$o;nva z-E5h{(0w}yMT==&LenjS;fIKj(eJFJiJ?>rWm0m+cLUzsby5ASMDh@}OaO`aa-=k;d6nR^o5FueTqqns_yV?&iTZ(JCn6M(ODlSkT00>9Orc{K0 z!w;IQ=2fGnF7`hkl3S(Pr7QBGKT3xhBx4A$taIIz7+xDro*C90?~^}Z zJ+5uL=Y@)ooUpE%{_;q%(-~{{`%P7M$;z>BH6>yEzRKhh!d(B)1=ZiqBWIdLK~af7 zUVNE6ydN67ne(55R)nV#KUhe75u4_<n3Fqf7F>`%y;yc&5jXcEA`hVMgBAp!J)$MK{lDvz` z%cK$uStO|j$7rU%^E~B^jq-C?ci=KaAAavXdNJe}e-vB9Dp_B6E8I7Ba7}ev-d>y> z1a4^W7n8jEF1aQHmyYmm9sx(^prMos`(UE!<0Bvg2J8kM;ivNQ7p7!dpDPVkNUJmE zWDO2NnE)Nmes|6G-7c{h{{FAG;ph7mG};Uc+XaA({Z2rtP->%{vR1oY+9%Yq->HIdkl`>Cgby>1DL#rU;NjVW%Hi?ez9rr~IMj}H diff --git a/Tests/images/hopper_jxl_bits.ppm b/Tests/images/hopper_jxl_bits.ppm deleted file mode 100644 index 881aca33369b3bdb1e0ae8671777188d89734bc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49167 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 LxjMIObL;;F0-tO! diff --git a/Tests/images/flower.jxl b/Tests/images/jxl/flower.jxl similarity index 100% rename from Tests/images/flower.jxl rename to Tests/images/jxl/flower.jxl diff --git a/Tests/images/flower2.jxl b/Tests/images/jxl/flower2.jxl similarity index 100% rename from Tests/images/flower2.jxl rename to Tests/images/jxl/flower2.jxl diff --git a/Tests/images/jxl/hopper.jxl b/Tests/images/jxl/hopper.jxl new file mode 100644 index 0000000000000000000000000000000000000000..3be85de648c283f0a0e46bb0965b8b09aba09e56 GIT binary patch literal 5998 zcmV-!7m?@y000b1SWF-d3Wo{+000zbba`-Ucx)g50001Lcx)g5000qccx-S000030 z3SU4Z0002eYGQI^K!4i+f*=Sj0|MX#Ak-)jQYHZa0F9Lbae(oELdF=Oj5N|ofRLlo z1a*)e#yw=9!GxiPlkH;$9CFY_4jTB8WsW***kcXdKkmp$7Fz1Sq5oINB>(_`B;=@u z{}(dCm=}~006>5oraH(lHPBG$9x~W)0}eXu(1ZUjFEZr7j~sK} z0K{^45B7j(SqlP5ek-BYdT47)HN}X64LD#Av0T=(+l>F9&8k5}&|CNjwziY3Eb|N} zAj7)MF#!Mo7HevFY;b@800H1z0uTUUNeE~NXb5Qj{|*2E7yt+W0RR91004l1C5cps zySW|$piI`{-aw&HN>WfzxCMusn~X8$3=&WXbwor|Y;-iE?vVhH$r!T2CP=EP(2`UJ zg)WDX7@AJJU0n#+f+M+YWb}YRpKIzDw^2t5Pq|W;t0x*}01i{BM!mOk2+HV;EU1i~ z-}h!s%r28q;h_Q%?xdflk*JyqAoo4udQpbvh-ET(Gr}F7wQpsc;Kq%47$Lz-{=yIg z19K|1?9^fXAY9sN5mLcrNBj2yUbgv%!B%p2njma(7sDzIIPx+ZE{`-^`aR-&1^K3| z*<@=DCaqx;*~A&Zvuk0bv*sm9KRz5GyoLsb#_er}>1uepY+8tX6&w`yB7 z1U?siY{;&f6$hEp?oT$)$8N?h@!R%p(NOTA-OR0^mP~Z0vG~b0l+07cqC{n0T2pM zw@ppr7M&c>wHI5=q|i^Jij=&*?58S5&YjZ5wc16R^RTXxCB)FVV$hs!VFkD(&E?mbvZ` z-H+<_orZN))hcaZCGcSIQszzN>RDXMop zTNF$(te$s&v7LFD(fZ33EKi`qyV2z;N|H3b5^q((>RT#n?sHmH(^q7>IU*v-wZt+c zYj%8>!&~WC$|_DeCJy?$dsc%igBQ?s3H{0n1fQr6M6HB@{EJ?wG*%9PZuINvFK!{| zg@RTNI6&D(I`jP5b}J@8Pf&$A2^~bD8_lKGiC+}eFrY>-nv}IE_sL3-=iHC_bcpCv z#V+4Jf31uH3&bfeWv#~2m;2IZ*p)lwrm1k@VVwIp`8S$*KyiOsk=hL7wzZsU#+-ty zHXa|LRzYKN)U-%rQJg}o;{358lo*DfuZdc$&R50CFFXm}kpLcBz?bTM zpCjvFzY1~=y|4R1E%|WXK<8LvRu(q5owXef3YmKFz59I0&C%Sp5ST}5h8G(}{CsrT zbhC81tPlVA*3yRocDpx{=b+QSL2@L30QDUJ59X5>^{3U)*dz5oy#sfoPqEo})PQ*D|bo5n?aW+q{<<6i@n zgm;Vk;wOahhQ5&qQ1!{eK0E$n8)k|^XMNqgvsacr}p6Ohc@oFX&q_@>_F#)@?|SLtvI;Q z1$kq5`EZcvlwbHpDEmek*mO{z z;2+uuh9L?MbFxlLV)1P=I6`0;Psy%u7dq*G{3cS$bYc0*1|`nH{RP!+Omi#!4B2Ps z&TJkK7;0d{N9(rQ8i~8+P3V!m)ZHgJ79(Rj>AoT@ZyO0-8m9SeJa|r#T4hB4lvXq$ z7Zm}-oAfFV7NAvZtW7B}9g|3fqMzY2eGUrFj^JVN)~P$RUlKMbSM$#%3yCosqf| zIeYpJb7uXGb#wy@Az{E9!qj|1s)IJ1N@3*+$~KimGU=S>vpoVYvB~1Jr^ja2LL7AJ_7Tt0<6& zknaSKZTN?8vD6qg3r2WrDQ5Z;z=JB0pz-kf7hL^A*wJrb~%1a00fo`3B!<$XZ`H z$?06=tu2}siSm4fKoO?&XFi&CK|6V)4B|F7SLBh4cV^`-(8GzFPTwtni-qq$=L7|c zpIRhDu7(#wuLYl$KjH2NMLugurY zN%p!FgK}BZJ|TzZ3K?N^aB19K$p%`B)=UPn261`#c&kMQstHR9L@I9V6AAXAvj{p0 z;?53v6Y-0ej<#u3i*(8m6#1cvx)NiU8YN>F6j2MtaBwAjTjr*$3~kmiX6p8{+hW$u z$rVrj@^SEH9_0hpnfAdqk%7ZdRLh8^UiT2PD607mdRtKe7N++wtvAO&irW~z}CA9wW zxBJMx;WeOg8Usj!uL;@VIg50bo$MfBOGt%t#}G|TeLXj1m*UOfv#kB7LUBm(BaI|f zKd?a%7338#Z`C^?018D*y@(DEp?Q8b_HsnBElbhR13rO?aPTcwyyR`G}yMYWpM z>;}5HRWa=bfZYoDYSc?r-p)hkoWx zZa?aIZX4odZpyTTw=KhNMf~SM^k8)Tn8r@CL^zx@;TZ2cO{GcOJbE=9x=e(Z++}0l z`)X^8NFZsL{TcWD1%z4ug5q)XYHWp9qHqLk4WdUy5r9~7Kq`0w*dU@8qTEGrNbIyF zpvlZ~P9{XO+MXRMu*huGU&^jQKl;gvqtZa{ERka2Nqr#+pkm!CmY;io$~y?6JTg>d zv{w$XM~9n}AE8#>(fF0;(jy51-#N;Toqs5%{W=U7JheZC)>Dl+)OK|I?om+O-UuOo z(k-Z(1rb=$pVl!rC9!=*s;*Z?mL66=%~UABvA$CzN9CazoJ5f7+)@;+M4Dd_w4|6! zrwxF|ocxD$07WuVMMfYd)>cX7XreUppVpLGouR7AImv)ZMJLf@**3LCW)wq)n@!rt za4hBNEwcchrvP6<<|qNVJtWVi8RS$)BwJ*Km@ezWxBf>r#86Ao7UhI%>S1*;Vu%`v~p{p=xFCjQUO@&4! zz76Fc{(%>=&e~syz5>zf8t>@1&4-l0sZ_pj{rqZr=##|&M}p2<+~MhB>AW=+(OD62 z_AgnW`PW3Z;3GgE@~8R3?>-WPpc_S?t-}F8@#pLTh<|{|f1DcYX8A=kiwqUXc;9^t zk~N45Y+9w0?k{_l-_!g7Qb_>dvv~}{G^=S*BDcsJaZrS6&7LylqM0CU*}%|}H`F_Pa(Qjx zn9=&UCRVUM%bSkKv5=f9YbIT6G?Q#c&dIpD4_hiObIgOat3?>C+$buE8zxdsP4zm_ zA>;RoZYabF8?&a`@%PCZ|H5N0nl_CF_pW0?sLwZ6Tl#@9OGj8H0YUw{g^Y%{Di2j2 zNtfxv5BHGc7Y()Xqu$A>vcLQk^KSX047HLW@(|>eikGz`9F)IiufW|vNe$q5!gf>f zfItA2WwT3`b@$hs`7g?9oSw}%A#+`I2VZ0I2aNiH~ixC!ASWgbl82fMpIO$9Rc{} zy>llBk2z)FSwPIZWP@dZ9yy>F7tzpv?t7YS_MDYzFi};C{9S_mb!VfA00YAOiVjUx z;i+R7GP^cPgjYWhO}@h58y@ZUA`^7kE;Ux-5U^KW2~Glc7OK{;qxjR2?%h>6wgm{x zEd`sD%<@Ht=vdM3Wmw!pW())XM)aa~$;iBE{^YJhyMC?|c1UC)LrbQ(@6CW}87i9q z0!9T}vYyv}50nV|Mb?ze_6VIp;YBm^U`m-FHT5Q=cCsRwwn z=Dn^Xz7m$oV8Wg0)M4`qZMrYpVvTPPjnI#MX~Rbxfg%iV4%--vxw4UlAMzeW`?9p( zV_rgE{ejGS2CKDh&?0@k9d%b&4#;sN9A|&SmpVZ58gZeI-j-x@mkS|f%b>~V$>n%( zVRibPnU^O-s>QYkjZk@Og)oZ*j;ONw!t{IBuUA7v)_<}Xy9nw1$E+U-Xq_bU)j$Yo z**+)?^P}6!cp#7Bq$>UK2G4I1G))b9cuUoRFNzy|H|kO`fcjD~bedHj1^R5$0;!Jo zUeVAjAo-1sX(E8v{zTWLnpKr&V?VWwT3?L~w_*|o<3I=`4cl{F6DVyNoC zPA<`Q0;@%FbGUpO<(J5Ug8fsg4ojUu7{~HZSU~y$)JT@9X6+2Rkjv;+T7qmm>xz8d zLj-OAT-Uuu$2>>ny`lvDYlmOtqp5z@+qp_0G}dBs?q#OnH0FhR%CbnDJzG!4oB_m8 z>L2&^{D#&g-fSJr1u^5b<-28>$jq%!Ghi{C(mi8EN^K1}pRk{LmiYI~rIq#=d zmt`ChyF9=YqZowKYy*@64wVorY0$G0QbJ~SQn|r_t8~y!w%UKX>lYq;7ywE~!gprG z`B0YEFUwEbB@%Kha#!kw5D(qQud!XFN^3;h-ju?$j-ckH3hBU^Bg*a$R)t3TNi$_e zqRg)bx7v`Rw{?W0b9i?3i#77*Wa5vw`Jl;ibG6bC*U^J^-@Ey$I)GoWEr4RhED~1fe!m>S zWx?HU8;BHn4!|aNc3K3}c|_>vYo~P0!A3|#r>s)TSrh7+#Z&3pOh;C!1?#U~(37%@ z7m}+hI%h*!8C!nM3cKqIIYj^kKP`TlqWqsOhU1fnK4bkqJ=#pmt<<2#x{Gz)W08K{ zFmY!ejav@;6XNjs`sZf0d;dNE`kxTIkKHuigh3g*<#M(iF(IyxXG%j{ zAgE~|O#dt(GC}B&$3kQMDPW@lpcgsdf4V?vnCa42UAUJMfUowTij&h%)YDsE?UAuHmXFhYNjQaUIYPLy5+by&>P(tnsOMVCRS>1T zl~uhR@U-c_8iFV$d0F_VtzH0ycZz3hGOZd@FN zmOZh0KUM%B`grm5F zhB1&n78u?ZTLkcd1LJ`%+;o)v1z{=Rf{9J{^>|D=|JFP;Era*;gyf<(f^E`ISRg8q z$lAR<3t3aTU^O7&9O{-cHy<5*<-z(+97M}b?x&F^u=3c4VSiFd0Q+V$M!uQ=UNQRxHd;eq~5|n-_t(WTuz^fCtP@)Bf z9R*6yyws?c5)9~D&@An&%tK2S@iWoV%-`(j2R47B*U||2n_1%C075i&3bj^paW>gy c>9iKI_F1U=&;Sh#9c&cT2@xvcZn+TP7%Pudp#T5? literal 0 HcmV?d00001 diff --git a/Tests/images/jxl/hopper_bw_500.jxl b/Tests/images/jxl/hopper_bw_500.jxl new file mode 100644 index 0000000000000000000000000000000000000000..79ad8883f0d0887c7457fb665536f9639221b27c GIT binary patch literal 92423 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

V5qz8P^H# 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

iYHF)6*e+GsT6FrA-6$%G%vd9ENJZx3dupjt4S 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jxl.py b/Tests/test_file_jxl.py index faefed00c1c..86e718a05c5 100644 --- a/Tests/test_file_jxl.py +++ b/Tests/test_file_jxl.py @@ -1,10 +1,11 @@ from __future__ import annotations +import os import re import pytest -from PIL import Image, JpegXlImagePlugin, features +from PIL import Image, JpegXlImagePlugin, UnidentifiedImageError, features from .helper import assert_image_similar_tofile, skip_unless_feature @@ -13,10 +14,6 @@ except ImportError: pass -# 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, monkeypatch: pytest.MonkeyPatch) -> None: @@ -24,7 +21,7 @@ def test_unsupported(self, monkeypatch: pytest.MonkeyPatch) -> None: with pytest.raises(OSError): with pytest.warns(UserWarning, match="JXL support not installed"): - Image.open("Tests/images/hopper.jxl") + Image.open("Tests/images/jxl/hopper.jxl") @skip_unless_feature("jpegxl") @@ -34,53 +31,34 @@ def test_version(self) -> None: assert version is not None assert re.search(r"\d+\.\d+\.\d+$", version) - def test_read_rgb(self) -> None: - """ - Can we read an RGB mode JPEG XL file without error? - Does it have the bits we expect? - """ - - with Image.open("Tests/images/hopper.jxl") as im: - assert im.mode == "RGB" - assert im.size == (128, 128) - assert im.format == "JPEG XL" - im.getdata() - - # generated with: - # djxl hopper.jxl hopper_jxl_bits.ppm - assert_image_similar_tofile(im, "Tests/images/hopper_jxl_bits.ppm", 1) - - def test_read_rgba(self) -> None: - # Generated with `cjxl transparent.png transparent.jxl -q 100 -e 8` - with Image.open("Tests/images/transparent.jxl") as im: - assert im.mode == "RGBA" - assert im.size == (200, 150) + @pytest.mark.parametrize( + "mode, test_file", + ( + ("1", "hopper_bw_500.png"), + ("L", "hopper_gray.jpg"), + ("I;16", "jxl/16bit_subcutaneous.cropped.png"), + ("RGB", "hopper.jpg"), + ("RGBA", "transparent.png"), + ), + ) + def test_read(self, mode: str, test_file: str) -> None: + with Image.open( + "Tests/images/jxl/" + + os.path.splitext(os.path.basename(test_file))[0] + + ".jxl" + ) as im: assert im.format == "JPEG XL" - im.getdata() + assert im.mode == mode - im.tobytes() + assert_image_similar_tofile(im, "Tests/images/" + test_file, 1.9) - assert_image_similar_tofile(im, "Tests/images/transparent.png", 1) - - 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 im: - assert im.mode == "I;16" - assert im.size == (128, 64) - assert im.format == "JPEG XL" - im.getdata() - - assert_image_similar_tofile( - im, "Tests/images/jxl/16bit_subcutaneous.cropped.png", 1 - ) + def test_unknown_mode(self) -> None: + with pytest.raises(UnidentifiedImageError): + Image.open("Tests/images/jxl/unknown_mode.jxl") def test_JpegXlDecode_with_invalid_args(self) -> None: """ Calling decoder functions with no arguments should result in an error. """ - with pytest.raises(TypeError): _jpegxl.JpegXlDecoder() diff --git a/Tests/test_file_jxl_animated.py b/Tests/test_file_jxl_animated.py index ebb342d3e78..f4283a71ea6 100644 --- a/Tests/test_file_jxl_animated.py +++ b/Tests/test_file_jxl_animated.py @@ -12,17 +12,17 @@ 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: + with Image.open("Tests/images/jxl/hopper.jxl") as im: assert im.n_frames == 1 assert not im.is_animated - with Image.open("Tests/images/iss634.jxl") as im: + with Image.open("Tests/images/jxl/iss634.jxl") as im: assert im.n_frames == 41 assert im.is_animated def test_duration() -> None: - with Image.open("Tests/images/iss634.jxl") as im: + with Image.open("Tests/images/jxl/iss634.jxl") as im: assert im.info["duration"] == 70 assert im.info["timestamp"] == 0 @@ -43,17 +43,17 @@ def test_seek() -> None: assert im1.is_animated # Traverse frames in reverse, checking timestamps and durations - total_dur = 0 + total_duration = 0 for frame in reversed(range(im1.n_frames)): im1.seek(frame) im2.seek(frame) assert_image_equal(im1.convert("RGB"), im2.convert("RGB")) - total_dur += im1.info["duration"] + total_duration += im1.info["duration"] assert im1.info["duration"] == im2.info["duration"] assert im1.info["timestamp"] == im1.info["timestamp"] - assert total_dur == 8000 + assert total_duration == 8000 assert im1.tell() == 0 assert im2.tell() == 0 @@ -65,7 +65,7 @@ def test_seek() -> None: def test_seek_errors() -> None: - with Image.open("Tests/images/iss634.jxl") as im: + with Image.open("Tests/images/jxl/iss634.jxl") as im: with pytest.raises(EOFError, match="attempt to seek outside sequence"): im.seek(-1) diff --git a/Tests/test_file_jxl_metadata.py b/Tests/test_file_jxl_metadata.py index c8848351871..e9f25c3eedb 100644 --- a/Tests/test_file_jxl_metadata.py +++ b/Tests/test_file_jxl_metadata.py @@ -27,7 +27,7 @@ def test_read_exif_metadata() -> None: - with Image.open("Tests/images/flower.jxl") as im: + with Image.open("Tests/images/jxl/flower.jxl") as im: assert im.format == "JPEG XL" exif_data = im.info["exif"] @@ -44,7 +44,7 @@ def test_read_exif_metadata() -> None: def test_read_exif_metadata_without_prefix() -> None: - with Image.open("Tests/images/flower2.jxl") as im: + with Image.open("Tests/images/jxl/flower2.jxl") as im: # Assert prefix is not present assert im.info["exif"][:6] != b"Exif\x00\x00" @@ -53,12 +53,12 @@ def test_read_exif_metadata_without_prefix() -> None: def test_read_icc_profile() -> None: - with Image.open("Tests/images/flower.jxl") as im: + with Image.open("Tests/images/jxl/flower.jxl") as im: assert "icc_profile" in im.info def test_getxmp() -> None: - with Image.open("Tests/images/flower.jxl") as im: + with Image.open("Tests/images/jxl/flower.jxl") as im: assert "xmp" not in im.info if ElementTree is None: with pytest.warns( @@ -70,7 +70,7 @@ def test_getxmp() -> None: xmp = im.getxmp() assert xmp == {} - with Image.open("Tests/images/flower2.jxl") as im: + with Image.open("Tests/images/jxl/flower2.jxl") as im: if ElementTree is None: with pytest.warns( UserWarning, @@ -105,10 +105,10 @@ def get_xmp(self) -> None: monkeypatch.setattr(JpegXlImagePlugin, "_jpegxl", _mock_jpegxl) - with Image.open("Tests/images/hopper.jxl") as im: + with Image.open("Tests/images/jxl/hopper.jxl") as im: assert "exif" not in im.info def test_read_exif_metadata_empty() -> None: - with Image.open("Tests/images/hopper.jxl") as im: + with Image.open("Tests/images/jxl/hopper.jxl") as im: assert im.getexif() == {} diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index 62a6f4fa512..c4c2adac995 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -55,7 +55,8 @@ def _open(self) -> None: self.info["exif"] = exif[exif_start_offset + 4 :] if xmp := self._decoder.get_xmp(): self.info["xmp"] = xmp - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] + rawmode = "L" if self.mode == "1" else self.mode + self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, rawmode)] @property def n_frames(self) -> int: diff --git a/src/_jpegxl.c b/src/_jpegxl.c index e9c6284d454..a91a8c34e01 100644 --- a/src/_jpegxl.c +++ b/src/_jpegxl.c @@ -14,21 +14,16 @@ void _jxl_get_pixel_format(JxlPixelFormat *pf, const JxlBasicInfo *bi) { pf->num_channels = bi->num_color_channels + bi->num_extra_channels; - - if (bi->exponent_bits_per_sample) { - pf->data_type = JXL_TYPE_FLOAT; - } else if (bi->bits_per_sample > 8) { - pf->data_type = JXL_TYPE_UINT16; - } else { - pf->data_type = JXL_TYPE_UINT8; - } - + pf->data_type = bi->bits_per_sample > 8 ? JXL_TYPE_UINT16 : JXL_TYPE_UINT8; pf->align = 0; } char * _jxl_get_mode(const JxlBasicInfo *bi) { if (bi->num_color_channels == 1 && !bi->alpha_bits) { + if (bi->bits_per_sample == 1) { + return "1"; + } if (bi->bits_per_sample == 16) { return "I;16"; } @@ -40,9 +35,6 @@ _jxl_get_mode(const JxlBasicInfo *bi) { if (bi->num_color_channels == 3) { return bi->alpha_premultiplied ? "RGBa" : "RGBA"; } - if (bi->num_color_channels == 1) { - return bi->alpha_premultiplied ? "La" : "LA"; - } } else { // image has no transparency if (bi->num_color_channels == 3) { @@ -290,7 +282,7 @@ _jxl_decoder_new(PyObject *self, PyObject *args) { realloc(final_jxl_buf, final_jxl_buf_len + compressed_box_size); if (!_new_jxl_buf) { PyErr_SetString(PyExc_OSError, "failed to allocate final_jxl_buf"); - goto end; + goto end_with_custom_error; } final_jxl_buf = _new_jxl_buf; diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 203bcac2ca9..7456168b3e9 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -256,6 +256,14 @@ unpack18(UINT8 *out, const UINT8 *in, int pixels) { } } +static void +unpack1L(UINT8 *out, const UINT8 *in, int pixels) { + int i; + for (i = 0; i < pixels; i++) { + out[i] = in[i] > 128 ? 255 : 0; + } +} + /* Unpack to "L" image */ static void @@ -1564,6 +1572,7 @@ static struct { {IMAGING_MODE_1, IMAGING_RAWMODE_1_R, 1, unpack1R}, {IMAGING_MODE_1, IMAGING_RAWMODE_1_IR, 1, unpack1IR}, {IMAGING_MODE_1, IMAGING_RAWMODE_1_8, 8, unpack18}, + {IMAGING_MODE_1, IMAGING_RAWMODE_L, 8, unpack1L}, /* grayscale */ {IMAGING_MODE_L, IMAGING_RAWMODE_L_2, 2, unpackL2}, From 05ef7b4c48ba3a14ae982cd43727886ee75b8289 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Dec 2025 17:18:16 +1100 Subject: [PATCH 63/66] Added JPEGXL_ROOT --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 292f0f60393..43a0ffdf243 100644 --- a/setup.py +++ b/setup.py @@ -48,8 +48,9 @@ def get_version() -> str: HARFBUZZ_ROOT = None FRIBIDI_ROOT = None IMAGEQUANT_ROOT = None -JPEG2K_ROOT = None JPEG_ROOT = None +JPEG2K_ROOT = None +JPEGXL_ROOT = None LCMS_ROOT = None RAQM_ROOT = None TIFF_ROOT = None @@ -511,6 +512,7 @@ def build_extensions(self) -> None: "AVIF_ROOT": "avif", "JPEG_ROOT": "libjpeg", "JPEG2K_ROOT": "libopenjp2", + "JPEGXL_ROOT": "jxl", "TIFF_ROOT": ("libtiff-5", "libtiff-4"), "ZLIB_ROOT": "zlib", "FREETYPE_ROOT": "freetype2", From 193ca321f4717d1c261fb9e177c95a7eb1d0a393 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 27 Dec 2025 18:26:36 +1100 Subject: [PATCH 64/66] Test on Windows --- .github/workflows/test-windows.yml | 8 +++++++ checks/check_wheel.py | 18 ++++++++++---- setup.py | 13 +++++++--- winbuild/build_prepare.py | 38 +++++++++++++++++++++++++++++- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7afafe07c91..a5ddfb73de0 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -175,6 +175,14 @@ jobs: if: steps.build-cache.outputs.cache-hit != 'true' run: "& winbuild\\build\\build_dep_libimagequant.cmd" + - name: Build dependencies / highway + if: steps.build-cache.outputs.cache-hit != 'true' + run: "& winbuild\\build\\build_dep_highway.cmd" + + - name: Build dependencies / libjxl + if: steps.build-cache.outputs.cache-hit != 'true' + run: "& winbuild\\build\\build_dep_libjxl.cmd" + # Raqm dependencies - name: Build dependencies / HarfBuzz if: steps.build-cache.outputs.cache-hit != 'true' diff --git a/checks/check_wheel.py b/checks/check_wheel.py index 9d5574630b7..ef39811493f 100644 --- a/checks/check_wheel.py +++ b/checks/check_wheel.py @@ -8,7 +8,15 @@ def test_wheel_modules() -> None: - expected_modules = {"pil", "tkinter", "freetype2", "littlecms2", "webp", "avif"} + expected_modules = { + "pil", + "tkinter", + "freetype2", + "littlecms2", + "webp", + "avif", + "jpegxl", + } if sys.platform == "win32": # tkinter is not available in cibuildwheel installed CPython on Windows @@ -19,15 +27,17 @@ def test_wheel_modules() -> None: except ImportError: expected_modules.remove("tkinter") + expected_modules.remove("jpegxl") + # libavif is not available on Windows for ARM64 architectures if platform.machine() == "ARM64": expected_modules.remove("avif") elif sys.platform == "ios": # tkinter is not available on iOS - expected_modules.remove("tkinter") - elif os.environ.get("AUDITWHEEL_POLICY") != "manylinux2014": - expected_modules.add("jpegxl") + expected_modules -= {"tkinter", "jpegxl"} + elif os.environ.get("AUDITWHEEL_POLICY") == "manylinux2014": + expected_modules.remove("jpegxl") assert set(features.get_supported_modules()) == expected_modules diff --git a/setup.py b/setup.py index 43a0ffdf243..fbb6df17c0c 100644 --- a/setup.py +++ b/setup.py @@ -445,6 +445,7 @@ def _update_extension( libraries: list[str] | list[str | bool | None], define_macros: list[tuple[str, str | None]] | None = None, sources: list[str] | None = None, + args: list[str] | None = None, ) -> None: for extension in self.extensions: if extension.name == name: @@ -453,6 +454,8 @@ def _update_extension( extension.define_macros += define_macros if sources is not None: extension.sources += sources + if args is not None: + extension.extra_compile_args += args if FUZZING_BUILD: extension.language = "c++" extension.extra_link_args = ["--stdlib=libc++"] @@ -782,8 +785,8 @@ def build_extensions(self) -> None: 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_include_file(self, "jxl/decode.h") and _find_include_file( + self, "jxl/thread_parallel_runner.h" ): if _find_library_file(self, "jxl") and _find_library_file( self, "jxl_threads" @@ -1017,7 +1020,11 @@ def build_extensions(self) -> None: jpegxl = feature.get("jpegxl") if isinstance(jpegxl, str): libs = [jpegxl, jpegxl + "_threads"] - self._update_extension("PIL._jpegxl", libs) + args: list[str] | None = None + if sys.platform == "win32": + libs.extend(["brotlicommon", "brotlidec", "brotlienc", "hwy"]) + args = ["-DJXL_STATIC_DEFINE"] + self._update_extension("PIL._jpegxl", libs, args=args) else: self._remove_extension("PIL._jpegxl") diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 30fe26d1415..1ceabd970b0 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -117,7 +117,9 @@ def cmd_msbuild( "FREETYPE": "2.14.1", "FRIBIDI": "1.0.16", "HARFBUZZ": "12.2.0", + "HIGHWAY": "1.3.0", "JPEGTURBO": "3.1.3", + "JPEGXL": "0.11.1", "LCMS2": "2.17", "LIBAVIF": "1.3.0", "LIBIMAGEQUANT": "4.4.1", @@ -255,7 +257,10 @@ def cmd_msbuild( "filename": f"brotli-{V['BROTLI']}.tar.gz", "license": "LICENSE", "build": [ - *cmds_cmake(("brotlicommon", "brotlidec"), "-DBUILD_SHARED_LIBS:BOOL=OFF"), + *cmds_cmake( + ("brotlicommon", "brotlidec", "brotlienc"), + "-DBUILD_SHARED_LIBS:BOOL=OFF", + ), cmd_xcopy(r"c\include", "{inc_dir}"), ], "libs": ["*.lib"], @@ -332,6 +337,35 @@ def cmd_msbuild( ], "libs": [r"bin\*.lib"], }, + "highway": { + "url": f"https://github.com/google/highway/archive/{V['HIGHWAY']}.tar.gz", + "filename": f"highway-{V['HIGHWAY']}.tar.gz", + "license": "LICENSE", + "build": [*cmds_cmake("hwy")], + "libs": ["hwy.lib"], + }, + "libjxl": { + "url": f"https://github.com/libjxl/libjxl/archive/v{V['JPEGXL']}.tar.gz", + "filename": f"libjxl-{V['JPEGXL']}.tar.gz", + "license": "LICENSE", + "build": [ + *cmds_cmake( + "jxl", + rf"-DHWY_INCLUDE_DIR=..\highway-{V['HIGHWAY']}", + r"-DLCMS2_LIBRARY=..\..\lib\lcms2_static", + r"-DLCMS2_INCLUDE_DIR=..\..\inc", + "-DJPEGXL_ENABLE_SJPEG:BOOL=OFF", + "-DJPEGXL_ENABLE_SKCMS:BOOL=OFF", + "-DBUILD_TESTING:BOOL=OFF", + "-DBUILD_SHARED_LIBS:BOOL=OFF", + ), + cmd_copy(r"lib\jxl.lib", "{lib_dir}"), + *cmds_cmake("jxl_threads"), + cmd_copy(r"lib\jxl_threads.lib", "{lib_dir}"), + cmd_mkdir(r"{inc_dir}\jxl"), + cmd_copy(r"lib\include\jxl\*.h", r"{inc_dir}\jxl"), + ], + }, "libimagequant": { "url": "https://github.com/ImageOptim/libimagequant/archive/{V['LIBIMAGEQUANT']}.tar.gz", "filename": f"libimagequant-{V['LIBIMAGEQUANT']}.tar.gz", @@ -630,6 +664,8 @@ def build_dep_all(disabled: list[str], prefs: dict[str, str], verbose: bool) -> print(f"Skipping disabled dependency {dep_name}") continue script = build_dep(dep_name, prefs, verbose) + if dep_name in ("highway", "libjxl"): + continue if gha_groups: lines.append(f"@echo ::group::Running {script}") lines.append(rf'cmd.exe /c "{{build_dir}}\{script}"') From 9486b530dfda070dd6ec4d7739ed7c68a77f51d5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Dec 2025 15:13:48 +1100 Subject: [PATCH 65/66] Assert fp is not None --- src/PIL/JpegXlImagePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/JpegXlImagePlugin.py b/src/PIL/JpegXlImagePlugin.py index c4c2adac995..f4dd6da1e06 100644 --- a/src/PIL/JpegXlImagePlugin.py +++ b/src/PIL/JpegXlImagePlugin.py @@ -28,6 +28,7 @@ class JpegXlImageFile(ImageFile.ImageFile): __frame = 0 def _open(self) -> None: + assert self.fp is not None self._decoder = _jpegxl.JpegXlDecoder(self.fp.read()) ( From 5c5ed1de504b10e459e152a38744965ee330c603 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Dec 2025 17:57:47 +1100 Subject: [PATCH 66/66] Build in Windows CPython wheels --- checks/check_wheel.py | 3 ++- winbuild/build_prepare.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/checks/check_wheel.py b/checks/check_wheel.py index ef39811493f..7c025ccb6fa 100644 --- a/checks/check_wheel.py +++ b/checks/check_wheel.py @@ -27,7 +27,8 @@ def test_wheel_modules() -> None: except ImportError: expected_modules.remove("tkinter") - expected_modules.remove("jpegxl") + if hasattr(sys, "pypy_translation_info"): + expected_modules.remove("jpegxl") # libavif is not available on Windows for ARM64 architectures if platform.machine() == "ARM64": diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index e167934d045..058e652e977 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -341,7 +341,17 @@ def cmd_msbuild( "url": f"https://github.com/google/highway/archive/{V['HIGHWAY']}.tar.gz", "filename": f"highway-{V['HIGHWAY']}.tar.gz", "license": "LICENSE", - "build": [*cmds_cmake("hwy")], + "patch": { + r"CMakeLists.txt": { + "cmake_minimum_required(VERSION 3.10)": "cmake_minimum_required(VERSION 3.15)", # noqa: E501 + } + }, + "build": [ + *cmds_cmake( + "hwy", + '-DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>"', + ) + ], "libs": ["hwy.lib"], }, "libjxl": { @@ -356,6 +366,7 @@ def cmd_msbuild( r"-DLCMS2_INCLUDE_DIR=..\..\inc", "-DJPEGXL_ENABLE_SJPEG:BOOL=OFF", "-DJPEGXL_ENABLE_SKCMS:BOOL=OFF", + "-DJPEGXL_STATIC:BOOL=ON", "-DBUILD_TESTING:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF", ), @@ -664,7 +675,7 @@ def build_dep_all(disabled: list[str], prefs: dict[str, str], verbose: bool) -> print(f"Skipping disabled dependency {dep_name}") continue script = build_dep(dep_name, prefs, verbose) - if dep_name in ("highway", "libjxl"): + if dep_name in ("highway", "libjxl") and hasattr(sys, "pypy_translation_info"): continue if gha_groups: lines.append(f"@echo ::group::Running {script}")