From 667e648c018ee8fc5f9c97ff5eea30f209c25c6e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 15 Sep 2026 12:30:21 +0300 Subject: [PATCH 1/3] Add lossless WEBP for testing --- Tests/images/hopper_lossless.webp | Bin 0 -> 22926 bytes Tests/test_file_webp.py | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 Tests/images/hopper_lossless.webp diff --git a/Tests/images/hopper_lossless.webp b/Tests/images/hopper_lossless.webp new file mode 100644 index 0000000000000000000000000000000000000000..b6e8d6f698092415c7f3d522dd419473723c3c27 GIT binary patch literal 22926 zcmV(%K;pkrNk&G7SpWc6MM6+kP&iC^SpWbpf50CA4M=PoNs^=s)%MoTgvx(|pFS5N z`ac1HE#Nt8tp#7;m-iR_o!yeK61{Q2f0^I!mpuoYH=6J~>j0>>f|4sf50Iwa=6`wX z37mkva7>zwyNNfq1iXUgxF#q$gW}7qwcIz8jCEj(Z=3V*@N%saNla4hhIVTb(e5WN zZ0K4baPan==KMZLTg^M3XItyLkXl?aQ(47G?x=+y7p)5y%I?p^j3H9_Hb$N_$F@ANGtflCE^SW^aL&wyOWp zZ9IWws5U$M0B$2mj-dHtzs{5deTrCkYaR zp%s7~Gy6CzApziOKnREc27;vMYyc981lRxpM{q1NSXo&BGk|2nB*~Q%G@C;pBNJ*6 zVxiLMm>FY|1!Ig=j4}2R85uTMS=q6%B0yLKnS4lsU=P4N0HiO{Heu#Hv%(3G0Cyi~0ID3gsE9%k1OOo3COgc@50u^mW&i*?;D!&}P9uYQVLAs&8|rV#sC=O0vIY|41lN+wN}}<`$4Wg zu#i#AMF14Y6{r|f5j26RR4O&aAAt&Jtn-7w01_ZM4hSJu04gh;bd_5zRSD)TMo$cY zpaIUBra%yt0>A*^%=-@=0{{yZ1HCW$0$|43wB!dRlu!V4A9xnh5ddJWfIwYk%jAaz ztVzyVATb7n6WEvm-f-3RHa7T&RG}q1U?ccK5(ohdvzP45VFuTLd38x!Qc>R(|M=2$ zAP@imfdK&CDuNagXfr&cHYqtW=yWO}Az0cLeu#@gz>gY%Kq*{&A|aWWx(ZMkh@cWc zumF&(TfhhyQ4o7XZ1EDI8cPP*Qy~E3K?7X@k&GucP?|iD65{HED@IGogGUbwJ&9;n zpw}+S#ud1NObh@9Ns>SUg^dDKPJY#KM9xV88$v115pDKy$Wp z(reSJ0Qgi-{*J$l5F%Lsu$~Ym0Ry0`s8{j8HuZD*Q2tF!J?%&P;!7YBlOzCOjHw7= zf&{1uQ6Ftpw2UL|M5A43cj}dT1ptfyp=~5VQYGP!dS>?T8AQYcj5EIj?8&Vkv1Mw> z7ki9(F@C|e`AoZ|c3kaI_WlF~8dV#E`oAq?UpBT9q2ID?!9*ht9|sgC%f>W>+WnIW1q~|j;LYhs5J1JgJ8?c z%D(7E9lLD``DL|ZJD{Mn556TyvLs2eEn-oxOW&{m|C@Dl69zD0(fS3-ktA8R70;UY z9v}c~^cRoiuQMq&lH^E|EF+JqbNWlg?@2|5Ljho^Xy_tF%wD=5@EA$9YTLGLMP%)L zZ05c77imku%rHl*nVV(sBiXiU+qP|aF17WsXoC#r9R0*UP^jXZqt%jq3zXzYk|bG& zh(*mjGP5vzuK)k1dcqhvR1u<3*tQL_U4ZTA0*nFx7_uZOl&-8S*JHnaZM??Vx?bD1 zb+&CAd-i&Fc5F1pGmX~|jptWZI)lHVTL2_Qk|aq)RNXzGDmx2$nRkyFzk%QGYZ!iF z-|lPnZJ&;rhM}RVGBYCG%~VB`woi&A$EPt!YBVcOv~BzGZ&ca%$DH@ zb0JzYGfzETlpPrn4yr055uR?_Hf>wm_9ZoRj4{W=@-Z*ocE~n@zVDzJvhO==+rC2x z*|v>i?6ub1YhtMPhQNXE+O}O=k|e3mwa$Hl)G@}0Tqns%Kuo|d#U`R6_ny6$Z%L9Y zNs?>}S=6F3`|L?o;|Nr%q`}=>@cZK~MS<(5;|2Fvt|LIqF`mg`Z*&lpgh;QEdwZCu4ojV@? z8_fhGK$Sdf`StkH@gaA7J-2_8><2y|V;W9Q*JGnPnExQ{U-I$WEZ?N0=b_r+VPG^h z9ri^18S+)04u1J4Pv3w1_wRdDY#h$kb*^?pJ>9UOkN0#q|NJLFKm-Fq2zc_*s`Tg& z*B@M^QUR3d#)d^`1p&jqwNjbTj z=i9i>?!@$}i(@8mjc#IJb3W#{0(Mz*TO2(gDP`n>0T24?A%>;p5|pbihDe7=kd9OJ zuFspC1;%d9r{w5jrr9^Z;H#@;BWuSh;Zk$KUO9`xaZ0vAAmOy9q&1BiI4%H;0T2`B z(b0o2&4WRND9DH64X9KIxF{x5y19of*m(i+x@X(lZbY$}c2h-xJOmb?-5YJ)&TR&c z4&R;BLxnL(jmg%W^==WO&4$tIOg87U;uZ9ZZa+$9h?Zbd%zVci{x0*J?r_PJ=&GHn zt|P_73bEg7_p06=%D31ZLPL#nQ z-Z%+NG>-}o4JC|fhENo(c5(tV9&FA%=WZNq7=|JDI z?apXpkcoK3u~8vjcC-(CgWuoP(xDD7&Rv~!?WBP{p|1v)0ch+a9p8HI6r*sKAX{V( z6B{Oq5{(KdqJEQ2#e-4L@HB?%dU)`w4Fj;=_K1I*D=8+imc$%FhB%ukiGzch&f)IrPV-7>yFVw0PK!8<+kO+a%A_NnTGH?txGai~O znM4GcWi3l4*JhgQo(l_JXtAyA4SR6{Gn_$B+II|M9^7s+%z~-ffn5v0G#(MKdlkHb z%yRV}Z5{j=Y|{KIjar{#-l6L%p1%|JX097w`+Muo)!tdFm>#~1c}{aYf6B)vYW^s1 zK7P4AukYEPwST!1G+~$APO%Y4gkP10E_^L5W7&nWl^JOOw=#Hsxl>jQc)Dz z03w7)k5G5S(4*m^=+F!d08Exyn&raU%#O(%sgQc(k#4q8Nlm4J@gIP$DECfKspdth~Q!A^?pJvT*2x95)iSAV44`lBxrF#*WoxbihXG z0z?I66&KN2p5*~~O5%~|N$IJZZ^+-|B4ESZR5k}T#4NG?ffngUP#Imw@fkMn-|dh@Jf=Fs){x2ODIzI@C--a9}2KOX1sw9Wk?ZVF^JbpR$8 z_W7OhA3BYOp#lnW&>(FF4VZ9ZBgcu2(y?P2BZM&`N`)w_r9W`68WFtceTZ1^a`#j1 zf5wt?U>q2yHw>-sP>uv4!(d?CEEt0+8H8h4CRcJzfZ~2cgS*;GY81ocQ0y|g;tr=<$f+>o<}*EdJ}re`8!*wVm8V?Bo~@6s$Z33|e1wsI9ZfBI9;%EsRqAIb$k`UCGi zWX>0uFDP2w?Ho35^uO*$wWD_8TUv1d&`o%tAOP@2;=L`o!7ladw!aGiYecWZ4uxvEJfVe(ng{Qz>|3m)Odaqb zQyNN_8L2{Ts0&`Lnl)GepduKM1A39+ZiaPSY)UBJ-zNKv-@tsA%IJF5F-NC4nyJC| z^lD=_xII08`UqJaDfGrS|!j&D%dA)n4rGYDa}& z-T|(3y-)wsf3&7Zgl2Y-~O4$LJP6cK0|7`6XzKFLL$)IrRE2rVwi zpCK-GtvP#kUi7vAa|hhbX83UhL&~vM(59YwVK_} zvUrl)DTghcUb|g(-p0(q3v(aK?gnWLq_>}X``q20c_fb(^Xkq3_zW$O^Y;YGWEM1tER`EtR4m5La^m7=W`1Aq>r-L!*j4J_S1foy1zeyO2ur zo?rpkZ^h_oKvF7^F6&3Je$v z6qTYAtTTYgWYRRv!$Wr(13=f=nF$-?lhnHB5Y5G##_LjFnM{>S!s-e{t=5e_9;nFS z4T9T+f&2k10lBusW^>G9Q6>)vp8rDA@`bBnc_S?_b4XJg)%-(%hKb>sTlNOE*dy&QX+rp5nH zc(-&t;e1nZ?eM8_vc~SzC#ZSdda3Iz=B;zZwzIB+G)WwcA^?`$%RYtzM3txq#3{-G zdW5DBf1tIA9MurcEQsrQfhzQt@&<;ec%Fp02Wj^$$&Wt@iotJ!lXB9qc4aR+-pwc@ zxXaN@$gzAq=bPtZ*o?gj1&is+%JHRs9D(dF;nzg(#gk#zKhF%ekKq5CIzNa#V>Q{P zGP@D)>e*S=xs3Bzd-*oAExNYe?>^U(gR&I4$SmwO8nVW&_op}M^&vo(-Pe9YtT)Gf zZxqk#XuG9(d$-1aq^BtjN2D>BV-OAwTi{(}Z&4INoH|%kCWz5QhE6T6tjo4?(t(zr znt?QOS`9mg$Z6=882{lB29JW5xd1GIC3dHsM83^A3kYD4$iO{MGh|{d9(mhmQaxDr z2mj&pb@j?>8s3?h@4*S&<7M3c%l>KSA~=v%37!?r{?Cmqay=``6Vg9~zMbWc`sL>* z5#G&iE^Q7g^B8}5>fguQalM=VXM8&Ga5k@eYsEM}A6h^E|663H##z*yVwj~JwrmLs z0ktKD1pG<(5Bk89Z49z!;&b|gj=#ML;}J=a!c^gBeSP}^;!P=((#KnAJglf3Ai8l zsrdN?{>t6I9A17v|MmUlI8Lu)YaM#e)qnrv87Bumoq=dqnT~{CqAhjGV^S|i*}f88T>(hcklJR zdUpzc_i|wQ?FwJ;ZSmVXeD_p8(zhS}_4`+UvW7YPxG02yLjeV1X_<+xJ~50K5s<$E zptX@cnT5X$`<5prdt~KO8S!OU4Hl8EF(qlzIQ|+mxL5$xyJP~dfLDzV33S5qrn>zx z&~@r_A0ib_p?HB#NaQMRP9y$=?vmn+<8bQPZ{kz_SDxcPk-zA59oJc;=sHF(j81d2 zUym2p{ylA%?XR)b-EYLFkJ)dyF?thPk0{5s*WOywo1f=B&AOkEZI-uoy5@M@@5yZp`U>s>0yQ3kY}wwoBgxR*Kr9XfB+3!Obt#LEVBY9xU(MWvO%E+=0@xZh0pp~e^@^pibEkpX_0#{@fZOy2&=ag#bIZthj~O{ zKY2H!e5#7pMC*6WvSp~>)6*;ZvS`1)`$?SFS}JGCyTejr)$pls&x>aZTD#u%-bG!rJlAf>sgC87KIcL zfT?PPxGciZo?{E3hn-ip(PBv*!;O;LZX2Hv2-=^U5r=c!Dyq%GWdwc}O=nT}-cZKfYvaJ9bywS_|8A zbw(7NEnuGZbS9r1bwSP!=~AW9?y9^m|2n;4AW!aS(9x_t!BtBCH^8m4&j(exxTKiIQnK9Lqd9OGlA%eX2KD z)c*@@0o`v;drKL2lLmrj2I9O9fL}12)I#LCw#m(F zQA_%X;!NwtYZ^9=M;!OD+Ao8D<@-hRavkrOQ5ca86EZ<^-YBTUJf4lny?PWPS&Q+`GMWX z>*;sDvgXxU4#oPs7x%gSzKV9Q1S>7%tITGt$Q$M92+grWIh*%)o}2gLGtE(Fz^Q@2 zBK6OWKr!QuLUU>yjyBdGR@1%0&nk)ye4vj3XXDK_EC+16)qF*dnjj?yBo%GYg@(m@ zp73V*t6UV)w*X9{?F1Y%1SO*gzfC0u($gBVh=}e9joFkFr0zM$EKBdw-1qX`=l!sv zFYY51_c_$VH_ANRJm1opBbanEp|U5S;9zlK;UWj^urZ93QUYc9r7i~lR;@v}Ci3g2 zFy=hwQStmb1i+;3gpcXw1y|kljoWi0#LTVqNRis9Xp3bEhY7qg0pn39JEf(?V4q?U zRtQPTA}fIX()29G9Z0!BXo~_lLNzlTt=s*MJ^GOGoxR*UFZbqCq5BS8w)?`3?G4!k z#$0JpP`%-1t5r+Pj5%17o@R!=^83o$RfyQ*m5TPTF+QLYV5B?-n{HzI!)jhxyJ0!+ zvF1YYh99_wOR9}PQz{b(!JLLcQNy?>4o3wNvd+di1q?Ja#e=h=(Mut=2op|(QXwv$nzt^z-OK7gudCOs*R4e#0t`?#YKlFuS0DWQX)n_c0Hw({9KnvP04F4^rlX;dyYb4mZ?pckb;bR|RaD&323F zp#;)oO0U?(E|Bd30qCSq%Mb8Ycmkg@g{6H5AKc`!WI9MC!xdzr;waNF!#K;|2D^DT zRTt5cEAa?L59hI4E$(RMa~W z;sJv^*t8T)lRM9QzA?$XGM~#(q%W-BIDq-+MTc5pLbj(|85Uy2?z6aBOe>EIB00Ot@hQb;YiD#qPQT8xGd<<-{n)WtzLkg)>xzJ>iJ4x1_GfHGW4Y(DrX@s^KOzIb5X>0LplyUR~~>DaESMIxK*akOIPDGUAb^4&!N8 zpN_DRSZYfVp%Ydv7lRV4CL2iMQ*FIZr2hTFrI7%Ez?|x><$qu=x<11$b^M`ElIEgx zLd;MHh)oN4hs5IN`{~%(%#6&k0Aky)cwZr&m$OqL9vKqM)Ujnc3^4_p^;$$^y3Gs> zsL9UcI2?%bcV|RHQ7WzGDMfHP8@5zpnc8?IRJ5p17kn1o2!ss$L3oEAqNQF(k&dVl z#nFTs)&}~`>|sRg=4sXxxYA{qr$o1w(jW|?i2_zrN|=nQvti1~ii{1UI;zOqf#}Qo z`kO7aPvJBX&4wL_acEipM-gt2Ju^C{V)5c&X7$DpxgfRz?aP?gl}~+2d`Di$CZ{6p z2u%;tZDPC?_vv=)qxIonaj|tsW#coGGI|meUzBvFi$^xyk+!s_6gN$$F!*URA4spc zCFok+M(%0e)a$a1+m7gyZYf*FU1Gyy5|A?VVrFPRo3)W_!<#TS8I7xf;rfawQ4Gk- zC8KPrpNLsXNVsmU_+#8A^!cJ+&g6=-^jL7=bMeG1e;K06)&H0BW+;EfQ3aY zKuS-S4Iekrf`=x%rSv?NEuJuK+$@4;1yxC#t%kzVL@w2QYMIW^k#o_HvTCztl<}4{RV%HEi!7O6iJ5A`T`C*1 za5PI=@ua#Jqz=Uk1en+YMP~t~vKvPjK3ZUk|IUh;g;11CVWfTeRP&ESFTF!Hq5iLs zCnl36NyyrqhABzQmdfd5IQQfkW>kd~jKpSO(OIg6tQcBl>(DP-D{a+*UpiEv)kvU8a|0VI zml>{D+H-S76A1@$y769L`sPjRk{zIcB!F>tdI+6FD98v;qmjj$0a5gN6yoJQ^(kb% z1rL4hzl9BJgPU2x#8G;7^PG|{(9UtbsqeTMwz*UrsmtEtZU zcI>&i+`089WLm++VKJV@nVMcgDxRR;2C!CHN45zfhsf+Y{q5W3_K{;5vOCrqm>!I@ zZIu{h7@(}aop2v^9a5xLZ?b$C8g|RBx4{Q;i%kzrP}`jcyneCt+s?1_wp-gI7-|xR zp?xY6c#&MePaehGa62C5uJy(~VlkZ`3BD7^18KADu~+AX;d3W0ByZ#PTao{Tc|diu zY|Csn#M4p!r}r5>kT^Wop6mIne28k^&FFGM>#aU9*dN-w=(P%^cIa`^e_{&D^?-SX z7u$twH4XO|J@nqJbj@_FJhP4X4Axt348R%~G=_7+B+X;RdGG?#9i^e>5PDZ_E%j6lZI@i!FXflX7C#4H>gvs)xmN5dSr7ci)|kWvqo5#TeZ=_ zkf2NgsuH`;ayk{u_hti-`j;gUZ&Wz`{9_pJ2z<0RVjaR#TK=<6}x(V z#qn*(bh}h6S&@rd8`d?>pN&$M6L}tNRrVnWOfUrIQV8q~0&P!&DYS*Idq!QIS?<`D zIt$ILv6yF1_G0pe=V$ZKi!HIf;>?oY>b677!D7k!&GIKJ99@PbS%%#__%#b)AYltV znN<8>G*3wCi=Xwa4NmIeCD>V~gS(CO68x=UZuS|?fMq?CU=i0EwS69gZjVA-Yin@`7hixm~aFH|7r5$+r0u{ z>z&gr@<_ZC)}!QZn1|M)@56d~&-?n`uJws6hX=bw7PuKWo|J1Y-wo=P74A|a*iEGB zf~3>Kkk%5A^fTWWtW=cDSUhjOd`=q>h8NmthRjgOezxySm$z58PFfxz4+d$M&VJ0k zbF=K14W+5g;e_ofQqFDTdvF#+-iY+~&ilK4o>tG02SIvl5=%IT;raG&g}(2;&wPl^$}MsJu0}tCd;(;IRGg#(RTE%3jlvuC#h3Wg6R4Gz>yxXO|F zOb(uG_po1q4t)e#tZSCGcZ|*1E;eq}Q8xyoGcS&sA39h`?xrEY<(P*KW<#klsjVWN zd%Q$AL<$qYWxLy?&?c;hs7sb>7(#&n?qRnw@1NCxm2T2zFNVUU(zenqmU#gp)45%wl<%{xHuKonHj^)%^xPPt}#?EwB2mJ)`Imt!0(LjoByo z^+Sb3qQG#=GsX`<2r71otC32cZEev9EPU?!j;1b&lB%jR00M zG^iR({Fz0o_DQQ}3r298+{zwi;#^4EhKj&<;hKk43D$U2Fl5%LwF@`P4XR=L zN$zQ!D>yhk>N)7QBChdlocdJf0Mr$>tF)nP438g0^|1hRH}br6GmI)A!mV%9QFtCT zQ4G2$!_`VK3JDI^-B4M|p5JeIz+D0~VL5{B8?+QwL7`V$JKIqygThvKDGP>e7rXXU zOplBl%eAH=#b)g^MKjCo1Ade}OrOc#zf%X$X`d|4KvZqy5EZUz5%G%p_Z9~B3g~19 z0L#|Q%zxotZUEtA5F1?-H-=ZyZ{Bo>#@U4NQ$JuU_QBL6sGwjybkStQRRy~$prWJxM4TtGW;ngU{^5Nn|q2(Vfp`L2^05dZr(<|p^_6My~HXFsp^ zx3m4>Z(q-!FF*MC*Zlm$D+Tx`1q@F`ne+le(Nq@g0z6;OwwmBeRnSX-_`xICq=_J z+SNSun(b(YJL2hFa^ABcZBy;)>uP%SY;A?7grU86Ut8^PVs-)GeYV5L+uz+zcPSCx z$#4<|);_s^1a*bSyY+K_@#BZfAD_UBA9%UXznJ;^Eq!N@2mN%?&o17lzB6XY9Z{I3 z5hY+m`oc&c^dKQ@rrYjV-HZ)8d)QJ`uBf8|*kH)A$84Df$SjU5)1pd*g1|H;i11JX zy=KQ$S0tjRDz{i8RgidsAXVqoP6TSCBpEIewSBXtBqLK#EN|ShXl*A2=^F~qASorY z0|(@is^q-FGjSU3+v`zLAXog%P!xwmRHHaRtF;-(j;8`+X>?=^%hFT>j3Ux=*Dsoo zd;&#Q890drS*~SRCN`vLm+`@MA7ByWCaj{RZ9Fh+oUcz!iVTSW)3Ij)tOh07NYNbE zr>`Pc_OZ~m6nI%NYI+cY4$SWqLLu|6t0h48QtlVJS5X&)aATU)2j~mOl0iX0WIzG{ z4AE8f(CJmjNg0Wn?hEa^ox4{Ouq0Gbl^PbLc9Ed%@R75fd1k+@O5<%J3N4eQ2n{-g z$TDG~f`9^0%g|sH&wR9I8N6pngo;ep%#dLtBCm#w{wr@>&Bg?i`&jSH8k>@gWRhpZ zsVuW`Jj^&gl1w^#2Ro++viXebgzah%HN*N-0V+im9OK)uM75dPx!fC4)_X+k8Oj%5 z{PkJf0OGg+2^h{C=5PSB!|%&%FS-_#k@2n$nl4fY@Sum)QDt|$fTXK|J$1}eWr7xX zG1Lxz_$xRIcBmF5QkwS_u*|KP1F1nE&LN@*vy4lE%~q7qv%^f#v7(9**{~f3u`?^6 zlB={=RT4)EA|`@s$7dcgLW)6lsYwbZR?8{Vf!|1hl-5_(TY6)@byYdqs7K zdT@>WH1T5SQ;@rGvw;_5J{vBWp_rz=38}ZvK`;R35hGD5ZeR{(;6*M>8LM>w2qU)w zuyq2XrfWpxFsqt+JM++#PSOoCk$1?E!==?08+oxxr`@78ln1G+TU}NO*1#F&M!2b( zwO55K-f3@_HWE{LYkVzD83BTW;Eb{NIdeHV2p%ibW;^GZxQTHD;>I{UR(VA09?$Gz z?ZFb=z>DA))G!I#Ij?UHc)Z*`{|9+v9wd7JG62C~2?YeKiDaiGgMzu!N&^rsv@2n+ zRHQ`Zfw6EC5}}+1hmr-DqDO{@*~VL8p{UcEXyIry!gV>f{c`L?)=4SQRL~@$tpd^k zco{;YmK(Xyr;T6IlvY6>Tm&94frRy$@5tF?`|JUql?}*ph7DIr2-j`h8GDZ5b%C*& zH#1&D20-lgW~x2sVrDG%7nKA8W=+H$?xaI{ON$N04-Cx=q$~oS^m%fJNm_4Nd|D-B z1AWmJn`2I``3+?ZAW(ASS~e-E-vNg-3p!c5$&V$V`q2R zK-hu&jqF+6!_E{%dOc?xch#H5%t187@C$HBPn}WS*r>&SHcIOJ$aFe$2S) zo;o&QukiZdP;21{!lMpRVW4CDT&5Xzuk6x4C9wzs+_<;t7^>2*$f_11y-G8vD%(im z>~_RNCZmj%sq095$r8t=CepIlT#+nxm#yDfq^aq` zCaAN1?o}5t*%47BgT2YM);1n?bBnmbXlKP4aG4!&9u5wm4u(A{ZP51l?EfX6g;kS^ z&4vvW;bjKL!{>yu#AJ68eXw9w)bBtOi!_wUxZ-JPcv-@15ajqnD_2Yf4u|vOLmUW-pCk3Fh zB3mM}AwSPck@_1vouV8zKz9u|IF=f&W7(LftOBSwWS^V+pAt%Fc@BD8h zn`oohl-CtCx?%jr_YH$Owa<<7(h0C3LM70C)&e`u5&pu1-?kqc`#(s>9nvZcInXRP z8f_sRy>7sV4Qq@A_oMQXX}K3nr2e(1fw=W z8_A{CMm2CBuu?9ipFAL?bejTV+emrOl=8|Ph+`)yvG~~anbw(}tvf-*yo$wjnq|^N zsGrQR7R5J!tWi~~+oHD%&<9;huidZ1iFFnpk@~{Ldmvv-)W5)*24n${wKWq{0HgkP z6x{{1$%#hs5;f_{lZGpAPgVJFd0{1nR}7_@IiPPl$03BuRjDqm2J4DI-N*(fIO-H& zTJXeg@u(wNyJSJ-VrQkKFW3i#CpJUV%_cFyrg-i+R8Et+QuLK0E{#D%IB1y{R++sQ zy4sYZDu?Q<^_<(-PnI?hXp^(=u|3;Gy3LHr60>{=vbfS@Nxd_Z0?7hM5Z=e%komhK z`~Q1(f7Hi4HVQkUoeY~n_jMLLSBlPjE8Kn+^K<{FLcl%o{Q4i?y#DgOzy0UO^RfF& zq8}dR?;hA`sJA_N#MFTMCCJ|d`G?76sjq%WD*C6VGQTD~{BLsjzg`!9zUHv?FQ?>V z@4_O7b3713iz8+cM5@b*Y-Mca8YKuPZOikMKIea*=XmezdfR19h3FP)8(^Dotjoi< zpNox(bgi#r(^0*B4L^UBPhYd^pQQe4==pD0-@BWC#-c5M_~+GU7oWd__mApf@OQuR zob%&x{vYrE3ISnxA5!?4&m#bu`47MgArL6U!v_bbH_#+?PE{ZQawq76hA07{$Y@@{ zfFu}xf_LI=e>tCr1|J%sNey`jmQ<9;BwR8AZJ!iwq|s{+lK8E>jR~@vYuiuh$oxLc z+%*u!#+O}=w+J{g(;63}W`|U4s-z`nl@<)=dgi2@SYM|LolW3EgxLm(+^wpH7Qqf^ zi-w3#CXG*VT%(>^qqSq60=nrxmBiG1KmuKmmBuj6B1B#unrTrK&02;8As)TkcU*50 zKPp?++J>shG&E8yP*C88GfhD|QG;2y;S=4<(>v2>4{M9Yh)Kwc6aW!m3%(`r&ls=R z2A&QUrMAee;cS{c(^2;Q!QWrS8?uEBcHbu*KRYP|4M^TnrbbJUVY|`EVMB^`ufXvN zSWf5+_^3qgST$r(762+SV9Huj$@qeR;qj|FZn*<=9Ha1XepmTsBQaqE5zqk;L{ez8 z1c-!SKH}2VjA!7rCo75XNF!BV=4KAWHuH_35Bg3h5mZX4D8s@V9r&!~<x@xxq{1M;nDGY+VFBK;)YJJG@D}rMxw^(271T0#oy4ci_gWOOYcdp8iOX) zDW2LZ8bVbtAkM|MKA-^5;5eLLJh~LW;H{Lk2mR>tUqv`iiU>4#3cPL8m+kgWPNuQ4 zuse~njc%omBmy=Rw4vwO)0N9TK?{XKstj$wODqcOkl3f9Bft&zP4pY`6>EwZId|Gv zVdmP*#vQE`a=$2I49NwTHKNUyAW8aBu? zf4}&Bn=D@}dc*m5HWTeQ73*j5xia72IncQ=K2Wu2fkeWUI0^wEQz%Mh>mHwWa{6bU zx1M)}%*!6o7YmOEBkFyt_7WEin1N&i;}B6{x>3ZgXO4RgY)N5N4YdZ}3)=LekyJC@ zIV&QT;h@JrV}M>5cUmKM=1I*FU-H2~Mk7Y%i-Qmg?;^oCs9U}>qYisf*Y(h;H&T}1 zkhUWgda{O)0lkSnH{6Y4{+|DCa+U$~)|R%aEeunu`WDa9cqNZ_?JHyBBw&0M+GY|H zgqv>o2rw|v>|k)-I;VeZHEm!E+PT@>rWmk$EDvVmIHZ!;6I}_XWjZGba5^CFDciX~ z;caH;0G=UBO}G92=+e`w$7YWc%XKg7Nsn&BD*<0>uljC7qdYoR@; zF*U}+*W?*pg&3F1%s3GXP^y=ON{V8sQ>$Au9XUR7JJM$Pq(XXJ4lUV$B5VZL#xTeG z$0@PGS>GK@MNSYhqp4y0(V8tznZ#+#JO?H5>75a!TB^|zOuV?|hV;Q`2U4F1u{A#v z#X$py4~Lu&WA?Dr@yTPSKx8Seg5`g@{%#Sra1VOS(K%=|G#^ptDi>L7 zOlb09SMEzVisYKzj5Jbf%z{tKp4%GsQVx?(=U>A9V*6h8N&Nfm$Yg-Cbf6U+NOD?S zC)8|knn@-0_XWboRwRG93t;Xm`*i4x4Qb(^O_y-!6YP-^GGutXcEcTWS^|CAA7%NCq~8hHY>bCr?JLcOox}7T4h61nM~sPoiNMQxEjEXJcn=jhrsUPg$%F zY%60Kos&U;$EMwU_>}y3dMTUFGtr~5{T|L+ZSjtuH;>ycAn88HSB|q&n`Y?{sXLle z@6_x#1naX}%huG@ER~)h!Z4eppoyxY_L1VUja%~iPG%PY(&fHmM8|v^cdd`-DrfS! z_Dn~+a^>wcz2O%3c&xqh)#}x&2LL5IYG&oYsk)*qpwoz}4dPex6^32yd;Ea{Bs>lk zHo77>$ak6Lcz?{^qQ&5ADmvSZ+$?=zMc4<= zfqyn_Y){tp7wFkeF1M7{O*~*e@b3^;b)!QBe-HcWIVqmhpW%!eSh74 zvT(1bVD7Si)6S1aJ`bNixxaQb;z!Ki#NVo`*lHW>Li#?~n=Hf#UXwnRfB)iNt^bQn zHGB^I|CS&B%6}K0q0XhOO)kH3cv<*J{WIt9U;n!Q=X2tR!2gH&L(~87{n~xl zAGv49D>|%frnr&YaW`-7Q5~W;3ku(VSB$Vj;n=L9$(GQhmE!}aAL*AuMx&9_V?W(j z_I3Fni##`Lxx7w6k9;$Zg^)sPEO_@8%WY3MM7INE$sjsX!v7y&5j=Nz5x5k~)eQ~i&V^EJPhms*1jM&%`OjY z@7H?2-Fpdb(B26L&(@-gGMOBxV36Jj!(ursgj*1V10e{&9(}~1BYnI4xXT`n;`J3P zP|G>k$sKX+)K#giBuZ_On(zTFv66`5>3}#Oo=@sE@!i}VQS~&v-CWUMv3(~hMC`oc z*j04KW9W01NZM1n@afY^^9c~L7G@RMk>OEnXgtMJuZVUhrv3&$w0jFbt_)HGFr5-f zL+vtK3NAQck8?LQH43pyUB7>I0BtQW5i2eJSPGEf~1c%iSCR}v39$BkYurAB)eUJLC^&EHr6bjv;9GcPrd8cUSnj8m*Y>o|RARH;*FSP(W75opYKv<6_T!lO69dOu*B3 zPEG0aPZu_gFGZQ)i1GoNL?TpD@n{#Gfycsk_~Qk)8y-gCa%PBBo9jbiZOWA6{5+$v zskTi3&gQijUbE@|gdh}#gO+dy?HCQi0r2_yz<9T)+1}UEb{U=h0dX0Q=1Qstr)qu| zb0CIS590yA)QuNIU;qmCz#h55s}?TS5+YKaTr=xo;X55FQ9`a0-y z!RHdA)U@T-?!NYL2J4X3>^DS*i8m%+en;Cl;qA6Zk$)to;W!9MbqE^mHR#t4|B6a1 zIgMp?jz;E?IJ~jVTY}^xY~cfX|0!4C5vqYhz(rdesZ*fSQf_w|)9lflyNl^dDRf3r zIUULc5cY^gpgB2TaZ+-026FCy?r*hMHy^$}P@v|x;y$)3LL=a&&N#fo$Y`Ts0YcR> zt8z04Xho|b69%3Fq(p!~C{tIdO-zc@!q)h~?yg1nv;XjGdy+5ghpJfsA{dCN=fR{+ z@G(-2Rfji6LGukjuN7U-d+s>vjh=$#h0|5{C2G8R<81YnKk*t*Y^2WAq#>(nX}ApBZfdaZEXHS^HrCdaB;DEc_0q}*b#uj1OX(+2_u@r z!^NHJ_FKunjR)le+S4T^K|(DcJAOhLW=BpTMk5rULo+Z}1`Mm+!}nm0bp%KoZIP8B zDu%!~W-Ja4Ql$Z4j{sTRjMtIj z>}7rKv{Q`kbg~|=uVb!55mDtOY9Ym?#gf#qN~-SW$e#F%!Qw)mmXXn8QxI2{{4E*j zQ{gCZr)>MfU$f{yaFxhE#47>Zc(>TmjyAeKsDWX#HeRk#Yw#iI<}QS4fSH=C_hhi^ z&M#$sA{<|vTCMI0W`&O_>jYg84Csw;9$*lfB1N`LPOL0|5Tk32hcrCy%}m2s3lH0{ zfrJ1+KqtsbWRZjm*l!2`Y5<%%nTJ@C!HMEI%+sG)p}hgRwo7Zp#@#|PRM2PxN^4oS z$>%8n5yVcb>C-)EA^3Bth=g6Zs@${+|0Pr=RH?GK6B)8WnA~5rCw;Wx$z@_S|>&bp(<$98GYK$#-X2@G{TL5R`uTfZ+r~ z%3vLK6^88${SAc(>>S_(Zsu#et2`Ep6SMsEnou$4o7(EI$>!Uu7>>*Ub|QK(mVB;JvW*<}N*U zurg$}Y?q+d+c|(B%M1f3o+e)>_B8fEPjH80x}FD-cGEdDRx7M*Cf)*E9e;lOd9-=k zGyI;dQ|L+$9qLnY6`bZTKff7LP{k!vGE+v5dK{=Vp-*HblO=wb^bXPy976qH*4C@( zu!+v`1>XaQPFT?LpA7Xg6EE#)?@oF?!>uQPr_XT4_2 z3e0sHMRSb8VR{zL94@i$fy5IpgzXqulFn>!Ooo?WBfh>6!3|&mtY4$ib$!Osk^a6g zg(p-fB^E~kEC+`qf?`w;YjycRjpf40N@pRzC0cSO*Ii3kLwPSRmfMLwCG&ecb9lw& zq&y_cm<%LDnsmPybi6EhnazyO9R+N}it`Z#8$cCsxp=Y)?^ehrAnqc>+$F3RjPAJe zWgJi7o~|lGv80o>a8lOGJCwph`C5hv5i$)3Cn!I~b3Frq)hS^0#!UNRk-ui1T)hF& z;GDsb&^?7q9j-h0I)k8334i|PyY-PXkc$w6;=wua3zji*OfrV z-!VEV{o%6#fOdXO;>>nh-m-*+wJVF4Gzd88P~Lc1oC(YRSPWv)hgCokP`KP$xC;6w z6|F(13-YinkfRn{CeI)$l?P#O4dUTWWWzc2B&JwJ;t ze;oX9{Bf#0bLV$0Jzhj8nv3`UUb`my`zo9*Ld}&2oClw44}W@c ze))U%g&#i~o+8_JE+-y%|1FU<}E)(ZEyUn&LM7x`yCmhGViP^ayamQurvC=Rz5jf$7cyz?QgXfmtfZ*k6#PkiDMQYhE z80BMtF$dE$lN&A54Cf3$q-O!;Z~+BWB9aqPqqq`LB-0=&HEUiFm;Z7!^}20L#5r}i zNX%sBeMJEn5Fls&_pH;*^3Z*Zui#e^I*{4|g}HPb%r(gJh%A?Qeo=NP2}u#h^^Hfq zAAp%*_CWav%g|gG=3*GO3`p zhX&0W8E#$$bzlo4?^{=0NQ1;UFLT+h7z zEo7a58OH13{b2=`2RWb_ZCqm}7|&a(&~A8VqqL^5-K1tjB$H(~hsip&2HptpICux- ziIGVF0}iDCV{j92jSwSe{FZz!FEh#7POPs=#Xu7elML3wAZrXFZxoXa1F%7ku%L-4 zGiGv5y;_>c{mf%2XtuTTf(o3CIC*GR&F!ekdP49n0#1RO!KD~I6M)h{+o2=~A@m`L zdR*|z%KR$56PXwiBeX$lReMAo;_mcrO!2tX#P-!GnApqPfXd#@8Aw-xhmjXD`2xvk32jUMz z10t;2K-4tmVX#6WnuW+1JM94gBb?RZQp|B6O-*YAO06!pxf{}sp|lKJwjGQ(y@QBv zv$LhxdC2eOCI@?P+i?I#WFpak1Pf#il?7)yn@HfAh8_n;@nFo)sD1#7Boh%c_TdLi zX}pILtZ#n}XRz5b04+-fjX-4r_E0Br00UI1xB^7QhR*^Z3$mtpNQa0Rte4v1FI}gm zd>?BK^R{xtgqVP|CSQ@fE05_311Icw`KYDE0A?CP8RM)9a{US;Ru5OZI~piy5MkOBoDoX#n@ja#Q6_r28-iG$;xb#c0}>R-Rl>Z-F`TGTfL39>Osav2TpC zV!1VQpPGjzD7p9#?&q7>Oy&$@xFhq480cSzvKiQ4(=^CLJpuLn85csXcVHTNAkMma zU1!DDbz47#AT5zhEylXx&|APHm{O+jKoq^g5;nMn{0%dKnnSiIiVzM^d{}-hXc`cQ zwO)JtcR~*hpe&0(25Y?=uD+Z`dWcjc<~-uJ3(Ho!086}GvB!648gm^kn8iV=Ap(^^ zORXK50e6rJ(F-lQQh>|l%qTEP{tYDOD21|NRXe-JZsIH7i?{t2XeJXH81853&1$S* zb=UryD5tf@JdI#}!(_%V0+ReBJtq%}qG*CrF71;65lo;mQ&1k@lhR=@0eC9x0zg&A z0f7ZN#9@?%q9}yQ7*iBtaXcYn5GFPuECOy28w3Ctf&o%Pv%flsA?W!6$e&kPM8H73 zlf)ifEybWS*PkUAn87%!$OupH`Q;Wc3A6?mGb!sSFA+WfvtUye$;7WulOA1TdLnB>@O#*32kqkQjv@SQLSe7awm-b@_do=hqj%p7(jqLmzwo zkthHF0KzVz*M67I)NdcG-U(mnbM#Jg1ka(#9`3Zw4hQ;iM8Lcv?}nz~vuLL(k(H3* zk7S>-)+xS0eyHe!^dD^*H*;+89yXR<@YxiAT6s72?)alB7jnpIWVZ9)7vE?9{M^6s zv)}hW+~soc<-z%Wznr4Uds0d_!K#w{Vo0jLlN zmQ+g4o&eE;+;YJ;77-S5W-0f#5#e z!DP4-5Rg`8CmW^^)0sghhh^Ydaypx0G=9fig-^)EfiE=Eh!|oM;n~vgSGznJeiMJ# z_-io-tW7P3IuPzgYcA#xh~fa^P?kked}oIPFta2~02utl1wREcKy2yFEfa@w%aLo0 ziqfDk!mhI-G5|#OVMJ!!2+H4bD0S?e(omo)3th6EgqET`)ies!;GJwHpqX^U1fT`C zg2!gAnGMt;(4aN|(gv>R>3BL?yuZ8g&fze8qxBnjkH18pv-;xT%LCsS6*4QD@Lcr5 zgmYss?YIFUw!qI)B8?3W@^s zI?@>6`r%Rb(8&!AF6I~e26&)EoCS#u2vcvs0CAQ>73L0zZ&*(WVm8ST0X9eerue~- zMAtFg@>TO^r~i=WWK10JlAOpE$W%bAHETSJT~vODg&Xwln|O$2pzF|xIEo$XuD8$g zUwiylw&>S{w{X9c43ZCKT^KBX<+?xrg|!YcuK-3bX4l}KF6O1vm1Iub;M+;k)l<+* z8?Bn{9Z1M+wLN1kp-9w-w42o4s^4GfyaRLijPtry=6 zqIaC7U2}8_jKG&6mu0@8``Of#mJZVOCzq@7ui%g7eIxuba&hMmN*<1MLI1mv{uxtG z-TD2S-B$z2o_ZE%cqwoR4o=i6tyU@y#7jA!b3uW+0-$Z9+P`lfC(6YQDI)JoChSxg3H0*b7|f_`F8k8 zd@+>Dq+Ayi6aZNQz$F`&!u{sW|22|daU1peqwcuo$&Z{pf3g3`$bYb-@ZjYn-&z0S zQ{W1?<$pd5{`A-DeEan8dh54aylI25hvuxg6RM9l|NG;D`r?RmEc0VZi$a`700cH~ zL|w19yvx1txn}1#Ow9!ZwTGh7Z04cyCq%A;W#Cb;1y-DcCBo8Tw74rMI!5!-LjHj* zhy+AvU}l(xXMq6gU|rmY#z~&9JXM>*RVqnUwQg8&VrSU61WpE~LkI1f=k%|Y{)amJ z#EGx--Slt&VLo`;uU$?4N4w=8y>0!ai}71rU3uN1r|$Z~pW1!z{X%W$6VV(DUQ3fqdLxAkf-=Ej}6L+1IN;=ppZUz$)W zd_#}-t-LemoW`Gj@*j{$B1#|-OJK-l3OGjUhHxS@Uw)_59Dd;s-rNGYl?bwIT?Y5S zzTUP4>+IL;CnFB-ce3P?9#g+o7hT|5Y)KHMq0--=_>a=n@A)u63;u(HcLi^P06e6N zr9BVtQVp(aRnH{)r^_xq;dWu`y(CM%ydSN^K*B5qaQ55s_1_&{KaQGX4Ba=(VO4h7c(Xe`Z7lgsE!Oh`N zl4*(pP!u(dX-x7W+1*?LA$;AuJ#jih3 zEH(9|}8V6&~$ zBDDSQCJ16fa1a0nT;z>fu!y^wLk|4v&(r@?{Qqa;>;Lut%W)}u>gIP1Gd?M#fBl>G z;os@ID~??9gTF@cYn%Erx$k$q_8WWatFz{bumQW-9jWV&H>^8 z^Z>w&3mmA`7~|rEgDtbq%3!bA@D z=^V73*5+>|{$c=}IDs+%u7k2_txwf#rzXb?8;<|O3AwlsN8`a1g&`}5A$7l&NKC}B zBA^Hexu)s~G6fTER@l%*($ITh`m@uXGSlv!7hhZrlz)SQ-62akp^Gzh@4*~&^-uoP z3Nk^Kk8J%r6W1@yPxZ_n*M+a{oM-<__xsZKHTf7lGxWgd;p#T_1Da`d8uuJ84pC1@_<>;bqX*(xAK8!TT*cgF8=# z|MxF#?`R)X{$}Za;&6Z#S7(d*^q;mi6A^$jPu)Rzy@ z6?+Zzl=z#QJR&}KR>hAlkk1(pw91)|J`=rwAMY$TwV0hQe+hm8$kAI5+R+JZCEP)D zxCBLH=*9q92?mUKKpMn&o}i4HX(7-E5cxNRqJSg--~a*<{ef_dwDfckj8O~-G0YJ~ zgb-AQy;x~>`jL0o;N%0Z>MU}@T|MDeOsqyj)~5IcolAtD8v<%by#l_!>A%{ None: # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0) + def test_read_lossless(self) -> None: + # generated with: cwebp -z 9 hopper.ppm -o hopper_lossless.webp + with Image.open("Tests/images/hopper_lossless.webp") as webp: + assert_image_equal(webp, hopper("RGB")) + def _roundtrip( self, tmp_path: Path, mode: str, epsilon: float, args: dict[str, Any] = {} ) -> None: From 13e24379fb15a3b088a319ccbcf3a4a3adf5db3b Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Tue, 15 Sep 2026 12:58:22 +0300 Subject: [PATCH 2/3] Benchmark saving and loading various common formats --- Tests/benchmarks.py | 101 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 19 deletions(-) diff --git a/Tests/benchmarks.py b/Tests/benchmarks.py index 11ed3cf743b..6d27a595a47 100644 --- a/Tests/benchmarks.py +++ b/Tests/benchmarks.py @@ -20,16 +20,21 @@ TYPE_CHECKING = False if TYPE_CHECKING: from collections.abc import Callable + from typing import Any - BenchmarkSave = Callable[[Image.Image], None] - + from _pytest.mark.structures import ParameterSet from pytest_benchmark.fixture import ( # type: ignore[unused-ignore, import-not-found] BenchmarkFixture, ) + BenchmarkSave = Callable[[Image.Image], None] + + if not (find_spec("pytest_benchmark") or find_spec("pytest_codspeed")): pytest.skip("pytest-benchmark or pytest-codspeed required", allow_module_level=True) +IMAGES_PATH = pathlib.Path(__file__).parent / "images" + _save_results = os.environ.get("PILLOW_BENCHMARK_SAVE_RESULTS_PATH") SAVE_RESULTS_PATH = pathlib.Path(_save_results) if _save_results else None @@ -43,12 +48,6 @@ # for the benchmark run, so that throughput (Mpx/s) can be recomputed in the future. SIZES = [(1237, 811)] # Primes, non-power-of-two, asymmetric, approximately 1024x1024 -# For benchmarks that act on test fixture files, these are the paths loaded. -IMAGES_PATH = pathlib.Path(__file__).parent / "images" -PATHS = [ - IMAGES_PATH / "flower2.jpg", -] - # These are derived from the other configuration, above. RGB_MODES = [mode for mode in MODES if mode.startswith("RGB")] ALPHA_MODES = [mode for mode in MODES if mode.endswith("A")] @@ -59,10 +58,6 @@ def _format_size(size: tuple[int, int]) -> str: return f"{size[0]}x{size[1]}" -def _format_path(path: pathlib.Path) -> str: - return path.name - - @pytest.fixture def bench( request: pytest.FixtureRequest, @@ -567,21 +562,89 @@ def test_draw_lines_blend( @pytest.mark.benchmark(group="load") -@pytest.mark.parametrize("path", PATHS, ids=_format_path) +@pytest.mark.parametrize( + "path", + # Commonly used formats and decoder settings. + [ + IMAGES_PATH / "avif" / "hopper.avif", # AVIF, RGB + IMAGES_PATH / "hopper.bmp", # BMP, RGB + IMAGES_PATH / "hopper.gif", # GIF, P + IMAGES_PATH / "hopper.jpg", # JPEG, RGB + IMAGES_PATH / "hopper.png", # PNG, RGB + IMAGES_PATH / "hopper.tif", # TIFF, raw + IMAGES_PATH / "hopper.webp", # WebP, lossy RGB + IMAGES_PATH / "hopper_gray.jpg", # JPEG, L + IMAGES_PATH / "hopper_lossless.webp", # WebP, lossless + IMAGES_PATH / "hopper_lzw.tif", # TIFF, LZW + IMAGES_PATH / "hopper_wal.png", # PNG, P + IMAGES_PATH / "pil123rgba.png", # PNG, RGBA + IMAGES_PATH / "pil_sample_cmyk.jpg", # JPEG, CMYK + IMAGES_PATH / "tiff_adobe_deflate.tif", # TIFF, deflate + IMAGES_PATH / "transparent.webp", # WebP, RGBA + IMAGES_PATH / "uncompressed_rgb.dds", # DDS, uncompressed (see PR #9943) + ], + ids=lambda path: path.name, +) def test_load(bench: BenchmarkFixture, path: pathlib.Path) -> None: def run() -> None: with Image.open(path) as im: im.load() + try: + run() + except Exception as e: # e.g. the codec is not available in this build + pytest.skip(f"cannot load {path.name}: {e}") bench(run) +def _save_format( + format: str, + mode: str, + **options: Any, +) -> ParameterSet: + id = "-".join([format, mode, *(f"{k}={v}" for k, v in options.items())]) + return pytest.param(format, mode, options, id=id) + + @pytest.mark.benchmark(group="save") -@pytest.mark.parametrize("path", PATHS, ids=_format_path) -def test_save_jpeg(bench: BenchmarkFixture, path: pathlib.Path) -> None: - with Image.open(path) as im: - im.load() - bench(lambda: im.save(BytesIO(), format="JPEG", quality=85)) +@pytest.mark.parametrize( + "format, mode, options", + [ + _save_format("AVIF", "RGB"), + _save_format("BMP", "RGB"), + _save_format("GIF", "P"), + _save_format("JPEG", "RGB"), + _save_format("JPEG", "L"), + _save_format("PNG", "L"), + _save_format("PNG", "P"), + _save_format("PNG", "RGB"), + _save_format("PNG", "RGBA"), + _save_format("TIFF", "RGB"), + _save_format("TIFF", "RGB", compression="tiff_lzw"), + _save_format("TIFF", "RGB", compression="tiff_adobe_deflate"), + _save_format("WEBP", "RGB"), + _save_format("WEBP", "RGBA", lossless=True), + ], +) +@pytest.mark.parametrize("size", SIZES, ids=_format_size) +def test_save( + bench: BenchmarkFixture, + format: str, + mode: str, + options: dict[str, Any], + size: tuple[int, int], +) -> None: + im = make_pillow_image(mode, size) + bench.extra_info["label"] = [format, *(f"{k}={v}" for k, v in options.items())] + + def run() -> None: + im.save(BytesIO(), format, **options) + + try: + run() + except Exception as e: # e.g. the codec is not available in this build + pytest.skip(f"cannot save {format}: {e}") + bench(run) @pytest.mark.benchmark(group="allocate") @@ -853,7 +916,7 @@ def test_quantize_grayscale_to_palette( "source_type", [ "synthetic", - *(pytest.param(image, id=f"{image.stem}") for image in PATHS), + pytest.param(IMAGES_PATH / "flower2.jpg", id="flower2.jpg"), ], ) @pytest.mark.parametrize("palette_type", ["exact", "grayscale", "web"]) From e1cd7fd4ce6938854b6093e7a51e8a6f6941ff49 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 16 Sep 2026 11:10:48 +0300 Subject: [PATCH 3/3] Tests/test_file_webp.py: im, not webp Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_file_webp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 8c44b6f84d6..e7c1c7c9f9c 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -76,8 +76,8 @@ def test_read_rgb(self) -> None: def test_read_lossless(self) -> None: # generated with: cwebp -z 9 hopper.ppm -o hopper_lossless.webp - with Image.open("Tests/images/hopper_lossless.webp") as webp: - assert_image_equal(webp, hopper("RGB")) + with Image.open("Tests/images/hopper_lossless.webp") as im: + assert_image_equal(im, hopper("RGB")) def _roundtrip( self, tmp_path: Path, mode: str, epsilon: float, args: dict[str, Any] = {}