From 4deb447f59660aa938861e5e2406191ffdd1e074 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Mon, 17 Jul 2017 14:43:13 -0700 Subject: [PATCH 01/53] travis runs for fedora 24 and 26 in docker --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1f34cd7e8..874694e23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,8 @@ matrix: - env: DOCKER="debian-stretch-x86" - env: DOCKER="centos-6-amd64" - env: DOCKER="amazon-amd64" + - env: DOCKER="fedora-24-amd64" + - env: DOCKER="fedora-26-amd64" dist: trusty From 7397c232920608c0e9d51fb199a5393a940280af Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jul 2017 14:22:50 +1000 Subject: [PATCH 02/53] Updated redirect URL [ci skip] --- docs/_templates/sidebarhelp.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_templates/sidebarhelp.html b/docs/_templates/sidebarhelp.html index 111fa5dae..da3882e8d 100644 --- a/docs/_templates/sidebarhelp.html +++ b/docs/_templates/sidebarhelp.html @@ -1,4 +1,4 @@

Need help?

- You can get help via IRC at irc://irc.freenode.net#pil or Stack Overflow here and here. Please report issues on GitHub. + You can get help via IRC at irc://irc.freenode.net#pil or Stack Overflow here and here. Please report issues on GitHub.

From 96abb60059975b04dd5fd7a7410989085b3f9492 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 23 Jul 2017 23:39:40 +0300 Subject: [PATCH 03/53] Add newlines to error message for clarity --- PIL/Image.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 49c9f6ab2..fb418396f 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -56,8 +56,9 @@ try: from . import _imaging as core if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): raise ImportError("The _imaging extension was built for another " - "version of Pillow or PIL: Core Version: %s" - "Pillow Version: %s" % + "version of Pillow or PIL:\n" + "Core version: %s\n" + "Pillow version: %s" % (getattr(core, 'PILLOW_VERSION', None), PILLOW_VERSION)) From 10c7e20045cc4e40a7d70043ad0f6308b19aadff Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 23 Jul 2017 23:56:02 +0300 Subject: [PATCH 04/53] Test for #2639 --- Tests/test_imagefont.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index fde312f44..9f159e9e0 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -40,13 +40,15 @@ class SimplePatcher(object): else: delattr(self._parent_obj, self._attr_name) + @unittest.skipUnless(HAS_FREETYPE, "ImageFont not Available") class TestImageFont(PillowTestCase): LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC # Freetype has different metrics depending on the version. # (and, other things, but first things first) - METRICS = { ('2', '3'): {'multiline': 30, + METRICS = { + ('2', '3'): {'multiline': 30, 'textsize': 12, 'getters': (13, 16)}, ('2', '7'): {'multiline': 6.2, @@ -213,6 +215,14 @@ class TestImageFont(PillowTestCase): font=ttf, align="unknown")) + def test_draw_align(self): + im = Image.new('RGB', (300, 100), 'white') + draw = ImageDraw.Draw(im) + ttf = self.get_font() + line = "some text" + draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left') + del draw + def test_multiline_size(self): ttf = self.get_font() im = Image.new(mode='RGB', size=(300, 100)) @@ -395,7 +405,7 @@ class TestImageFont(PillowTestCase): def test_getsize_empty(self): font = self.get_font() - # should not crash. + # should not crash. self.assertEqual((0, 0), font.getsize('')) def _test_fake_loading_font(self, path_to_fake, fontname): @@ -494,5 +504,6 @@ class TestImageFont(PillowTestCase): class TestImageFont_RaqmLayout(TestImageFont): LAYOUT_ENGINE = ImageFont.LAYOUT_RAQM + if __name__ == '__main__': unittest.main() From ff81201971dff74add6686d57e4ec8ca43419d93 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 23 Jul 2017 23:58:05 +0300 Subject: [PATCH 05/53] Fix unexpected keyword argument 'align' --- PIL/ImageFont.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 5b2611717..19977b8bd 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -36,6 +36,7 @@ class _imagingft_not_installed(object): def __getattr__(self, id): raise ImportError("The _imagingft C module is not installed") + try: from . import _imagingft as core except ImportError: @@ -113,7 +114,6 @@ class ImageFont(object): return self.font.getmask(text, mode) - ## # Wrapper for FreeType fonts. Application code should use the # truetype factory function to create font objects. @@ -162,7 +162,7 @@ class FreeTypeFont(object): def getmask(self, text, mode="", direction=None, features=None): return self.getmask2(text, mode, direction=direction, features=features)[0] - def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None): + def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs): size, offset = self.font.getsize(text, direction, features) im = fill("L", size, 0) self.font.render(text, im.id, mode == "1", direction, features) @@ -250,7 +250,7 @@ def truetype(font=None, size=10, index=0, encoding="", and "armn" (Apple Roman). See the FreeType documentation for more information. :param layout_engine: Which layout engine to use, if available: - `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`. + `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`. :return: A font object. :exception IOError: If the file could not be read. """ From 8400660083b66076403d97a5d99cd065afacf5b3 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 27 Jul 2017 14:12:28 +0300 Subject: [PATCH 06/53] Move so isn't installed globally --- {Scripts => Tests}/createfontdatachunk.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {Scripts => Tests}/createfontdatachunk.py (100%) diff --git a/Scripts/createfontdatachunk.py b/Tests/createfontdatachunk.py similarity index 100% rename from Scripts/createfontdatachunk.py rename to Tests/createfontdatachunk.py From 0477278c68f93f2e0c9f37c0badde62bfe0301aa Mon Sep 17 00:00:00 2001 From: Istvan Fehervari Date: Thu, 3 Aug 2017 11:07:51 +0200 Subject: [PATCH 07/53] Fixed bc5 decoding BC5 decoding uses only 2 channels instead of 4. The current algorithm did not initialize the RGBA struct which resulted in random values in the output image. This commit should initialize the decoded RGBA struct, thus setting B and A values to default (0). --- libImaging/BcnDecode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libImaging/BcnDecode.c b/libImaging/BcnDecode.c index b24a3ebb5..58d3ecc10 100644 --- a/libImaging/BcnDecode.c +++ b/libImaging/BcnDecode.c @@ -815,6 +815,7 @@ static int decode_bcn(Imaging im, ImagingCodecState state, const UINT8* src, int case NN: \ while (bytes >= SZ) { \ TY col[16]; \ + memset(col, 0, 16 * sizeof(col[0])); \ decode_bc##NN##_block(col, ptr); \ put_block(im, state, (const char *)col, sizeof(col[0]), C); \ ptr += SZ; \ From 47aa59a0467bfbbf2ad0746c9464b8edeb06b539 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 4 Aug 2017 12:39:19 +0300 Subject: [PATCH 08/53] Add from https://samples.libav.org/fli-flc/ https://samples.libav.org/ says: This is the Libav samples collection. You can find samples of various common and uncommon multimedia formats here. The size of the collection currently is 54GB and growing. You are free to download any samples you want, but if you want to download more than just a few or want to mirror the whole collection please send a mail to webmaster(AT)libav(DOT)org *before* you start and ask for permission. We can also ship the samples collection to you for a reasonable fee, inquire for details. The best way to download samples is with rsync. If you decide to fetch a large amount (more than 1GB), please limit the rate to no more than 50 kBps (use the --bwlimit option of rsync). Please be aware that this samples collection contains a lot of files that are very obscure, broken in various ways or are just simply out of use. Hence, it doesn't make sense to download all of them, unless you are developing a player applic --- Tests/images/a.fli | Bin 0 -> 102180 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Tests/images/a.fli diff --git a/Tests/images/a.fli b/Tests/images/a.fli new file mode 100644 index 0000000000000000000000000000000000000000..afa8c6d6793d90377920c0ee1cd952a0b211c398 GIT binary patch literal 102180 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%0A1f6oAKA}1-Vx-rg7ZvwU;5x^B#BlK@ zRnU)^pdVq?s*_3Ka0%##^LnPBU%=f#Dd-oVaC8S{pqI(ACny2^T&*9lfxa=%>;_5@ zzqx{Vr4v{W;&J|P_MV_E;%UCqwkfSW=w~zNEuFx6KtCJk&<6?Y0eviTSbBgqh@Y*{ ze+lAo0BJUjWJmn#CgRuRNJ6!93`W1{HRGMcCPU(h^`|FF`$OqMi%rJd%**d{&I4{lMU`AsY|j}3>k zBA9rOi9@_bi03JwS7;u?xpp* z^e;m#D&`--wP1Pb2MF-5sepgM1pEuER*mAHF9H1Xc^y;0&ti%%1^g@&4vH@WyiAr9 zUjq06F$KhgDif<2iOhx0zLh*pU&@h`qWv!gvJV``!uy~hf@uWm>0H5m@ebxM zAI{;f&tE>wwN&OWedhN&RDM_IFMX5^e#fzD<@1+PMw0w5bCTERFM&>l=6{jXd_I2} z<6T98=YLT)g&8W)SC}3{uF@$?h-Fdyl{(EEQoV@21?crDOZO3psdT>51KAprmG0vxF`ucdKEczJS3@}}^64$0ME*;H=O0yh{sEKc zA0XxG^ON`Iz}My{@6Wwc@_Y>>ufblOoCK|a;|1!C^_!T|5~-5n6}*$h)6yNPTmF3> z3f!}~N%0+h>KA!1)Qcn5iUM?n<5QsP%Q(~d#wa6r1L#!CY*w=FxC21F@RyJ!tawA9G}+y!}vtx0zzI$;Ll3Z(n4f_q-gv z&kFQDq@wq36TNpA(R)`3dhg2XDDf5EsWP?#UHEZ)yFD7OAxV<-uX2(mTcm~JaLzOg z2doQWcua@ABKneFF!xJq0FKk$>5DeguR_A;s7>5WXMz)+CgQ7fHfDrmP_C?#bb3{R zfrTLBGsb!Of^`uqq0F-B@djR!WM3Qi3lz|1#-#2V;G}?KB8GNKg6SI8o}l^o|JNDQ zssOB^4b1Cmc824%HDV=B@PignsQ06J}tL+T?kmRt`IBPWz!jZEKU3yN$5K?iGZDcS-D z+A&B1h=L(t2jT4|2=B-ZoPzMq0tf}6r69bALFgQfHmZI6!MK)vJZO~0a(AjIywOA< z)?1k<#F7##3UA0OnWC_;MMJ)Q3`!RAhAw?E=eqdUR(=eslKBAA0y*M4BgFS~VxN^| z+PTKbL>+Aju}0@APsG1yhd(LO7JowKDpp<@58?o0`Q4enY)^v3(t`KU@Dkp`(c4UF zR=~-`-&z^(CKGHo=XOlNhS9dZP|6eRl!EPc2HRT=ti3tM8Yq#lc8iL&KbTmHN(~?1#4I;O%5_A)Blzg!@T+t9=;QFKxt94jJp8SS9%TgX|EA5*+x<<> zQ`%tL1%agul@G7|UlVxL?T&IakoT+dF|+qeor{cdvj58hXw*S*h#^h80dRoBQH}V^ z81Z^&isT&2z}Ee3h8JV3dUZ-R#{D4y7-I~3jkHHLhK1vsW7um^qB4fXm&#$Ojd}j> z|6Egl51Y%Mjn&6N$fw2&@5Xh*+0&^zm2Xv?${@_*RKE&P1^BI=kN;e6Xo&u#6c5*x z^Vi~P9VU{J$_1E3{TjZG1;2$8u@8p4`lR>ZI(yPkWpHY!`qgzq!MY(7HiW&d1<5bu1jvRtR`RImOIj#A#Q6MaJ9@Ha317-=;|Nyy{wXl)B-@J8b5FGn4BGh{6cWR&7?2o_y-TR}fW)xl4#(9?M;&i8 zZi9$_%MdYWLBwDY5&v3(h<`02;u~d%xUd8f7nUI6>o!Du-HwQ_wMWDSrHHto91+wn zmm%V7>p=wSUW$ls*b(usRz!T$f{1VBN>4%Z?E)gct0Cfh>qErXGDLjEf{3pa5phll zBF@PxmvY3}Wr#Sd1QBPIAmYn5M10wfh<|R6h%c2Q;!EX-_+lGG{PTJcfx4F>;%qx2 z=%nFnay4U)|7AhMzvN0!5kco_OCpgRK>=sZ5$CTD5&xVaVxdI=&yO!h+|6-acl`9j|UmzlUw#~WyY2?~rx z=;^V4xCV-BWbp|V6xrS7IVeWb!Nn1|>eE|Crl82i8w}}eym9n;#~aHsp7=)#PyC|< zhBDsxum+5Lyz$|zcIhx7{E!9=WxVl04H)@&DZ=*`Ad#ij4m9x~qJJ^+#drgCHzARYH$J36B8wV6tU@BYdr%IE z;gl3Uk}EyEb#MxaY`pO?s(m)z_&D=~{mliP&+HzUA>ut2M7*bnh`+ZZBKZ5faZ z7n0T9T>=Vv7ZSe94vZl>m+0W&U3PfgJZqlX~)UF?QybCDNgn&$I0K7 z$@NO$wDCO^MfT4u_ikP_?xA)I2BVj{#-)kZVH@$z*!1I0v zkMlX)RC^f&1nK<6z8QM@E$HcI1$DyXv-yj+mZ0aYc^xHs@I|$?JxkDYD>QKM;NW1m zhaEvTLAB1o2SX&>!;T{QE_)9GPO!TLNc7#?l{nnpilvZ_M8|gaVRd0t&N(-u(ZQ}| zc)F3$I~eR*iYmNiM#E~mu=wr3@3_}i`of7|8tmH2xAZ{yxlg1`HyV4n~;SliYP!MmBn zoPaz+!*=^NkVF}k%3!c{ z87S|fP3u(6VCzy`-YLvi+lqB+1?HWA3YAjdrad(8U>nHt(B?9H^0G9i;!&C5TdXL( zi(ul&SCGMWHmu%NW2|ECF6b-5_09-YMiEG^?T}+YV)jmI$RL*L(nEGMft$8B*qL;a zBKKh|O~iYoT^PErR-D(6sG?Ww8D{iulc9Hs1-(mH(;9jg+tC{?&TB5w`xuVLrGv`r zi%KB;2$e1%jF(SXAsKCM2Qrp)gG*B&d&m}UZU^&2fNCQJGi>J67Fqy}_2mv~h9z=? zh@EN+t)PBVa6+`8Ev`|NC?(vi4B8K+(01w6)6GiZ{YL>utTm6L`K)*=#Q!KEPHLzx zXbcPoS8VjIjK%3zL`4n|9hQ0FG1B(8OZgnDz> zNWlM503PcRz!x}|UZ1B*TOYiMG?W4!+Xt$XgYH%g_;Fa5P8wDbzYDJA`;LX#sB}|v zRJti^U3&|*i3ay{KZ*8c1n#(mda-_^HcQ7o4{x7VurI`-SsMP+0WWs*5ibRU;^8wbZjg^~ zQ7XBCeC`a*70BAT8%(1cUZz$aC@PVdOHH#5q} z`EkWHd^8pMg}O`!NypMprVfI`dvK~~OY@R+3{9J1axd|rJRE(qiRvktWbP>eDH#vP z;VbFOopk)ojYo;XTT;QE%wsaS%wyy;=cZnh4$_~HnyFFkLnarv4!uze0+? z8IRuS*cIF*?k2xc{fPWg`q9j+=V(}Zj)rt4CGL(jSIWlmVSY4YI{bcZ$_c09sIIwE zKAQh4T0nZh91ChblmHOl{}KhJookS&053 z2k|)Mk7|cLrR-%DcJWeqzSLzsG<}AenZM0^0mw&8k2p>06B;s|CY^0~sYbFaEDqzX zYi^UyGrYuZd~#+(rwbxLR2#GyH|JmdjH3Zjfi&;TiL zau6g$LXqPYu>CrFfgDD-)xklW+g7v5(|XQdXE zLDvWg4bmP0@#L^-dcufQ3XXoHCWI7zX1*{jIuWS@-%p)k7Tu2>n(bG6kq_cp_9E~B zvHHZPh+JQM;$nq=I&0(chTF^o0%uBM5iW<#E{MEBXKl8=1UG4giwXLGM0laev=!^2 zGcHTT33~RJ3d?(G?Fk+LWo3g1p_9NFlWS0r9BQZzk(9zzu@^55{{?A63v$jP}W(%804u|2-c7;2$-vC1D+yc;LS&7@gjQ4 zz?oB#>sFvPoJ@c)pY{q81brYI3t4a<=Z*npdl@Lh%!x!<6@*B>@8%1es*YUFZX*kH8++4C_ zKnFm|6y#Dsq$GqUi0X_WGy|PB5u2nTKfhl95p-g1Ff$Sc*7M zWvQodEm*38U4s>83cr*1#U=}Ed_un23=_B_UCgu~#CO*6L#^-#9L6p!Fv|l^W%L#} zYbbMf0UMr3ps!BIf#D0SK|05TClO@Afpp5+XzFmt?-$^ZB_XgWfWgN=4K?Mj98h|> zK;E{%AkAULK&VvC27ol(u)b_NG`rJRmMmtY(^^i|=HF$Te-GdJOENg! z4Lrc#5Mi+{-N%=XSYjqR&Jtap-L8~K$Ht`-P@?ak$Kfla6pK>vJ*qiLgwG!(7nWNz zB!aQYLGkPKrfEfAz`bQLAw7(zG!*-BMJ>i-S*zvfivqYRHnVY}VKY{;ZZImnXsrco ztyPA#F4U|wGqcu=5^E);)}p1oRxczapkRx&Fb+4Y)iSL$%kVsE?kvqze8;pCsQ}-s zQ1COpZ_q8*uf#|*ON}&>Dp6)6o;6WumFkGuA4Zsr{-`EkKZQz3Xf7McIoq%jt#oQQ zsZS+5wkPo}l&KEmsZb2k$e*+!JKwhs&k0f}uP_>?(t&1bqZoR#=9KX@3GgXO@ES3a z<~z!enXmPsG2Kdx;V<7ahPm`G5rFAqTA3euNAR7EV99_LmuH^t2i3P?RJ#1=pgjpOx2C@&LZ1sYb>NYE3qRz@{{M`XY z*8^Frl`w%PTxg!FjA@AqqIOoIfZDWDRJ2lDuo8!i@p^p8&#c7bVv8?TORYo)P?h=; z3by!Cqu@&z8tJ|iSgfRn|75o`P|l`0$l|tupYL0I39?oz`K4CE;G^7^Xb5IniKZqg zx>5Qb45*C02b@DQ4HqH58qb}F>%8y5AtKV3=v)HyGE}TfilXtPOA}G(eB}Fbnq8*b zUg=x_5j2Xl*oOv$Xrvspk?#0Fj&!z$G>?hNf)`;jk!mD|XDns&BFyDjy@;oE3@@Tn z)oomdWBN9|eLk7lr#i*PaVKq-8Mj-8 z-t<{L$3tNjv!JUt%z{ar+-<7n>`Qf>W{~PS(;BY$76mCK0VBRaL9v4ese%#QDXRti z(#aEl%Fun)m@0AgSvb-<*n_lwyrK0&bFGgFt*^Cd-991gttr(y3bSaP)-L4E;7)2P zA2-~filfpMAE=HOR6b@wzdL47lLx7y%E#;~A1gz=KW3_YOvk#5iFX6&AOxuVeTK&O z$~BJn26(wueH|sK(XWf6)(fGpJ8X@6eUwoMdaL;24$`xTc-=82+^vq{VWd_O>sCjV zK({)oN4eECMu=NoW5l=a8rH2I+)_3pvh~*FdR4+&Zw+*z#k5rjQtd=Q>#kL!+3GqD zWsf%Wd2p`J*93xJwITQwp;MF5UnxZ}3NsK~1++-5lFMDZ`%oBT*o5z}4&UJdd|$J{ z_car~kSfCWH9LG?%hjyUl=-hgODbDeUo+r4qQSQcJ_UB$==x=$>%Z7^jTer#-t)y$ zU868l*YbU7s_R!ZUB6=L`W0Q*uM~7G&UlwnNabGsWmDIXD(d=WyRKg@)%DA!u3t8E z{a0PrVA1xvenIH^d7G}Ewd?xXQeC4kQ`fKRxc-Z#>laO3^Gnq%G^xH=(De&8T)$xI z8d61FzhKw(3#Gb#!PNB&hOS@Ibq%j>uj^-ouAjE)8cRQHxGr6F4u2Gn&>*sY{KdtH7Tzyo_ zx(5ZOwJA;aL3><>GKz5>z((XD>w9%w-&@f2JvLq6 zW9k}GMP1)x*EP;01r{uCoBexCUEgEq`aWIP;LrBDzC-BxcAKto%BT(3x0dP}g_*j> zInb)E@78pEr>SeXr$SCSRnz?h*?gd@?v$XJIF6zDuuQf8gH%!1ci44(N2#vwFm-*0 zq3gSJUBj!}>-rXd2d3|-%*>l$9&Ue`AWU0-k0^|f|fUt6kc z6lUuBc3syuYr4MC)b)+Ju5T>pS{$(|rI58$}!LDoGT?T1VthV0Hzrob? z4Ti37(sd25hOXPh8rOINGniTR0Hc4QCe|eO5VtVhF#urX&2?AFPM7`ylHN4UzB#e6-$1oT~R{;9RfpZUCLLBuhY9_fHGH#?gHFGb=LmisCU+93@f+Zr%7sKcW^VI(3!}76l+q@K zs3aKU!gWJ`+o!(KTI>y!tJ>Ng*Wztf^=kTho7l&hs%ZMb&iJ5s(SuvR793DLnC|i_ z*zwr=HSsC-LAVoxI9S`gnhtj5zu?B?U{&}X?q358^L_7k?4Kx>st|kF+KtlaaD2L; zhoWpD68l$kwh^uevwxdz7uu}cjrlw)tTqCs3<1&S7o!@3)TPBTrChJPhA3CUlunov9*O_%gB zi0_%;3ocyR4o|)+)g&aubN#C_O{TeAd}AAHl2r*sP)4D~F{qc!$)yKZ+0;nFNQN56 zu)CRadsm5S(92ge|*>R4R;9W*^2w?X0M0)>}RejSBB$#s~c@F$86p`*()6#gR9 z9ieb_hQcd!6kdS}W_Y@S9;%@5@>F+_;H22UJkwp4>vAg!Ay`D=)f!7dl-%Tai_ zsEI`3&r}qWF59B;Vu8XRNewC}{Eu9NIST(n(ICKdaRG&BiS%be;g2&EUZSJ$5;Ap) z!b|Akc!m&({fkpg<|w>4(`1^JU;z9e%$=hu=4K_Kg@J^$@)C`JHnH{Ep@2$C*RCmMCC%;P%RXq7SsqS)5{!XU5EZ29eo(#dFCx6GLK6vtXg!*z%{*I`L z^yKfUo=m!I>&X`i6ygX<=0e4jFUU2Rd-4T}2I0vU7Ep+mC@37vQ1}fUh2IbuqyJ-% z0wL9taj2$=!V5D^W}bXu5rvQ{qVPhS8sW*b3|H$r_&cy%)%EToQ5yCz;`NFPQ|D7 zzf(xE@`nD0ivKo0)pG@8{-p?+uh<~-6&*6?79azwDUhK734%Gm;yfKPv?wYyzIUDh znRAPfIkyOzbBmCHR1q@gmO$oQ6Ef$DYDma@HG|AI7&6YKkXJGDZCp2;-JFwgY_{;*}(G&&nU(5jic^&YdMifTx}e<1_>*$ntLIy_D2@YJFXPqyjsWL<}+6?BMZ zC^|eN)8Xm54o_Egc)Fp((~3Ggt*FD(iaLZ;QHQ6M=OAtiv|r zmJ@|5pCEOpL_5dlI?P@9ctwYB*%LEYK0VVNx$?=GE1#shGOv0}UHK$>sN%{vW>fUh zxhtQT=`M5S6RoZc!J;dlXj30t`9z_<+?7uhHIc6TS=E(Emu;h+V+0C6Ej6fk@=>`4 zb5A}>(I7nem;wsX5(R}TG87)GqwrW2g~!sv6;D1U)nx9;$7Gt!Jo%U+3L#ZQ;W0Kf z!jq3NQFx4~hD70U846EYpC>ODI^?rZF?v>Xc(_f6hwD0AUeF<$q3G~anGTQ8b$EoT z!y^nGE-&hEc~OVUi#mi<}1 z=nx*eEc4_eGTo6UAD(&gp}Hp@ii&4Gc_=+p@#JNx?s89Fmgz3bg>z&r^I#Axdh#-x z`rygSg!*z%UM6ZHJ^3)zlS!B3@gY=iULuKy<7X~@W6Zi@9i`j+9@^gD-|zI}<{nO6 znIk($Ae0Yf)e-ukT)VlKe@M|Ty!@a7LeWYEp&!i<`Vk$WA3<$XfAu~hppnf^9F%G~ z_ws`>EvLEMgNg`+R1u*#<8+Yj;)(fGBfR_|q2jTuzdlG*Lwfna8A1+YoBTs=$lOi-K`|uU;lRvIK9U)b+~h-< zn|wfblMj$O(hlzf0-V`&@qwuUb2m9KGhmkMK&zWTu;?ZS+H43nIZ)UzcasA}O{ANA zP<0bBX#3c8Kj8~}9IfsP@6J`2`@*{w6~Y(xEBFGMqWHr5GGEwV_l5n@h!oY{{sM%V zFYK49GWUi3GF7Iz-2IBa0I8xc;OthbFYG7unES$hq8ic{-kbTt2iOu{ff4$%xnhwsXCxUa55Ite$`hPSVw!+nZ6+^49+eTq7SR8fcfl<06D zQ-}L#I(%oQ!~I!@zlXRw-ldsuaTY-p+dBI3E!BmOw^p5&yBGWU%lI?klW#R&e~E7I znJYT;cdW{7wfsF5ZI8!%&)X~Yd~vyNrf)clxb^mw1D^NjtK4ctpqT#(3f)LL(El}X5yU=r#tR13Zv*Ydj-VY)pjAMMcD30~zUNy2}# z4(O|uDFM$PZP6C99jPWQ>{N29c>J#HLNAJY0XlK~h@*Nnhk9NJVBIKcUE3))dd5)C zW)iW}3U^YBKNib0S$w#w>3Dj&2PNlj_$-QRwXmK7A5O4G>XXli$Aw&>mQ&kJb5U6! zj=n_meL7b-^;fMpe>YNo9`ZjepM{$(C)zayPsWB%iic9M<4$eY3_@w)PYR7w`k3SG zTJZ5FJGd>pIt0&%P(s1wpPPIsK#Ov8g0>gsc=`Slk)cM30A~tE7(=E zO|a_Iz{Nz%+dbulmoTaMHl_yW3CO9)qE&l@tW6m=efX|6Y&QgMwnhpTHEq=V@SV>yD$RvY3Riq=L;xHMfeH0CWOyngujUo zsU%P@LLsAxi_^yFB18BnBYY7mINGYRNDb+mQ-vAf(}eIUT%npIwXM0wln^F^;|w9; zsdYYQTI)eo{Ix2m$5^YrWsdZ$LnU{|vr?qvdnPNbXiEj`B?)VEH% zTUh|r2R@Ji-HDciRPh5;^ktYwcavg_Ntb$a2f&|LPR=O$joP-t0mkFUMctYxn&bem zY5cSpYs;NrIhP-A!{$uae<;<#OO?twIIYWR0LCqrMiCL6x1Dy(PjphLvIl@uI3D?i zQ#}WjIkYPK(fT%I={x{U0u_`>H(D|j>en^+R_tVkdi9FJeY|D#0n6l?5=6sjLb zm3t7L9q0Y{P8qOG?yWBwOjIdUOdz(`0 zbcY;EXbL`B!aa}DdBajr57iQ(lG8zN4D~KZf?9MzZut{VW9_gs%EJr@@8 zKu+~y0$i4{u|?XUNf#*b=PEoDRjdNymEX7}G z*Y{{!52s~$sH)n|&ZT6sJaGGSxwi-8jYjs4C8_6Up&jiufdR`r-=!AAM3`XdT#}JM zTK|Vl5(r7R_^ITTM$ULip-;u886j$618E%j$U$nbx*qExQXu!XDlZ_`f?+t1jT^>d z%BFAwl*^tf42pamOGpz+Kq(Gu7c3DJyv=3M5P*lg2JmJPUhWqHYGS&l*Pis;x(C!mLnpd4XFk$BR~$@~KSJSc1| z=toBDV9Vf8u7Ya6#7CIfIlsU`*cZr-EP#kWB;}x#ti{ujxjw%@L)f22nb9F4_yuQi zG(tp@W^(9>gAmXth&GOC0Y9O)+@A=dWamJV&j0B3qot++Jcms9_Q4MYYq;j#9%2i)AJBn z(*j#@l(dGKX>)RQVx~3BOo+#^>FpUbSY@UAahPX5$9%zs=cHm4hWubVU1*uBlBZ)~otP7`Eiq+&&oSXtwm5*9% zd_=tvpOrNlH|4>XVi9`knf9TK!VXjd>pSl3Ne4Il$*>M?^k9hc2UWBiUxvLY zGgLaF@@fdYOASYq(`_~^) zaVZG>kqdPni@Q*nL0o9VuKOsdB)p;m#7SkNXo-#6#$(D~8fPJGT_WCK0k50jQXQAw z$LJ}KxN>?MjpI$oYtlUn4-#{jkrx0N&0&fY1Gh(6NXB2n=K`X@@%UN%k8nh?+$gi$ zro-qZ6mq74mp_&9TN-r*bO4neaz&G*2_C~0 zpm0l+)Csue@z(tt6K|m&9W&FAix3KBZ%D`$u8EUwE$ULdeuJVeBE~gwvm4J$BQLGy z8yIb6+K8U}xpzJPEF%y@Zg(ZmVK^a>TCRGlKRaYxe32nkM znY)>g#+0b+E|I`fw(X-b46YJkK`~&wmzctg%4RTSVZ8V-r|MDJrW}<$2@x|YYeHIy z%4Ty^_7HTSMrAkST0~{l2@c7;5oAl_F@Gc@t27u3M$lM25{rhZ{k91w3d8EmR!Uf3 z2*V=MfW}rTAy~sO=pYTlGPb%`)FNdoF*l45o(7d!7#3=bm1jY5WnoxSH~C6t+vso% zAkFD2b=8dt$2wBR;=>c(KsYA8eIT8rQrfY>;n-;06+_XfkKg;o!!bk*{Ou1pd)@s; z!!cBVs%oVLTch9iVcj#>ME(-O^jlR#cGX|*#5{8dLY)##P+x) z5DQXb6M+~`s6`;=+zVZ+%yuoVC9}OInC(TC*>I{JSrzkG8MD#5e}mchK(sPu^LT)% z^#28BMM)uIpC=mDk}eWU|Sr5zi?Xw>Ty zc_=DflZUUMC6w`8iShESI4j1ZF-%L5)58C~l+m&Q=Iug98_8&xc`{zr-M+Dm*5q#l zGzXZszX4K1Wd=z@>bCUT(5ww2HP1f4r2HF5YBYbS4lr-yNNod2jb~NT0p^`dS@bqg zncAlTX27H-2AB`9kj`sFdgJk<2Cum^MpQS)bWk$1#PkyO?kiqo4lNr1H+&mE=+s0(rDIC0{YiKEOXQ{uX3X4U zEau>Am2t$tSS%&iJBUO`hgTnTDiCU6WZRe_B+bhX@{|eob>FTq*m|ezX$VQHlXxp2 zVGv5)&>Ht)>Y=1~zr@vIuJNf?d&2Q#+GA}AW=84(n{*(e(uMmdQPaLn+D_EDI= zZ5s{7@+gs8#iLS^u?_$sJ4j{bvUo&ke8d(*shK=gbE5(zSpv8vb+C;X?{!G1+#(vb zv9VDXvKw{Zh@gnSyblfkuY^O*bgACWZ#)j_bX*)iC80puL4YPfGaEL=j%j6QoutYe zbA|!f);qI~*U;!@#0c)jjSqG@K~L1TFo15L|2JS3y^v|cBAZ55QE{@`hJ`c{#X}%( zaDi=r7P9KWD|fMg9>>5Bfa*V*am))4m2phIBqoKR(iSu#$rzWS>vfwD;|b$sQZbpFyoSRG$UC_A*aWTMNpA(GC#z>Q^) z4$7+Z@EbU=Y9JeROK_Hrr46`C+Ip(&Mo>lv<@~SkMJK*g@WwYz6%DMExn4AS;}`;Q zTzvh;asxkY<5_{ST}N)W0c4QMZWsr^R`GQlRcx5|*C2mmiR%YQW3Oy!UQgfp_<-s1 zGzLjs9*34=++v=~_CMFefJ6}DXMOs(5n?K1{sPx@D#u)$Y>4O?SL;0v5l6XUC5KyKz;-*pXvyQN0oC&qRPJv z)FH^=f8rd`LC0a?}@~jh;Rf#;y;!+ zlj5&O)4#kwC(ab#^Zu0m^$?eg9Nr%(Z{_4`lgi0AXTMcSxhIiD)+ZC6Zal-&PGdl?(Bi(BU_8qQK_*TB(BWNQeJrVe1|boyKw&so5*y|q<~c(UtPIgWWGZQlCE;hF zZIJ~_i;+fBE=i3KK^i4>yq47H_=j)^R}jFS0Tx2BYo$o%Yl(k+Oqn2q|AstKqapm@ ztZ(E&Ii0U2K`KNtj`sr9hyJ3XlCEJqN7N7JPy#rK3w@(1GQInCq(gFy9*P2u)R82l z)bSzA%i?KBtyJ;w(WHAA&lM@KkZ4*mdl)Sj4=1U|Wx`l88#bRx0y>NV=U_gWS71#t z4@sZp{L1aulesWB-R5dOB?}r(ypvh9^u}w6cPc#^NP3-`AMn**6KoxF)?h6;l!lw9 zz$OrS6)=John&Bng-Imu0;MBRFZ`XJPUm*MjmP`*1;mfEzLH1S3j!e*s6cMd3MbHc(Qb&H-(D0?7NX;6$WA85Q%YoNE7B%APGxPP7KNqDK3bgAjthiVT%@Cdc*M(ufW$-V zk!#Fp9OtvzyfJoueiXC_LHtRA74Qe3LATZ`n@(JlaEyY8m*!ZTuT%Xtr*4TIKm0T-+6`WF)>;(Wtcf3 zL=(ZnzCwtmT2RXC(?B?vl0FO6fa(&0xozQOQd==RUBL^g*G4PkKqv@)L;m3*?fAb^ zJ*KB|$*rYz!R&#Y9Gvh9zL_0GQQ}kZ0EktIXkMk;DC{|E7_|!7asOo8rwTaHaDx4V zR1MtrPv%laN5iRv{?#ZaRXRA?#nvVleV5P+xC(`U@=4=JJ_8jbXdy0y4E_N{>-cuW zPeHA!Q%M9xOeP+L>k+q_impx2O&>S$Zi!0ZC6S2_O2JcBP9^*b8}IaS2Yr`|JI%jR z*Xklm*K0{b{FMx+e}UjT#Jz6x2J4a!LKSKpun(>q&Y4c9Q~C7hbmge={P$?A+CCcB zN3Wf(e0mLjU!SfVhrf@19>32`S3dLHboGSSr)yY{`DLTjlVzzhoXU}t)0O4;{Ul1` zUzE6<{>JCBk?HEkUz?s!AFoW$eH|ZvneJILT|MOG>3L(*)g#wT&m&cPN4`ECubuWP z(?JDy|1|B>UHZdCoj?2M>B?!3^Y0NxJwD9!_#&dE$|t6$D<6gQ!Dz)t@O#i0eo>F~ z_h2fr@^Q5DkWo}%bowph;sflHw)9Z`2{$EQ!4;50a{yAJXz^~tO zD%-oE7aIT9{IIf}SFZ=P%65JuuGQoJ-QKy!$W`5U{LH;~X7%wj2 zO08 z8}@2*`vx<~@CI$Kmf>+{B58-THkgLAoO6L6JI6JwZok`{altK7rPdfUC%G$CyOPG> z-Q0&a+e)n~Ng6|ggC?Nsl#6Z-;(D>zXf$^0HNl3gtD{ugx?RW$?XHea+nF=~x3ERQ zc7*gsQPzFhwY$q9-KH>TPOBVGTL~M~xl<^UqPtFOqZ~ExQ_nFM+%e~_-Rpw2Q?6%+ z3vL^AT`zLE;406!Xw4~C+~+9E=8&^%j=SKBGc!#yhAWy)=LatP&)>O@H6Qr9voeO8 z1f*jFF23!sZ{xy_rT}F;qq^$H;0)q*SH0~>N=uvGCF!;~yLMB@I+^-D9BjlyHtjKnromb=nDc14@iG zO)R4Ie_lM0TcIM|!uG8S`q0(O!|tY5SEMRu$aRIS1vJ7R6QgxGpWMob%?g8(c(Vh3Xq31OJ!~t6rMf+%(Vwx(b7wX z_^2In;V)fO-KY_N!4;o$QT++ef0OHY#+ACC_YI=!={LI0ZLWOHmUgEG?P1qF;7ZG1 z^8JYP?z-1qf7ErXd}Ute6vCV4A@{|GtK7IF9TKysD{|44e!zWU+S!|4_XL{HndLb5 zYSY~BI={Rp9S(>AY{lwUNHa%Dbb#LP>bLCmgR@>ef6G`#2iNRg`$6HVIaj}R{~~IQ z0hzV{BSBEyxa$foyU+j5LkYR2cFRIR`H*1rT+^VECi(@}Wk%ep_fo#)RsyEeQwhmi z8~{)6W_1{vo_BTrU-f|pqeLc@;=_Hrh@|tY{h;eG z2VCtpsfFkL7!D8#54wtZ(^W`Vbfjp)OhfHU1g%G0OsOnxR`Ss$D4ur_C0q=Ae~D3b ze%}HD)n63Q@!u>S*OHbJynq`r37L<4D3t>Xg|OR=k25-T5ua)7GSb}CXKta*`~Md> zEse;_#~$YBmJ`^TvhR?1s;Mp~Oaf-2&u`_U%Ca+{uxg>gz7}}Ds|SD&woL<*7GOyL zb&-Mv4R6qCkr{BEQa2BKxKE{uNRSc_P{p&7v2J)6nqU$5J+4d2s3NYmOrI9??~q<)X4E(_Z0ryQKPrM`08`9rT|*wvg#UN-OgL zoqkYj+b);L1Q10@a=9p%*K?HwJ<>c;KY0ltyZRJD7^&wu9k%;fBksmSOJciaPPW8l zNui?kGpnwdmvXXqy81QSS~0>u83Pp2tn$kXHqZi=je}eMY70k&W>iB;^5pXHqBo~$ zUYUpNzK*@u$+kI}XH|xuamwvN3y-8s@-DaR`r|Gq;AT16n&^gNx$B= z79$v~I_@HL%y^?cM0lD{Wt0{{G@&oe1vVI|kX>&1O_)PzW?UuWrv+TvU~+}^P(6kO zvP7xh_9T1UwO=~v3d#&&s>Y`?Yf|uqNVQWQM_e%JdcML=UMs1(C9_GHWfp6Hp3Bp2 z+3ItykOwOZRaEcQ&^%Zdo2T5eHN!5@>`_uRLp_@ZV?Da4sS4iclG__DWP&rmuqf$^ zmBtrFxDJ^tBbwO?d&G5r?NSOuNuRNrQ+W;Wxy%xEy*lh6*Y)*D7n1ifqKlTQtDh=G zSt;TduJ(;9E;{5@R41s6#iS3o&Tq=Tr+he(&J0mUmCO7+dpUGZDbcu2eb zpT5m(gwcJBKbvdc@1k|A>Wom)8!lMKWuuGk?d6Ki%YJy+ZCR6;loY0Y|xc9x!}2duJmgcZW(kDmhW`o z)&W--bL9aS{C1m*5AfaNx32!43wG~tJ%?Q|(C}9!_>I{1h!}AC} zT0ehm|Ir`+pY!3LmP`D3Sl#;hTiWt`?rC{GXN_Irb8Tc32G$n2B$nrMa*CA5U5DN! zHd+%|xrZ;OTA$eH)}r+toLouI$F}&@tjCjaHt+{J18e0N031gnS8Z{1$>Zdizwe#) zWUGa&b(-MWuP*RPqbC?mqITnA65YZGC&Ms2pKqK^qMI4flZ{g}0xOTwh3-Vc12vm; zh^ohO_d~7w5M!fsKpN%aitms{g|o*`ta6Zc*J&Lr%Wr!pJsc0t*zgYDxR&$Kd~ z#Ws^34p85>#oN^W$)t-@*FoB9=y+0Lyx$hrsG%2=;`zk9VoR;|fy5laRP?tU^SS~e zd$m_AJ8a7>W)Kfqd84*h{_8a%S7djzhW97tLt^hoJw?##862>7t!f7afANSjn-U}P z)1vq<%ilQkZX8U^N0!%^OfNrP5K_27k9!D~(O z?1a5RqM?LWF~|Sa9#K4i4tAHUEf3qz3ei5#O^M*e;JCG=leR}Cov@_0F`MMbgEaR? z=>7dIt;TI!8nQK5obtx;Z|%qk;!05VXLMuM7I8L5w~(H;rc-p^w5i`SHFBT>Y0&Sb zkJ+lwP9?!WV$QVL_KWvsQ^{jC7Ajs|K;D~)Icp6ES^SGWFvT-Q&gL2M)`*U6%3+*o zjQrz2X0lCFYt!sb%&2g~rM*}Im$^-H_AEDTK($Q7Rs1dvt$ik0g(P@OJTaY8kJ(Op zET!HlFH!ijG>$7{m4Y8ym>K+l_UJukYgL@DB3_rt-3FnU=mts+;Z)l+$*F#0KCs0n zZM}|@C%o<;LrQ{Z&&gk!G`Nzivv^E`Wcp3i&xhG>JD#zf-RE*(sX9iE`(|XWF#iKI zMN9bYw4K{*^%@+iqWV2YISLbdn9LmX<>|NC?g3j{-pJAG$r-$KmOvg!5KXfc#pmty zM{RB8r5wbc3WCsCo=4ER$$qh6J8zs?f}Scc$yAXHkzDIHQ$qfQXPt%B`bP`CBwbRG6GJ9*8F{dF_z(2iwU@M5`FhBU($)ga=4gkEQe<+lo19Z`hM# z-zGknr#fm^GP^S++Eiy(U=fMt7rOf_Pay4b3I=au;LvROyFA$vI_1eRp^%sd(DopO zCFx!axHM_T)fXVJhlGC!k3c??)b=joujIbq%!)Tj6+k5A!)OHPQc|JfW$ODmF+OQ< zw-hsv;O&mYJcf)9sJuSN92v7*$QibrQ4=|tNoHhHGta*-b38G-Y;?ia+o%O0hJRzy zT=DvlJ0u7^K1|FW#f*7LRIW-Y1|cCCF8U)1-6Ud|uc;-Cp~SqQdM=5kgzy746PhM6 z-G>vim+cRK$7VE@jG(z0d?O|T4NxEhYt>`kh+MGyY&Wa1Y5Z@nI=Lv=C8Rj^h9^zad+f3&q6+yx+@kaTSYqD7c^L~FPFA#Hgzvzg^OF(7z*lH9jm4$j zhu*XB*pD0jCmv@eO(-P?E~JC#o?m-lFktHes|lMMNKhh+AYSyDc(%;}XA1{i42k|5 z?AlmVGVFW<5_6dfG7l|5k|^{Wb_hOwy2`9A?8H=fXXXJGb26x}ug?@Vo0;`yjJJ3^ z6?h^cOM;Q|Nid$au`b?nepIfha%-Yz?nTwr%y!XoJqCpr>Uk7GJ~am65A{=r&KkGol}2 z{zEoiwdJf|82~BFDrR0zD<${q%)iT?s&*X<*XbnK?UnMc*Pv2r1F?h2XiLOX@_K0J z_1+V_R)`xIWaq+ei-_W`pwEX}(hQ7RkCgMaYn6JLcQy$bDBXYfTU>=0A8QqqDmTn> ztYJktZzHmVceBr25%!Ws@ETu_jm$mYK-{S9`I2TeI90;G4JN5TKF+O2;q~j+L$6%v zfXlT1#9!RQWXdLA(Gmj_*y9s<`G`TJUlr-X!23&hEnx6e(z99%_!4NvJib$(IV1S%9ju9;)9WG10#mg+gfc zSYjHs>l;GXOrp2)8f&FOV_c=Omtyc}!tSL0P2TZ(djXilKwqRQShry)&NmT;^o6y5 z74h}&0F__jQ%^D2+Eu9Cs=V$;7r-BpJgeWuiq7KPf+JID)z?{b0rX!4+I{EV1yI$J z*?`e1Slz0L!F{^heOR2HtYiAk6Rj~saW(o=!2g`^g|BN782?7h;8eK$R(~$1+p-@@ z%!I9fi+$KU*_s;Yj^grlZ+(A%+lEwckL_B7^*fS2Do4R5Hsy+pFtRJpqLrN-;1e@~iP853<+3c#4m)q1&B&D&W@@=TI-TlQpX^!#FlFD}}!>OE6*}Ukg zf+jh-p(OfsQvDuNBu5%cc)X?Dv}Oz0lTsW^qRmP5`|q}UsG=5i6_O%Im=y6&61|XA zfAITu4=V7SL4zn4EW$bB9m1T#4kXcwN&Sb1+dY@0P>UF$Bz&N76f;ud$0bD$B+<)B z?MENBdnna@w-@FOeaxMoT^IT1f}(<;~kp(U5{j(EjzT;7PUcv!{b!=^Dug|_r^eBF3#`M1S+ zwlv!--%p#p^2-=wJT#pG+I<^?0@Psj2;*LjQ!b4iI z>_jgaBWl2?U<$24^pnA}z4FCn!jJSR4C4S}76Cyj_gi8sN&(?h)w$qtgK@`Y%%K;M zn$E7PA$} Date: Fri, 4 Aug 2017 14:41:28 +0300 Subject: [PATCH 09/53] Test animated FLI --- Tests/test_file_fli.py | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 0530f6146..164ddfed2 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -4,17 +4,28 @@ from PIL import Image, FliImagePlugin # created as an export of a palette image from Gimp2.6 # save as...-> hopper.fli, default options. -test_file = "Tests/images/hopper.fli" +static_test_file = "Tests/images/hopper.fli" + +# From https://samples.libav.org/fli-flc/ +animated_test_file = "Tests/images/a.fli" class TestFileFli(PillowTestCase): def test_sanity(self): - im = Image.open(test_file) + im = Image.open(static_test_file) im.load() self.assertEqual(im.mode, "P") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "FLI") + self.assertFalse(im.is_animated) + + im = Image.open(animated_test_file) + self.assertEqual(im.mode, "P") + self.assertEqual(im.size, (320, 200)) + self.assertEqual(im.format, "FLI") + self.assertEqual(im.info["duration"], 71) + self.assertTrue(im.is_animated) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -23,12 +34,16 @@ class TestFileFli(PillowTestCase): lambda: FliImagePlugin.FliImageFile(invalid_file)) def test_n_frames(self): - im = Image.open(test_file) + im = Image.open(static_test_file) self.assertEqual(im.n_frames, 1) self.assertFalse(im.is_animated) + im = Image.open(animated_test_file) + self.assertEqual(im.n_frames, 385) + self.assertTrue(im.is_animated) + def test_eoferror(self): - im = Image.open(test_file) + im = Image.open(animated_test_file) n_frames = im.n_frames while True: @@ -39,6 +54,24 @@ class TestFileFli(PillowTestCase): except EOFError: self.assertLess(im.tell(), n_frames) + def test_seek_tell(self): + im = Image.open(animated_test_file) + + layer_number = im.tell() + self.assertEqual(layer_number, 0) + + im.seek(0) + layer_number = im.tell() + self.assertEqual(layer_number, 0) + + im.seek(1) + layer_number = im.tell() + self.assertEqual(layer_number, 1) + + im.seek(2) + layer_number = im.tell() + self.assertEqual(layer_number, 2) + if __name__ == '__main__': unittest.main() From d039be7cddf22f2c7a2f6b02150b4e66e4920ded Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 4 Aug 2017 14:51:02 +0300 Subject: [PATCH 10/53] Python 3 division fix --- PIL/FliImagePlugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index a2d0e0fab..2a9145b42 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -38,7 +38,7 @@ class FliImageFile(ImageFile.ImageFile): format = "FLI" format_description = "Autodesk FLI/FLC Animation" _close_exclusive_fp_after_loading = False - + def _open(self): # HEAD @@ -56,7 +56,7 @@ class FliImageFile(ImageFile.ImageFile): # animation speed duration = i32(s[16:20]) if magic == 0xAF11: - duration = (duration * 1000) / 70 + duration = (duration * 1000) // 70 self.info["duration"] = duration # look for palette @@ -176,6 +176,7 @@ class FliImageFile(ImageFile.ImageFile): def tell(self): return self.__frame + # # registry From 430c53707f5ba5637a34a2820930685a1340df2f Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 5 Aug 2017 18:54:27 +0300 Subject: [PATCH 11/53] no reasons to release GIL for one calloc --- libImaging/Storage.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libImaging/Storage.c b/libImaging/Storage.c index fb27572d3..ac9c6b2de 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -46,11 +46,9 @@ int ImagingNewCount = 0; */ Imaging -ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, - int size) +ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) { Imaging im; - ImagingSectionCookie cookie; im = (Imaging) calloc(1, size); if (!im) @@ -212,14 +210,10 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, /* Setup image descriptor */ strcpy(im->mode, mode); - ImagingSectionEnter(&cookie); - /* Pointer array (allocate at least one line, to avoid MemoryError exceptions on platforms where calloc(0, x) returns NULL) */ im->image = (char **) calloc((ysize > 0) ? ysize : 1, sizeof(void *)); - ImagingSectionLeave(&cookie); - if (!im->image) { free(im); return (Imaging) ImagingError_MemoryError(); From 152104bba387572ea7a6643038d7907147103f3c Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 5 Aug 2017 18:59:16 +0300 Subject: [PATCH 12/53] check args before allocate memory --- libImaging/Storage.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libImaging/Storage.c b/libImaging/Storage.c index ac9c6b2de..e5f620363 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -50,15 +50,16 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) { Imaging im; - im = (Imaging) calloc(1, size); - if (!im) - return (Imaging) ImagingError_MemoryError(); - /* linesize overflow check, roughly the current largest space req'd */ if (xsize > (INT_MAX / 4) - 1) { return (Imaging) ImagingError_MemoryError(); } + im = (Imaging) calloc(1, size); + if (!im) { + return (Imaging) ImagingError_MemoryError(); + } + /* Setup image descriptor */ im->xsize = xsize; im->ysize = ysize; @@ -228,8 +229,7 @@ Imaging ImagingNewPrologue(const char *mode, int xsize, int ysize) { return ImagingNewPrologueSubtype( - mode, xsize, ysize, sizeof(struct ImagingMemoryInstance) - ); + mode, xsize, ysize, sizeof(struct ImagingMemoryInstance)); } Imaging @@ -371,7 +371,6 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) } im->destroy = ImagingDestroyBlock; - } return ImagingNewEpilogue(im); From fd9cf03d018e3aacc742bf9831de0ecd699a4851 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 5 Aug 2017 20:25:57 +0300 Subject: [PATCH 13/53] FIX memory leak ImagingNewEpilogue now is always success The Imaging object itself is freed through ImagingDelete in case when memory is not allocated in ImagingNewBlock or ImagingNewArray --- libImaging/Imaging.h | 2 +- libImaging/Storage.c | 41 +++++++++++++++++++++-------------------- map.c | 6 ++---- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 99fff7f67..086a7dc6d 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -172,7 +172,7 @@ extern Imaging ImagingNewPrologue(const char *mode, extern Imaging ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int structure_size); -extern Imaging ImagingNewEpilogue(Imaging im); +extern void ImagingNewEpilogue(Imaging im); extern void ImagingCopyInfo(Imaging destination, Imaging source); diff --git a/libImaging/Storage.c b/libImaging/Storage.c index e5f620363..640211e33 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -232,15 +232,9 @@ ImagingNewPrologue(const char *mode, int xsize, int ysize) mode, xsize, ysize, sizeof(struct ImagingMemoryInstance)); } -Imaging +void ImagingNewEpilogue(Imaging im) { - /* If the raster data allocator didn't setup a destructor, - assume that it couldn't allocate the required amount of - memory. */ - if (!im->destroy) - return (Imaging) ImagingError_MemoryError(); - /* Initialize alias pointers to pixel data. */ switch (im->pixelsize) { case 1: case 2: case 3: @@ -250,8 +244,6 @@ ImagingNewEpilogue(Imaging im) im->image32 = (INT32 **) im->image; break; } - - return im; } void @@ -316,10 +308,15 @@ ImagingNewArray(const char *mode, int xsize, int ysize) ImagingSectionLeave(&cookie); - if (y == im->ysize) - im->destroy = ImagingDestroyArray; + if (y != im->ysize) { + ImagingDelete(im); + return (Imaging) ImagingError_MemoryError(); + } + + im->destroy = ImagingDestroyArray; + ImagingNewEpilogue(im); - return ImagingNewEpilogue(im); + return im; } @@ -364,16 +361,20 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) im->block = (char *) calloc(im->ysize, im->linesize); } - if (im->block) { - for (y = i = 0; y < im->ysize; y++) { - im->image[y] = im->block + i; - i += im->linesize; - } - - im->destroy = ImagingDestroyBlock; + if ( ! im->block) { + ImagingDelete(im); + return (Imaging) ImagingError_MemoryError(); } - return ImagingNewEpilogue(im); + for (y = i = 0; y < im->ysize; y++) { + im->image[y] = im->block + i; + i += im->linesize; + } + + im->destroy = ImagingDestroyBlock; + ImagingNewEpilogue(im); + + return im; } /* -------------------------------------------------------------------- diff --git a/map.c b/map.c index 9d4751e31..8c36660d0 100644 --- a/map.c +++ b/map.c @@ -229,8 +229,7 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) im->destroy = ImagingDestroyMap; - if (!ImagingNewEpilogue(im)) - return NULL; + ImagingNewEpilogue(im); mapper->offset += size; @@ -387,8 +386,7 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) ((ImagingBufferInstance*) im)->target = target; ((ImagingBufferInstance*) im)->view = view; - if (!ImagingNewEpilogue(im)) - return NULL; + ImagingNewEpilogue(im); return PyImagingNew(im); } From ab92adf7c96f80790ee661d8a56c73bc63247f09 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 5 Aug 2017 20:39:14 +0300 Subject: [PATCH 14/53] move ImagingNewEpilogue functionality to ImagingNewPrologueSubtype doublechecked: no im->image or im->image8 or im->image32 access between ImagingNewPrologue and ImagingNewEpilogue anywhere --- libImaging/Imaging.h | 1 - libImaging/Storage.c | 33 ++++++++++++++------------------- map.c | 7 +------ 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 086a7dc6d..ede35f74c 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -172,7 +172,6 @@ extern Imaging ImagingNewPrologue(const char *mode, extern Imaging ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int structure_size); -extern void ImagingNewEpilogue(Imaging im); extern void ImagingCopyInfo(Imaging destination, Imaging source); diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 640211e33..5cddfd66c 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -215,11 +215,21 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) exceptions on platforms where calloc(0, x) returns NULL) */ im->image = (char **) calloc((ysize > 0) ? ysize : 1, sizeof(void *)); - if (!im->image) { + if ( ! im->image) { free(im); return (Imaging) ImagingError_MemoryError(); } + /* Initialize alias pointers to pixel data. */ + switch (im->pixelsize) { + case 1: case 2: case 3: + im->image8 = (UINT8 **) im->image; + break; + case 4: + im->image32 = (INT32 **) im->image; + break; + } + ImagingNewCount++; return im; @@ -232,20 +242,6 @@ ImagingNewPrologue(const char *mode, int xsize, int ysize) mode, xsize, ysize, sizeof(struct ImagingMemoryInstance)); } -void -ImagingNewEpilogue(Imaging im) -{ - /* Initialize alias pointers to pixel data. */ - switch (im->pixelsize) { - case 1: case 2: case 3: - im->image8 = (UINT8 **) im->image; - break; - case 4: - im->image32 = (INT32 **) im->image; - break; - } -} - void ImagingDelete(Imaging im) { @@ -312,9 +308,8 @@ ImagingNewArray(const char *mode, int xsize, int ysize) ImagingDelete(im); return (Imaging) ImagingError_MemoryError(); } - + im->destroy = ImagingDestroyArray; - ImagingNewEpilogue(im); return im; } @@ -338,8 +333,9 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) Py_ssize_t y, i; im = ImagingNewPrologue(mode, xsize, ysize); - if (!im) + if ( ! im) { return NULL; + } /* We shouldn't overflow, since the threshold defined below says that we're only going to allocate max 4M @@ -372,7 +368,6 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) } im->destroy = ImagingDestroyBlock; - ImagingNewEpilogue(im); return im; } diff --git a/map.c b/map.c index 8c36660d0..76b316012 100644 --- a/map.c +++ b/map.c @@ -229,8 +229,6 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) im->destroy = ImagingDestroyMap; - ImagingNewEpilogue(im); - mapper->offset += size; return PyImagingNew(im); @@ -367,8 +365,7 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) } im = ImagingNewPrologueSubtype( - mode, xsize, ysize, sizeof(ImagingBufferInstance) - ); + mode, xsize, ysize, sizeof(ImagingBufferInstance)); if (!im) return NULL; @@ -386,8 +383,6 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) ((ImagingBufferInstance*) im)->target = target; ((ImagingBufferInstance*) im)->view = view; - ImagingNewEpilogue(im); - return PyImagingNew(im); } From 6b50ba07fd4f2bf6ed13c9b11e743072658c5863 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 5 Aug 2017 21:58:31 +0300 Subject: [PATCH 15/53] add tests for Image.new modes --- Tests/test_image.py | 19 +++++++++++++++++++ libImaging/Storage.c | 7 ++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 1f9c4d798..a83b917a0 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -7,6 +7,25 @@ import sys class TestImage(PillowTestCase): + def test_image_modes_success(self): + for mode in [ + '1', 'P', 'PA', + 'L', 'LA', 'La', + 'F', 'I', 'I;16', 'I;16L', 'I;16B', 'I;16N', + 'RGB', 'RGBX', 'RGBA', 'RGBa', + 'CMYK', 'YCbCr', 'LAB', 'HSV', + ]: + Image.new(mode, (1, 1)) + + def test_image_modes_fail(self): + for mode in [ + '', 'bad', 'very very long', + 'BGR;15', 'BGR;16', 'BGR;24', 'BGR;32' + ]: + with self.assertRaises(ValueError) as e: + Image.new(mode, (1, 1)); + self.assertEqual(e.exception.message, 'unrecognized image mode') + def test_sanity(self): im = Image.new("L", (100, 100)) diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 5cddfd66c..6acda2af0 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -205,7 +205,7 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) } else { free(im); - return (Imaging) ImagingError_ValueError("unrecognized mode"); + return (Imaging) ImagingError_ValueError("unrecognized image mode"); } /* Setup image descriptor */ @@ -387,16 +387,13 @@ ImagingNew(const char* mode, int xsize, int ysize) int bytes; Imaging im; - if (strcmp(mode, "") == 0) - return (Imaging) ImagingError_ValueError("empty mode"); - if (strlen(mode) == 1) { if (mode[0] == 'F' || mode[0] == 'I') bytes = 4; else bytes = 1; } else - bytes = strlen(mode); /* close enough */ + bytes = strlen(mode) || 1; /* close enough */ if (xsize < 0 || ysize < 0) { return (Imaging) ImagingError_ValueError("bad image size"); From eafa258bd159f404ad13fbc8c2255d55396e9c8e Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 00:50:56 +0300 Subject: [PATCH 16/53] destroy image and set MemoryError on overflow check failure --- libImaging/Storage.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 6acda2af0..7ab5ae1f5 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -344,7 +344,8 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) if (im->linesize && im->ysize > INT_MAX / im->linesize) { /* punt if we're going to overflow */ - return NULL; + ImagingDelete(im); + return (Imaging) ImagingError_MemoryError(); } if (im->ysize * im->linesize <= 0) { From d55557152ba8ce2df1bbab963fc00b66bb72816f Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 01:20:00 +0300 Subject: [PATCH 17/53] =?UTF-8?q?rename=20ImagingNewBlock=20=E2=86=92=20Im?= =?UTF-8?q?agingAllocateBlock=20rename=20ImagingNewArray=20=E2=86=92=20Ima?= =?UTF-8?q?gingAllocateArray=20new=20utility=20function=20with=20old=20nam?= =?UTF-8?q?e=20ImagingNewBlock=20call=20ImagingNewPrologue=20outside=20of?= =?UTF-8?q?=20ImagingAllocateBlock=20and=20ImagingAllocateArray?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libImaging/Imaging.h | 1 - libImaging/Storage.c | 50 +++++++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index ede35f74c..35b563b1c 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -163,7 +163,6 @@ extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn); extern void ImagingDelete(Imaging im); extern Imaging ImagingNewBlock(const char* mode, int xsize, int ysize); -extern Imaging ImagingNewArray(const char* mode, int xsize, int ysize); extern Imaging ImagingNewMap(const char* filename, int readonly, const char* mode, int xsize, int ysize); diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 7ab5ae1f5..155bac883 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -277,18 +277,13 @@ ImagingDestroyArray(Imaging im) } Imaging -ImagingNewArray(const char *mode, int xsize, int ysize) +ImagingAllocateArray(Imaging im) { - Imaging im; ImagingSectionCookie cookie; int y; char* p; - im = ImagingNewPrologue(mode, xsize, ysize); - if (!im) - return NULL; - ImagingSectionEnter(&cookie); /* Allocate image as an array of lines */ @@ -305,7 +300,6 @@ ImagingNewArray(const char *mode, int xsize, int ysize) ImagingSectionLeave(&cookie); if (y != im->ysize) { - ImagingDelete(im); return (Imaging) ImagingError_MemoryError(); } @@ -327,16 +321,10 @@ ImagingDestroyBlock(Imaging im) } Imaging -ImagingNewBlock(const char *mode, int xsize, int ysize) +ImagingAllocateBlock(Imaging im) { - Imaging im; Py_ssize_t y, i; - im = ImagingNewPrologue(mode, xsize, ysize); - if ( ! im) { - return NULL; - } - /* We shouldn't overflow, since the threshold defined below says that we're only going to allocate max 4M here before going to the array allocator. Check anyway. @@ -344,7 +332,6 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) if (im->linesize && im->ysize > INT_MAX / im->linesize) { /* punt if we're going to overflow */ - ImagingDelete(im); return (Imaging) ImagingError_MemoryError(); } @@ -359,7 +346,6 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) } if ( ! im->block) { - ImagingDelete(im); return (Imaging) ImagingError_MemoryError(); } @@ -400,15 +386,41 @@ ImagingNew(const char* mode, int xsize, int ysize) return (Imaging) ImagingError_ValueError("bad image size"); } + im = ImagingNewPrologue(mode, xsize, ysize); + if (!im) + return NULL; + if ((int64_t) xsize * (int64_t) ysize <= THRESHOLD / bytes) { - im = ImagingNewBlock(mode, xsize, ysize); - if (im) + if (ImagingAllocateBlock(im)) { return im; + } /* assume memory error; try allocating in array mode instead */ ImagingError_Clear(); } - return ImagingNewArray(mode, xsize, ysize); + if (ImagingAllocateArray(im)) { + return im; + } + + ImagingDelete(im); + return NULL; +} + +Imaging +ImagingNewBlock(const char* mode, int xsize, int ysize) +{ + Imaging im; + + im = ImagingNewPrologue(mode, xsize, ysize); + if ( ! im) + return NULL; + + if (ImagingAllocateBlock(im)) { + return im; + } + + ImagingDelete(im); + return NULL; } Imaging From 0990dadd6d452a3c4d19af5f82c3d1999b46b09d Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 01:31:31 +0300 Subject: [PATCH 18/53] use accurate im->linesize instead of strlen(mode) approximation --- libImaging/Storage.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 155bac883..3c365207d 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -371,26 +371,17 @@ ImagingAllocateBlock(Imaging im) Imaging ImagingNew(const char* mode, int xsize, int ysize) { - int bytes; Imaging im; - if (strlen(mode) == 1) { - if (mode[0] == 'F' || mode[0] == 'I') - bytes = 4; - else - bytes = 1; - } else - bytes = strlen(mode) || 1; /* close enough */ - if (xsize < 0 || ysize < 0) { return (Imaging) ImagingError_ValueError("bad image size"); } im = ImagingNewPrologue(mode, xsize, ysize); - if (!im) + if ( ! im) return NULL; - if ((int64_t) xsize * (int64_t) ysize <= THRESHOLD / bytes) { + if (im->ysize && im->linesize <= THRESHOLD / im->ysize) { if (ImagingAllocateBlock(im)) { return im; } From 0649da02c61058d34b9bb72cb1f8cd6d28162dae Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 01:31:51 +0300 Subject: [PATCH 19/53] fix tests on python 3 --- Tests/test_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index a83b917a0..dcfc28e4b 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -24,7 +24,7 @@ class TestImage(PillowTestCase): ]: with self.assertRaises(ValueError) as e: Image.new(mode, (1, 1)); - self.assertEqual(e.exception.message, 'unrecognized image mode') + self.assertEqual(str(e.exception), 'unrecognized image mode') def test_sanity(self): From 7a1e70d99748bc8d0ee2d3a2a3e3dcc7b5755199 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 01:50:55 +0300 Subject: [PATCH 20/53] new internal API function ImagingNewDirty --- libImaging/Imaging.h | 1 + libImaging/Storage.c | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 35b563b1c..cb16b440e 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -159,6 +159,7 @@ struct ImagingPaletteInstance { extern int ImagingNewCount; extern Imaging ImagingNew(const char* mode, int xsize, int ysize); +extern Imaging ImagingNewDirty(const char* mode, int xsize, int ysize); extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn); extern void ImagingDelete(Imaging im); diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 3c365207d..6eb48d63c 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -277,7 +277,7 @@ ImagingDestroyArray(Imaging im) } Imaging -ImagingAllocateArray(Imaging im) +ImagingAllocateArray(Imaging im, int dirty) { ImagingSectionCookie cookie; @@ -289,7 +289,11 @@ ImagingAllocateArray(Imaging im) /* Allocate image as an array of lines */ for (y = 0; y < im->ysize; y++) { /* malloc check linesize checked in prologue */ - p = (char *) calloc(1, im->linesize); + if (dirty) { + p = (char *) malloc(im->linesize); + } else { + p = (char *) calloc(1, im->linesize); + } if (!p) { ImagingDestroyArray(im); break; @@ -321,7 +325,7 @@ ImagingDestroyBlock(Imaging im) } Imaging -ImagingAllocateBlock(Imaging im) +ImagingAllocateBlock(Imaging im, int dirty) { Py_ssize_t y, i; @@ -341,8 +345,13 @@ ImagingAllocateBlock(Imaging im) platforms */ im->block = (char *) malloc(1); } else { - /* malloc check ok, overflow check above */ - im->block = (char *) calloc(im->ysize, im->linesize); + if (dirty) { + /* malloc check ok, overflow check above */ + im->block = (char *) malloc(im->ysize * im->linesize); + } else { + /* malloc check ok, overflow check above */ + im->block = (char *) calloc(im->ysize, im->linesize); + } } if ( ! im->block) { @@ -369,7 +378,7 @@ ImagingAllocateBlock(Imaging im) #endif Imaging -ImagingNew(const char* mode, int xsize, int ysize) +ImagingNewInternal(const char* mode, int xsize, int ysize, int dirty) { Imaging im; @@ -382,14 +391,14 @@ ImagingNew(const char* mode, int xsize, int ysize) return NULL; if (im->ysize && im->linesize <= THRESHOLD / im->ysize) { - if (ImagingAllocateBlock(im)) { + if (ImagingAllocateBlock(im, dirty)) { return im; } /* assume memory error; try allocating in array mode instead */ ImagingError_Clear(); } - if (ImagingAllocateArray(im)) { + if (ImagingAllocateArray(im, dirty)) { return im; } @@ -397,6 +406,18 @@ ImagingNew(const char* mode, int xsize, int ysize) return NULL; } +Imaging +ImagingNew(const char* mode, int xsize, int ysize) +{ + return ImagingNewInternal(mode, xsize, ysize, 0); +} + +Imaging +ImagingNewDirty(const char* mode, int xsize, int ysize) +{ + return ImagingNewInternal(mode, xsize, ysize, 1); +} + Imaging ImagingNewBlock(const char* mode, int xsize, int ysize) { @@ -406,7 +427,7 @@ ImagingNewBlock(const char* mode, int xsize, int ysize) if ( ! im) return NULL; - if (ImagingAllocateBlock(im)) { + if (ImagingAllocateBlock(im, 0)) { return im; } From 19a86495897ec3c8bf89423a84123a5b8bbcd8a4 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 13:32:46 +0300 Subject: [PATCH 21/53] create dirty images for some operations --- _imaging.c | 14 +++++++------- libImaging/AlphaComposite.c | 2 +- libImaging/Bands.c | 2 +- libImaging/Blend.c | 2 +- libImaging/BoxBlur.c | 2 +- libImaging/Fill.c | 4 ++-- libImaging/Matrix.c | 4 ++-- libImaging/Negative.c | 2 +- libImaging/Offset.c | 2 +- libImaging/Resample.c | 8 ++++---- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/_imaging.c b/_imaging.c index 7b3380b40..a2db5c998 100644 --- a/_imaging.c +++ b/_imaging.c @@ -599,7 +599,7 @@ _fill(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) return NULL; - im = ImagingNew(mode, xsize, ysize); + im = ImagingNewDirty(mode, xsize, ysize); if (!im) return NULL; @@ -874,7 +874,7 @@ _gaussian_blur(ImagingObject* self, PyObject* args) return NULL; imIn = self->image; - imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; @@ -1539,7 +1539,7 @@ _resize(ImagingObject* self, PyObject* args) a[0] = (double) imIn->xsize / xsize; a[4] = (double) imIn->ysize / ysize; - imOut = ImagingNew(imIn->mode, xsize, ysize); + imOut = ImagingNewDirty(imIn->mode, xsize, ysize); imOut = ImagingTransform( imOut, imIn, IMAGING_TRANSFORM_AFFINE, @@ -1665,12 +1665,12 @@ _transpose(ImagingObject* self, PyObject* args) case 0: /* flip left right */ case 1: /* flip top bottom */ case 3: /* rotate 180 */ - imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); break; case 2: /* rotate 90 */ case 4: /* rotate 270 */ case 5: /* transpose */ - imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); + imOut = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize); break; default: PyErr_SetString(PyExc_ValueError, "No such transpose operation"); @@ -1715,7 +1715,7 @@ _unsharp_mask(ImagingObject* self, PyObject* args) return NULL; imIn = self->image; - imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; @@ -1738,7 +1738,7 @@ _box_blur(ImagingObject* self, PyObject* args) return NULL; imIn = self->image; - imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; diff --git a/libImaging/AlphaComposite.c b/libImaging/AlphaComposite.c index 538fd88e3..7e99c59ce 100644 --- a/libImaging/AlphaComposite.c +++ b/libImaging/AlphaComposite.c @@ -42,7 +42,7 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc) imDst->ysize != imSrc->ysize) return ImagingError_Mismatch(); - imOut = ImagingNew(imDst->mode, imDst->xsize, imDst->ysize); + imOut = ImagingNewDirty(imDst->mode, imDst->xsize, imDst->ysize); if (!imOut) return NULL; diff --git a/libImaging/Bands.c b/libImaging/Bands.c index cc8d634dd..04976c2b6 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -43,7 +43,7 @@ ImagingGetBand(Imaging imIn, int band) if (imIn->bands == 2 && band == 1) band = 3; - imOut = ImagingNew("L", imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty("L", imIn->xsize, imIn->ysize); if (!imOut) return NULL; diff --git a/libImaging/Blend.c b/libImaging/Blend.c index 885a1bb82..919856714 100644 --- a/libImaging/Blend.c +++ b/libImaging/Blend.c @@ -40,7 +40,7 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) else if (alpha == 1.0) return ImagingCopy(imIn2); - imOut = ImagingNew(imIn1->mode, imIn1->xsize, imIn1->ysize); + imOut = ImagingNewDirty(imIn1->mode, imIn1->xsize, imIn1->ysize); if (!imOut) return NULL; diff --git a/libImaging/BoxBlur.c b/libImaging/BoxBlur.c index 854a53386..1a415ed16 100644 --- a/libImaging/BoxBlur.c +++ b/libImaging/BoxBlur.c @@ -265,7 +265,7 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) strcmp(imIn->mode, "La") == 0)) return ImagingError_ModeError(); - imTransposed = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); + imTransposed = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize); if (!imTransposed) return NULL; diff --git a/libImaging/Fill.c b/libImaging/Fill.c index 15107b6d6..d641a5996 100644 --- a/libImaging/Fill.c +++ b/libImaging/Fill.c @@ -68,7 +68,7 @@ ImagingFillLinearGradient(const char *mode) return (Imaging) ImagingError_ModeError(); } - im = ImagingNew(mode, 256, 256); + im = ImagingNewDirty(mode, 256, 256); if (!im) { return NULL; } @@ -91,7 +91,7 @@ ImagingFillRadialGradient(const char *mode) return (Imaging) ImagingError_ModeError(); } - im = ImagingNew(mode, 256, 256); + im = ImagingNewDirty(mode, 256, 256); if (!im) { return NULL; } diff --git a/libImaging/Matrix.c b/libImaging/Matrix.c index 5d2f031cf..5cc7795a4 100644 --- a/libImaging/Matrix.c +++ b/libImaging/Matrix.c @@ -32,7 +32,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) if (strcmp(mode, "L") == 0 && im->bands == 3) { - imOut = ImagingNew("L", im->xsize, im->ysize); + imOut = ImagingNewDirty("L", im->xsize, im->ysize); if (!imOut) return NULL; @@ -49,7 +49,7 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) } else if (strlen(mode) == 3 && im->bands == 3) { - imOut = ImagingNew(mode, im->xsize, im->ysize); + imOut = ImagingNewDirty(mode, im->xsize, im->ysize); if (!imOut) return NULL; diff --git a/libImaging/Negative.c b/libImaging/Negative.c index eaa790768..4dedcb245 100644 --- a/libImaging/Negative.c +++ b/libImaging/Negative.c @@ -29,7 +29,7 @@ ImagingNegative(Imaging im) if (!im) return (Imaging) ImagingError_ModeError(); - imOut = ImagingNew(im->mode, im->xsize, im->ysize); + imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); if (!imOut) return NULL; diff --git a/libImaging/Offset.c b/libImaging/Offset.c index f6d6e510e..7d69f38a7 100644 --- a/libImaging/Offset.c +++ b/libImaging/Offset.c @@ -27,7 +27,7 @@ ImagingOffset(Imaging im, int xoffset, int yoffset) if (!im) return (Imaging) ImagingError_ModeError(); - imOut = ImagingNew(im->mode, im->xsize, im->ysize); + imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); if (!imOut) return NULL; diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 0f6a9e9dd..ac010c219 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -249,7 +249,7 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, xsize, imIn->ysize); if ( ! imOut) { free(kk); free(xbounds); @@ -354,7 +354,7 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, ysize); if ( ! imOut) { free(kk); free(xbounds); @@ -451,7 +451,7 @@ ImagingResampleHorizontal_32bpc(Imaging imIn, int xsize, struct filter *filterp) return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, xsize, imIn->ysize); + imOut = ImagingNewDirty(imIn->mode, xsize, imIn->ysize); if ( ! imOut) { free(kk); free(xbounds); @@ -511,7 +511,7 @@ ImagingResampleVertical_32bpc(Imaging imIn, int ysize, struct filter *filterp) return (Imaging) ImagingError_MemoryError(); } - imOut = ImagingNew(imIn->mode, imIn->xsize, ysize); + imOut = ImagingNewDirty(imIn->mode, imIn->xsize, ysize); if ( ! imOut) { free(kk); free(xbounds); From eb4096ffd5d9a017d495c5f1ff32c537a4ed13b1 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 15:01:17 +0300 Subject: [PATCH 22/53] create dirty images for cropping --- libImaging/Crop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libImaging/Crop.c b/libImaging/Crop.c index 0db67e8e9..893cdb5ba 100644 --- a/libImaging/Crop.c +++ b/libImaging/Crop.c @@ -37,7 +37,7 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) if (ysize < 0) ysize = 0; - imOut = ImagingNew(imIn->mode, xsize, ysize); + imOut = ImagingNewDirty(imIn->mode, xsize, ysize); if (!imOut) return NULL; From 559249283b923e626b9ee22a22eca98aa3422aca Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 19:56:27 +0300 Subject: [PATCH 23/53] Remove unused im.copy2 and core.copy methods --- _imaging.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/_imaging.c b/_imaging.c index 7b3380b40..7591f2b23 100644 --- a/_imaging.c +++ b/_imaging.c @@ -791,23 +791,6 @@ _copy(ImagingObject* self, PyObject* args) return PyImagingNew(ImagingCopy(self->image)); } -static PyObject* -_copy2(ImagingObject* self, PyObject* args) -{ - ImagingObject* imagep1; - ImagingObject* imagep2; - if (!PyArg_ParseTuple(args, "O!O!", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) - return NULL; - - if (!ImagingCopy2(imagep1->image, imagep2->image)) - return NULL; - - Py_INCREF(Py_None); - return Py_None; -} - static PyObject* _crop(ImagingObject* self, PyObject* args) { @@ -2940,7 +2923,6 @@ static struct PyMethodDef methods[] = { {"convert_matrix", (PyCFunction)_convert_matrix, 1}, {"convert_transparent", (PyCFunction)_convert_transparent, 1}, {"copy", (PyCFunction)_copy, 1}, - {"copy2", (PyCFunction)_copy2, 1}, {"crop", (PyCFunction)_crop, 1}, {"expand", (PyCFunction)_expand_image, 1}, {"filter", (PyCFunction)_filter, 1}, @@ -3321,7 +3303,6 @@ static PyMethodDef functions[] = { /* Functions */ {"convert", (PyCFunction)_convert2, 1}, - {"copy", (PyCFunction)_copy2, 1}, /* Codecs */ {"bcn_decoder", (PyCFunction)PyImaging_BcnDecoderNew, 1}, From a519851903cdced7e1f18496263afef55313a014 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Aug 2017 20:08:07 +0300 Subject: [PATCH 24/53] ImagingNew2 is always Dirty --- libImaging/Convert.c | 10 +++++----- libImaging/Copy.c | 2 +- libImaging/Imaging.h | 2 +- libImaging/Storage.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 0ec5ef864..f4253bda4 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -1035,7 +1035,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) else return (Imaging) ImagingError_ValueError("conversion not supported"); - imOut = ImagingNew2(mode, imOut, imIn); + imOut = ImagingNew2Dirty(mode, imOut, imIn); if (!imOut) return NULL; @@ -1073,7 +1073,7 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) if (!palette) return (Imaging) ImagingError_ValueError("no palette"); - imOut = ImagingNew2("P", imOut, imIn); + imOut = ImagingNew2Dirty("P", imOut, imIn); if (!imOut) { if (palette != inpalette) ImagingPaletteDelete(palette); @@ -1211,7 +1211,7 @@ tobilevel(Imaging imOut, Imaging imIn, int dither) if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) return (Imaging) ImagingError_ValueError("conversion not supported"); - imOut = ImagingNew2("1", imOut, imIn); + imOut = ImagingNew2Dirty("1", imOut, imIn); if (!imOut) return NULL; @@ -1344,7 +1344,7 @@ convert(Imaging imOut, Imaging imIn, const char *mode, } #endif - imOut = ImagingNew2(mode, imOut, imIn); + imOut = ImagingNew2Dirty(mode, imOut, imIn); if (!imOut) return NULL; @@ -1407,7 +1407,7 @@ ImagingConvertTransparent(Imaging imIn, const char *mode, g = b = r; } - imOut = ImagingNew2(mode, imOut, imIn); + imOut = ImagingNew2Dirty(mode, imOut, imIn); if (!imOut){ return NULL; } diff --git a/libImaging/Copy.c b/libImaging/Copy.c index b5b0b0aea..992b8b505 100644 --- a/libImaging/Copy.c +++ b/libImaging/Copy.c @@ -28,7 +28,7 @@ _copy(Imaging imOut, Imaging imIn) if (!imIn) return (Imaging) ImagingError_ValueError(NULL); - imOut = ImagingNew2(imIn->mode, imOut, imIn); + imOut = ImagingNew2Dirty(imIn->mode, imOut, imIn); if (!imOut) return NULL; diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index cb16b440e..d46114133 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -160,7 +160,7 @@ extern int ImagingNewCount; extern Imaging ImagingNew(const char* mode, int xsize, int ysize); extern Imaging ImagingNewDirty(const char* mode, int xsize, int ysize); -extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn); +extern Imaging ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn); extern void ImagingDelete(Imaging im); extern Imaging ImagingNewBlock(const char* mode, int xsize, int ysize); diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 6eb48d63c..615c5fa20 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -436,7 +436,7 @@ ImagingNewBlock(const char* mode, int xsize, int ysize) } Imaging -ImagingNew2(const char* mode, Imaging imOut, Imaging imIn) +ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn) { /* allocate or validate output image */ @@ -449,7 +449,7 @@ ImagingNew2(const char* mode, Imaging imOut, Imaging imIn) } } else { /* create new image */ - imOut = ImagingNew(mode, imIn->xsize, imIn->ysize); + imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize); if (!imOut) return NULL; } From e4927b8f2fd7c8af6e90809359ab397d88cf94ec Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Aug 2017 19:21:54 +1000 Subject: [PATCH 25/53] Added SGI test for saving an image in an unsupported mode --- Tests/test_file_sgi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index c9aeea76c..7af9d0949 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -55,6 +55,12 @@ class TestFileSgi(PillowTestCase): for mode in ('L', 'RGB', 'RGBA'): roundtrip(hopper(mode)) + def test_unsupported_mode(self): + im = hopper('LA') + out = self.tempfile('temp.sgi') + + self.assertRaises(ValueError, lambda: im.save(out, format='sgi')) + if __name__ == '__main__': unittest.main() From 667fff78628998b5a95239cd14c5c2f98610d548 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Aug 2017 20:10:39 +1000 Subject: [PATCH 26/53] Added SGI test for an incorrect number of bands --- Tests/test_file_sgi.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index 7af9d0949..3201889d4 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -61,6 +61,13 @@ class TestFileSgi(PillowTestCase): self.assertRaises(ValueError, lambda: im.save(out, format='sgi')) + def test_incorrect_number_of_bands(self): + im = hopper('YCbCr') + im.mode = 'RGB' + out = self.tempfile('temp.sgi') + + self.assertRaises(ValueError, lambda: im.save(out, format='sgi')) + if __name__ == '__main__': unittest.main() From 5d5a2a3762eed3968c00309f116ce89ef0d959ba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Aug 2017 21:57:59 +1000 Subject: [PATCH 27/53] Added test for 1 dimensional L mode SGI save --- Tests/test_file_sgi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index 3201889d4..a535a1730 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -55,6 +55,9 @@ class TestFileSgi(PillowTestCase): for mode in ('L', 'RGB', 'RGBA'): roundtrip(hopper(mode)) + # Test 1 dimension for an L mode image + roundtrip(Image.new('L', (10, 1))) + def test_unsupported_mode(self): im = hopper('LA') out = self.tempfile('temp.sgi') From 446e7b7bd44a13f0222b6fb31184a01627638091 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Aug 2017 19:07:58 +1000 Subject: [PATCH 28/53] Updated libimagequant to 2.10.2 --- depends/install_imagequant.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 0b19525d2..a1b49a3ea 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -archive=libimagequant-2.10.1 +archive=libimagequant-2.10.2 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz From 0002e18c74c66791b0b7a14fc4903fafd52ce8ec Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Aug 2017 01:58:22 +0300 Subject: [PATCH 29/53] New Image.getchannel method --- PIL/Image.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/PIL/Image.py b/PIL/Image.py index 49c9f6ab2..f67009452 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1959,6 +1959,22 @@ class Image(object): ims.append(self._new(self.im.getband(i))) return tuple(ims) + def getchannel(self, channel): + """ + Returns an image contained single channel of the source image. + + :param channel: What channel to return. Could be index + (0 for "R" channel of "RGB") or channel name + ("A" for alpha channel of "RGBA"). + :returns: An image in "L" mode. + """ + + if isStringType(channel) and len(channel) == 1: + if channel in self.im.mode: + channel = self.im.mode.index(channel) + + return self._new(self.im.getband(channel)) + def tell(self): """ Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. From 349e300d7b0bcdca0e4fb4d9edb2d41fb5ed3d35 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Aug 2017 02:36:07 +0300 Subject: [PATCH 30/53] use getchannel where is possible --- PIL/ImageEnhance.py | 6 +++--- Tests/test_file_png.py | 12 ++++++------ Tests/test_format_hsv.py | 27 +++++++++++++++------------ Tests/test_image_convert.py | 4 ++-- Tests/test_imagecms.py | 5 ++--- Tests/test_imageenhance.py | 2 +- Tests/test_numpy.py | 2 +- 7 files changed, 30 insertions(+), 28 deletions(-) diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index b38f406a3..11c9c3a06 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -67,7 +67,7 @@ class Contrast(_Enhance): self.degenerate = Image.new("L", image.size, mean).convert(image.mode) if 'A' in image.getbands(): - self.degenerate.putalpha(image.split()[-1]) + self.degenerate.putalpha(image.getchannel('A')) class Brightness(_Enhance): @@ -82,7 +82,7 @@ class Brightness(_Enhance): self.degenerate = Image.new(image.mode, image.size, 0) if 'A' in image.getbands(): - self.degenerate.putalpha(image.split()[-1]) + self.degenerate.putalpha(image.getchannel('A')) class Sharpness(_Enhance): @@ -97,4 +97,4 @@ class Sharpness(_Enhance): self.degenerate = image.filter(ImageFilter.SMOOTH) if 'A' in image.getbands(): - self.degenerate.putalpha(image.split()[-1]) + self.degenerate.putalpha(image.getchannel('A')) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 7dab44333..7ee2c9fb7 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -200,7 +200,7 @@ class TestFilePng(PillowTestCase): self.assert_image(im, "RGBA", (162, 150)) # image has 124 unique alpha values - self.assertEqual(len(im.split()[3].getcolors()), 124) + self.assertEqual(len(im.getchannel('A').getcolors()), 124) def test_load_transparent_rgb(self): test_file = "Tests/images/rgb_trns.png" @@ -212,7 +212,7 @@ class TestFilePng(PillowTestCase): self.assert_image(im, "RGBA", (64, 64)) # image has 876 transparent pixels - self.assertEqual(im.split()[3].getcolors()[0][0], 876) + self.assertEqual(im.getchannel('A').getcolors()[0][0], 876) def test_save_p_transparent_palette(self): in_file = "Tests/images/pil123p.png" @@ -234,7 +234,7 @@ class TestFilePng(PillowTestCase): self.assert_image(im, "RGBA", (162, 150)) # image has 124 unique alpha values - self.assertEqual(len(im.split()[3].getcolors()), 124) + self.assertEqual(len(im.getchannel('A').getcolors()), 124) def test_save_p_single_transparency(self): in_file = "Tests/images/p_trns_single.png" @@ -258,7 +258,7 @@ class TestFilePng(PillowTestCase): self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0)) # image has 876 transparent pixels - self.assertEqual(im.split()[3].getcolors()[0][0], 876) + self.assertEqual(im.getchannel('A').getcolors()[0][0], 876) def test_save_p_transparent_black(self): # check if solid black image with full transparency @@ -287,7 +287,7 @@ class TestFilePng(PillowTestCase): # There are 559 transparent pixels. im = im.convert('RGBA') - self.assertEqual(im.split()[3].getcolors()[0][0], 559) + self.assertEqual(im.getchannel('A').getcolors()[0][0], 559) def test_save_rgb_single_transparency(self): in_file = "Tests/images/caption_6_33_22.png" @@ -550,7 +550,7 @@ class TestTruncatedPngPLeaks(PillowTestCase): # linux # man 2 getrusage # ru_maxrss (since Linux 2.6.32) - # This is the maximum resident set size used (in kilobytes). + # This is the maximum resident set size used (in kilobytes). return mem / 1024 # megs def test_leak_load(self): diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index b965a854f..2cc54c910 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -103,11 +103,11 @@ class TestFormatHSV(PillowTestCase): # im.split()[0].show() # comparable.split()[0].show() - self.assert_image_similar(im.split()[0], comparable.split()[0], + self.assert_image_similar(im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong") - self.assert_image_similar(im.split()[1], comparable.split()[1], + self.assert_image_similar(im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong") - self.assert_image_similar(im.split()[2], comparable.split()[2], + self.assert_image_similar(im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong") # print(im.getpixel((192, 64))) @@ -120,11 +120,11 @@ class TestFormatHSV(PillowTestCase): # print(im.getpixel((192, 64))) # print(comparable.getpixel((192, 64))) - self.assert_image_similar(im.split()[0], comparable.split()[0], + self.assert_image_similar(im.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong") - self.assert_image_similar(im.split()[1], comparable.split()[1], + self.assert_image_similar(im.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong") - self.assert_image_similar(im.split()[2], comparable.split()[2], + self.assert_image_similar(im.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong") def test_convert(self): @@ -137,11 +137,11 @@ class TestFormatHSV(PillowTestCase): # print(im.split()[0].histogram()) # print(comparable.split()[0].histogram()) - self.assert_image_similar(im.split()[0], comparable.split()[0], + self.assert_image_similar(im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong") - self.assert_image_similar(im.split()[1], comparable.split()[1], + self.assert_image_similar(im.getchannel(1), comparable.getchannel(1), 1, "Saturation conversion is wrong") - self.assert_image_similar(im.split()[2], comparable.split()[2], + self.assert_image_similar(im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong") def test_hsv_to_rgb(self): @@ -155,11 +155,14 @@ class TestFormatHSV(PillowTestCase): # print([ord(x) for x in target.split()[1].tobytes()[:80]]) # print([ord(x) for x in converted.split()[1].tobytes()[:80]]) - self.assert_image_similar(converted.split()[0], comparable.split()[0], + self.assert_image_similar(converted.getchannel(0), + comparable.getchannel(0), 3, "R conversion is wrong") - self.assert_image_similar(converted.split()[1], comparable.split()[1], + self.assert_image_similar(converted.getchannel(1), + comparable.getchannel(1), 3, "G conversion is wrong") - self.assert_image_similar(converted.split()[2], comparable.split()[2], + self.assert_image_similar(converted.getchannel(2), + comparable.getchannel(2), 3, "B conversion is wrong") diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 5687232f3..f56e2f4ed 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -133,7 +133,7 @@ class TestImageConvert(PillowTestCase): alpha = hopper('L') im.putalpha(alpha) - comparable = im.convert('P').convert('LA').split()[1] + comparable = im.convert('P').convert('LA').getchannel('A') self.assert_image_similar(alpha, comparable, 5) @@ -185,7 +185,7 @@ class TestImageConvert(PillowTestCase): if converted_im.mode == 'RGB': self.assert_image_similar(converted_im, target, 3) else: - self.assert_image_similar(converted_im, target.split()[0], 1) + self.assert_image_similar(converted_im, target.getchannel(0), 1) matrix_convert('RGB') matrix_convert('L') diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index ef30f3e00..aa592c398 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -360,8 +360,7 @@ class TestImageCms(PillowTestCase): return Image.merge(mode, chans) source_image = create_test_image() - preserved_channel_ndx = source_image.getbands().index(preserved_channel) - source_image_aux = source_image.split()[preserved_channel_ndx] + source_image_aux = source_image.getchannel(preserved_channel) # create some transform, it doesn't matter which one source_profile = ImageCms.createProfile("sRGB") @@ -374,7 +373,7 @@ class TestImageCms(PillowTestCase): result_image = source_image else: result_image = ImageCms.applyTransform(source_image, t, inPlace=False) - result_image_aux = result_image.split()[preserved_channel_ndx] + result_image_aux = result_image.getchannel(preserved_channel) self.assert_image_equal(source_image_aux, result_image_aux) diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index 7278ec8c1..e9727613b 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -34,7 +34,7 @@ class TestImageEnhance(PillowTestCase): def _check_alpha(self, im, original, op, amount): self.assertEqual(im.getbands(), original.getbands()) - self.assert_image_equal(im.split()[-1], original.split()[-1], + self.assert_image_equal(im.getchannel('A'), original.getchannel('A'), "Diff on %s: %s" % (op, amount)) def test_alpha(self): diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 8b9208cd6..74825cd41 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -52,7 +52,7 @@ class TestNumpy(PillowTestCase): a = numpy.array([[x]*bands for x in data], dtype=dtype) a.shape = TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], bands i = Image.fromarray(a) - if list(i.split()[0].getdata()) != list(range(100)): + if list(i.getchannel(0).getdata()) != list(range(100)): print("data mismatch for", dtype) # print(dtype, list(i.getdata())) return i From 1a7cb317be40c275d35ea975a858bbc81ef3cdd0 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Aug 2017 02:39:53 +0300 Subject: [PATCH 31/53] load image before getting channels --- PIL/Image.py | 1 + 1 file changed, 1 insertion(+) diff --git a/PIL/Image.py b/PIL/Image.py index f67009452..db26b9f20 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1968,6 +1968,7 @@ class Image(object): ("A" for alpha channel of "RGBA"). :returns: An image in "L" mode. """ + self.load() if isStringType(channel) and len(channel) == 1: if channel in self.im.mode: From 7c8e0e44578df91496a74c4007313717971f75b2 Mon Sep 17 00:00:00 2001 From: Vytis Banaitis Date: Wed, 9 Aug 2017 16:16:14 +0300 Subject: [PATCH 32/53] Fix ZeroDivisionError when EXIF contains invalid DPI (0/0). --- PIL/JpegImagePlugin.py | 3 ++- Tests/images/exif-dpi-zerodivision.jpg | Bin 0 -> 10949 bytes Tests/test_file_jpeg.py | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 Tests/images/exif-dpi-zerodivision.jpg diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 47fb0a6df..6837ba450 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -131,9 +131,10 @@ def APP(self, marker): # 1 dpcm = 2.54 dpi dpi *= 2.54 self.info["dpi"] = dpi, dpi - except (KeyError, SyntaxError): + except (KeyError, SyntaxError, ZeroDivisionError): # SyntaxError for invalid/unreadable exif # KeyError for dpi not included + # ZeroDivisionError for invalid dpi rational value self.info["dpi"] = 72, 72 diff --git a/Tests/images/exif-dpi-zerodivision.jpg b/Tests/images/exif-dpi-zerodivision.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2e784cf6a883c2e94171b1996a0db7a7a8b2c9da GIT binary patch literal 10949 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 7487263d7..7f701d0f6 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -535,6 +535,16 @@ class TestFileJpeg(PillowTestCase): # Act / Assert self.assertEqual(im.info.get("dpi"), (508, 508)) + def test_dpi_exif_zero_division(self): + # Arrange + # This is photoshop-200dpi.jpg with EXIF resolution set to 0/0: + # exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg + im = Image.open("Tests/images/exif-dpi-zerodivision.jpg") + + # Act / Assert + # This should return the default, and not raise a ZeroDivisionError + self.assertEqual(im.info.get("dpi"), (72, 72)) + def test_no_dpi_in_exif(self): # Arrange # This is photoshop-200dpi.jpg with resolution removed from EXIF: From 6c5eaf57e1f112ff3c59b19a3fbd77fe35e4f47a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Aug 2017 19:09:30 +1000 Subject: [PATCH 33/53] Updated Tk Tcl to 8.6.7 --- winbuild/config.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/winbuild/config.py b/winbuild/config.py index c7a6fe675..62b01958b 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -55,15 +55,15 @@ libs = { 'version': '8.5.19', }, 'tcl-8.6': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.6/tcl866-src.zip', - 'filename': PILLOW_DEPENDS_DIR + 'tcl866-src.zip', + 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.7/tcl867-src.zip', + 'filename': PILLOW_DEPENDS_DIR + 'tcl867-src.zip', 'dir': '', }, 'tk-8.6': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.6/tk866-src.zip', - 'filename': PILLOW_DEPENDS_DIR + 'tk866-src.zip', + 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.7/tk867-src.zip', + 'filename': PILLOW_DEPENDS_DIR + 'tk867-src.zip', 'dir': '', - 'version': '8.6.6', + 'version': '8.6.7', }, 'webp': { 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-0.6.0.tar.gz', From 23716a57cf2fc966f8cc42539d73f46d429f9f61 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 11 Aug 2017 00:01:38 +1000 Subject: [PATCH 34/53] Updated openjpeg to 2.2.0 --- depends/install_openjpeg.sh | 2 +- winbuild/config.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/depends/install_openjpeg.sh b/depends/install_openjpeg.sh index cac86dbbb..7577b2285 100755 --- a/depends/install_openjpeg.sh +++ b/depends/install_openjpeg.sh @@ -1,7 +1,7 @@ #!/bin/bash # install openjpeg -archive=openjpeg-2.1.2 +archive=openjpeg-2.2.0 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz diff --git a/winbuild/config.py b/winbuild/config.py index c7a6fe675..389964b7b 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -71,9 +71,9 @@ libs = { 'dir': 'libwebp-0.6.0', }, 'openjpeg': { - 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.1.2/openjpeg-2.1.2.tar.gz', - 'filename': PILLOW_DEPENDS_DIR + 'openjpeg-2.1.2.tar.gz', - 'dir': 'openjpeg-2.1.2', + 'url': SF_MIRROR+'/project/openjpeg/openjpeg/2.2.0/openjpeg-2.2.0.tar.gz', + 'filename': PILLOW_DEPENDS_DIR + 'openjpeg-2.2.0.tar.gz', + 'dir': 'openjpeg-2.2.0', }, } From b46b5c4e84ec565af3945572c7849c9ccde089a0 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 01:24:53 +0300 Subject: [PATCH 35/53] release notes autodocs fix docstring note for `Image.split` --- PIL/Image.py | 9 +++++++-- docs/reference/Image.rst | 1 + docs/releasenotes/4.3.0.rst | 9 +++++++++ docs/releasenotes/index.rst | 3 +-- 4 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 docs/releasenotes/4.3.0.rst diff --git a/PIL/Image.py b/PIL/Image.py index db26b9f20..b2111c97c 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1947,6 +1947,9 @@ class Image(object): containing a copy of one of the original bands (red, green, blue). + If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` + method can be more convinient and faster. + :returns: A tuple containing bands. """ @@ -1964,9 +1967,11 @@ class Image(object): Returns an image contained single channel of the source image. :param channel: What channel to return. Could be index - (0 for "R" channel of "RGB") or channel name - ("A" for alpha channel of "RGBA"). + (0 for "R" channel of "RGB") or channel name + ("A" for alpha channel of "RGBA"). :returns: An image in "L" mode. + + .. versionadded:: 4.3.0 """ self.load() diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index e867494b2..8977a4332 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -146,6 +146,7 @@ ITU-R 709, using the D65 luminant) to the CIE XYZ color space: .. automethod:: PIL.Image.Image.seek .. automethod:: PIL.Image.Image.show .. automethod:: PIL.Image.Image.split +.. automethod:: PIL.Image.Image.getchannel .. automethod:: PIL.Image.Image.tell .. automethod:: PIL.Image.Image.thumbnail .. automethod:: PIL.Image.Image.tobitmap diff --git a/docs/releasenotes/4.3.0.rst b/docs/releasenotes/4.3.0.rst new file mode 100644 index 000000000..a356a67a2 --- /dev/null +++ b/docs/releasenotes/4.3.0.rst @@ -0,0 +1,9 @@ +4.3.0 +----- + +Get One Channel From Image +============================ + +New method :py:meth:`PIL.Image.Image.getchannel` added. +It returns single channel by index or name. For example, +``image.getchannel("A")`` will return alpha channel as seperate image. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 27b12face..4ea95f659 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 4.3.0 4.2.1 4.2.0 4.1.1 @@ -21,5 +22,3 @@ Release Notes 3.0.0 2.8.0 2.7.0 - - From 25e467a756d42d875de7f6a019ce36d8aa836c8a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Aug 2017 12:11:36 +1000 Subject: [PATCH 36/53] Use frame count from FLI header --- PIL/FliImagePlugin.py | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index a2d0e0fab..94b82540a 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -38,7 +38,7 @@ class FliImageFile(ImageFile.ImageFile): format = "FLI" format_description = "Autodesk FLI/FLC Animation" _close_exclusive_fp_after_loading = False - + def _open(self): # HEAD @@ -49,6 +49,9 @@ class FliImageFile(ImageFile.ImageFile): s[20:22] == b"\x00\x00"): # reserved raise SyntaxError("not an FLI/FLC file") + # frames + self.__framecount = i16(s[6:8]) + # image characteristics self.mode = "P" self.size = i16(s[8:10]), i16(s[10:12]) @@ -86,8 +89,6 @@ class FliImageFile(ImageFile.ImageFile): self.__frame = -1 self.__fp = self.fp self.__rewind = self.fp.tell() - self._n_frames = None - self._is_animated = None self.seek(0) def _palette(self, palette, shift): @@ -110,43 +111,22 @@ class FliImageFile(ImageFile.ImageFile): @property def n_frames(self): - if self._n_frames is None: - current = self.tell() - try: - while True: - self.seek(self.tell() + 1) - except EOFError: - self._n_frames = self.tell() + 1 - self.seek(current) - return self._n_frames + return self.__framecount @property def is_animated(self): - if self._is_animated is None: - current = self.tell() - - try: - self.seek(1) - self._is_animated = True - except EOFError: - self._is_animated = False - - self.seek(current) - return self._is_animated + return self.__framecount > 1 def seek(self, frame): if frame == self.__frame: return if frame < self.__frame: self._seek(0) + if frame >= self.__framecount: + raise EOFError("no more images in FLI file") - last_frame = self.__frame for f in range(self.__frame + 1, frame + 1): - try: - self._seek(f) - except EOFError: - self.seek(last_frame) - raise EOFError("no more images in FLI file") + self._seek(f) def _seek(self, frame): if frame == 0: From 4744fc0584b8d16c8dc67ad6b4daddbabc53b9a6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Aug 2017 15:54:54 +1000 Subject: [PATCH 37/53] Added test for ImImagePlugin tell() --- Tests/test_file_im.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index fcf4f52f4..19720dffb 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -15,6 +15,16 @@ class TestFileIm(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "IM") + def test_tell(self): + # Arrange + im = Image.open(TEST_IM) + + # Act + frame = im.tell() + + # Assert + self.assertEqual(frame, 0) + def test_n_frames(self): im = Image.open(TEST_IM) self.assertEqual(im.n_frames, 1) From a1e2d42ea08091803615377727d7008e182d33ee Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 10:32:42 +0300 Subject: [PATCH 38/53] text fixes --- PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index b2111c97c..f23c4e32c 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1948,7 +1948,7 @@ class Image(object): blue). If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` - method can be more convinient and faster. + method can be more convenient and faster. :returns: A tuple containing bands. """ @@ -1964,7 +1964,7 @@ class Image(object): def getchannel(self, channel): """ - Returns an image contained single channel of the source image. + Returns an image containing a single channel of the source image. :param channel: What channel to return. Could be index (0 for "R" channel of "RGB") or channel name From 7088e1a201d861da8991343924d5c41ea429ab14 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Aug 2017 19:07:06 +1000 Subject: [PATCH 39/53] Added tests --- Tests/test_file_fli.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index 0530f6146..33dce344f 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -16,6 +16,16 @@ class TestFileFli(PillowTestCase): self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "FLI") + def test_tell(self): + # Arrange + im = Image.open(test_file) + + # Act + frame = im.tell() + + # Assert + self.assertEqual(frame, 0) + def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" @@ -39,6 +49,15 @@ class TestFileFli(PillowTestCase): except EOFError: self.assertLess(im.tell(), n_frames) + def test_seek_outside(self): + # Test negative seek + im = Image.open(test_file) + im.seek(-1) + self.assertEqual(im.tell(), 0) + + # Test seek past end of file + self.assertRaises(EOFError, lambda: im.seek(2)) + if __name__ == '__main__': unittest.main() From e16ab0ad2ead98205143d06940f79a45300b4f33 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 14:10:39 +0300 Subject: [PATCH 40/53] add tests, fix implementation --- PIL/Image.py | 9 ++++++--- Tests/test_image.py | 24 +++++++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index f23c4e32c..4555561a1 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1975,9 +1975,12 @@ class Image(object): """ self.load() - if isStringType(channel) and len(channel) == 1: - if channel in self.im.mode: - channel = self.im.mode.index(channel) + if isStringType(channel): + try: + channel = self.getbands().index(channel) + except ValueError: + raise ValueError( + 'The image has no channel "{}"'.format(channel)) return self._new(self.im.getband(channel)) diff --git a/Tests/test_image.py b/Tests/test_image.py index 1f9c4d798..205c58e43 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -145,14 +145,28 @@ class TestImage(PillowTestCase): self.assertEqual(im.size[1], orig_size[1] + 2*ymargin) def test_getbands(self): - # Arrange + # Assert + self.assertEqual(hopper('RGB').getbands(), ('R', 'G', 'B')) + self.assertEqual(hopper('YCbCr').getbands(), ('Y', 'Cb', 'Cr')) + + def test_getchannel_wrong_params(self): im = hopper() - # Act - bands = im.getbands() + self.assertRaises(ValueError, im.getchannel, -1) + self.assertRaises(ValueError, im.getchannel, 3) + self.assertRaises(ValueError, im.getchannel, 'Z') + self.assertRaises(ValueError, im.getchannel, '1') - # Assert - self.assertEqual(bands, ('R', 'G', 'B')) + def test_getchannel(self): + im = hopper('YCbCr') + Y, Cb, Cr = im.split() + + self.assert_image_equal(Y, im.getchannel(0)) + self.assert_image_equal(Y, im.getchannel('Y')) + self.assert_image_equal(Cb, im.getchannel(1)) + self.assert_image_equal(Cb, im.getchannel('Cb')) + self.assert_image_equal(Cr, im.getchannel(2)) + self.assert_image_equal(Cr, im.getchannel('Cr')) def test_getbbox(self): # Arrange From 56f957b5db110880ce818caa81c3c127fbbbb86f Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 14:22:36 +0300 Subject: [PATCH 41/53] fix number of equals [skipci] --- docs/releasenotes/4.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/4.3.0.rst b/docs/releasenotes/4.3.0.rst index a356a67a2..c0a9d3c75 100644 --- a/docs/releasenotes/4.3.0.rst +++ b/docs/releasenotes/4.3.0.rst @@ -2,7 +2,7 @@ ----- Get One Channel From Image -============================ +========================== New method :py:meth:`PIL.Image.Image.getchannel` added. It returns single channel by index or name. For example, From c23b65c670970e1ae07af668f3cf8d3113d4b1e4 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 13:10:26 +0300 Subject: [PATCH 42/53] make ImagingGetBand faster --- libImaging/Bands.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libImaging/Bands.c b/libImaging/Bands.c index d7424ab25..c1e20d8aa 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -22,6 +22,13 @@ #define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) +#ifdef WORDS_BIGENDIAN + #define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24)) +#else + #define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24)) +#endif + + Imaging ImagingGetBand(Imaging imIn, int band) { @@ -51,7 +58,12 @@ ImagingGetBand(Imaging imIn, int band) for (y = 0; y < imIn->ysize; y++) { UINT8* in = (UINT8*) imIn->image[y] + band; UINT8* out = imOut->image8[y]; - for (x = 0; x < imIn->xsize; x++) { + x = 0; + for (; x < imIn->xsize - 3; x += 4) { + *((UINT32*) (out + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]); + in += 16; + } + for (; x < imIn->xsize; x++) { out[x] = *in; in += 4; } From 559836d97d4ecb58e58a7561dc9b4049d27c8304 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 03:43:19 +0300 Subject: [PATCH 43/53] im.split method ImagingSplit function without implementation --- PIL/Image.py | 4 +--- _imaging.c | 27 +++++++++++++++++++++++++++ libImaging/Bands.c | 13 +++++++++++++ libImaging/Imaging.h | 1 + 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/PIL/Image.py b/PIL/Image.py index 49c9f6ab2..335f1c1ad 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -1954,9 +1954,7 @@ class Image(object): if self.im.bands == 1: ims = [self.copy()] else: - ims = [] - for i in range(self.im.bands): - ims.append(self._new(self.im.getband(i))) + ims = map(self._new, self.im.split()) return tuple(ims) def tell(self): diff --git a/_imaging.c b/_imaging.c index 7b3380b40..a87ae67d1 100644 --- a/_imaging.c +++ b/_imaging.c @@ -1905,6 +1905,32 @@ _putband(ImagingObject* self, PyObject* args) return Py_None; } +static PyObject* +_split(ImagingObject* self, PyObject* args) +{ + int fails = 0; + Py_ssize_t i; + PyObject* list; + PyObject* imaging_object; + Imaging bands[4] = {NULL, NULL, NULL, NULL}; + + if ( ! ImagingSplit(self->image, bands)) + return NULL; + + list = PyTuple_New(self->image->bands); + for (i = 0; i < self->image->bands; i++) { + imaging_object = PyImagingNew(bands[i]); + if ( ! imaging_object) + fails += 1; + PyTuple_SET_ITEM(list, i, imaging_object); + } + if (fails) { + Py_DECREF(list); + list = NULL; + } + return list; +} + /* -------------------------------------------------------------------- */ #ifdef WITH_IMAGECHOPS @@ -2972,6 +2998,7 @@ static struct PyMethodDef methods[] = { {"getband", (PyCFunction)_getband, 1}, {"putband", (PyCFunction)_putband, 1}, + {"split", (PyCFunction)_split, 1}, {"fillband", (PyCFunction)_fillband, 1}, {"setmode", (PyCFunction)im_setmode, 1}, diff --git a/libImaging/Bands.c b/libImaging/Bands.c index c1e20d8aa..9614ebd2c 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -72,6 +72,19 @@ ImagingGetBand(Imaging imIn, int band) return imOut; } + +int +ImagingSplit(Imaging imIn, Imaging bands[4]) +{ + int i; + for (i = 0; i < imIn->bands; i++) { + bands[i] = ImagingNew("L", imIn->xsize, imIn->ysize); + } + + return imIn->bands; +} + + Imaging ImagingPutBand(Imaging imOut, Imaging imIn, int band) { diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 99fff7f67..9386fc260 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -267,6 +267,7 @@ extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn); extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius, int passes); extern Imaging ImagingGetBand(Imaging im, int band); +extern int ImagingSplit(Imaging im, Imaging bands[4]); extern int ImagingGetBBox(Imaging im, int bbox[4]); typedef struct { int x, y; INT32 count; INT32 pixel; } ImagingColorItem; extern ImagingColorItem* ImagingGetColors(Imaging im, int maxcolors, From ca75d63f3a67b2a05be23481ad6745ac1a5677af Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 10:13:23 +0300 Subject: [PATCH 44/53] ImagingSplit implementation --- libImaging/Bands.c | 57 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/libImaging/Bands.c b/libImaging/Bands.c index 9614ebd2c..17e1e1f26 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -76,11 +76,66 @@ ImagingGetBand(Imaging imIn, int band) int ImagingSplit(Imaging imIn, Imaging bands[4]) { - int i; + int i, x, y; + + /* Check arguments */ + if (!imIn || imIn->type != IMAGING_TYPE_UINT8) { + (Imaging) ImagingError_ModeError(); + return 0; + } + + /* Shortcuts */ + if (imIn->bands == 1) { + bands[0] = ImagingCopy(imIn); + return imIn->bands; + } + for (i = 0; i < imIn->bands; i++) { bands[i] = ImagingNew("L", imIn->xsize, imIn->ysize); } + /* Extract bands from image */ + if (imIn->bands == 2) { + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out0 = bands[0]->image8[y]; + UINT8* out1 = bands[1]->image8[y]; + for (x = 0; x < imIn->xsize; x++) { + out0[x] = in[0]; + out1[x] = in[3]; + in += 4; + } + } + } else if (imIn->bands == 3) { + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out0 = bands[0]->image8[y]; + UINT8* out1 = bands[1]->image8[y]; + UINT8* out2 = bands[2]->image8[y]; + for (x = 0; x < imIn->xsize; x++) { + out0[x] = in[0]; + out1[x] = in[1]; + out2[x] = in[2]; + in += 4; + } + } + } else { + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out0 = bands[0]->image8[y]; + UINT8* out1 = bands[1]->image8[y]; + UINT8* out2 = bands[2]->image8[y]; + UINT8* out3 = bands[3]->image8[y]; + for (x = 0; x < imIn->xsize; x++) { + out0[x] = in[0]; + out1[x] = in[1]; + out2[x] = in[2]; + out3[x] = in[3]; + in += 4; + } + } + } + return imIn->bands; } From 6ce6cc75374bc3fac42d80cbde4001aaa7102167 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 15:49:23 +0300 Subject: [PATCH 45/53] faster ImagingSplit --- libImaging/Bands.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/libImaging/Bands.c b/libImaging/Bands.c index 17e1e1f26..c152a4eb7 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -100,7 +100,13 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) UINT8* in = (UINT8*) imIn->image[y]; UINT8* out0 = bands[0]->image8[y]; UINT8* out1 = bands[1]->image8[y]; - for (x = 0; x < imIn->xsize; x++) { + x = 0; + for (; x < imIn->xsize - 3; x += 4) { + *((UINT32*) (out0 + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]); + *((UINT32*) (out1 + x)) = MAKE_UINT32(in[0+3], in[4+3], in[8+3], in[12+3]); + in += 16; + } + for (; x < imIn->xsize; x++) { out0[x] = in[0]; out1[x] = in[3]; in += 4; @@ -112,7 +118,14 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) UINT8* out0 = bands[0]->image8[y]; UINT8* out1 = bands[1]->image8[y]; UINT8* out2 = bands[2]->image8[y]; - for (x = 0; x < imIn->xsize; x++) { + x = 0; + for (; x < imIn->xsize - 3; x += 4) { + *((UINT32*) (out0 + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]); + *((UINT32*) (out1 + x)) = MAKE_UINT32(in[0+1], in[4+1], in[8+1], in[12+1]); + *((UINT32*) (out2 + x)) = MAKE_UINT32(in[0+2], in[4+2], in[8+2], in[12+2]); + in += 16; + } + for (; x < imIn->xsize; x++) { out0[x] = in[0]; out1[x] = in[1]; out2[x] = in[2]; @@ -126,7 +139,15 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) UINT8* out1 = bands[1]->image8[y]; UINT8* out2 = bands[2]->image8[y]; UINT8* out3 = bands[3]->image8[y]; - for (x = 0; x < imIn->xsize; x++) { + x = 0; + for (; x < imIn->xsize - 3; x += 4) { + *((UINT32*) (out0 + x)) = MAKE_UINT32(in[0], in[4], in[8], in[12]); + *((UINT32*) (out1 + x)) = MAKE_UINT32(in[0+1], in[4+1], in[8+1], in[12+1]); + *((UINT32*) (out2 + x)) = MAKE_UINT32(in[0+2], in[4+2], in[8+2], in[12+2]); + *((UINT32*) (out3 + x)) = MAKE_UINT32(in[0+3], in[4+3], in[8+3], in[12+3]); + in += 16; + } + for (; x < imIn->xsize; x++) { out0[x] = in[0]; out1[x] = in[1]; out2[x] = in[2]; From 294da4e8f58c644620c4c008d5250fc5e1d39354 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 12 Aug 2017 17:42:49 +0300 Subject: [PATCH 46/53] error handling --- libImaging/Bands.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libImaging/Bands.c b/libImaging/Bands.c index c152a4eb7..a6bd730ee 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -76,7 +76,7 @@ ImagingGetBand(Imaging imIn, int band) int ImagingSplit(Imaging imIn, Imaging bands[4]) { - int i, x, y; + int i, j, x, y; /* Check arguments */ if (!imIn || imIn->type != IMAGING_TYPE_UINT8) { @@ -92,6 +92,12 @@ ImagingSplit(Imaging imIn, Imaging bands[4]) for (i = 0; i < imIn->bands; i++) { bands[i] = ImagingNew("L", imIn->xsize, imIn->ysize); + if ( ! bands[i]) { + for (j = 0; j < i; ++j) { + ImagingDelete(bands[j]); + } + return 0; + } } /* Extract bands from image */ From c2e0092589d695e010376de75c5ce44b7cb600a6 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 13 Aug 2017 01:10:19 +0300 Subject: [PATCH 47/53] Speed up resampling again a bit ) --- libImaging/Resample.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/libImaging/Resample.c b/libImaging/Resample.c index 0f6a9e9dd..00918bb49 100644 --- a/libImaging/Resample.c +++ b/libImaging/Resample.c @@ -5,6 +5,13 @@ #define ROUND_UP(f) ((int) ((f) >= 0.0 ? (f) + 0.5F : (f) - 0.5F)) +#ifdef WORDS_BIGENDIAN + #define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24)) +#else + #define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24)) +#endif + + struct filter { double (*filter)(double x); double support; @@ -281,8 +288,8 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) ss0 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 0]) * k[x]; ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x]; } - imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 3] = clip8(ss3); + ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( + clip8(ss0), 0, 0, clip8(ss3)); } } } else if (imIn->bands == 3) { @@ -297,9 +304,8 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) ss1 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 1]) * k[x]; ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x]; } - imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 1] = clip8(ss1); - imOut->image[yy][xx*4 + 2] = clip8(ss2); + ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( + clip8(ss0), clip8(ss1), clip8(ss2), 0); } } } else { @@ -315,10 +321,8 @@ ImagingResampleHorizontal_8bpc(Imaging imIn, int xsize, struct filter *filterp) ss2 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 2]) * k[x]; ss3 += ((UINT8) imIn->image[yy][(x + xmin)*4 + 3]) * k[x]; } - imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 1] = clip8(ss1); - imOut->image[yy][xx*4 + 2] = clip8(ss2); - imOut->image[yy][xx*4 + 3] = clip8(ss3); + ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( + clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3)); } } } @@ -386,8 +390,8 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) ss0 += ((UINT8) imIn->image[y + ymin][xx*4 + 0]) * k[y]; ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y]; } - imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 3] = clip8(ss3); + ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( + clip8(ss0), 0, 0, clip8(ss3)); } } } else if (imIn->bands == 3) { @@ -402,9 +406,8 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) ss1 += ((UINT8) imIn->image[y + ymin][xx*4 + 1]) * k[y]; ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y]; } - imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 1] = clip8(ss1); - imOut->image[yy][xx*4 + 2] = clip8(ss2); + ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( + clip8(ss0), clip8(ss1), clip8(ss2), 0); } } } else { @@ -420,10 +423,8 @@ ImagingResampleVertical_8bpc(Imaging imIn, int ysize, struct filter *filterp) ss2 += ((UINT8) imIn->image[y + ymin][xx*4 + 2]) * k[y]; ss3 += ((UINT8) imIn->image[y + ymin][xx*4 + 3]) * k[y]; } - imOut->image[yy][xx*4 + 0] = clip8(ss0); - imOut->image[yy][xx*4 + 1] = clip8(ss1); - imOut->image[yy][xx*4 + 2] = clip8(ss2); - imOut->image[yy][xx*4 + 3] = clip8(ss3); + ((UINT32 *) imOut->image[yy])[xx] = MAKE_UINT32( + clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3)); } } } From 11399b18ce1f77bcede47d9b8db4f5b92c9b4632 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Jul 2017 19:51:26 +1000 Subject: [PATCH 48/53] Moved SgiImagePlugin save error to before the start of write operations --- PIL/SgiImagePlugin.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index bca7eb13f..d5db91c2f 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -104,6 +104,11 @@ def _save(im, fp, filename): z = len(im.mode) if dim == 1 or dim == 2: z = 1 + # assert we've got the right number of bands. + if len(im.getbands()) != z: + raise ValueError("incorrect number of bands in SGI write: %s vs %s" % + (z, len(im.getbands()))) + # Minimum Byte value pinmin = 0 # Maximum Byte value (255 = 8bits per pixel) @@ -131,11 +136,6 @@ def _save(im, fp, filename): fp.write(struct.pack('404s', b'')) # dummy - # assert we've got the right number of bands. - if len(im.getbands()) != z: - raise ValueError("incorrect number of bands in SGI write: %s vs %s" % - (z, len(im.getbands()))) - for channel in im.split(): fp.write(channel.tobytes()) From 64a8dae95e329fa131329ec3d299eb855181a2f3 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 16 Aug 2017 11:47:37 +0100 Subject: [PATCH 49/53] Update CHANGES.rst [ci skip] --- CHANGES.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 05b07d53c..4b0a4fa36 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,27 @@ Changelog (Pillow) 4.3.0 (unreleased) ------------------ +- Test: Test animated FLI file #2650 + [hugovk] + +- Bug: Fixed uninitalized memory in bc5 decoding #2648 + [ifeherva] + +- Moved SgiImagePlugin save error to before the start of write operations #2646 + [radarhere] + +- Move createfontdatachunk.py so isn't installed globally #2645 + [hugovk] + +- Bug: Fix unexpected keyword argument 'align' #2641 + [hugovk] + +- Add newlines to error message for clarity #2640 + [hugovk] + +- Docs: Updated redirected URL #2637 + [radarhere] + - Bug: Fix JPEG DPI when EXIF is invalid #2632 [wiredfool] From 72f0c7b8732dfada7a3dc87f14d79cdbc358dc19 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Aug 2017 20:57:10 +1000 Subject: [PATCH 50/53] Updated tests --- Tests/test_file_fli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index a5be63f77..0da197155 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -29,7 +29,7 @@ class TestFileFli(PillowTestCase): def test_tell(self): # Arrange - im = Image.open(test_file) + im = Image.open(static_test_file) # Act frame = im.tell() @@ -49,7 +49,7 @@ class TestFileFli(PillowTestCase): self.assertFalse(im.is_animated) im = Image.open(animated_test_file) - self.assertEqual(im.n_frames, 385) + self.assertEqual(im.n_frames, 384) self.assertTrue(im.is_animated) def test_eoferror(self): @@ -66,7 +66,7 @@ class TestFileFli(PillowTestCase): def test_seek_outside(self): # Test negative seek - im = Image.open(test_file) + im = Image.open(static_test_file) im.seek(-1) self.assertEqual(im.tell(), 0) From e5b7733cdaf591deb2ed29ef5f94c6315c357fee Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 16 Aug 2017 12:02:11 +0100 Subject: [PATCH 51/53] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4b0a4fa36..06349586d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 4.3.0 (unreleased) ------------------ +- Storage cleanup #2654 + [homm] + - Test: Test animated FLI file #2650 [hugovk] From e6752af33697d8a809d388be5bd699b848601f4b Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 16 Aug 2017 15:44:31 +0100 Subject: [PATCH 52/53] Update CHANGES.rst [ci skip] --- CHANGES.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 06349586d..52854fedd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,8 +4,38 @@ Changelog (Pillow) 4.3.0 (unreleased) ------------------ +- JPEG: Fix ZeroDivisionError when EXIF contains invalid DPI (0/0). #2667 + [vytisb] + +- Depends: Updated openjpeg to 2.2.0 #2669 + [radarhere] + +- Depends: Updated Tk Tcl to 8.6.7 #2668 + [radarhere] + +- Depends: Updated libimagequant to 2.10.2 #2660 + [radarhere] + +- Test: Added test for ImImagePlugin tell() #2675 + [radarhere] + +- Test: Additional tests for SGIImagePlugin #2659 + [radarhere] + +- New Image.getchannel method #2661 + [homm] + +- Remove unused im.copy2 and core.copy methods #2657 + [homm] + +- Fast image allocation #2655 + [homm] + - Storage cleanup #2654 [homm] + +- FLI: Use frame count from FLI header #2674 + [radarhere] - Test: Test animated FLI file #2650 [hugovk] From 52e9c831767eaa60cc1686842f4019e0e9964ad8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Wed, 16 Aug 2017 16:36:24 +0100 Subject: [PATCH 53/53] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 52854fedd..653797ff0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ Changelog (Pillow) 4.3.0 (unreleased) ------------------ +- CI: Add Fedora 24 and 26 to Docker tests + [wiredfool] + - JPEG: Fix ZeroDivisionError when EXIF contains invalid DPI (0/0). #2667 [vytisb] @@ -28,6 +31,9 @@ Changelog (Pillow) - Remove unused im.copy2 and core.copy methods #2657 [homm] +- Fast Image.split() #2676 + [homm] + - Fast image allocation #2655 [homm]