From 0ace3fcd2677dab6e98d976fe58787115b48b6d0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Mar 2022 22:35:48 +1100 Subject: [PATCH 1/5] Added BMP RLE8 decoder --- Tests/images/hopper_rle8.bmp | Bin 0 -> 27086 bytes Tests/test_file_bmp.py | 12 +++++++++- src/PIL/BmpImagePlugin.py | 43 ++++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 Tests/images/hopper_rle8.bmp diff --git a/Tests/images/hopper_rle8.bmp b/Tests/images/hopper_rle8.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0fff4a0d43d4e44dd4df3f6eb2f94d047d74efcc GIT binary patch literal 27086 zcmYM62b>gD*2Uj@)jiG(Gr$akFa#MSXOJxG2ogn-VL-uvy9R>dsz?w}L_h^ZaY2$9 z6N;jsAS#%UsIp*KGa?G8tA1cYR@m=+@w%$3YP#>a_uTMqz3S8YzcbR=zYZzq z8nZVwGrY@op8Z9aI`>A!IP3ri^Yijt_Hnr`m0Qc@RVZ}X+0|TrK?Rprc)Tmj&v!K# z$LAjBs^k~C8WjuO@p&~|Q9-_|S(xvtSIu!%3#+)ARf=4#q8hGtaZOjPYO%}8Y2Z>h zon3C#My`5p16N$Ffva59!Bsu6ovY9HHOtR)jdHSF{roDfX?|7LpfJxhXEZ9zawis5 zbthJ>;p)|_<(gOFe6?cNp6feStl~Np<-4{V`%6(3*Qt83Ygk<0b*fpeX_+8#HofwQA`u?by=w?%c=qZ86sU{qzp*%)V#4{%4PI=UjH3yRz(TH*(l$clGG8 zZq&sWx~h4%x@v{Dx=Q)CyXq&+c8xpDajjdea1E+2am`BJaZP*9a?ScKbnQ;R)wMrk zf$Lnh*mW3ko9i^>G1q#)kXf$R(5KzNQS)5y%a*&|BiFjVV_$Sb#;2qU8#kZt z-kf!TdvoT+Zquww+@{+{yREY?bKB;QcAI8i>Nekgt=m5DO1EkL74EJ1*SIYUCb*q< zjdAa?|Nf$JZs**;ySh8StE_chK7u>vg%iW?!*17r1o^?;mo#P%|@}yh- zFk|^z_uP|f-P)CF+{~q$-0X$#xts6#(cSX!Hh0To|8k2T-|XhE+Uah2{2Mp(@xyM> z+TY#0RR`TutGBvG*8S{ObN#cM-gl2~JmyyY^IP}u&hOop#W%aRpIhZNzqHxy*#55D z`SHKq$NPSDJHGwReY10?`|7j(?!edIxP5y6>rr>?k7Mq)-;TInzdPu@{_<0|`^^o` zf97xZr9SwS_deUFy$fmfQaN|G_`Fb%s>Ja^U&S}}^*Pdz^Nn~d4h6nSXvLYjzHzAHtMl0f zp+2t_dc1ipZW0`JsYYLrC&{ zWuz*$QH?9Bv0nqrvmu^^vC;-e44nRPsqhexe2VY&#C0= zg(&kB@d);5;3bKqPOt&e@K@5UWGa^({U=DsHIZ4I;ac%1N|=q#bJ1ZI(vx-x#j-f8&b3 zB^Pu3NpQ-GR~fAozybUaeac%p_#gc{@aqeU0g3=7IsT6(#2nctF<<5{acY?Ae}(Vo6bqmnB3opYRPIhY$TnYOx zGj7B>kNWF4Gt=MVZw-ILig&`)!M;xXZlpBM8zO0pnC;K>TQkw^bK+Ym-z2LDk!%|M zDhoaD0?T1ouoGE#B_AfAz===6`4QjzkuZvDSSQA7St%Ro^{OmV77lXFIzHt?_}vgW z>W2!Ufv*Eg8X|@AP66IV^aA7AT=hFRWT8j-d?CD%_n-|!LB9@s7lZC%kS;`TgONH# zHj~w&Z-{?UhI;;7|4s(s7a$xA3uUvcKP^`Zjzj2C*mn6(vJT?M2ZfH;oqia6kUr2# zh`uGw6kx#uvz z$kPHa*0Zy%QhEMGJnhzm!S-8NUC6Z+{P9UuivYDk4iP|cO}Q~fcfB_A&-v~CZQ{je zSoK}%Axy+|;-l&waZCKzKj7F;{PlT$sBh~lg~ptzm^3hSjT70d14k-=u@&-X`*mp$VHu5YVQF)Ym7qE0 z-2@9s!W=?&zR4-?$?+9a!nNLSGhBQ8XGD$|;l-keOqnSgANOGSH2ep-gzE!v2-g9& zACj$SBd2WTkxwx^i@zz4j#g{<9Bfd4tUIyqQU4#)(T89g#!=M^8mjT?>V2qUaiszo zL{*TG{)I2(3WCWgww`VK{|Y^w;+CU|74TE1WS(f<=$eSxiR$6D2qEPB?ol#R8X>Vo*~%E_u{ z>yj_=EyH)ebDlyt#)s#Ugcx5X^dvTp_F}5o|O3DrL z&9tm7{~T&j(kfV(r<$n>o?e+f)q@RrR{-{6Uzp^o}9rx9PfqpqSPk zEQkEztpDIahlnT2y$zAMVY*6EIVGPVE)v^iU3rBvxZY`-4XVf=S=v#lIhZEfm~2?% zdE(n!7PI#R%_d|I@~t$?Fm4&nB;=!g%%#1-g z*R0`xe9#_pl6U1B#6n~hD(z?F<(>J0&4!d`#Ew`yedLef=|>Xj{V)+vH7Vz^3d|d- zAZLAauiS&2i8L+$Wq>kPQs|s-%rO+wRzCS?eL^+~ics%TwJ3)ox#5d>L5#3#ku0Cw zDcQ?oks;)LoUO@%h9XeoAc>-ky!bq{ebm@;4AwKNgaMV1&f*fXRnCetG;%4`1GYl1 zCO2|wU-o16f#&n2Cv1{JmP)<_HTP!pu57p`Q$H)iC$K>{VJ%uL%3_b}sne|9iR_3` zD#jc|Yx!1`H$jF^{IQI@LqVW3l_hb|Y(TGgsI(2M6=lI=aw4VaJi`~U20bLOMbT%F z@^*O3WL1rIJKC=11D+{XfE$VB&0>P&HCl@u}0*(@!M=6i)3^UDrOHB!;l0Vb^XgA!2*V4FOg9;m_n~0PxTW>Br}yGdoh}@ zXI8Lze@6e=${Kd9xG26f#I}m@(q2Bi5RWP&n>s1&i7!RsIqZ-hD6+(gLS9N`j2%Fd zDoEs=_2C!%fi01n2@F4h>Pk{?jq%*#;S}o@DY~eZl5a|O$r}B%NIuHDa{b5HDhbPL zbY+hMu5L(1l`qD;C7UQ3XCZGcNYWrEkBahCW#p_BdtyF$Kxj;6ZOv2bC(?R+Cy#~G zHZ!kBr)xc)y3zmBeDzE7)&KEd^IY+cYycAJ-T03z;(I#3+)ITt3#rRs`B45snD7#! zO^l$qiK1m1%}QD%ghBG$7}J$q8#B|8d<~?3iiBKFcevHQ&nJ5t~`OO zs(|AdSa)Foi&KhNp@=xE>c?ukP(K~HW$_k{C_YIm`S^A-tO2{i8_2zp94wh*wNs1) zl3U-R%1Ck}FHwlsKjSI;lGLsdqv2K&b2&-=huH!@ zfrsQUMJm%bJmN3JFZ?8Am}LyBz}!aFgl1Rum^ExC|8A7_!xl7u08W)sKWGk{hZQ3V z)vMYQ7In2$7&7b~YR*8O5XsFE3`J32RDz}AndjcHVWJor%eZy}vEblQRQm3Z~OtOa9tC%J{ zu}3GKfJ}Z)m0|t5EZvhZUp6_T^yFs$d-$1|^a18E?cimpKgK-Zm{~^L$&zn~JLciA zKT8owJ}kzR@LC^_QpZc0&!)?}!M_P_HWQsTnY>@}nNddNfuUF+&LLxtfbW_E*gQa5 zmcCV$C?|+#nvdyTik&mxZNOajFcrlS{|mLsLgK~u$u|}?eomzQKd23)dwHJZ4y^r7gQ6xq8Aeiju`#by$`mrvZ=-GtF#&qHp5Q!7DbWcHkHC ziIX^|{)qUUXSqX|8gopuV(CkN7kxz1D-wZ=%2AlmDP8Ss^Id(1dNQy{^8mxN*e?Rt zBtOB=2tsw8|2h0&bzsz<*kUoA?l^lOp*xkTpMZiGRiEld8Bvm{_fgj}-gdvmtn#K= zrI603{5?VTkFks9@i~SkvHLgpvN9Z!>rPJ^&EV*^nH58iP^FLci;?#R zBp&N8LF&u>aNbR(!;IXY~4z6kUcY3HJ zko9h4o$N>XOa00I^5inp^(A=n??`)?NR+4h4^}x@JOg_+cVikSb&*UOE=Uz(w>k{^ zN6h0JP}7}=o}2kIsRERdw}c(|@{aH(Y^m)VC3Vn17Blzn(6{QwsEs6%|3G#*eNT4@y&csIKP3}nDX!zqS6jt!Q(fBs1cZ&hB`=E z4O{*=?rVcYcXJF&IAERBUBY9Xa&gLX0=}CgErK)NpSHWY;8nGk!^pNe zNd48Hu*3+EO!HTUiR@ht(=H05!f5O>(T@i0$%<7L#dL2$-l|Fi8{DyU=fJRs*Vu|Y+BanYAm_~snj*0N_Qaira|B@U= z59$jG3k>Enm6LL@jp`)3^GtkdOt-TGcq%0w;Nt+kYlHtWwOt)!#>VhA$Tq{e&4#Nf zxa)x{htDmhs@fag_Xol^bZk2^bFCTzcF9fqpczrTJD#6J%gH{TPd|i5$SxE79bt^W zGK1uDv(9MjG#V`8-zct7bohzrb(ndqss-gK&=9p{ojj0eRwyL(k*coYXvXWA{+zG@ zyS!s;ds}@B!(;xFV4;NU0oxSaG)IfxBP?GMnZD)T$0?zc-3^X+vV~TN6c=*PQh9#Y z7CWXA@*z(zu$^G2F2hD6xC00>Sa>PCQ-uBuPdd!se9C8QRub>fS{%hj z!lwE}_oX@{TF>w(cG(_&G{(InY*>b_SbB@1oZkWGTKN(~BqkNZw7o><58&Rn%n8SW z=zMUU&$eT7JXPfJ)bI$7rRW@lieyG*ssyjoov+SFKa-q(C5)O3j!{H_Zp4G(VI1<0 z;`7E~y-8d#8E*fMh15?K3X9a}DpsAcQv)JEeUnW*>i`GVQ=M!NTT=`o!CP6(ErQ|z zSj0B4G~)V3yW~VY-vQ&|lzB zL6*+gq?(~HEUDP5I7D>n41eZ>KwdFAjN)B4KRh{=*UP|hiOD}29E#kN_=f$)tTJ>b zuaBc5Qq;8=EG}(8^Y6$4cn1BCIPej5(LjQ)Y-i?#w?rhVI$-heYc?3Z#SWJty)c}Q z^ncYJzS1dN2*Ymh6T%fpT#YUkokZ*AerFn;;P3O3;Qy#(L>Lam-G>Xb|^Vm>Wj0D<3n9MTI(qIgB0*rb00pV%6Mf1CdYQeMFw`k}P zEs+OYhvcW?Po0T;9l?AuD8^w2;ZYP=OilJP-&T>iX`JyZcAQ`_cDp@!)7eof-RIC#Vqy>(Oa@kXpWE6w*HRpo;78K&ME$a z1O!PZe+n2bVlBw1y9YD~83$oysdCF*F(3Qrq+mfsN)nK`XF%mg7pZtL6b{RIAK}?xHXDvZs5w#sKy9TSfnDhAgX4O7tpS9x6s7Mq%8QB9gVR{ z9Kr&MQhy?mMbzmR3e}U`;h=Ua0YeR8@L=$rQu6j&z;H*zK>Qz_TQOCnL#Dm4xd4FXRSTq!<-0uxP8fk^K8M)BIIf;_C4C za2xX1z}qzsUxZ#Shf^IHC&Q-+u!hJK$PmfcFoGkOa6FzL!>0*Dg4NrS5w<5g=)j6Q z()#wSZF==@C)k5}LteZCi8U8NZkv^8n~@&LmG30Kb)B$hBRHiat;?~bFEkFFkA#cK z=hGsVWDIDoN&e2eF(9c1hdxI#%{?yS{iSea8f$Rw&?~;K{2))B5vCEJMud~G#$<4- zpErmK<6lVpProCPuA|Q1NuhO6$qSSZWCv`5KW||UqOr+vnO~SE)Wa6K>zEjd=48y; zuv2p<<5F@y)&L7xXB{+eZw3O@^1AOWe~>4DBWQbZ(CV@|$ng(9FHkWBERy;=Sh|2z_4))PoCcDq zTyv*i8tx2B{R94M5Zue_KF0mwZbLCD2#dP+FWCLJA7RzTlr+9AY)4{zgg6>Grej;k z3-XG{Eg{jKaLXR*`!q|MXQGnsao2)ZO;o{$x@M8%K_d_8gjHsOV=vk7HZsOMwo71( zAqm%ni;#a}xCwpV1cD{JE)7e<*U3J=JA54;2=|joreUdR`2LJA1?}tZVT_+_Eutzb zXej=OTk;dV#=Tf?Y^R#tNY6@x4hiSXLkvj=vRyswQbLT?8eN~1fTT86!3pL$ScYoq zeAbW19=hlHV3@~B$wg$bss3s#a*e+jdky!~(DJkZ4(zZ5Jooy2sjpde_;u<5u2+1! z86-2Y-AvX)4P`ZqX5Sl-=8g2$KwKi4$`dR~$sdsVjTDkI-sHICwy1=*>sY4Fq0f^? zoM>gdYKheHcy%$vD6(yk9cY@_6>~bewfHx#J zoBX@EatTr|@HYpYpGRkfeU429$?astA;u|XYvO5^GMwTqb@!iEFl!j2wqUO}h+5V= z#K+ktn~1cV;FB_hDs$a6Q0Gi5ms;~q{!+{4`6YNh>jaZdX!>*iEqE5m65&3WeI+)y z+ACV!g;%R~AHgy6>3~I&yV$>y`2}$+8O;?pVWpe=-6_4^fE`DYU&`$P4#g$omSU5v z0hfe8$B~-~HW3cx3Skkawgh7p8AEITG-FZ+k#&={AM?1pq@H2aN|%lxnM~D7KO%hT z_k;)iTr9JYDv`Mn`C|-zuI$_e>)e5yL??8Ab+{|sOwBPhFgFje-w@B;#o1{fokYx- z98Lq#Z1`oCq1uyl^!q`u4K}fNoa%5pYSDH}N)gNYy3R4buTE$8QK}8CKhj#k9JW+c z<{9cyXkV3;7XX*f&5mzcBBHl9t87u^f0!=&Yq8?ZaqLr-E)VBato@J9x{zm2_ z^oKyT7yI0geZFEl3q0NEzjda^dJ@@_jtNMXf`@pS{9C2+4d2%Vdn-5Pf=Dv{)kOe~haGYdra z=dg)73VSosw?Xy_NdG9_qB{qT&D)!RKv`bCqB=z@f4>bMtGngDkk5zcG2P&~I#crb zZssp{k;m^gdnn#2YfKE&S?hf-mbis62~;sQkJ4Va2alOV9jV!s?h`AsSf?64vCJXA z&yw{28=HBzDd_&$n~KCKbr{v_JD zKkQ8&4Eyo@kw||9etS2utt0xpBe@%PjYi|L!@c;!Zg71ab|D9IkE%>PUouW zK}@^@ADhgl%mT;GWPOxh9Fn~FJg?f%c!-W?P#Ck+ReUZeW5O^HeSnm^luhsq-G^?J zh*|QKrWTD=LzaMt6}RCNn59^zSTzS-$U0Xb&3(yNNHhstcfc0;%NV4+N}A8w?e|fw z?8Zh*K(hq7?=+5$CUPrx>Tl-5(XTi&*J{Z1$c{}wWYO3V2}8uSiMU`R^=42M?IruO){* zAFGLBp4my*f^0Mk-9CUc_aoJAq*Fb)6ls-3rXu}l>=N_L0`SemGSfISlfP0uqxTV? z`o`0U9kSY2uyZl5U+|4_EsV0wdi=$hCp>9I>gZ33)7UNYF3umVzHAA{nu0FQ!*W2< zhxcdr9t^OB+MzC2)PUVFL1FE~Rps=aeC}gT7iCIWKRi0%TbSbh< z#QRk-j7w&4%~UMfiFw0xLnsf^ow5B`?tZp=u-p0!3)ctLnTm0dm1e1+tRp|$XlR(F zVJXdE`oOh;*dyB_bT5B;=mDZ09Ph`r3G~bvC?pCF%<-zh-8{~X>NQGbJOAM&E$M1hN7RCDXlEL`bJ+HpVX-;~yC?^3)fV}!<^;>1GZ+xXIe`!<3e?3%DzR;m^x;TPvx{cS z^}vHC5%aiTX>qWO9CW|MIc3nfXcJxp&5b8A*VJq!&SR)HG)^Djw z(rgoCX0JwYPI2!HqFzsq^sxvLv6Yy;O6W6{ST}YG)_IU`Kfs-rh1h8k*zOlL>~tmD zCDbxg12sSxg{5u=Pn

6ZZLk+w55}a2(fB@qlkBj1w;Pk~PDg$e9_~YD0|WiS9IP zOfwHqwO|oXI5cz1%r#X_SWl@Lc)G*1MnV0x76BAw)}FrCpJ){6N{D-k8F~t`CXxF= zQ0UIfBECIFr$yjUJcwwfVj>2x&$QnjUs^L))0=39R1&ZJg(l1&D?Y*F)LZ7RUO(LbUoppy5iKMpz8so&#G@2hs2qh2#l&!&9iLZc&Q`iS8y}MUdC?C zjd9J%LoJcLAM85=9DT4`+*2<{-E%ldom3mtC$L|Q`e7C|>1W732@YwtJq>wOmEMVj z>KHx%&3-G&+kvfLPg9w$pv@w3B%3XUIa_*i)>15 zME1x)cuDjqf4>E*Y)UqUO+rI_kkmawZ!{ibwsKTIvx~f|pW=ttz4iEnkF2BllHz1(CGM5mM%nCZ^DxcM#af|Szqa_281>`eAK1r(Y|T#8)keJEz=-rsa2p25ESP0`|B zV&oFDm7?JtSQEq?SH*F)4gIC&C#orgPLI( zpK{!5)VH7uubB;HwO+Cn9@Y*H$jAD_Mv$cX;90G>n^DZ%WGLPzPgCD?vRUs`s)Un4 zGA?Ltem6L#(p|rpI5^!uK3Oevka@(l>#5%cqea~hpWv^fY8XME=OQXORTIiQ3limH*=V%C2P8|`djQ#~ zClnk{I`om{#+8 z)<5)uU3A_-p{Lc_F+11OxXr;a0HjTQeYyxkVfcB}9a{A$Jeut)>S-=}1K)fJcgsM7 zwZ?Mf67Z-XNb6%yt04nNOVS-)6JMg5U|4c#Zra=~}MSakoD(QQj z%{C;p%sMgVb)*iw9aIxQakB3gbZ`AKx(3%-$DuT+TXz}fK!$A;{u$l)Y z8~qyj5LSDY?W@>Lchg&g>I|M`Y=v!p3R-(G6i&*&`V$fBFqa$>hVsh=%%~XE`TkkN zz*4#=it6eOm4d1U*mMMh^zwACjXPLDt2Wdxo=26a8L&{Ps^y*{S9PNAcM;eoQ{$s0 zG@`q_iVL5B>_`6+@@@ndwV;1F^{V#31R~ii{TdyYh-Qs(a5mEG`TTz|Giqn|%D)H8 zcgzA?6Y*m1)!eEKo6TlkSOyxcjZi0PGF%a+u7+VGn$*3X$*Zdko^=ihm_R_`E)5k~ANn<`sD;Z>?L!fM{s>%0)sdeCDJD93pU{FLCs#@gV zF>pmII%GT0=K?n%pH9eMwa{~UB$S-KdB*$nWuTzLM zT4(!%{k`tjmlA<)WV~QcQh!P0d!2o(#{Mh#*ehw6$94_)#6Z^avGz?* zXb&Q4=OO1$LC^0U1RXt#M4ncM`{k9n52;zd=J$=L0~Ir>vD!y#t;fR|^?th{NmsLz z{7X3FuW59_Zj*_JR1zSIHM_F$gm5x{x!6xZ2Q#qY9RCa7UzJbPuXB|7^Fk?+Koth^ z2e1iO5H=g&1Q9r2p607yVyEHe}`Y+Aq z2lBoNbw+JsWHs(hToJP9*~)6;K}IwTx0{V-AfxQ4WAZlEx`0D<{{2>S>}M~o7ajo< zBM-+0aZ%mqDd-2=hr#%?dUYGH+Ox6~^1sGjLTa<#=_rV zVPhDV3)bJ@p6-^f+%Lw!Es5WW;E_Wo>}`R<{&$*xXQPm3dcsXOXnCvi}} zzM?hd9f=+4-K%dlC$-2gV9umF-Rf^H0>SsFfktapnB*Id^un z5`Rt@pTVKkAus!9v4-S*2@JBPiB(4E)bzDH6UMyeX=1G46A_VHZvbH5iYSpXa6DC9E>5 ztg>hjEn^l+gN1yo$iQA|jbRcl256LVRju}7jp#spLTkgCfJbwwULYF4Xo06_HbuQo zRV6%43=eh*33f^Ons{+H*tQrS*&{z8Vyj=fFQs`evneoXTR!%H@EfjBM=y2>VH&7L zVqdMf%t3dn{WFYJ!p1;)C4p`(T_^{ihN#I$GeqF3SoRar!Z?22Ud=u-$$e`A^u z!7SpNI%LhnI3hw1uB(ksRhI|urR$e@PQsHE{R|&g6x%JC8O#FldUf+Pz9t5! z#?x%}K6+=;KOPbEG?jiiqpoijW%jERBqLfeawMVTUCVezS|j+KaSaUEhPa~JkbfaCQtdY8ofzD}@buUJ7XnI(_b&PVK)I&2R&J?XO(mK^g$qa)=SfpJHri_ZJH*&nEd|P+^78t&e{M+ zFE}r)izT%ab#6}wlV-!riNPW6sAu*9ION0X!l-*Oi`>PpGz5LSo?#V>^eh9R|}m3~^% zM>&ys5c*b6Q_lho;xomW)3FaVEODm9@8n6&667ApY^xCIsqhl}4HiksI$23+K45#q zO>jwey$YeQ$QB5$XBFcayH>xGBn_OvO5fEmXf@KU^v|ZBPCb<%r)2f)r=Ip}LGvW< zp9zw^J9?(d4*ii>@ljEb9vBsba&JG5_d#NHz#e6Guk5vxy%whXE8vm(V~(dyrCa^B zL^@Q$0>YqQ+R-yCT2HMN|9U1!Y!ZrS56w#S1XmVF^!Jsq8Bb|&Ot1CO-)jH3;wE@j zVv(oJ2V|j$hb?&G{z|qhvCB&Ef!n_XHuYT9YZ(NGn)0rnAspy`^}; zs=wcZe$A^L*R&x%YCagB9oG|CJQ0JICAneJYtR#KLZhdYi}(bdPJ#>2hCjn>kopi zHRE|^C{2-B^Molu&(`V}g4B1>N*_IqUMJC7RsF(be1fzJI5e+_Pm=Omm&~H#^L2VY zUC-?n^Nf*x%|=}t!vx{tQ~k;fpa_rq)#0&hNiQy~P|RZQ2}#f1O1AQUSwhiENO~dr znbf8oLQmbpfK~m#@CR&K2by0&*$=CfQNtcG6eschEvagmGBT0H>_rsN_27<_xo zKjEKDKNX(zD-sZ~eFB8oi-C2Ll|hK$8JJ{`Gm-r)q*HfTo>Rg;v!n0_vx(n8r!xbY zSMXb8{%AM=(^x&9!BylN6A7Bbv|3=1rWJP+xK)kkNs*Hg5eS_S)nu+*lUh{s)UI}4 zdv6Ajx;0(7O3xwdx1RI_&%Pi*WO#*_bR@>$_A+7 z1TSBPv>?M~Po_YX*hp4HdnLpJVutLtwtRp~HQu?fPF>_dplHhLP7wW({7!ruYG`W{ z3mb7>-IiT^yH>ii2URz|pK6A)nu|07jhsFtDF)XN z{{x!)h37JU;QS9zzhTOt+yyxLF+-7+lqgnv9NH -+XL9&fUtU^&g%m1A`uEh5~>x2prKU;VrfgpD~H zSBZ$l2f$v8?-aB5Ly)B;_b=fG;^S#S9E6{2yYjc+f$s~>&*iK%aT?xUks@Si3^_z-3Q-ha-I2M_`cOX$%CB_^78`KLnAi zast*Fgin?QtrBSsE?KStR}UcD_29^P=>2zWP>rV`72&EOZ#9@Ft_k0L*l8|jJ^=9# z91}hb{sK#m=T-BV59sXZcZN2A?QtZQWfVQc$tUD>?13?ZQ%4`te(H&U2E^Ue3NaIG zA0xef2jwWAI23*>Gc^UCMX)71+G)^zMfpKdmyc-OLXB#3F#E-@HVwmOo89KZgCmJpc$#ldbQyy6ghtQ39^w0s@m-IQX;+3Pi1AO! zV#Yzq4;%5bawJcIW@UOAR$88kCE{Oe7$Y?DrVsem9xzbz_=DK3F)^bjcpk-0TAkd4 zcV{v~5+8-c_gL;b@D{`7 z;-nhr-shO*d!Dg7V(}YNIL40IJhX>h}T));oX_#7vUtyS@ZSNpNu&57c^s0W*8SssHCo}!N zp}TodnW1UPIaTNlh-t(DezWI4DWPE{FOjb_{D9oQ5MzD{hmct)zn9HG-PZ5S61HEF z#;Q6bUz?FDMmNc*I4;j;Yp+7EEUn0}GAZ|-#{wA{42{g|&*fE59n?n0>JqiU=X)Tf zdKR)1IM{*$Y%O7C#L*h74PYn|jD%xtsMPc;L*mj=BBwG@E98#32TY*+!*bGKe5Msv z-H9EJTHW%0u!|~{7%V?nD@|J+6mwFvi17?WkA-FUO^kE$BVke;T;U%}#5Lv0N0~{e z^B7k_G(k=(b3;VeE@9OkeCx&0?qDlNRO(wMr*pJT22BUk&VP|n9<3G4#lixDxkQ`q z@XznqJ3{SHh85ddPSr0K9fj#HCz?q-gB@favX3N;QCK!fi)+I3kgUYk7$^VX_~Sxi zUelaNrT&0AGxexob+-(;6Y+=6ebOCiWg%sqHb`+FzS1b@oNRIh^?0#wp0q^vXq8=b zNzVd98M#Ik(MRDJ9yEj~Hk*zY_f`0-cE~S|{g=oOhJ@Uome-R_OxMOK*t9Z8b`5-- z`ffQ_sD9NPn^)|hUWI1XTGOFkfo9eqNJaKb^Ud%J>&7BA|B>)`V6Gq;@mn!Pd-7+~e1&KM zSz<-99G@UggF*b`6%6VE3xoO`!q6N2Yagv6xn$JZ_7-@C=H&8~KG=fa@G(zskIeEY zRo|!4y|X)K{Jmne?u4=`+h!OIvA|(tnd-m)2%$U!&(|-g5Ax3^kAPt{8rQt%31$Vt zqAFXnRm&;ImHWG}h;hrTh7(q}mpn=Tz^sA=)YBG*6Ah2p(jozkc*EK5hD=$dHS>nS z_;qKr-3%O>+t#77*#nxEe8w4}pKnKuBLC5MYJ;r$#bf;%_Yrvuy_1h%T6;3?BgERr z@#v?>@YGp&FMMH$RgxcxIcCupbP>T5c7-Cd$&UO)UD$G|rzg_OwU?3&mL;lI9^-H5xTb}IPA1Hf1)A`x zo~687(KX)1Iz7=o^5ZG}h-Bv>D-aw8G z z&=WbDTmDuTP!@=HtjHay7-i)bS}XYiHT~d>UkpPoX5~oP84vGD4pn`2E(|)9(yXC9 zvEq4n^E|cB^GJ^$Yv2LW=tCfq|4J%Z!!#@u$+GlvllTd|*`gCtFNgm~E&ZbfS-@D7 zv4CnP&0L$o6l6wL2C=q(h^!Kew6df*mN-n+s+H;o*gp~7YOd26Ua8(1$gy9;QKUa; zT3?+YwSO4tRv`c5;o-0xx#hJF8`CAX(3tj9VoAgXK0L)_w;77|!3Tm>h9KGiFL3_< E0k1{J$p8QV literal 0 HcmV?d00001 diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index 47fc97df055..e0c70f037e5 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -4,7 +4,12 @@ from PIL import BmpImagePlugin, Image -from .helper import assert_image_equal, assert_image_equal_tofile, hopper +from .helper import ( + assert_image_equal, + assert_image_equal_tofile, + assert_image_similar_tofile, + hopper, +) def test_sanity(tmp_path): @@ -125,6 +130,11 @@ def test_rgba_bitfields(): assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp") +def test_rle8(): + with Image.open("Tests/images/hopper_rle8.bmp") as im: + assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12) + + def test_offset(): # This image has been hexedited # to exclude the palette size from the pixel data offset diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 72e40b05f66..650478db941 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -24,6 +24,8 @@ # +import os + from . import Image, ImageFile, ImagePalette from ._binary import i16le as i16 from ._binary import i32le as i32 @@ -167,6 +169,7 @@ def _bitmap(self, header=0, offset=0): raise OSError(f"Unsupported BMP pixel depth ({file_info['bits']})") # ---------------- Process BMP with Bitfields compression (not palette) + decoder_name = "raw" if file_info["compression"] == self.BITFIELDS: SUPPORTED = { 32: [ @@ -208,6 +211,8 @@ def _bitmap(self, header=0, offset=0): elif file_info["compression"] == self.RAW: if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" + elif file_info["compression"] == self.RLE8: + decoder_name = "bmp_rle" else: raise OSError(f"Unsupported BMP compression ({file_info['compression']})") @@ -247,7 +252,7 @@ def _bitmap(self, header=0, offset=0): self.info["compression"] = file_info["compression"] self.tile = [ ( - "raw", + decoder_name, (0, 0, file_info["width"], file_info["height"]), offset or self.fp.tell(), ( @@ -271,6 +276,40 @@ def _open(self): self._bitmap(offset=offset) +class BmpRleDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def decode(self, buffer): + data = bytearray() + while True: + num_pixels = self.fd.read(1)[0] + byte = self.fd.read(1) + if num_pixels: + # encoded mode + data += byte * num_pixels + else: + if byte[0] == 0: + # end of line + while len(data) % self.state.xsize != 0: + data += b"\x00" + elif byte[0] == 1: + # end of bitmap + break + elif byte[0] == 2: + # delta + right, up = self.fd.read(2) + data += b"\x00" * (right + up * self.state.xsize) + else: + # absolute mode + data += self.fd.read(byte[0]) + + # align to 16-bit word boundary + if self.fd.tell() % 2 != 0: + self.fd.seek(1, os.SEEK_CUR) + self.set_as_raw(bytes(data), ("P", 0, self.args[-1])) + return -1, 0 + + # ============================================================================= # Image plugin for the DIB format (BMP alias) # ============================================================================= @@ -372,6 +411,8 @@ def _save(im, fp, filename, bitmap_header=True): Image.register_mime(BmpImageFile.format, "image/bmp") +Image.register_decoder("bmp_rle", BmpRleDecoder) + Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) Image.register_save(DibImageFile.format, _dib_save) From 11f1ba35408288332cc29202bd1c8bf65399a608 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Mar 2022 11:55:18 +1100 Subject: [PATCH 2/5] Skip additional data if past end of row --- Tests/images/hopper_rle8_row_overflow.bmp | Bin 0 -> 27086 bytes Tests/test_file_bmp.py | 5 +++++ src/PIL/BmpImagePlugin.py | 8 ++++++++ 3 files changed, 13 insertions(+) create mode 100644 Tests/images/hopper_rle8_row_overflow.bmp diff --git a/Tests/images/hopper_rle8_row_overflow.bmp b/Tests/images/hopper_rle8_row_overflow.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d606dc3e41accb4a1847f74829761c0fcb81fa21 GIT binary patch literal 27086 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

t5T?h4L<4BIHile0jievrCd%D!Vc ze;GQsgnQLmu`vNFsiL?6eQB+YRu61q1^b?$zVJG1rM0cw@SYbfQfvz^^B#;~H4jR* z`VI0Sto9yoRoi^MMSL0Tyjtt%r6%(qheIy`)3gY zJJUT;R9A1PGpMS9O-DdTFHiT{xPuk6YD4|v`BaIT0SlF?TJ9-wRR{Whmw;^&H9lHG zBf870xbPv!zW2`|?^bY83;O3%FKQ1=Ad=0}FVS&{Xf_xJXCu9y&wrPhQ5(Bg{w-L( zVHVhuh!=CO=2j)xY&P@463}REggQx+;EFJHF$}}etnTqvWweJ^T6d{??CtH&SG*5# zAKgdwTD5*?irHBf7HVC0D*Ije6phZj@5r}KBCmhV?BFTcnLHw`RCcb z9A3Q}{0-NN~J!x62J@3ErpMXv)OP3(Ad~FmyraMEr55at&JFNR*9i1}lbtukTXzt{cx&P1S_7|+;~6riAT2MLzb{tJorUS=PwvHt=-_CgxwvE2YZF_5)v3>Kz2B}#(#7l~ z{}RslYZ{%f+a%&4l?2FQ&8}=bKAgf|F7cDm!3=CT$N$9lSK<@(>l`Kig3y^rpb7)| z1K5Ns2%8OXf(V>1ac)ByESJR*Flil@#sye}6?}Y>P!7U^zfiF>$7etPRR^6aebv9JbQ`d$@G2Oom9P3$jxotNFwOOMg(b{d zw9=V%T{eX5UQmII8tM?U>czx)trpRGoy!zoU|^?2UN+D)GMUdinNMZq9q~faKW8=q zp{&Gv;d&uU>|+R#sFfjOxN<@GggZN0 zi9aWd%iz%JkmvoASVQtY2L@T|$;dbDb4Ix0`3)T5I*nulv1NmK*ALi3PY(`(ch&h^ zG8?G+z&Em3=*DX=-#w`TBUO#`23b>9HO|7rwB9NYeRekeifgPq3X8)6BxHRB_WA-W zUxhe|l0Z1fyYz zIH)i-q~%Y)GHYo`e0UcdHK%rH&O58xLn(%S$gRlGjf|zbun9cN!y2Q&Q4~_df)aAD zGE3}^ea5QMLlzId2%m+62`K!*LN0*~fWJweou(F{+~Y>IlF zs!Dho86NBs66})l)$ro3ux$xG@|paEh^>C@!Ib8`%%;GkZRz+7gkN!mI(o572-83{ z9Q$g`We&Pq@1J0-6E+6Y8|T&;4jnagsVDuqAf`Qu7rhV%s7fL#WLG4sL!Uy3&N8M6 z5zHdKsYBL8j3Xj+=eio`RCRgaUb=pn=VUzjG{%i^=QE_Gmu_~VGa>nth~|qBXZN~- z$j6;G{rtdg7uT+**%^Jcq2$BlB~9T@n$j%Qopj@O%J65X10^q>s&HYa*5I z!^ktbW%a>2thxk`W{n^Vjj4cCEhwv|F@@$Cx?j_UqaWkLiemdEGlN+mUaxMx#+SqZ z)p(lC-b?RH`o|-Jo~F_-XVmshqRf7Mf@DN1Mvf$uyc-!$Nb5x5jX^vzKTghKtFFHK z``v;t^a*E$eteeZJzSB z(WXWjbd0X6!;Ct}c?L*MRQ$2Xsx_#3YDrIs=y`!))tGANNr~2Y+!(YrYIHINzt&Kl zHiw#wGvHCp){$9-*-Q zTE(oYguoe&{I&F`S4WdcyjC=9}_GYDv!bWUKqACxT);kuP9vR#o$anGmFLRaT2@ZEY&R z-71`~!n?{G<*C3jtbMW^e&0{eVt&>Ua^8&GD{wtIUq?e{8cZM4<+cZslG5P(IxsUtRoV5Xt zo^W1T7fWg+>fD|ICe4PK6N5wCQP1odaL9+%g;Don7P*UGX$bmuJ;N#%=~)Jz-AVA@ zgy(lSB24mTWdvkaW{{hB2S{Pg=hM#iE8z>eH<_jDDDxU5em=vMQr=)6`6_4Dq#u{` zQBGtYfWFn!)U!YX_)Kx;4D3Sjwey$YeQ$QB5$XBFcayH>xGBn_O%O5gP`Xg$)c^-rcBPpwIiQ?h#YQ;++Npm~h< zPXx){4Lwt3hqI7a@ljEb9vBsba&KRb_eNrMz#d|Duk5v#y=JEStKgCPV~(dyrCa^B zL^_nm0>YqQ+R-yCT2HMN|9U1!Y!ZrS56w#S1XmVF^!F988Bb|&Os{p&-+KRu;wE_3 zVv#lG1F}%W!xlVoe=Xa!*kvvF!0n#{n|iM5wG4nmjd|DC5cc;!^PFpc;S0LErn!0^ z)!(l{zvk73Yg!Q>H6M)6j_Zjmo`^xqlH4%qHRy>qq0v*yMSKF!awYM(Nv^ardNL@3 z6oz`;7sEQ@*_y=g1S}FQgD1!;YlEz{HZ3bXnJA(*f?ETqw7Vm(`jJ%JK{MAs{SnZ$ zWIW9br7<#Vo-jG+*;@TVkoqoK>7%F7YbIK&s$ZClPmopwhvpUWNm736l37%IzE01l z>$%-xo-xv|*{Ew{m>^tys$aPQ6yXuSK0KT)>BXhhidpPED(Tr<%U1p`ODK8?Nl#=y zo7%Kp=%ITUu&OT@euqt)K=U&w`(l+6YS^QO;$*(RIhD==Pz3f`qGPQqu-_B4d7sem zOPFBPuioi*FBQ4;3~>wIfs0Sj^QgsCfy&?XGx+2#>{aGUJzW@Ct0#cLlsw=chHnr1 zNBv{zHQ_P8Isp;eM?r|a7+5D+8-xg+fl2l_8`;l6I(3KTIknhlb`*YRHt`GSbf!P^ z3Vw^s{}B$uG*-`Na25H6M1rO;tvXnwX~o?HZdK-aQsiVr1VSf7)tD<+qZZXXwTs=? zK9E7AZcP`i(sRiAttUOf^B`Gdg?}JCj78Q6jejgK{A!^y%dG{Wc^Ce(8gG)t6c-vB zia(h7whYb9GDE!P<-Y~Z0Y3y$Yoc5orRUJ3Dlm?67uEFIudjdwn*Qx|yvC>ry+7ev1&zY^aD8`>Jg z!UmjIw`CvSu9YrrLDiM-r<&oc<{}M1qvw@{sR-$2BY8*0Payi#f0z8kUk>6^iorF= ze~0FN;<=3PIR72j9^jQH|9BM}+aizrmPmy^uQyaew*t#O>K_TKjFk_|c0wsTTD)6> z^oT{Kluf4+i<2Mt=~Ak9x}WJrKz% zCt{re_+&}YDv_4plI7}gbw9FQcaB_u-haggm3azM5w0@wR)%@vn(*C=o#t}p9T0!V zG2zqTFRD!$9#zTxcgL{ATYi{-unZ!v5x zPAY@$ZH|gBznNyYSS=x&NM7Q+VNt}Fm2{kaL-&AL(kzs$NUb*ZwL-i6+6K6ef#f{! z#6Cn{Y;(SOR9~uAd6oKinzfb2h|c(gqOz>ik1OiIpSzG&(cm30jN>fXDHP)?{I-N) z`ipvJVj< ztRT9rz^hgm(}YNIL40IJhX>h}T))vjW|&%rpJAAuZEq(;u&6g^^s0W*nedHyCo}y% zp__S7iJ@uEIaTQOh-t(DezWJlDWPE{FOjcv_zt;$BF6j>jv}*Aek+@Sy0zb%C2T(< zja79>zA+k z^(&H+e7!Jo;QK{)yhQy^mh@8qqEs#6r9x#FOcgslw@tGD_ zbuV`K!|Im*gI!dq#9;ZsMrqpWpqP`QMT}=4dN{1WZ(^L29|@D<;A;PHBCaV{KEzBy zoyWKeq7iaZnHwUyb_uKY;9F0Qb^}`}qRzf~at23RWze)U?fi+1@@TDSE*2IL%q7}< zgMWU*-fz?nC0Mbwm<>9*~vT8sp?Y9DhVe z%xjtwsnj1(XQmDntnQW}cOw4Kxlg(wtt_Oh(+Vl>#a9{xos&(jq8=~yO_S!x9<8#E zF6lX7C?VIVB6>d@$AbnD#b(p-;=T%h)du;+u|J9YU`WXQX?Z=_#B^<(f=z3KWY@sQ zsqa>Ch3Z$$v3bP~>Q!iFtu-C$6=-G+f>dO`G~Wcju-*(;H0HY_uNuNK#jf7e*r$W3 ztueA6KBgK_ab3SwSAfjQhL#m&BQ!6Ia91w0AtxL$`B#QV0&@k)h~J7S+LJ$<=F3J4 z$P%lQRrmyP8VuqeuV7FYSQymj5QbjpU;Ahs$t9!Kwl~8wG$)s@^u`wahL3rATV$3; zsro*h?w#E^hqRRxPKLDfM?@5#v@^4JWK{FL{jqfmsC$sHZIqCm9~GrC9-0RP= z{#?}>=JZGV=Og!ne6Qq?L=PZ~d92AV4NH29qso5Zi#}}@Nw2WK<0`xZ=^0O@O68|l z8G0Z`Q_J7#0?GpMjup8h6{D>DLTe?Tp{5_0@rxnI#jG4DJL2J8$f2sw&WAxqQ<^oj zC00BQZ=R<1c^c{QV+}k&8hrpn@?S|MYnXQ(R`sil9kAPX3a zG8Rznq?v0In1am6${^OB6(Xy|BCRZGiY1OwwQ8mMVfIf#x0>s8gjcG!`g82(@CVW# zF|DspklH_pbgPm7k?>$xh1~Mm2aV~HTWC!CDX}DC10SAZvfB(r```n?Iztd`fEPIb E|4{47!vFvP literal 0 HcmV?d00001 diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index e0c70f037e5..15b4b3670ba 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -134,6 +134,11 @@ def test_rle8(): with Image.open("Tests/images/hopper_rle8.bmp") as im: assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12) + # This test image has been manually hexedited + # to have rows with too much data + with Image.open("Tests/images/hopper_rle8_row_overflow.bmp") as im: + assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12) + def test_offset(): # This image has been hexedited diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 650478db941..60617dce3d7 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -281,17 +281,23 @@ class BmpRleDecoder(ImageFile.PyDecoder): def decode(self, buffer): data = bytearray() + x = 0 while True: num_pixels = self.fd.read(1)[0] byte = self.fd.read(1) if num_pixels: # encoded mode + if x + num_pixels > self.state.xsize: + # Too much data for row + num_pixels = max(0, self.state.xsize - x) data += byte * num_pixels + x += num_pixels else: if byte[0] == 0: # end of line while len(data) % self.state.xsize != 0: data += b"\x00" + x = 0 elif byte[0] == 1: # end of bitmap break @@ -299,9 +305,11 @@ def decode(self, buffer): # delta right, up = self.fd.read(2) data += b"\x00" * (right + up * self.state.xsize) + x = len(data) % self.state.xsize else: # absolute mode data += self.fd.read(byte[0]) + x += byte[0] # align to 16-bit word boundary if self.fd.tell() % 2 != 0: From ae06f2e2743b3883081b2dbd20682f1d464e0161 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 5 Mar 2022 00:34:17 +1100 Subject: [PATCH 3/5] Added file to supported list --- Tests/test_bmp_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 440bc325b45..b17aad2ea50 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -40,6 +40,7 @@ def test_questionable(): "rgb32fakealpha.bmp", "rgb24largepal.bmp", "pal8os2sp.bmp", + "pal8rletrns.bmp", "rgb32bf-xbgr.bmp", ] for f in get_files("q"): From 9db527a473dac6368348ed4d16de7f38b6cca310 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 5 Mar 2022 09:15:07 +1100 Subject: [PATCH 4/5] Finish once enough data has been read --- Tests/test_file_bmp.py | 7 +++++++ src/PIL/BmpImagePlugin.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index 15b4b3670ba..bfe24e0007d 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -139,6 +139,13 @@ def test_rle8(): with Image.open("Tests/images/hopper_rle8_row_overflow.bmp") as im: assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12) + # Signal end of bitmap before the image is finished + with open("Tests/images/bmp/g/pal8rle.bmp", "rb") as fp: + data = fp.read(1063) + b"\x01" + with Image.open(io.BytesIO(data)) as im: + with pytest.raises(ValueError): + im.load() + def test_offset(): # This image has been hexedited diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 60617dce3d7..7221d7f065e 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -282,7 +282,7 @@ class BmpRleDecoder(ImageFile.PyDecoder): def decode(self, buffer): data = bytearray() x = 0 - while True: + while len(data) < self.state.xsize * self.state.ysize: num_pixels = self.fd.read(1)[0] byte = self.fd.read(1) if num_pixels: From 039b7ecd561b4e9755738bc4f169083ad4c4d83f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Mar 2022 22:35:00 +1100 Subject: [PATCH 5/5] Finish reading data at eof --- Tests/test_file_bmp.py | 19 +++++++++++++++++++ src/PIL/BmpImagePlugin.py | 13 +++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index bfe24e0007d..f214fd6bda1 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -147,6 +147,25 @@ def test_rle8(): im.load() +@pytest.mark.parametrize( + "file_name,length", + ( + # EOF immediately after the header + ("Tests/images/hopper_rle8.bmp", 1078), + # EOF during delta + ("Tests/images/bmp/q/pal8rletrns.bmp", 3670), + # EOF when reading data in absolute mode + ("Tests/images/bmp/g/pal8rle.bmp", 1064), + ), +) +def test_rle8_eof(file_name, length): + with open(file_name, "rb") as fp: + data = fp.read(length) + with Image.open(io.BytesIO(data)) as im: + with pytest.raises(ValueError): + im.load() + + def test_offset(): # This image has been hexedited # to exclude the palette size from the pixel data offset diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 7221d7f065e..7e7e742cd74 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -283,8 +283,11 @@ def decode(self, buffer): data = bytearray() x = 0 while len(data) < self.state.xsize * self.state.ysize: - num_pixels = self.fd.read(1)[0] + pixels = self.fd.read(1) byte = self.fd.read(1) + if not pixels or not byte: + break + num_pixels = pixels[0] if num_pixels: # encoded mode if x + num_pixels > self.state.xsize: @@ -303,12 +306,18 @@ def decode(self, buffer): break elif byte[0] == 2: # delta + bytes_read = self.fd.read(2) + if len(bytes_read) < 2: + break right, up = self.fd.read(2) data += b"\x00" * (right + up * self.state.xsize) x = len(data) % self.state.xsize else: # absolute mode - data += self.fd.read(byte[0]) + bytes_read = self.fd.read(byte[0]) + data += bytes_read + if len(bytes_read) < byte[0]: + break x += byte[0] # align to 16-bit word boundary