From 4e69b9c5531b3308725e023ce77e1574608451d4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Jan 2018 20:46:04 +1100 Subject: [PATCH 01/87] Skip outline if the draw operation fills with the same colour --- src/PIL/ImageDraw.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 89df27338..80a9f6023 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -138,7 +138,7 @@ class ImageDraw(object): ink, fill = self._getink(outline, fill) if fill is not None: self.draw.draw_chord(xy, start, end, fill, 1) - if ink is not None: + if ink is not None and ink != fill: self.draw.draw_chord(xy, start, end, ink, 0) def ellipse(self, xy, fill=None, outline=None): @@ -146,7 +146,7 @@ class ImageDraw(object): ink, fill = self._getink(outline, fill) if fill is not None: self.draw.draw_ellipse(xy, fill, 1) - if ink is not None: + if ink is not None and ink != fill: self.draw.draw_ellipse(xy, ink, 0) def line(self, xy, fill=None, width=0): @@ -161,7 +161,7 @@ class ImageDraw(object): ink, fill = self._getink(outline, fill) if fill is not None: self.draw.draw_outline(shape, fill, 1) - if ink is not None: + if ink is not None and ink != fill: self.draw.draw_outline(shape, ink, 0) def pieslice(self, xy, start, end, fill=None, outline=None): @@ -169,7 +169,7 @@ class ImageDraw(object): ink, fill = self._getink(outline, fill) if fill is not None: self.draw.draw_pieslice(xy, start, end, fill, 1) - if ink is not None: + if ink is not None and ink != fill: self.draw.draw_pieslice(xy, start, end, ink, 0) def point(self, xy, fill=None): @@ -183,7 +183,7 @@ class ImageDraw(object): ink, fill = self._getink(outline, fill) if fill is not None: self.draw.draw_polygon(xy, fill, 1) - if ink is not None: + if ink is not None and ink != fill: self.draw.draw_polygon(xy, ink, 0) def rectangle(self, xy, fill=None, outline=None): @@ -191,7 +191,7 @@ class ImageDraw(object): ink, fill = self._getink(outline, fill) if fill is not None: self.draw.draw_rectangle(xy, fill, 1) - if ink is not None: + if ink is not None and ink != fill: self.draw.draw_rectangle(xy, ink, 0) def _multiline_check(self, text): From 4d3339b70376a25158156130a27788fdde6bf114 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 5 Jan 2018 11:26:24 +1100 Subject: [PATCH 02/87] Added tests --- Tests/images/imagedraw_outline_chord_L.png | Bin 0 -> 212 bytes Tests/images/imagedraw_outline_chord_RGB.png | Bin 0 -> 259 bytes Tests/images/imagedraw_outline_ellipse_L.png | Bin 0 -> 290 bytes .../images/imagedraw_outline_ellipse_RGB.png | Bin 0 -> 378 bytes Tests/images/imagedraw_outline_pieslice_L.png | Bin 0 -> 266 bytes .../images/imagedraw_outline_pieslice_RGB.png | Bin 0 -> 340 bytes Tests/images/imagedraw_outline_polygon_L.png | Bin 0 -> 309 bytes .../images/imagedraw_outline_polygon_RGB.png | Bin 0 -> 393 bytes .../images/imagedraw_outline_rectangle_L.png | Bin 0 -> 125 bytes .../imagedraw_outline_rectangle_RGB.png | Bin 0 -> 212 bytes Tests/images/imagedraw_outline_shape_L.png | Bin 0 -> 263 bytes Tests/images/imagedraw_outline_shape_RGB.png | Bin 0 -> 316 bytes Tests/test_imagedraw.py | 41 ++++++++++++++++++ 13 files changed, 41 insertions(+) create mode 100644 Tests/images/imagedraw_outline_chord_L.png create mode 100644 Tests/images/imagedraw_outline_chord_RGB.png create mode 100644 Tests/images/imagedraw_outline_ellipse_L.png create mode 100644 Tests/images/imagedraw_outline_ellipse_RGB.png create mode 100644 Tests/images/imagedraw_outline_pieslice_L.png create mode 100644 Tests/images/imagedraw_outline_pieslice_RGB.png create mode 100644 Tests/images/imagedraw_outline_polygon_L.png create mode 100644 Tests/images/imagedraw_outline_polygon_RGB.png create mode 100644 Tests/images/imagedraw_outline_rectangle_L.png create mode 100644 Tests/images/imagedraw_outline_rectangle_RGB.png create mode 100644 Tests/images/imagedraw_outline_shape_L.png create mode 100644 Tests/images/imagedraw_outline_shape_RGB.png diff --git a/Tests/images/imagedraw_outline_chord_L.png b/Tests/images/imagedraw_outline_chord_L.png new file mode 100644 index 0000000000000000000000000000000000000000..9c20ad217141450ac3ffd9bc6e6a6ae6e9ea6aeb GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5yWGui7$e$$~yckOl^Z2bE%#>1V}nANd-;FQjjRdFu*)*UA1hi7_Ad zl>B|;_m1=Av(OO#_bORSSFN@F7<}qfT1=i-ta-hE+ikDlt7VUtl$ieN`Je6g`=99? z+m+#lQKeRrp~mMd&I(L@R4bl$BC_xAYBt5=bN-aDF)$psTJ~?Z(DfyH1+pN!Jzf1= J);T3K0RW^dR@wjn literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_chord_RGB.png b/Tests/images/imagedraw_outline_chord_RGB.png new file mode 100644 index 0000000000000000000000000000000000000000..9e9cb5af006f84b806867b586948cd1b21e668c3 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^DImsV8LPK9oMvXgoV7CaQ=eTc7-)eiJ}~<9U@pyZkS}A z?YBW|Zm7bVrhB_G+MWc>3G3t24hJGrTi3{Qkas zk*s?Y&zgt7?t44ebY}SXxZ?E8zs~l>Jv+<8?pGd@`_ZPYeLE>!>hpP}%GfvGe!T0y qVis0&X!^-0`z2*w3n5NBuy-$G&o4H|_cdiRKq8*5elF{r5}E)|>1!?k literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_ellipse_L.png b/Tests/images/imagedraw_outline_ellipse_L.png new file mode 100644 index 0000000000000000000000000000000000000000..53b76b62b4210515d1dc2901d7b6fc59116ac085 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5A0m|KGp8*o2aut|H!8*U{VQ;_tq~xSue{b)7#QE5%iKTOT$UNRNqW%)$m2rRnyit4Zk@Z>VkeA7@=s zH?o?QU%M;G+vmI*{|DQM)%GhdlXg)SKC&ew24b8@vT8jr(DqFt!C>3zCMszcS<|_y6tKO>1#*lz3%tFmJ;_esyMy# zAFFk7&(7+wcQ(o5H3$8}-gm@ip8fx9?FQpL`4z|9_nZWUDi9Q~KRzKBoyz#e9K`c< L^>bP0l+XkKcy*%w literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_pieslice_L.png b/Tests/images/imagedraw_outline_pieslice_L.png new file mode 100644 index 0000000000000000000000000000000000000000..92972d54cc927111bbf41eb7b793e277493f04f4 GIT binary patch literal 266 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5b!~gswEzg%q zKMH)+k$h50pn8Vbk)SGp#cD3{OS6SSZ)WcN68K9kaLV3k*@8y#>+h;XHv3%9-tFli z=dAQ!_OeHer*3%2$=JyYChhrPs9IhAH)vVWFR{KelkTfCF5Mux{#4A`Hx=J?mL?xv zedXLb!=HD$)>Nkd(UJ!w#Kfx%vWm%ZX&Ot)Nb2YJ%d L)z4*}Q$iB}XDe~C literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_pieslice_RGB.png b/Tests/images/imagedraw_outline_pieslice_RGB.png new file mode 100644 index 0000000000000000000000000000000000000000..4be4fc4afbbf34a16d3278dd597362399de0ed76 GIT binary patch literal 340 zcmeAS@N?(olHy`uVBq!ia0vp^DIm&5H; z{@dSfz0e!R<9qB$;rUCw4<|7moCs781Kan`lY4Ndtm0DQ1wP|B7RP>76+EikxbIs3 zzqo(LOm6R8*2#AK*4j6wcGJYZ6?H5Pb-$!?ZQZWvp*o`9i@HNk8HC*}k~|g^seeAg zH77Y#XL_!V=$E3tseKWQ&o&nMFREMyKPBPFi`quD5e3+g`P!L3_gU?k8^i@}ll+ z_tNK8y**2-_gc9V a4ov0$CN31iGq1x9B8V+ZHr4o!2_x3LISRL{{F=aV--xaT)lN`S2 z@dRmX^;diu>+8-ZXzr%8@zO;drP75H9WrC(BHz8)&B?LfkaedPgQjARIPcE@0e_cs zyBGRtIpzAe-Q#k5dE$@#M+@QmdNRigLFl=ACF7Ud`XLHo5-y0C-!{qq_sP3SSDtguyUKhLrxanERCzpkvl6tx^<$`v0V_$^Pi>Zq=r#-7u zJ@>YI>EG{HrM%znEjnI$JSchn7f0reTfSA#>0J6NPV~Hd_tMtyrjp*Nd%NVUWxQ9v z%eI@gsru^G?Hx<^nCt8fo*r@QgRRujOQq{y-HF@yq&B{6qsZH<0XGe&ox8v1xtwqE z$xqc^dYATBZ%y6Wr@HxfQUB8YRi)Qr1iasOo7}(K7xC$b-1Q@uyx0G$nVod<@xEz4 im;En?hYbTmgYy1woJ>=gBP$PrL_J;oT-G@yGywpHPr0xF literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_rectangle_L.png b/Tests/images/imagedraw_outline_rectangle_L.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c47018fb435b7916a92051c6e3b78046c1828f GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5?LEc!(!8PgVbG9L%=Jzf1= J);T3K0RX!!He~<+ literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_shape_L.png b/Tests/images/imagedraw_outline_shape_L.png new file mode 100644 index 0000000000000000000000000000000000000000..20ebef1578cf9c919e8d4e6094bf231a5abe6fcb GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^DIm-NBp5zi$2M z)W*M`SRXD_+qQh}&ybT(n2IJm(4VQ|x!%HYlF9L@_Z>d1og@-wpF1g`&QEBoMy7se z>ck^1p6;F+t5uXv1$};~`$j%>?T;_#U&yAeHlOWZD=TlY+h*~bY~P#K%d1asyj!!Y z=1@xLCzT?OV3RDJKX(nDRw#GowQh{svi-wXgJ&MroqBC6O~R+fTnPDWuCmpeZO%lsV=7B^-+ki&`2`Bz?O_zrmzLe!>j?5DgQu&X%Q~lo FCIBt_Yasvt literal 0 HcmV?d00001 diff --git a/Tests/images/imagedraw_outline_shape_RGB.png b/Tests/images/imagedraw_outline_shape_RGB.png new file mode 100644 index 0000000000000000000000000000000000000000..6fb6f623e4b01621b35cb6e939343f1386734036 GIT binary patch literal 316 zcmeAS@N?(olHy`uVBq!ia0vp^DImziq!6ST9>6{ZG7haGT> z+}x`@zq~r~^OvnNucS(BSiMH_*jDxAHSLQ3f8UIEd}Ab&pR~}0{V~76M&^_J_a0Qw zzp?T2eBZlwkDOz%`+arc`p&sFxf?h1`mgagcj$D+hD}YJ+Hr2?V&R&hU#rg@of>*J z`0w_IQ~eumy)XTGdCiXtZodqejBL$HynsGO1<$@QZZzczjaa`hA0*-F>gTe~DWM4f DE;olO literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index a79a75ca0..62b11e98c 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -572,6 +572,47 @@ class TestImageDraw(PillowTestCase): draw.textsize("\n") draw.textsize("test\n") + def test_same_color_outline(self): + # Prepare shape + x0, y0 = 5, 5 + x1, y1 = 5, 50 + x2, y2 = 95, 50 + x3, y3 = 95, 5 + + s = ImageDraw.Outline() + s.move(x0, y0) + s.curve(x1, y1, x2, y2, x3, y3) + s.line(x0, y0) + + # Begin + for mode in ["RGB", "L"]: + for fill, outline in [ + ["red", None], + ["red", "red"], + ["red", "#f00"] + ]: + for operation, args in { + 'chord':[BBOX1, 0, 180], + 'ellipse':[BBOX1], + 'shape':[s], + 'pieslice':[BBOX1, -90, 45], + 'polygon':[[(18, 30), (85, 30), (60, 72)]], + 'rectangle':[BBOX1] + }.items(): + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + + # Act + draw_method = getattr(draw, operation) + args += [fill, outline] + draw_method(*args) + + # Assert + expected = ("Tests/images/imagedraw_outline" + "_{}_{}.png".format(operation, mode)) + self.assert_image_similar(im, Image.open(expected), 1) + if __name__ == '__main__': unittest.main() From 1ba14783d25bfb3dd3df7c5f0e62e1f900a79b78 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Tue, 1 Nov 2016 06:16:44 -0700 Subject: [PATCH 03/87] Avoid deprecated 'U' mode when opening files Instead, use PSFile() wrapper to handle all newline in the EPS spec. Update line ending tests to handle all combinations of '\n' and '\r'. Fixes warning "DeprecationWarning: 'U' mode is deprecated" in tests. --- Tests/test_file_eps.py | 56 +++++---------------------------------- src/PIL/EpsImagePlugin.py | 12 +-------- 2 files changed, 7 insertions(+), 61 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 9d6bed060..66fedee90 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -1,7 +1,6 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image, EpsImagePlugin -from PIL._util import py3 import io # Our two EPS test files (they are identical except for their bounding boxes) @@ -196,41 +195,15 @@ class TestFileEps(PillowTestCase): self.assertEqual(t.readline().strip('\r\n'), 'baz', ending) self.assertEqual(t.readline().strip('\r\n'), 'bif', ending) - def _test_readline_stringio(self, test_string, ending): - # check all the freaking line endings possible - try: - import StringIO - except ImportError: - # don't skip, it skips everything in the parent test - return - t = StringIO.StringIO(test_string) + def _test_readline_io_psfile(self, test_string, ending): + f = io.BytesIO(test_string.encode('latin-1')) + t = EpsImagePlugin.PSFile(f) self._test_readline(t, ending) - def _test_readline_io(self, test_string, ending): - if py3: - t = io.StringIO(test_string) - else: - t = io.StringIO(unicode(test_string)) - self._test_readline(t, ending) - - def _test_readline_file_universal(self, test_string, ending): - f = self.tempfile('temp.txt') - with open(f, 'wb') as w: - if py3: - w.write(test_string.encode('UTF-8')) - else: - w.write(test_string) - - with open(f, 'rU') as t: - self._test_readline(t, ending) - def _test_readline_file_psfile(self, test_string, ending): f = self.tempfile('temp.txt') with open(f, 'wb') as w: - if py3: - w.write(test_string.encode('UTF-8')) - else: - w.write(test_string) + w.write(test_string.encode('latin-1')) with open(f, 'rb') as r: t = EpsImagePlugin.PSFile(r) @@ -239,29 +212,12 @@ class TestFileEps(PillowTestCase): def test_readline(self): # check all the freaking line endings possible from the spec # test_string = u'something\r\nelse\n\rbaz\rbif\n' - line_endings = ['\r\n', '\n'] - not_working_endings = ['\n\r', '\r'] + line_endings = ['\r\n', '\n', '\n\r', '\r'] strings = ['something', 'else', 'baz', 'bif'] for ending in line_endings: s = ending.join(strings) - # Native Python versions will pass these endings. - # self._test_readline_stringio(s, ending) - # self._test_readline_io(s, ending) - # self._test_readline_file_universal(s, ending) - - self._test_readline_file_psfile(s, ending) - - for ending in not_working_endings: - # these only work with the PSFile, while they're in spec, - # they're not likely to be used - s = ending.join(strings) - - # Native Python versions may fail on these endings. - # self._test_readline_stringio(s, ending) - # self._test_readline_io(s, ending) - # self._test_readline_file_universal(s, ending) - + self._test_readline_io_psfile(s, ending) self._test_readline_file_psfile(s, ending) def test_open_eps(self): diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 1ae7865b1..cb2c00d20 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -26,7 +26,6 @@ import os import sys from . import Image, ImageFile from ._binary import i32le as i32 -from ._util import py3 __version__ = "0.5" @@ -206,16 +205,7 @@ class EpsImageFile(ImageFile.ImageFile): # Rewrap the open file pointer in something that will # convert line endings and decode to latin-1. - try: - if py3: - # Python3, can use bare open command. - fp = open(self.fp.name, "Ur", encoding='latin-1') - else: - # Python2, no encoding conversion necessary - fp = open(self.fp.name, "Ur") - except: - # Expect this for bytesio/stringio - fp = PSFile(self.fp) + fp = PSFile(self.fp) # go to offset - start of "%!PS" fp.seek(offset) From 607224320a8055aaa9a823ca010d1f72e6b4746a Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 1 Jul 2018 23:19:22 +0300 Subject: [PATCH 04/87] git checkout -> git clone [CI skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 6541d69d5..7cb3ad4e9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -83,7 +83,7 @@ Released as needed privately to individual vendors for critical security-related ### Mac and Linux * [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels): ``` - $ git checkout https://github.com/python-pillow/pillow-wheels + $ git clone https://github.com/python-pillow/pillow-wheels $ cd pillow-wheels $ git submodule init $ git submodule update From adfcbc94787a9f17c96f6da4fe61353f240d2d60 Mon Sep 17 00:00:00 2001 From: Daniel Plakhotich Date: Mon, 2 Jul 2018 00:50:02 +0300 Subject: [PATCH 05/87] Add LA to TGA test modes --- Tests/test_file_tga.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index c7e8ab71b..226b899dc 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -13,7 +13,7 @@ _TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common") class TestFileTga(PillowTestCase): - _MODES = ("L", "P", "RGB", "RGBA") + _MODES = ("L", "LA", "P", "RGB", "RGBA") _ORIGINS = ("tl", "bl") _ORIGIN_TO_ORIENTATION = { From af80067c8abb58a9bdd818e7df32bcf62845d140 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 2 Jul 2018 10:22:48 +0300 Subject: [PATCH 06/87] Remove non-applicable step Doesn't apply to the new PyPI (Warehouse) --- RELEASING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 7cb3ad4e9..4b763a136 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -7,7 +7,7 @@ Released quarterly on the first day of January, April, July, October. * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174 * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch. -* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in TravisCI. +* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI. * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `src/PIL/_version.py` * [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make release-test` in a freshly cloned repo. @@ -24,7 +24,6 @@ Released quarterly on the first day of January, April, July, October. ``` * [ ] Create [binary distributions](#binary-distributions) * [ ] Upload all binaries and source distributions with ``twine upload dist/Pillow-4.1.0-*`` -* [ ] Manually hide old versions on PyPI such that only the latest major release is visible when viewing https://pypi.org/project/Pillow/ (https://pypi.org/manage/project/Pillow/releases/) ## Point Release @@ -34,7 +33,7 @@ Released as needed for security, installation or critical bug fixes. * [ ] Update `CHANGES.rst`. * [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``. -* [ ] Checkout release branch e.g.: +* [ ] Check out release branch e.g.: ``` git checkout -t remotes/origin/2.9.x ``` From ae9f62040dfccb54b2030dd31073c78d08ad30c9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 2 Jul 2018 10:26:56 +0300 Subject: [PATCH 07/87] 5.3.0.dev0 version bump --- src/PIL/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/_version.py b/src/PIL/_version.py index b42628dde..f4bf34e00 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = '5.2.0' +__version__ = '5.3.0.dev0' From f0b841ba5af9f046adea33f3d0c83c9ff3d7472e Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 2 Jul 2018 11:19:29 +0300 Subject: [PATCH 08/87] Add final step to append .dev0 to version --- RELEASING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 4b763a136..936d68d8d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -8,7 +8,7 @@ Released quarterly on the first day of January, April, July, October. * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI. -* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `src/PIL/_version.py` +* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py` * [ ] Update `CHANGES.rst`. * [ ] Run pre-release check via `make release-test` in a freshly cloned repo. * [ ] Create branch and tag for release e.g.: @@ -24,6 +24,7 @@ Released quarterly on the first day of January, April, July, October. ``` * [ ] Create [binary distributions](#binary-distributions) * [ ] Upload all binaries and source distributions with ``twine upload dist/Pillow-4.1.0-*`` +* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), append `.dev0` to version identifier in `src/PIL/_version.py` ## Point Release @@ -37,7 +38,7 @@ Released as needed for security, installation or critical bug fixes. ``` git checkout -t remotes/origin/2.9.x ``` -* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `src/PIL/_version.py` +* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py` * [ ] Run pre-release check via `make release-test`. * [ ] Create tag for release e.g.: ``` From bf299602837a050b77ddae3754418619178fad6b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jun 2018 13:57:19 +1000 Subject: [PATCH 09/87] Fixed multiple spaces after operator --- src/PIL/ImageDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 5bc890252..920b977f4 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -384,4 +384,4 @@ def _color_diff(rgb1, rgb2): """ Uses 1-norm distance to calculate difference between two rgb values. """ - return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2]) + return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2]) From 5a33e02072f21fe8cdb33bb5b0688368d182befc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jun 2018 13:58:28 +1000 Subject: [PATCH 10/87] Commented unused variable --- src/PIL/SunImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/SunImagePlugin.py b/src/PIL/SunImagePlugin.py index fd5e82724..03b266bc4 100644 --- a/src/PIL/SunImagePlugin.py +++ b/src/PIL/SunImagePlugin.py @@ -62,7 +62,7 @@ class SunImageFile(ImageFile.ImageFile): self.size = i32(s[4:8]), i32(s[8:12]) depth = i32(s[12:16]) - data_length = i32(s[16:20]) # unreliable, ignore. + # data_length = i32(s[16:20]) # unreliable, ignore. file_type = i32(s[20:24]) palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary palette_length = i32(s[28:32]) From fe42591f5f5705e09a9ceccd05c1fedbd4592099 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jun 2018 13:59:17 +1000 Subject: [PATCH 11/87] Removed redundant backslash between brackets --- src/PIL/PdfParser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index c0635ef31..f63d47eb2 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -824,7 +824,7 @@ class PdfParser: def read_indirect(self, ref, max_nesting=-1): offset, generation = self.xref_table[ref[0]] - check_format_condition(generation == ref[1], "expected to find generation %s for object ID %s in xref table, instead found generation %s at offset %s" \ + check_format_condition(generation == ref[1], "expected to find generation %s for object ID %s in xref table, instead found generation %s at offset %s" % (ref[1], ref[0], generation, offset)) value = self.get_value(self.buf, offset + self.start_offset, expect_indirect=IndirectReference(*ref), max_nesting=max_nesting)[0] self.cached_objects[ref] = value From e7815ccd621016c16d0ab296fa7906c710ad8983 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Jun 2018 19:40:04 +1000 Subject: [PATCH 12/87] Block comment should start with '# ' --- Tests/test_imagefont.py | 2 +- Tests/test_imagefontctl.py | 2 +- src/PIL/PdfParser.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0a36734d2..906f7b06f 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -413,7 +413,7 @@ class TestImageFont(PillowTestCase): im = Image.new(mode='RGB', size=(300, 100)) target = im.copy() draw = ImageDraw.Draw(im) - #should not crash here. + # should not crash here. draw.text((10, 10), '', font=font) self.assert_image_equal(im, target) diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 89f9563f3..489ed40b5 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -11,7 +11,7 @@ FONT_PATH = "Tests/fonts/DejaVuSans.ttf" class TestImagecomplextext(PillowTestCase): def test_english(self): - #smoke test, this should not fail + # smoke test, this should not fail ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index f63d47eb2..9031330da 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -744,7 +744,7 @@ class PdfParser: m = cls.re_string_lit.match(data, offset) if m: return cls.get_literal_string(data, m.end()) - #return None, offset # fallback (only for debugging) + # return None, offset # fallback (only for debugging) raise PdfFormatError("unrecognized object: " + repr(data[offset:offset+32])) re_lit_str_token = re.compile(br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))") From 58cc23695d29b58d5329a29c7b5b25ae5c620805 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Jun 2018 19:37:33 +1000 Subject: [PATCH 13/87] Continuation line over-indented for visual indent --- Tests/test_imagefont.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 906f7b06f..e333346e9 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -210,10 +210,9 @@ class TestImageFont(PillowTestCase): ttf = self.get_font() # Act/Assert - self.assertRaises(AssertionError, - draw.multiline_text, (0, 0), TEST_TEXT, - font=ttf, - align="unknown") + self.assertRaises( + AssertionError, + draw.multiline_text, (0, 0), TEST_TEXT, font=ttf, align="unknown") def test_draw_align(self): im = Image.new('RGB', (300, 100), 'white') From aeab86c005bd5d710c34bb5874865d248da63b18 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jun 2018 15:51:12 +1000 Subject: [PATCH 14/87] Too many blank lines --- Tests/test_file_jpeg.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 2d9a9a091..c42d67885 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -364,7 +364,6 @@ class TestFileJpeg(PillowTestCase): with self.assertRaises(IOError): im.load() - def _n_qtables_helper(self, n, test_file): im = Image.open(test_file) f = self.tempfile('temp.jpg') From dcf6bc047b3f2e1ea180b921e1e9b58fb37032e0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jun 2018 16:00:57 +1000 Subject: [PATCH 15/87] Do not use bare except --- Tests/test_file_libtiff.py | 4 ++-- Tests/test_locale.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 05433d9b6..ce77e05de 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -31,7 +31,7 @@ class LibTiffTestCase(PillowTestCase): try: self.assertEqual(im._compression, 'group4') - except: + except AttributeError: print("No _compression") print(dir(im)) @@ -194,7 +194,7 @@ class TestFileLibTiff(LibTiffTestCase): for tag in im.tag_v2: try: del(core_items[tag]) - except: + except KeyError: pass # Type codes: diff --git a/Tests/test_locale.py b/Tests/test_locale.py index 142753791..5aef8427b 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -29,7 +29,7 @@ class TestLocale(PillowTestCase): Image.open(path) try: locale.setlocale(locale.LC_ALL, "polish") - except: + except locale.Error: unittest.skip('Polish locale not available') Image.open(path) From 32cebddd1ee185e807ab758afcafd82d29e6a309 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 Jun 2018 19:44:36 +1000 Subject: [PATCH 16/87] Multiple imports on one line --- src/PIL/_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 6618c625f..e6989d69d 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -1,4 +1,5 @@ -import os, sys +import os +import sys py3 = sys.version_info.major >= 3 From c19d77abed4919791e9e5d9a362af8a73856e9cf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Jun 2018 10:06:45 +1000 Subject: [PATCH 17/87] Continuation line under-indented for visual indent --- Tests/test_color_lut.py | 148 +++++++++++++++++++---------------- Tests/test_image_resample.py | 4 +- 2 files changed, 82 insertions(+), 70 deletions(-) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index c9793c950..e641c5ad3 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -38,112 +38,123 @@ class TestColorLut3DCoreAPI(PillowTestCase): im = Image.new('RGB', (10, 10), 0) with self.assertRaisesRegex(ValueError, "filter"): - im.im.color_lut_3d('RGB', Image.CUBIC, - *self.generate_identity_table(3, 3)) + im.im.color_lut_3d('RGB', + Image.CUBIC, + *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "image mode"): - im.im.color_lut_3d('wrong', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im.im.color_lut_3d('wrong', + Image.LINEAR, + *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "table_channels"): - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(5, 3)) + im.im.color_lut_3d('RGB', + Image.LINEAR, + *self.generate_identity_table(5, 3)) with self.assertRaisesRegex(ValueError, "table_channels"): - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(1, 3)) + im.im.color_lut_3d('RGB', + Image.LINEAR, + *self.generate_identity_table(1, 3)) with self.assertRaisesRegex(ValueError, "table_channels"): - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(2, 3)) + im.im.color_lut_3d('RGB', + Image.LINEAR, + *self.generate_identity_table(2, 3)) with self.assertRaisesRegex(ValueError, "Table size"): - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (1, 3, 3))) + im.im.color_lut_3d('RGB', + Image.LINEAR, + *self.generate_identity_table(3, (1, 3, 3))) with self.assertRaisesRegex(ValueError, "Table size"): - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (66, 3, 3))) + im.im.color_lut_3d('RGB', + Image.LINEAR, + *self.generate_identity_table(3, (66, 3, 3))) with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"): - im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, [0, 0, 0] * 7) + im.im.color_lut_3d('RGB', + Image.LINEAR, + 3, 2, 2, 2, [0, 0, 0] * 7) with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"): - im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, [0, 0, 0] * 9) + im.im.color_lut_3d('RGB', + Image.LINEAR, + 3, 2, 2, 2, [0, 0, 0] * 9) with self.assertRaises(TypeError): - im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, [0, 0, "0"] * 8) + im.im.color_lut_3d('RGB', + Image.LINEAR, + 3, 2, 2, 2, [0, 0, "0"] * 8) with self.assertRaises(TypeError): - im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, 16) + im.im.color_lut_3d('RGB', + Image.LINEAR, + 3, 2, 2, 2, 16) def test_correct_args(self): im = Image.new('RGB', (10, 10), 0) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) im.im.color_lut_3d('CMYK', Image.LINEAR, - *self.generate_identity_table(4, 3)) + *self.generate_identity_table(4, 3)) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (2, 3, 3))) + *self.generate_identity_table(3, (2, 3, 3))) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (65, 3, 3))) + *self.generate_identity_table(3, (65, 3, 3))) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (3, 65, 3))) + *self.generate_identity_table(3, (3, 65, 3))) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (3, 3, 65))) + *self.generate_identity_table(3, (3, 3, 65))) def test_wrong_mode(self): with self.assertRaisesRegex(ValueError, "wrong mode"): im = Image.new('L', (10, 10), 0) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): im = Image.new('RGB', (10, 10), 0) im.im.color_lut_3d('L', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): im = Image.new('L', (10, 10), 0) im.im.color_lut_3d('L', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): im = Image.new('RGB', (10, 10), 0) im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): im = Image.new('RGB', (10, 10), 0) im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(4, 3)) + *self.generate_identity_table(4, 3)) def test_correct_mode(self): im = Image.new('RGBA', (10, 10), 0) im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) im = Image.new('RGBA', (10, 10), 0) im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(4, 3)) + *self.generate_identity_table(4, 3)) im = Image.new('RGB', (10, 10), 0) im.im.color_lut_3d('HSV', Image.LINEAR, - *self.generate_identity_table(3, 3)) + *self.generate_identity_table(3, 3)) im = Image.new('RGB', (10, 10), 0) im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(4, 3)) + *self.generate_identity_table(4, 3)) def test_identities(self): g = Image.linear_gradient('L') @@ -154,12 +165,12 @@ class TestColorLut3DCoreAPI(PillowTestCase): for size in [2, 3, 5, 7, 11, 16, 17]: self.assert_image_equal(im, im._new( im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, size)))) + *self.generate_identity_table(3, size)))) # Not so fast self.assert_image_equal(im, im._new( im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (2, 2, 65))))) + *self.generate_identity_table(3, (2, 2, 65))))) def test_identities_4_channels(self): g = Image.linear_gradient('L') @@ -170,7 +181,7 @@ class TestColorLut3DCoreAPI(PillowTestCase): self.assert_image_equal( Image.merge('RGBA', (im.split()*2)[:4]), im._new(im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(4, 17)))) + *self.generate_identity_table(4, 17)))) def test_copy_alpha_channel(self): g = Image.linear_gradient('L') @@ -180,7 +191,7 @@ class TestColorLut3DCoreAPI(PillowTestCase): self.assert_image_equal(im, im._new( im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(3, 17)))) + *self.generate_identity_table(3, 17)))) def test_channels_order(self): g = Image.linear_gradient('L') @@ -191,13 +202,13 @@ class TestColorLut3DCoreAPI(PillowTestCase): self.assert_image_equal( Image.merge('RGB', im.split()[::-1]), im._new(im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, [ - 0, 0, 0, 0, 0, 1, - 0, 1, 0, 0, 1, 1, + 3, 2, 2, 2, [ + 0, 0, 0, 0, 0, 1, + 0, 1, 0, 0, 1, 1, - 1, 0, 0, 1, 0, 1, - 1, 1, 0, 1, 1, 1, - ]))) + 1, 0, 0, 1, 0, 1, + 1, 1, 0, 1, 1, 1, + ]))) def test_overflow(self): g = Image.linear_gradient('L') @@ -205,14 +216,14 @@ class TestColorLut3DCoreAPI(PillowTestCase): g.transpose(Image.ROTATE_180)]) transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, - [ - -1, -1, -1, 2, -1, -1, - -1, 2, -1, 2, 2, -1, + 3, 2, 2, 2, + [ + -1, -1, -1, 2, -1, -1, + -1, 2, -1, 2, 2, -1, - -1, -1, 2, 2, -1, 2, - -1, 2, 2, 2, 2, 2, - ])).load() + -1, -1, 2, 2, -1, 2, + -1, 2, 2, 2, 2, 2, + ])).load() self.assertEqual(transformed[0, 0], (0, 0, 255)) self.assertEqual(transformed[50, 50], (0, 0, 255)) self.assertEqual(transformed[255, 0], (0, 255, 255)) @@ -223,14 +234,14 @@ class TestColorLut3DCoreAPI(PillowTestCase): self.assertEqual(transformed[205, 205], (255, 255, 0)) transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, - 3, 2, 2, 2, - [ - -3, -3, -3, 5, -3, -3, - -3, 5, -3, 5, 5, -3, + 3, 2, 2, 2, + [ + -3, -3, -3, 5, -3, -3, + -3, 5, -3, 5, 5, -3, - -3, -3, 5, 5, -3, 5, - -3, 5, 5, 5, 5, 5, - ])).load() + -3, -3, 5, 5, -3, 5, + -3, 5, 5, 5, 5, 5, + ])).load() self.assertEqual(transformed[0, 0], (0, 0, 255)) self.assertEqual(transformed[50, 50], (0, 0, 255)) self.assertEqual(transformed[255, 0], (0, 255, 255)) @@ -366,22 +377,23 @@ class TestColorLut3DFilter(PillowTestCase): lut = ImageFilter.Color3DLUT( (3, 4, 5), array('f', [0, 0, 0, 0] * (3 * 4 * 5)), channels=4, target_mode='YCbCr', _copy_table=False) - self.assertEqual(repr(lut), + self.assertEqual( + repr(lut), "") class TestGenerateColorLut3D(PillowTestCase): def test_wrong_channels_count(self): with self.assertRaisesRegex(ValueError, "3 or 4 output channels"): - ImageFilter.Color3DLUT.generate(5, channels=2, - callback=lambda r, g, b: (r, g, b)) + ImageFilter.Color3DLUT.generate( + 5, channels=2, callback=lambda r, g, b: (r, g, b)) with self.assertRaisesRegex(ValueError, "should have either channels"): ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r)) with self.assertRaisesRegex(ValueError, "should have either channels"): - ImageFilter.Color3DLUT.generate(5, channels=4, - callback=lambda r, g, b: (r, g, b)) + ImageFilter.Color3DLUT.generate( + 5, channels=4, callback=lambda r, g, b: (r, g, b)) def test_3_channels(self): lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) @@ -392,8 +404,8 @@ class TestGenerateColorLut3D(PillowTestCase): 1.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.25, 0.0, 0.5, 0.25, 0.0]) def test_4_channels(self): - lut = ImageFilter.Color3DLUT.generate(5, channels=4, - callback=lambda r, g, b: (b, r, g, (r+g+b) / 2)) + lut = ImageFilter.Color3DLUT.generate( + 5, channels=4, callback=lambda r, g, b: (b, r, g, (r+g+b) / 2)) self.assertEqual(tuple(lut.size), (5, 5, 5)) self.assertEqual(lut.name, "Color 3D LUT") self.assertEqual(lut.table[:24], [ diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 1ecb6cda6..cf38c2345 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -245,8 +245,8 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): for y in range(i.size[1]): used_colors = {px[x, y][0] for x in range(i.size[0])} self.assertEqual(256, len(used_colors), - 'All colors should present in resized image. ' - 'Only {} on {} line.'.format(len(used_colors), y)) + 'All colors should present in resized image. ' + 'Only {} on {} line.'.format(len(used_colors), y)) @unittest.skip("current implementation isn't precise enough") def test_levels_rgba(self): From 0e61d4be9f4cca7cdd24fe5487b264c7e7af7b8a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Jun 2018 19:55:48 +1000 Subject: [PATCH 18/87] Removed unused variables --- Tests/test_file_libtiff.py | 2 +- src/PIL/GifImagePlugin.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index ce77e05de..4f2f9617e 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -525,7 +525,7 @@ class TestFileLibTiff(LibTiffTestCase): f.write(src.read()) im = Image.open(tmpfile) - count = im.n_frames + im.n_frames im.close() try: os.remove(tmpfile) # Windows PermissionError here! diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 1bfbb5ffd..e0da01129 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -543,7 +543,6 @@ def _write_local_header(fp, im, offset, flags): o8(0)) include_color_table = im.encoderinfo.get('include_color_table') if include_color_table: - palette = im.encoderinfo.get("palette", None) palette_bytes = _get_palette_bytes(im) color_table_size = _get_color_table_size(palette_bytes) if color_table_size: From e7cfa15216436e71ba2b17a006e78d9e4552d1a7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Jun 2018 23:20:24 +1000 Subject: [PATCH 19/87] Visually indented line with same indent as next logical line --- src/PIL/TiffImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 6f032f49d..a4f58b5ff 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -577,8 +577,8 @@ class ImageFileDirectory_v2(MutableMapping): # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. # Don't mess with the legacy api, since it's frozen. - if ((info.length == 1) or - (info.length is None and len(values) == 1 and not legacy_api)): + if (info.length == 1) or \ + (info.length is None and len(values) == 1 and not legacy_api): # Don't mess with the legacy api, since it's frozen. if legacy_api and self.tagtype[tag] in [5, 10]: # rationals values = values, From 145589ef1425765ac1a0080440e18a3a9c5ea3bb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Jun 2018 23:44:22 +1000 Subject: [PATCH 20/87] Ambiguous variable name 'l' --- Tests/test_image_convert.py | 62 +++++++++++++++++++------------------ Tests/test_imagecms.py | 12 +++---- src/PIL/ContainerIO.py | 6 ++-- src/PIL/ImageStat.py | 4 +-- src/PIL/TiffImagePlugin.py | 10 +++--- 5 files changed, 48 insertions(+), 46 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index c7c381382..14e634e9c 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -67,13 +67,13 @@ class TestImageConvert(PillowTestCase): f = self.tempfile('temp.png') - l = im.convert('L') - self.assertEqual(l.info['transparency'], 0) # undone - l.save(f) + im_l = im.convert('L') + self.assertEqual(im_l.info['transparency'], 0) # undone + im_l.save(f) - rgb = im.convert('RGB') - self.assertEqual(rgb.info['transparency'], (0, 0, 0)) # undone - rgb.save(f) + im_rgb = im.convert('RGB') + self.assertEqual(im_rgb.info['transparency'], (0, 0, 0)) # undone + im_rgb.save(f) # ref https://github.com/python-pillow/Pillow/issues/664 @@ -83,12 +83,12 @@ class TestImageConvert(PillowTestCase): im.info['transparency'] = 128 # Act - rgba = im.convert('RGBA') + im_rgba = im.convert('RGBA') # Assert - self.assertNotIn('transparency', rgba.info) + self.assertNotIn('transparency', im_rgba.info) # https://github.com/python-pillow/Pillow/issues/2702 - self.assertEqual(rgba.palette, None) + self.assertEqual(im_rgba.palette, None) def test_trns_l(self): im = hopper('L') @@ -96,19 +96,20 @@ class TestImageConvert(PillowTestCase): f = self.tempfile('temp.png') - rgb = im.convert('RGB') - self.assertEqual(rgb.info['transparency'], (128, 128, 128)) # undone - rgb.save(f) + im_rgb = im.convert('RGB') + self.assertEqual(im_rgb.info['transparency'], + (128, 128, 128)) # undone + im_rgb.save(f) - p = im.convert('P') - self.assertIn('transparency', p.info) - p.save(f) + im_p = im.convert('P') + self.assertIn('transparency', im_p.info) + im_p.save(f) - p = self.assert_warning( + im_p = self.assert_warning( UserWarning, im.convert, 'P', palette=Image.ADAPTIVE) - self.assertNotIn('transparency', p.info) - p.save(f) + self.assertNotIn('transparency', im_p.info) + im_p.save(f) def test_trns_RGB(self): im = hopper('RGB') @@ -116,23 +117,24 @@ class TestImageConvert(PillowTestCase): f = self.tempfile('temp.png') - l = im.convert('L') - self.assertEqual(l.info['transparency'], l.getpixel((0, 0))) # undone - l.save(f) + im_l = im.convert('L') + self.assertEqual(im_l.info['transparency'], + im_l.getpixel((0, 0))) # undone + im_l.save(f) - p = im.convert('P') - self.assertIn('transparency', p.info) - p.save(f) + im_p = im.convert('P') + self.assertIn('transparency', im_p.info) + im_p.save(f) - p = im.convert('RGBA') - self.assertNotIn('transparency', p.info) - p.save(f) + im_rgba = im.convert('RGBA') + self.assertNotIn('transparency', im_rgba.info) + im_rgba.save(f) - p = self.assert_warning( + im_p = self.assert_warning( UserWarning, im.convert, 'P', palette=Image.ADAPTIVE) - self.assertNotIn('transparency', p.info) - p.save(f) + self.assertNotIn('transparency', im_p.info) + im_p.save(f) def test_p_la(self): im = hopper('RGBA') diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 4138f455f..2f4708689 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -196,13 +196,13 @@ class TestImageCms(PillowTestCase): # not a linear luminance map. so L != 128: self.assertEqual(k, (137, 128, 128)) - l = i_lab.getdata(0) - a = i_lab.getdata(1) - b = i_lab.getdata(2) + l_data = i_lab.getdata(0) + a_data = i_lab.getdata(1) + b_data = i_lab.getdata(2) - self.assertEqual(list(l), [137] * 100) - self.assertEqual(list(a), [128] * 100) - self.assertEqual(list(b), [128] * 100) + self.assertEqual(list(l_data), [137] * 100) + self.assertEqual(list(a_data), [128] * 100) + self.assertEqual(list(b_data), [128] * 100) def test_lab_color(self): psRGB = ImageCms.createProfile("sRGB") diff --git a/src/PIL/ContainerIO.py b/src/PIL/ContainerIO.py index 496ed6826..682ad9031 100644 --- a/src/PIL/ContainerIO.py +++ b/src/PIL/ContainerIO.py @@ -107,10 +107,10 @@ class ContainerIO(object): :returns: A list of 8-bit strings. """ - l = [] + lines = [] while True: s = self.readline() if not s: break - l.append(s) - return l + lines.append(s) + return lines diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index cd58fc8ff..d4b38d856 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -110,11 +110,11 @@ class Stat(object): v = [] for i in self.bands: s = 0 - l = self.count[i]//2 + half = self.count[i]//2 b = i * 256 for j in range(256): s = s + self.h[b+j] - if s > l: + if s > half: break v.append(j) return v diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index a4f58b5ff..14b66a1b9 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1245,7 +1245,7 @@ class TiffImageFile(ImageFile.ImageFile): self.info["resolution"] = xres, yres # build tile descriptors - x = y = l = 0 + x = y = layer = 0 self.tile = [] self.use_load_libtiff = False if STRIPOFFSETS in self.tag_v2: @@ -1305,7 +1305,7 @@ class TiffImageFile(ImageFile.ImageFile): else: for i, offset in enumerate(offsets): - a = self._decoder(rawmode, l, i) + a = self._decoder(rawmode, layer, i) self.tile.append( (self._compression, (0, min(y, ysize), w, min(y+h, ysize)), @@ -1315,7 +1315,7 @@ class TiffImageFile(ImageFile.ImageFile): y = y + h if y >= self.size[1]: x = y = 0 - l += 1 + layer += 1 a = None elif TILEOFFSETS in self.tag_v2: # tiled image @@ -1324,7 +1324,7 @@ class TiffImageFile(ImageFile.ImageFile): a = None for o in self.tag_v2[TILEOFFSETS]: if not a: - a = self._decoder(rawmode, l) + a = self._decoder(rawmode, layer) # FIXME: this doesn't work if the image size # is not a multiple of the tile size... self.tile.append( @@ -1336,7 +1336,7 @@ class TiffImageFile(ImageFile.ImageFile): x, y = 0, y + h if y >= self.size[1]: x = y = 0 - l += 1 + layer += 1 a = None else: if DEBUG: From 0832f9c58b6fecf16d4bfc128931cddd5ddb9e7a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Jun 2018 09:10:58 +1000 Subject: [PATCH 21/87] Continuation line unaligned for hanging indent --- Tests/test_color_lut.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index e641c5ad3..5a51b279f 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -27,8 +27,8 @@ class TestColorLut3DCoreAPI(PillowTestCase): g / float(size2D-1) if size2D != 1 else 0, ][:channels] for b in range(size3D) - for g in range(size2D) - for r in range(size1D) + for g in range(size2D) + for r in range(size1D) ] return ( channels, size1D, size2D, size3D, From ce5d0e72b2d8493c73c6ffaa02171345ad8a16a9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 24 Jun 2018 21:55:22 +1000 Subject: [PATCH 22/87] Continuation line under-indented for visual indent --- Tests/test_file_webp_animated.py | 6 +- Tests/test_image_resize.py | 16 +- Tests/test_imageops_usm.py | 24 ++- Tests/test_imagepalette.py | 6 +- Tests/test_lib_pack.py | 327 +++++++++++++++++-------------- 5 files changed, 210 insertions(+), 169 deletions(-) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index f98cde764..6b3dc1622 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -83,7 +83,8 @@ class TestFileWebpAnimation(PillowTestCase): temp_file1 = self.tempfile("temp.webp") frame1.copy().save(temp_file1, - save_all=True, append_images=[frame2], lossless=True) + save_all=True, append_images=[frame2], + lossless=True) check(temp_file1) # Tests appending using a generator @@ -92,7 +93,8 @@ class TestFileWebpAnimation(PillowTestCase): yield im temp_file2 = self.tempfile("temp_generator.webp") frame1.copy().save(temp_file2, - save_all=True, append_images=imGenerator([frame2]), lossless=True) + save_all=True, append_images=imGenerator([frame2]), + lossless=True) check(temp_file2) def test_timestamp_and_duration(self): diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 8d14b9008..535f1d77e 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -39,15 +39,15 @@ class TestImagingCoreResize(PillowTestCase): self.assertEqual(r.im.bands, im.im.bands) def test_reduce_filters(self): - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, - Image.BICUBIC, Image.LANCZOS]: + for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, + Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: r = self.resize(hopper("RGB"), (15, 12), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (15, 12)) def test_enlarge_filters(self): - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, - Image.BICUBIC, Image.LANCZOS]: + for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, + Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: r = self.resize(hopper("RGB"), (212, 195), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (212, 195)) @@ -66,8 +66,8 @@ class TestImagingCoreResize(PillowTestCase): } samples['dirty'].putpixel((1, 1), 128) - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, - Image.BICUBIC, Image.LANCZOS]: + for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, + Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: # samples resized with current filter references = { name: self.resize(ch, (4, 4), f) @@ -90,8 +90,8 @@ class TestImagingCoreResize(PillowTestCase): self.assert_image_equal(ch, references[channels[i]]) def test_enlarge_zero(self): - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, - Image.BICUBIC, Image.LANCZOS]: + for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, + Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: r = self.resize(Image.new('RGB', (0, 0), "white"), (212, 195), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (212, 195)) diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index 2666d3482..20758e9f8 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -13,27 +13,25 @@ class TestImageOpsUsm(PillowTestCase): def test_ops_api(self): i = self.assert_warning(DeprecationWarning, - ImageOps.gaussian_blur, im, 2.0) + ImageOps.gaussian_blur, im, 2.0) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) + + i = self.assert_warning(DeprecationWarning, ImageOps.box_blur, im, 1) + self.assertEqual(i.mode, "RGB") + self.assertEqual(i.size, (128, 128)) + + i = self.assert_warning(DeprecationWarning, ImageOps.gblur, im, 2.0) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) i = self.assert_warning(DeprecationWarning, - ImageOps.box_blur, im, 1) + ImageOps.unsharp_mask, im, 2.0, 125, 8) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) i = self.assert_warning(DeprecationWarning, - ImageOps.gblur, im, 2.0) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - - i = self.assert_warning(DeprecationWarning, - ImageOps.unsharp_mask, im, 2.0, 125, 8) - self.assertEqual(i.mode, "RGB") - self.assertEqual(i.size, (128, 128)) - - i = self.assert_warning(DeprecationWarning, - ImageOps.usm, im, 2.0, 125, 8) + ImageOps.usm, im, 2.0, 125, 8) self.assertEqual(i.mode, "RGB") self.assertEqual(i.size, (128, 128)) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 889f022ae..3b7087d7a 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -9,7 +9,7 @@ class TestImagePalette(PillowTestCase): ImagePalette.ImagePalette("RGB", list(range(256))*3) self.assertRaises(ValueError, - ImagePalette.ImagePalette, "RGB", list(range(256))*2) + ImagePalette.ImagePalette, "RGB", list(range(256))*2) def test_getcolor(self): @@ -66,7 +66,7 @@ class TestImagePalette(PillowTestCase): # Act self.assertRaises(NotImplementedError, - ImagePalette.make_linear_lut, black, white) + ImagePalette.make_linear_lut, black, white) def test_make_gamma_lut(self): # Arrange @@ -133,7 +133,7 @@ class TestImagePalette(PillowTestCase): def test_invalid_palette(self): self.assertRaises(IOError, - ImagePalette.load, "Tests/images/hopper.jpg") + ImagePalette.load, "Tests/images/hopper.jpg") if __name__ == '__main__': diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 002db2e19..89c8e297f 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -57,14 +57,18 @@ class TestLibPack(PillowTestCase): def test_RGB(self): self.assert_pack("RGB", "RGB", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) - self.assert_pack("RGB", "RGBX", + self.assert_pack( + "RGB", "RGBX", b'\x01\x02\x03\xff\x05\x06\x07\xff', (1, 2, 3), (5, 6, 7)) - self.assert_pack("RGB", "XRGB", + self.assert_pack( + "RGB", "XRGB", b'\x00\x02\x03\x04\x00\x06\x07\x08', (2, 3, 4), (6, 7, 8)) self.assert_pack("RGB", "BGR", 3, (3, 2, 1), (6, 5, 4), (9, 8, 7)) - self.assert_pack("RGB", "BGRX", + self.assert_pack( + "RGB", "BGRX", b'\x01\x02\x03\x00\x05\x06\x07\x00', (3, 2, 1), (7, 6, 5)) - self.assert_pack("RGB", "XBGR", + self.assert_pack( + "RGB", "XBGR", b'\x00\x02\x03\x04\x00\x06\x07\x08', (4, 3, 2), (8, 7, 6)) self.assert_pack("RGB", "RGB;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_pack("RGB", "R", 1, (1, 9, 9), (2, 9, 9), (3, 9, 9)) @@ -72,17 +76,19 @@ class TestLibPack(PillowTestCase): self.assert_pack("RGB", "B", 1, (9, 9, 1), (9, 9, 2), (9, 9, 3)) def test_RGBA(self): - self.assert_pack("RGBA", "RGBA", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack("RGBA", "RGBA;L", 4, - (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_pack( + "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack( + "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_pack("RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16)) self.assert_pack("RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16)) - self.assert_pack("RGBA", "BGRA", 4, + self.assert_pack( + "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_pack("RGBA", "ABGR", 4, - (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) - self.assert_pack("RGBA", "BGRa", 4, + self.assert_pack( + "RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + self.assert_pack( + "RGBA", "BGRa", 4, (191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12)) self.assert_pack("RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) self.assert_pack("RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) @@ -90,22 +96,24 @@ class TestLibPack(PillowTestCase): self.assert_pack("RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_RGBa(self): - self.assert_pack("RGBa", "RGBa", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack("RGBa", "BGRa", 4, - (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_pack("RGBa", "aBGR", 4, - (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + self.assert_pack( + "RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack( + "RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) + self.assert_pack( + "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) def test_RGBX(self): self.assert_pack("RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) self.assert_pack("RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_pack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) self.assert_pack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) - self.assert_pack("RGBX", "BGRX", + self.assert_pack( + "RGBX", "BGRX", b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00', (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) - self.assert_pack("RGBX", "XBGR", + self.assert_pack( + "RGBX", "XBGR", b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c', (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) self.assert_pack("RGBX", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) @@ -115,19 +123,22 @@ class TestLibPack(PillowTestCase): def test_CMYK(self): self.assert_pack("CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack("CMYK", "CMYK;I", 4, + self.assert_pack( + "CMYK", "CMYK;I", 4, (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) - self.assert_pack("CMYK", "CMYK;L", 4, - (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_pack( + "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_pack("CMYK", "K", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_YCbCr(self): self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) self.assert_pack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) - self.assert_pack("YCbCr", "YCbCrX", + self.assert_pack( + "YCbCr", "YCbCrX", b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', (1, 2, 3), (5, 6, 7), (9, 10, 11)) - self.assert_pack("YCbCr", "YCbCrK", + self.assert_pack( + "YCbCr", "YCbCrK", b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', (1, 2, 3), (5, 6, 7), (9, 10, 11)) self.assert_pack("YCbCr", "Y", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) @@ -135,8 +146,8 @@ class TestLibPack(PillowTestCase): self.assert_pack("YCbCr", "Cr", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) def test_LAB(self): - self.assert_pack("LAB", "LAB", 3, - (1, 130, 131), (4, 133, 134), (7, 136, 137)) + self.assert_pack( + "LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137)) self.assert_pack("LAB", "L", 1, (1, 9, 9), (2, 9, 9), (3, 9, 9)) self.assert_pack("LAB", "A", 1, (9, 1, 9), (9, 2, 9), (9, 3, 9)) self.assert_pack("LAB", "B", 1, (9, 9, 1), (9, 9, 2), (9, 9, 3)) @@ -149,35 +160,35 @@ class TestLibPack(PillowTestCase): def test_I(self): self.assert_pack("I", "I;16B", 2, 0x0102, 0x0304) - self.assert_pack("I", "I;32S", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - 0x01000083, -2097151999) + self.assert_pack( + "I", "I;32S", + b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) if sys.byteorder == 'little': self.assert_pack("I", "I", 4, 0x04030201, 0x08070605) - self.assert_pack("I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - 0x01000083, -2097151999) + self.assert_pack( + "I", "I;32NS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) else: self.assert_pack("I", "I", 4, 0x01020304, 0x05060708) - self.assert_pack("I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - -2097151999, 0x01000083) + self.assert_pack( + "I", "I;32NS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) def test_F_float(self): - self.assert_pack("F", "F;32F", 4, - 1.539989614439558e-36, 4.063216068939723e-34) + self.assert_pack( + "F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) if sys.byteorder == 'little': - self.assert_pack("F", "F", 4, - 1.539989614439558e-36, 4.063216068939723e-34) - self.assert_pack("F", "F;32NF", 4, - 1.539989614439558e-36, 4.063216068939723e-34) + self.assert_pack( + "F", "F", 4, 1.539989614439558e-36, 4.063216068939723e-34) + self.assert_pack( + "F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34) else: - self.assert_pack("F", "F", 4, - 2.387939260590663e-38, 6.301941157072183e-36) - self.assert_pack("F", "F;32NF", 4, - 2.387939260590663e-38, 6.301941157072183e-36) + self.assert_pack( + "F", "F", 4, 2.387939260590663e-38, 6.301941157072183e-36) + self.assert_pack( + "F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36) class TestLibUnpack(PillowTestCase): @@ -260,7 +271,8 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("RGB", "BGRX", 4, (3, 2, 1), (7, 6, 5), (11, 10, 9)) self.assert_unpack("RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12)) self.assert_unpack("RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10)) - self.assert_unpack("RGB", "YCC;P", + self.assert_unpack( + "RGB", "YCC;P", b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data (127, 102, 0), (192, 227, 0), (213, 255, 170), (98, 255, 133)) self.assert_unpack("RGB", "R", 1, (1, 0, 0), (2, 0, 0), (3, 0, 0)) @@ -269,48 +281,58 @@ class TestLibUnpack(PillowTestCase): def test_RGBA(self): self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) - self.assert_unpack("RGBA", "LA;16B", 4, - (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11)) - self.assert_unpack("RGBA", "RGBA", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_unpack("RGBA", "RGBa", 4, + self.assert_unpack( + "RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11)) + self.assert_unpack( + "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_unpack( + "RGBA", "RGBa", 4, (63, 127, 191, 4), (159, 191, 223, 8), (191, 212, 233, 12)) - self.assert_unpack("RGBA", "RGBa", + self.assert_unpack( + "RGBA", "RGBa", b'\x01\x02\x03\x00\x10\x20\x30\xff', (0, 0, 0, 0), (16, 32, 48, 255)) - self.assert_unpack("RGBA", "RGBa;16L", 8, + self.assert_unpack( + "RGBA", "RGBa;16L", 8, (63, 127, 191, 8), (159, 191, 223, 16), (191, 212, 233, 24)) - self.assert_unpack("RGBA", "RGBa;16L", + self.assert_unpack( + "RGBA", "RGBa;16L", b'\x88\x01\x88\x02\x88\x03\x88\x00' b'\x88\x10\x88\x20\x88\x30\x88\xff', (0, 0, 0, 0), (16, 32, 48, 255)) - self.assert_unpack("RGBA", "RGBa;16B", 8, + self.assert_unpack( + "RGBA", "RGBa;16B", 8, (36, 109, 182, 7), (153, 187, 221, 15), (188, 210, 232, 23)) - self.assert_unpack("RGBA", "RGBa;16B", + self.assert_unpack( + "RGBA", "RGBa;16B", b'\x01\x88\x02\x88\x03\x88\x00\x88' b'\x10\x88\x20\x88\x30\x88\xff\x88', (0, 0, 0, 0), (16, 32, 48, 255)) - self.assert_unpack("RGBA", "BGRa", 4, + self.assert_unpack( + "RGBA", "BGRa", 4, (191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12)) - self.assert_unpack("RGBA", "BGRa", + self.assert_unpack( + "RGBA", "BGRa", b'\x01\x02\x03\x00\x10\x20\x30\xff', (0, 0, 0, 0), (48, 32, 16, 255)) - self.assert_unpack("RGBA", "RGBA;I", 4, + self.assert_unpack( + "RGBA", "RGBA;I", 4, (254, 253, 252, 4), (250, 249, 248, 8), (246, 245, 244, 12)) - self.assert_unpack("RGBA", "RGBA;L", 4, - (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_unpack( + "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_unpack("RGBA", "RGBA;15", 2, (8, 131, 0, 0), (24, 0, 8, 0)) self.assert_unpack("RGBA", "BGRA;15", 2, (0, 131, 8, 0), (8, 0, 24, 0)) self.assert_unpack("RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0)) self.assert_unpack("RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) self.assert_unpack("RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) - self.assert_unpack("RGBA", "BGRA", 4, - (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_unpack("RGBA", "ARGB", 4, - (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9)) - self.assert_unpack("RGBA", "ABGR", 4, - (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) - self.assert_unpack("RGBA", "YCCA;P", + self.assert_unpack( + "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) + self.assert_unpack( + "RGBA", "ARGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9)) + self.assert_unpack( + "RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + self.assert_unpack( + "RGBA", "YCCA;P", b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data (0, 161, 0, 4), (255, 255, 255, 237), (27, 158, 0, 206), (0, 118, 0, 17)) self.assert_unpack("RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) @@ -319,14 +341,14 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) def test_RGBa(self): - self.assert_unpack("RGBa", "RGBa", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_unpack("RGBa", "BGRa", 4, - (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_unpack("RGBa", "aRGB", 4, - (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9)) - self.assert_unpack("RGBa", "aBGR", 4, - (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + self.assert_unpack( + "RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_unpack( + "RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) + self.assert_unpack( + "RGBa", "aRGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9)) + self.assert_unpack( + "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) def test_RGBX(self): self.assert_unpack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) @@ -336,20 +358,21 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("RGBX", "RGB;15", 2, (8, 131, 0, X), (24, 0, 8, X)) self.assert_unpack("RGBX", "BGR;15", 2, (0, 131, 8, X), (8, 0, 24, X)) self.assert_unpack("RGBX", "RGB;4B", 2, (17, 0, 34, X), (51, 0, 68, X)) - self.assert_unpack("RGBX", "RGBX", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_unpack("RGBX", "RGBXX", 5, - (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) - self.assert_unpack("RGBX", "RGBXXX", 6, - (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) - self.assert_unpack("RGBX", "RGBX;L", 4, - (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_unpack( + "RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_unpack( + "RGBX", "RGBXX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) + self.assert_unpack( + "RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) + self.assert_unpack( + "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_unpack("RGBX", "RGBX;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) self.assert_unpack("RGBX", "RGBX;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) self.assert_unpack("RGBX", "BGRX", 4, (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) self.assert_unpack("RGBX", "XRGB", 4, (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X)) self.assert_unpack("RGBX", "XBGR", 4, (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) - self.assert_unpack("RGBX", "YCC;P", + self.assert_unpack( + "RGBX", "YCC;P", b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data (127, 102, 0, X), (192, 227, 0, X), (213, 255, 170, X), (98, 255, 133, X)) self.assert_unpack("RGBX", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) @@ -358,40 +381,47 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("RGBX", "X", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) def test_CMYK(self): - self.assert_unpack("CMYK", "CMYK", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_unpack("CMYK", "CMYKX", 5, - (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) - self.assert_unpack("CMYK", "CMYKXX", 6, - (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) - self.assert_unpack("CMYK", "CMYK;I", 4, + self.assert_unpack( + "CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_unpack( + "CMYK", "CMYKX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) + self.assert_unpack( + "CMYK", "CMYKXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) + self.assert_unpack( + "CMYK", "CMYK;I", 4, (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) - self.assert_unpack("CMYK", "CMYK;L", 4, - (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_unpack( + "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_unpack("CMYK", "C", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) self.assert_unpack("CMYK", "M", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) self.assert_unpack("CMYK", "Y", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) self.assert_unpack("CMYK", "K", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) - self.assert_unpack("CMYK", "C;I", 1, - (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0)) - self.assert_unpack("CMYK", "M;I", 1, - (0, 254, 0, 0), (0, 253, 0, 0), (0, 252, 0, 0)) - self.assert_unpack("CMYK", "Y;I", 1, - (0, 0, 254, 0), (0, 0, 253, 0), (0, 0, 252, 0)) - self.assert_unpack("CMYK", "K;I", 1, - (0, 0, 0, 254), (0, 0, 0, 253), (0, 0, 0, 252)) + self.assert_unpack( + "CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0)) + self.assert_unpack( + "CMYK", "M;I", 1, (0, 254, 0, 0), (0, 253, 0, 0), (0, 252, 0, 0)) + self.assert_unpack( + "CMYK", "Y;I", 1, (0, 0, 254, 0), (0, 0, 253, 0), (0, 0, 252, 0)) + self.assert_unpack( + "CMYK", "K;I", 1, (0, 0, 0, 254), (0, 0, 0, 253), (0, 0, 0, 252)) def test_YCbCr(self): - self.assert_unpack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) - self.assert_unpack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) - self.assert_unpack("YCbCr", "YCbCrK", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) - self.assert_unpack("YCbCr", "YCbCrX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) - self.assert_unpack("YCbCr", "YCbCrXX", 5, (1, 2, 3), (6, 7, 8), (11, 12, 13)) - self.assert_unpack("YCbCr", "YCbCrXXX", 6, (1, 2, 3), (7, 8, 9), (13, 14, 15)) + self.assert_unpack( + "YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) + self.assert_unpack( + "YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) + self.assert_unpack( + "YCbCr", "YCbCrK", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) + self.assert_unpack( + "YCbCr", "YCbCrX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) + self.assert_unpack( + "YCbCr", "YCbCrXX", 5, (1, 2, 3), (6, 7, 8), (11, 12, 13)) + self.assert_unpack( + "YCbCr", "YCbCrXXX", 6, (1, 2, 3), (7, 8, 9), (13, 14, 15)) def test_LAB(self): - self.assert_unpack("LAB", "LAB", 3, - (1, 130, 131), (4, 133, 134), (7, 136, 137)) + self.assert_unpack( + "LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137)) self.assert_unpack("LAB", "L", 1, (1, 0, 0), (2, 0, 0), (3, 0, 0)) self.assert_unpack("LAB", "A", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0)) self.assert_unpack("LAB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3)) @@ -410,30 +440,30 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("I", "I;16B", 2, 0x0102, 0x0304) self.assert_unpack("I", "I;16BS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("I", "I;32", 4, 0x04030201, 0x08070605) - self.assert_unpack("I", "I;32S", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - 0x01000083, -2097151999) + self.assert_unpack( + "I", "I;32S", + b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) self.assert_unpack("I", "I;32B", 4, 0x01020304, 0x05060708) - self.assert_unpack("I", "I;32BS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - -2097151999, 0x01000083) + self.assert_unpack( + "I", "I;32BS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) if sys.byteorder == 'little': self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403) self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605) - self.assert_unpack("I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - 0x01000083, -2097151999) + self.assert_unpack( + "I", "I;32NS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) else: self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708) self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304) self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708) - self.assert_unpack("I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - -2097151999, 0x01000083) + self.assert_unpack( + "I", "I;32NS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) def test_F_int(self): self.assert_unpack("F", "F;8", 1, 0x01, 0x02, 0x03, 0x04) @@ -443,55 +473,66 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("F", "F;16B", 2, 0x0102, 0x0304) self.assert_unpack("F", "F;16BS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("F", "F;32", 4, 67305984, 134678016) - self.assert_unpack("F", "F;32S", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - 16777348, -2097152000) + self.assert_unpack( + "F", "F;32S", + b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000) self.assert_unpack("F", "F;32B", 4, 0x01020304, 0x05060708) - self.assert_unpack("F", "F;32BS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - -2097152000, 16777348) + self.assert_unpack( + "F", "F;32BS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097152000, 16777348) if sys.byteorder == 'little': self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403) self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack("F", "F;32N", 4, 67305984, 134678016) - self.assert_unpack("F", "F;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - 16777348, -2097152000) + self.assert_unpack( + "F", "F;32NS", + b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000) else: self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304) self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708) - self.assert_unpack("F", "F;32NS", + self.assert_unpack( + "F", "F;32NS", b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097152000, 16777348) def test_F_float(self): - self.assert_unpack("F", "F;32F", 4, + self.assert_unpack( + "F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) - self.assert_unpack("F", "F;32BF", 4, + self.assert_unpack( + "F", "F;32BF", 4, 2.387939260590663e-38, 6.301941157072183e-36) - self.assert_unpack("F", "F;64F", + self.assert_unpack( + "F", "F;64F", b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', # by struct.pack 0.15000000596046448, -1234.5) - self.assert_unpack("F", "F;64BF", + self.assert_unpack( + "F", "F;64BF", b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00', # by struct.pack 0.15000000596046448, -1234.5) if sys.byteorder == 'little': - self.assert_unpack("F", "F", 4, + self.assert_unpack( + "F", "F", 4, 1.539989614439558e-36, 4.063216068939723e-34) - self.assert_unpack("F", "F;32NF", 4, + self.assert_unpack( + "F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34) - self.assert_unpack("F", "F;64NF", + self.assert_unpack( + "F", "F;64NF", b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', 0.15000000596046448, -1234.5) else: - self.assert_unpack("F", "F", 4, + self.assert_unpack( + "F", "F", 4, 2.387939260590663e-38, 6.301941157072183e-36) - self.assert_unpack("F", "F;32NF", 4, + self.assert_unpack( + "F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36) - self.assert_unpack("F", "F;64NF", + self.assert_unpack( + "F", "F;64NF", b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00', 0.15000000596046448, -1234.5) From c2189235afc3fdc36cb781ee6bdcd3e6692f91aa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 24 Jun 2018 22:32:25 +1000 Subject: [PATCH 23/87] Line too long --- Tests/helper.py | 6 +- Tests/test_color_lut.py | 3 +- Tests/test_file_gif.py | 3 +- Tests/test_file_libtiff.py | 17 ++- Tests/test_file_pdf.py | 19 ++- Tests/test_file_png.py | 6 +- Tests/test_file_tiff.py | 3 +- Tests/test_font_leaks.py | 3 +- Tests/test_image_access.py | 3 +- Tests/test_image_array.py | 6 +- Tests/test_image_convert.py | 3 +- Tests/test_image_resample.py | 31 ++-- Tests/test_image_rotate.py | 5 +- Tests/test_image_toqimage.py | 5 +- Tests/test_imagecms.py | 135 +++++++++++++----- Tests/test_imagecolor.py | 18 ++- Tests/test_imageenhance.py | 5 +- Tests/test_imagefile.py | 8 +- Tests/test_imagefont.py | 13 +- Tests/test_imagefont_bitmap.py | 3 +- Tests/test_imagefontctl.py | 12 +- Tests/test_imagemorph.py | 5 +- Tests/test_lib_pack.py | 155 +++++++++++++------- Tests/test_map.py | 3 +- Tests/test_numpy.py | 4 +- Tests/test_pdfparser.py | 61 +++++--- setup.py | 15 +- src/PIL/BmpImagePlugin.py | 92 ++++++++---- src/PIL/DdsImagePlugin.py | 3 +- src/PIL/FtexImagePlugin.py | 22 +-- src/PIL/GbrImagePlugin.py | 6 +- src/PIL/GifImagePlugin.py | 19 ++- src/PIL/ImageCms.py | 56 ++++---- src/PIL/ImageDraw.py | 3 +- src/PIL/ImageEnhance.py | 3 +- src/PIL/ImageFile.py | 35 +++-- src/PIL/ImageFont.py | 34 +++-- src/PIL/ImageMode.py | 3 +- src/PIL/ImageQt.py | 3 +- src/PIL/ImageTk.py | 3 +- src/PIL/Jpeg2KImagePlugin.py | 3 +- src/PIL/JpegImagePlugin.py | 3 +- src/PIL/MspImagePlugin.py | 5 +- src/PIL/PdfImagePlugin.py | 63 ++++++--- src/PIL/PdfParser.py | 251 +++++++++++++++++++++++---------- src/PIL/PngImagePlugin.py | 7 +- src/PIL/PpmImagePlugin.py | 3 +- src/PIL/SgiImagePlugin.py | 3 +- src/PIL/SunImagePlugin.py | 3 +- src/PIL/TiffImagePlugin.py | 3 +- src/PIL/TiffTags.py | 7 +- src/PIL/WmfImagePlugin.py | 3 +- 52 files changed, 805 insertions(+), 381 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 5dbdb66be..207f497d2 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -153,7 +153,8 @@ class PillowTestCase(unittest.TestCase): pass raise e - def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None): + def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, + mode=None): with Image.open(filename) as img: if mode: img = img.convert(mode) @@ -246,7 +247,8 @@ class PillowLeakTestCase(PillowTestCase): mem = getrusage(RUSAGE_SELF).ru_maxrss if sys.platform == 'darwin': # man 2 getrusage: - # ru_maxrss the maximum resident set size utilized (in bytes). + # ru_maxrss + # This is the maximum resident set size utilized (in bytes). return mem / 1024 # Kb else: # linux diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 5a51b279f..6d0b76fed 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -410,7 +410,8 @@ class TestGenerateColorLut3D(PillowTestCase): self.assertEqual(lut.name, "Color 3D LUT") self.assertEqual(lut.table[:24], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25, - 0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125]) + 0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125 + ]) def test_apply(self): lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index b1006a630..086a0f5d0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -402,7 +402,8 @@ class TestFileGif(PillowTestCase): def test_comment(self): im = Image.open(TEST_GIF) - self.assertEqual(im.info['comment'], b"File written by Adobe Photoshop\xa8 4.0") + self.assertEqual(im.info['comment'], + b"File written by Adobe Photoshop\xa8 4.0") out = self.tempfile('temp.gif') im = Image.new('L', (100, 100), '#000') diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 4f2f9617e..77caa0b9d 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -126,7 +126,8 @@ class TestFileLibTiff(LibTiffTestCase): im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) im.load() - self.assert_image_equal_tofile(im, 'Tests/images/tiff_adobe_deflate.png') + self.assert_image_equal_tofile(im, + 'Tests/images/tiff_adobe_deflate.png') def test_write_metadata(self): """ Test metadata writing through libtiff """ @@ -217,7 +218,8 @@ class TestFileLibTiff(LibTiffTestCase): if info.length == 0: new_ifd[tag] = tuple(values[info.type] for _ in range(3)) else: - new_ifd[tag] = tuple(values[info.type] for _ in range(info.length)) + new_ifd[tag] = tuple(values[info.type] + for _ in range(info.length)) # Extra samples really doesn't make sense in this application. del(new_ifd[338]) @@ -578,10 +580,14 @@ class TestFileLibTiff(LibTiffTestCase): self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (100, 40)) - self.assertEqual(im.tile, [('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))]) + self.assertEqual( + im.tile, + [('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))] + ) im.load() - self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png") + self.assert_image_equal_tofile( + im, "Tests/images/tiff_16bit_RGBa_target.png") def test_gimp_tiff(self): # Read TIFF JPEG images from GIMP [@PIL168] @@ -607,7 +613,8 @@ class TestFileLibTiff(LibTiffTestCase): im = Image.open("Tests/images/copyleft.tiff") self.assertEqual(im.mode, 'RGB') - self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode='RGB') + self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", + mode='RGB') def test_lzw(self): im = Image.open("Tests/images/hopper_lzw.tif") diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 3d359f445..f012fb9d8 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -20,7 +20,8 @@ class TestFilePdf(PillowTestCase): self.assertTrue(os.path.isfile(outfile)) self.assertGreater(os.path.getsize(outfile), 0) with PdfParser.PdfParser(outfile) as pdf: - if kwargs.get("append_images", False) or kwargs.get("append", False): + if kwargs.get("append_images", False) or \ + kwargs.get("append", False): self.assertGreater(len(pdf.pages), 1) else: self.assertGreater(len(pdf.pages), 0) @@ -116,7 +117,9 @@ class TestFilePdf(PillowTestCase): def test_pdf_open(self): # fail on a buffer full of null bytes - self.assertRaises(PdfParser.PdfFormatError, PdfParser.PdfParser, buf=bytearray(65536)) + self.assertRaises( + PdfParser.PdfFormatError, + PdfParser.PdfParser, buf=bytearray(65536)) # make an empty PDF object with PdfParser.PdfParser() as empty_pdf: @@ -153,7 +156,10 @@ class TestFilePdf(PillowTestCase): im = hopper("RGB") temp_dir = tempfile.mkdtemp() try: - self.assertRaises(IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True) + self.assertRaises(IOError, + im.save, + os.path.join(temp_dir, "nonexistent.pdf"), + append=True) finally: os.rmdir(temp_dir) @@ -204,7 +210,8 @@ class TestFilePdf(PillowTestCase): # append two images mode_CMYK = hopper("CMYK") mode_P = hopper("P") - mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P]) + mode_CMYK.save(pdf_filename, + append=True, save_all=True, append_images=[mode_P]) # open the PDF again, check pages and info again with PdfParser.PdfParser(pdf_filename) as pdf: @@ -219,7 +226,9 @@ class TestFilePdf(PillowTestCase): def test_pdf_info(self): # make a PDF file - pdf_filename = self.helper_save_as_pdf("RGB", title="title", author="author", subject="subject", keywords="keywords", creator="creator", producer="producer") + pdf_filename = self.helper_save_as_pdf( + "RGB", title="title", author="author", subject="subject", + keywords="keywords", creator="creator", producer="producer") # open it, check pages and info with PdfParser.PdfParser(pdf_filename) as pdf: diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index bc2b557d2..e9dcd5203 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -355,7 +355,8 @@ class TestFilePng(PillowTestCase): broken_crc_chunk_data = chunk_data[:-1] + b'q' # break CRC image_data = HEAD + broken_crc_chunk_data + TAIL - self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data)) + self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, + BytesIO(image_data)) ImageFile.LOAD_TRUNCATED_IMAGES = True try: @@ -371,7 +372,8 @@ class TestFilePng(PillowTestCase): ImageFile.LOAD_TRUNCATED_IMAGES = True try: - self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data)) + self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, + BytesIO(image_data)) finally: ImageFile.LOAD_TRUNCATED_IMAGES = False diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index aac845e0f..79630c773 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -444,7 +444,8 @@ class TestFileTiff(PillowTestCase): for im in ims: yield im mp = io.BytesIO() - im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims)) + im.save(mp, format="TIFF", save_all=True, + append_images=imGenerator(ims)) mp.seek(0, os.SEEK_SET) reread = Image.open(mp) diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 4de28a95a..f1ce44e6d 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -16,7 +16,8 @@ class TestTTypeFontLeak(PillowLeakTestCase): self._test_leak(lambda: draw.text((0, 0), "some text "*1024, # ~10k font=font, fill="black")) - @unittest.skipIf(not features.check('freetype2'), "Test requires freetype2") + @unittest.skipIf(not features.check('freetype2'), + "Test requires freetype2") def test_leak(self): ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20) self._test_font(ttype) diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 9cc623856..7a9378bbd 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -294,7 +294,8 @@ int main(int argc, char* argv[]) compiler = ccompiler.new_compiler() compiler.add_include_dir(sysconfig.get_python_inc()) - libdir = sysconfig.get_config_var('LIBDIR') or sysconfig.get_python_inc().replace('include', 'libs') + libdir = (sysconfig.get_config_var('LIBDIR') or + sysconfig.get_python_inc().replace('include', 'libs')) print(libdir) compiler.add_library_dir(libdir) objects = compiler.compile(['embed_pil.c']) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 11c2648bb..7a86a3e54 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -15,9 +15,11 @@ class TestImageArray(PillowTestCase): self.assertEqual(test("L"), (3, (100, 128), '|u1', 12800)) # FIXME: wrong? - self.assertEqual(test("I"), (3, (100, 128), Image._ENDIAN + 'i4', 51200)) + self.assertEqual(test("I"), (3, (100, 128), + Image._ENDIAN + 'i4', 51200)) # FIXME: wrong? - self.assertEqual(test("F"), (3, (100, 128), Image._ENDIAN + 'f4', 51200)) + self.assertEqual(test("F"), (3, (100, 128), + Image._ENDIAN + 'f4', 51200)) self.assertEqual(test("LA"), (3, (100, 128, 2), '|u1', 25600)) self.assertEqual(test("RGB"), (3, (100, 128, 3), '|u1', 38400)) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 14e634e9c..ed971e698 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -193,7 +193,8 @@ class TestImageConvert(PillowTestCase): if converted_im.mode == 'RGB': self.assert_image_similar(converted_im, target, 3) else: - self.assert_image_similar(converted_im, target.getchannel(0), 1) + self.assert_image_similar(converted_im, + target.getchannel(0), 1) matrix_convert('RGB') matrix_convert('L') diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index cf38c2345..3687a1a2c 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -288,10 +288,14 @@ class CoreResampleAlphaCorrectTest(PillowTestCase): def test_dirty_pixels_rgba(self): case = self.make_dirty_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0)) self.run_dirty_case(case.resize((20, 20), Image.BOX), (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), + (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.HAMMING), + (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), + (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), + (255, 255, 0)) def test_dirty_pixels_la(self): case = self.make_dirty_case('LA', (255, 128), (0, 0)) @@ -367,23 +371,28 @@ class CoreResampleCoefficientsTest(PillowTestCase): im = Image.new('RGBA', (1280, 1280), (0x20, 0x40, 0x60, 0xff)) histogram = im.resize((256, 256), Image.BICUBIC).histogram() - self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000) # first channel - self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000) # second channel - self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000) # third channel - self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel + # first channel + self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000) + # second channel + self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000) + # third channel + self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000) + # fourth channel + self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) class CoreResampleBoxTest(PillowTestCase): def test_wrong_arguments(self): im = hopper() - for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING, - Image.BICUBIC, Image.LANCZOS): + for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, + Image.HAMMING, Image.BICUBIC, Image.LANCZOS): im.resize((32, 32), resample, (0, 0, im.width, im.height)) im.resize((32, 32), resample, (20, 20, im.width, im.height)) im.resize((32, 32), resample, (20, 20, 20, 100)) im.resize((32, 32), resample, (20, 20, 100, 20)) - with self.assertRaisesRegex(TypeError, "must be sequence of length 4"): + with self.assertRaisesRegex(TypeError, + "must be sequence of length 4"): im.resize((32, 32), resample, (im.width, im.height)) with self.assertRaisesRegex(ValueError, "can't be negative"): diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 8ddb5ddf8..17d394a02 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -95,8 +95,9 @@ class TestImageRotate(PillowTestCase): im = hopper() self.rotate(im, im.mode, 45, center=(0, 0)) self.rotate(im, im.mode, 45, translate=(im.size[0]/2, 0)) - self.rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0]/2, 0)) - + self.rotate(im, im.mode, 45, center=(0, 0), + translate=(im.size[0]/2, 0)) + def test_rotate_no_fill(self): im = Image.new('RGB', (100, 100), 'green') target = Image.open('Tests/images/rotate_45_no_fill.png') diff --git a/Tests/test_image_toqimage.py b/Tests/test_image_toqimage.py index 6d7715c80..c9971cf73 100644 --- a/Tests/test_image_toqimage.py +++ b/Tests/test_image_toqimage.py @@ -43,8 +43,9 @@ class TestToQImage(PillowQtTestCase, PillowTestCase): if mode == '1': # BW appears to not save correctly on QT4 and QT5 # kicks out errors on console: - # libpng warning: Invalid color type/bit depth combination in IHDR - # libpng error: Invalid IHDR data + # libpng warning: Invalid color type/bit depth combination + # in IHDR + # libpng error: Invalid IHDR data continue # Test saving the file diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 2f4708689..1fdfe916d 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -286,53 +286,104 @@ class TestImageCms(PillowTestCase): self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2)) self.assertEqual(p.attributes, 4294967296) - assert_truncated_tuple_equal(p.blue_colorant, ((0.14306640625, 0.06060791015625, 0.7140960693359375), (0.1558847490315394, 0.06603820639433387, 0.06060791015625))) - assert_truncated_tuple_equal(p.blue_primary, ((0.14306641366715667, 0.06060790921083026, 0.7140960805782015), (0.15588475410450106, 0.06603820408959558, 0.06060790921083026))) + assert_truncated_tuple_equal( + p.blue_colorant, + ((0.14306640625, 0.06060791015625, 0.7140960693359375), + (0.1558847490315394, 0.06603820639433387, 0.06060791015625))) + assert_truncated_tuple_equal( + p.blue_primary, + ((0.14306641366715667, 0.06060790921083026, 0.7140960805782015), + (0.15588475410450106, 0.06603820408959558, 0.06060790921083026))) assert_truncated_tuple_equal(p.chromatic_adaptation, (((1.04791259765625, 0.0229339599609375, -0.050201416015625), (0.02960205078125, 0.9904632568359375, -0.0170745849609375), (-0.009246826171875, 0.0150604248046875, 0.7517852783203125)), ((1.0267159024652783, 0.022470062342089134, 0.0229339599609375), (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875)))) self.assertIsNone(p.chromaticity) - self.assertEqual(p.clut, {0: (False, False, True), 1: (False, False, True), 2: (False, False, True), 3: (False, False, True)}) + self.assertEqual(p.clut, { + 0: (False, False, True), + 1: (False, False, True), + 2: (False, False, True), + 3: (False, False, True) + }) self.assertEqual(p.color_space, 'RGB') self.assertIsNone(p.colorant_table) self.assertIsNone(p.colorant_table_out) self.assertIsNone(p.colorimetric_intent) self.assertEqual(p.connection_space, 'XYZ ') - self.assertEqual(p.copyright, 'Copyright International Color Consortium, 2009') - self.assertEqual(p.creation_date, datetime.datetime(2009, 2, 27, 21, 36, 31)) + self.assertEqual(p.copyright, + 'Copyright International Color Consortium, 2009') + self.assertEqual(p.creation_date, + datetime.datetime(2009, 2, 27, 21, 36, 31)) self.assertEqual(p.device_class, 'mntr') - assert_truncated_tuple_equal(p.green_colorant, ((0.3851470947265625, 0.7168731689453125, 0.097076416015625), (0.32119769927720654, 0.5978443449048152, 0.7168731689453125))) - assert_truncated_tuple_equal(p.green_primary, ((0.3851470888162112, 0.7168731974161346, 0.09707641738998518), (0.32119768793686687, 0.5978443567149709, 0.7168731974161346))) + assert_truncated_tuple_equal( + p.green_colorant, + ((0.3851470947265625, 0.7168731689453125, 0.097076416015625), + (0.32119769927720654, 0.5978443449048152, 0.7168731689453125))) + assert_truncated_tuple_equal( + p.green_primary, + ((0.3851470888162112, 0.7168731974161346, 0.09707641738998518), + (0.32119768793686687, 0.5978443567149709, 0.7168731974161346))) self.assertEqual(p.header_flags, 0) self.assertEqual(p.header_manufacturer, '\x00\x00\x00\x00') self.assertEqual(p.header_model, '\x00\x00\x00\x00') - self.assertEqual(p.icc_measurement_condition, {'backing': (0.0, 0.0, 0.0), 'flare': 0.0, 'geo': 'unknown', 'observer': 1, 'illuminant_type': 'D65'}) + self.assertEqual(p.icc_measurement_condition, { + 'backing': (0.0, 0.0, 0.0), + 'flare': 0.0, + 'geo': 'unknown', + 'observer': 1, + 'illuminant_type': 'D65' + }) self.assertEqual(p.icc_version, 33554432) self.assertIsNone(p.icc_viewing_condition) - self.assertEqual(p.intent_supported, {0: (True, True, True), 1: (True, True, True), 2: (True, True, True), 3: (True, True, True)}) + self.assertEqual(p.intent_supported, { + 0: (True, True, True), + 1: (True, True, True), + 2: (True, True, True), + 3: (True, True, True) + }) self.assertTrue(p.is_matrix_shaper) self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0))) self.assertIsNone(p.manufacturer) - assert_truncated_tuple_equal(p.media_black_point, ((0.012054443359375, 0.0124969482421875, 0.01031494140625), (0.34573304157549234, 0.35842450765864337, 0.0124969482421875))) - assert_truncated_tuple_equal(p.media_white_point, ((0.964202880859375, 1.0, 0.8249053955078125), (0.3457029219802284, 0.3585375327567059, 1.0))) - assert_truncated_tuple_equal((p.media_white_point_temperature,), (5000.722328847392,)) - self.assertEqual(p.model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') + assert_truncated_tuple_equal( + p.media_black_point, + ((0.012054443359375, 0.0124969482421875, 0.01031494140625), + (0.34573304157549234, 0.35842450765864337, 0.0124969482421875))) + assert_truncated_tuple_equal( + p.media_white_point, + ((0.964202880859375, 1.0, 0.8249053955078125), + (0.3457029219802284, 0.3585375327567059, 1.0))) + assert_truncated_tuple_equal( + (p.media_white_point_temperature,), + (5000.722328847392,)) + self.assertEqual(p.model, + 'IEC 61966-2-1 Default RGB Colour Space - sRGB') self.assertEqual(p.pcs, 'XYZ') self.assertIsNone(p.perceptual_rendering_intent_gamut) - self.assertEqual(p.product_copyright, 'Copyright International Color Consortium, 2009') + self.assertEqual(p.product_copyright, + 'Copyright International Color Consortium, 2009') self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled') - self.assertEqual(p.product_description, 'sRGB IEC61966-2-1 black scaled') + self.assertEqual(p.product_description, + 'sRGB IEC61966-2-1 black scaled') self.assertEqual(p.product_manufacturer, '') - self.assertEqual(p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') - self.assertEqual(p.profile_description, 'sRGB IEC61966-2-1 black scaled') - self.assertEqual(p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r') - assert_truncated_tuple_equal(p.red_colorant, ((0.436065673828125, 0.2224884033203125, 0.013916015625), (0.6484536316398539, 0.3308524880306778, 0.2224884033203125))) - assert_truncated_tuple_equal(p.red_primary, ((0.43606566581047446, 0.22248840582960838, 0.013916015621759925), (0.6484536250319214, 0.3308524944738204, 0.22248840582960838))) + self.assertEqual( + p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB') + self.assertEqual( + p.profile_description, 'sRGB IEC61966-2-1 black scaled') + self.assertEqual( + p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r') + assert_truncated_tuple_equal( + p.red_colorant, + ((0.436065673828125, 0.2224884033203125, 0.013916015625), + (0.6484536316398539, 0.3308524880306778, 0.2224884033203125))) + assert_truncated_tuple_equal( + p.red_primary, + ((0.43606566581047446, 0.22248840582960838, 0.013916015621759925), + (0.6484536250319214, 0.3308524944738204, 0.22248840582960838))) self.assertEqual(p.rendering_intent, 0) self.assertIsNone(p.saturation_rendering_intent_gamut) self.assertIsNone(p.screening_description) self.assertIsNone(p.target) self.assertEqual(p.technology, 'CRT ') self.assertEqual(p.version, 2.0) - self.assertEqual(p.viewing_condition, 'Reference Viewing Condition in IEC 61966-2-1') + self.assertEqual(p.viewing_condition, + 'Reference Viewing Condition in IEC 61966-2-1') self.assertEqual(p.xcolor_space, 'RGB ') def test_profile_typesafety(self): @@ -346,7 +397,8 @@ class TestImageCms(PillowTestCase): with self.assertRaises(TypeError): ImageCms.ImageCmsProfile(1).tobytes() - def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel): + def assert_aux_channel_preserved(self, mode, + transform_in_place, preserved_channel): def create_test_image(): # set up test image with something interesting in the tested aux # channel. @@ -379,29 +431,35 @@ class TestImageCms(PillowTestCase): # create some transform, it doesn't matter which one source_profile = ImageCms.createProfile("sRGB") destination_profile = ImageCms.createProfile("sRGB") - t = ImageCms.buildTransform(source_profile, destination_profile, inMode=mode, outMode=mode) + t = ImageCms.buildTransform( + source_profile, destination_profile, inMode=mode, outMode=mode) # apply transform if transform_in_place: ImageCms.applyTransform(source_image, t, inPlace=True) result_image = source_image else: - result_image = ImageCms.applyTransform(source_image, t, inPlace=False) + result_image = ImageCms.applyTransform( + source_image, t, inPlace=False) result_image_aux = result_image.getchannel(preserved_channel) self.assert_image_equal(source_image_aux, result_image_aux) def test_preserve_auxiliary_channels_rgba(self): - self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=False, preserved_channel='A') + self.assert_aux_channel_preserved(mode='RGBA', + transform_in_place=False, preserved_channel='A') def test_preserve_auxiliary_channels_rgba_in_place(self): - self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=True, preserved_channel='A') + self.assert_aux_channel_preserved(mode='RGBA', + transform_in_place=True, preserved_channel='A') def test_preserve_auxiliary_channels_rgbx(self): - self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=False, preserved_channel='X') + self.assert_aux_channel_preserved(mode='RGBX', + transform_in_place=False, preserved_channel='X') def test_preserve_auxiliary_channels_rgbx_in_place(self): - self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=True, preserved_channel='X') + self.assert_aux_channel_preserved(mode='RGBX', + transform_in_place=True, preserved_channel='X') def test_auxiliary_channels_isolated(self): # test data in aux channels does not affect non-aux channels @@ -422,20 +480,29 @@ class TestImageCms(PillowTestCase): source_profile = ImageCms.createProfile(src_format[1]) destination_profile = ImageCms.createProfile(dst_format[1]) source_image = src_format[3] - test_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[0], outMode=dst_format[0]) + test_transform = ImageCms.buildTransform( + source_profile, destination_profile, + inMode=src_format[0], outMode=dst_format[0]) # test conversion from aux-ful source if transform_in_place: test_image = source_image.copy() - ImageCms.applyTransform(test_image, test_transform, inPlace=True) + ImageCms.applyTransform( + test_image, test_transform, inPlace=True) else: - test_image = ImageCms.applyTransform(source_image, test_transform, inPlace=False) + test_image = ImageCms.applyTransform( + source_image, test_transform, inPlace=False) # reference conversion from aux-less source - reference_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[2], outMode=dst_format[2]) - reference_image = ImageCms.applyTransform(source_image.convert(src_format[2]), reference_transform) + reference_transform = ImageCms.buildTransform( + source_profile, destination_profile, + inMode=src_format[2], outMode=dst_format[2]) + reference_image = ImageCms.applyTransform( + source_image.convert(src_format[2]), + reference_transform) - self.assert_image_equal(test_image.convert(dst_format[2]), reference_image) + self.assert_image_equal(test_image.convert(dst_format[2]), + reference_image) if __name__ == '__main__': diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index 0aac21278..1ea37544b 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -31,7 +31,8 @@ class TestImageColor(PillowTestCase): # case insensitivity self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def")) - self.assertEqual(ImageColor.getrgb("#CDEF"), ImageColor.getrgb("#cdef")) + self.assertEqual(ImageColor.getrgb("#CDEF"), + ImageColor.getrgb("#cdef")) self.assertEqual(ImageColor.getrgb("#DEFDEF"), ImageColor.getrgb("#defdef")) self.assertEqual(ImageColor.getrgb("#CDEFCDEF"), @@ -80,18 +81,23 @@ class TestImageColor(PillowTestCase): self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(0,100%,100%)")) self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360,100%,100%)")) - self.assertEqual((0, 255, 255), ImageColor.getrgb("hsv(180,100%,100%)")) + self.assertEqual((0, 255, 255), + ImageColor.getrgb("hsv(180,100%,100%)")) # alternate format self.assertEqual(ImageColor.getrgb("hsb(0,100%,50%)"), ImageColor.getrgb("hsv(0,100%,50%)")) # floats - self.assertEqual((254, 3, 3), ImageColor.getrgb("hsl(0.1,99.2%,50.3%)")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(360.,100.0%,50%)")) + self.assertEqual((254, 3, 3), + ImageColor.getrgb("hsl(0.1,99.2%,50.3%)")) + self.assertEqual((255, 0, 0), + ImageColor.getrgb("hsl(360.,100.0%,50%)")) - self.assertEqual((253, 2, 2), ImageColor.getrgb("hsv(0.1,99.2%,99.3%)")) - self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360.,100.0%,100%)")) + self.assertEqual((253, 2, 2), + ImageColor.getrgb("hsv(0.1,99.2%,99.3%)")) + self.assertEqual((255, 0, 0), + ImageColor.getrgb("hsv(360.,100.0%,100%)")) # case insensitivity self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"), diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index e9727613b..54288f4db 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -45,8 +45,9 @@ class TestImageEnhance(PillowTestCase): for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']: for amount in [0, 0.5, 1.0]: - self._check_alpha(getattr(ImageEnhance, op)(original).enhance(amount), - original, op, amount) + self._check_alpha( + getattr(ImageEnhance, op)(original).enhance(amount), + original, op, amount) if __name__ == '__main__': diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 4b750af0d..837e81d30 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -215,12 +215,16 @@ class TestPyDecoder(PillowTestCase): buf = BytesIO(b'\x00'*255) im = MockImageFile(buf) - im.tile = [("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)] + im.tile = [ + ("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None) + ] d = self.get_decoder() self.assertRaises(ValueError, im.load) - im.tile = [("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)] + im.tile = [ + ("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None) + ] self.assertRaises(ValueError, im.load) def test_no_format(self): diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index e333346e9..f2116bdc4 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -67,8 +67,11 @@ class TestImageFont(PillowTestCase): } def setUp(self): - freetype_version = tuple(ImageFont.core.freetype2_version.split('.'))[:2] - self.metrics = self.METRICS.get(freetype_version, self.METRICS['Default']) + freetype_version = tuple( + ImageFont.core.freetype2_version.split('.') + )[:2] + self.metrics = self.METRICS.get(freetype_version, + self.METRICS['Default']) def get_font(self): return ImageFont.truetype(FONT_PATH, FONT_SIZE, @@ -202,7 +205,8 @@ class TestImageFont(PillowTestCase): target_img = Image.open(target) # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics['multiline']) + self.assert_image_similar(im, target_img, + self.metrics['multiline']) def test_unknown_align(self): im = Image.new(mode='RGB', size=(300, 100)) @@ -427,7 +431,8 @@ class TestImageFont(PillowTestCase): # Make a copy of FreeTypeFont so we can patch the original free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font): - def loadable_font(filepath, size, index, encoding, *args, **kwargs): + def loadable_font(filepath, size, index, encoding, + *args, **kwargs): if filepath == path_to_fake: return ImageFont._FreeTypeFont(FONT_PATH, size, index, encoding, *args, **kwargs) diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index bee415fd7..8376728a2 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -19,7 +19,8 @@ class TestImageFontBitmap(PillowTestCase): font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) size_outline = font_outline.getsize(text) size_bitmap = font_bitmap.getsize(text) - size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1]) + size_final = (max(size_outline[0], size_bitmap[0]), + max(size_outline[1], size_bitmap[1])) im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) im_outline = im_bitmap.copy() draw_bitmap = ImageDraw.Draw(im_bitmap) diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 489ed40b5..04432b14f 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -30,7 +30,8 @@ class TestImagecomplextext(PillowTestCase): self.assert_image_similar(im, target_img, .5) def test_y_offset(self): - ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE) + ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", + FONT_SIZE) im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) @@ -70,7 +71,8 @@ class TestImagecomplextext(PillowTestCase): im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'سلطنة عمان Oman', font=ttf, fill=500, direction='ltr') + draw.text((0, 0), 'سلطنة عمان Oman', + font=ttf, fill=500, direction='ltr') target = 'Tests/images/test_direction_ltr.png' target_img = Image.open(target) @@ -82,7 +84,8 @@ class TestImagecomplextext(PillowTestCase): im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'Oman سلطنة عمان', font=ttf, fill=500, direction='rtl') + draw.text((0, 0), 'Oman سلطنة عمان', + font=ttf, fill=500, direction='rtl') target = 'Tests/images/test_direction_ltr.png' target_img = Image.open(target) @@ -120,7 +123,8 @@ class TestImagecomplextext(PillowTestCase): im = Image.new(mode='RGB', size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500, features=['-fina', '-init', '-medi']) + draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500, + features=['-fina', '-init', '-medi']) target = 'Tests/images/test_arabictext_features.png' target_img = Image.open(target) diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 83fa0b5a9..dceadebf4 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -265,8 +265,9 @@ class MorphTests(PillowTestCase): # Act / Assert with self.assertRaises(Exception) as e: lb.build_lut() - self.assertEqual(str(e.exception), - 'Syntax error in pattern "a pattern with a syntax error"') + self.assertEqual( + str(e.exception), + 'Syntax error in pattern "a pattern with a syntax error"') def test_load_invalid_mrl(self): # Arrange diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 89c8e297f..e0fcf264a 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -34,7 +34,8 @@ class TestLibPack(PillowTestCase): self.assert_pack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X) self.assert_pack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0) - self.assert_pack("1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0) + self.assert_pack( + "1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0) def test_L(self): self.assert_pack("L", "L", 1, 1, 2, 3, 4) @@ -80,8 +81,10 @@ class TestLibPack(PillowTestCase): "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) self.assert_pack( "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_pack("RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16)) - self.assert_pack("RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16)) + self.assert_pack( + "RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16)) + self.assert_pack( + "RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16)) self.assert_pack( "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) @@ -90,10 +93,14 @@ class TestLibPack(PillowTestCase): self.assert_pack( "RGBA", "BGRa", 4, (191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12)) - self.assert_pack("RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) - self.assert_pack("RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) - self.assert_pack("RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) - self.assert_pack("RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) + self.assert_pack( + "RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) + self.assert_pack( + "RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) + self.assert_pack( + "RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) + self.assert_pack( + "RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_RGBa(self): self.assert_pack( @@ -104,10 +111,14 @@ class TestLibPack(PillowTestCase): "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) def test_RGBX(self): - self.assert_pack("RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack("RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_pack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) - self.assert_pack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) + self.assert_pack( + "RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack( + "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_pack( + "RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) + self.assert_pack( + "RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) self.assert_pack( "RGBX", "BGRX", b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00', @@ -116,23 +127,30 @@ class TestLibPack(PillowTestCase): "RGBX", "XBGR", b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c', (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) - self.assert_pack("RGBX", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) - self.assert_pack("RGBX", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) - self.assert_pack("RGBX", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) - self.assert_pack("RGBX", "X", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) + self.assert_pack("RGBX", "R", 1, + (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) + self.assert_pack("RGBX", "G", 1, + (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) + self.assert_pack("RGBX", "B", 1, + (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) + self.assert_pack("RGBX", "X", 1, + (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_CMYK(self): - self.assert_pack("CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack("CMYK", "CMYK", 4, + (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) self.assert_pack( "CMYK", "CMYK;I", 4, (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) self.assert_pack( "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_pack("CMYK", "K", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) + self.assert_pack("CMYK", "K", 1, + (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_YCbCr(self): self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) - self.assert_pack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) + self.assert_pack("YCbCr", "YCbCr;L", 3, + (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_pack( "YCbCr", "YCbCrX", b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', @@ -141,9 +159,12 @@ class TestLibPack(PillowTestCase): "YCbCr", "YCbCrK", b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', (1, 2, 3), (5, 6, 7), (9, 10, 11)) - self.assert_pack("YCbCr", "Y", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) - self.assert_pack("YCbCr", "Cb", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) - self.assert_pack("YCbCr", "Cr", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) + self.assert_pack("YCbCr", "Y", 1, + (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) + self.assert_pack("YCbCr", "Cb", 1, + (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) + self.assert_pack("YCbCr", "Cr", 1, + (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) def test_LAB(self): self.assert_pack( @@ -269,8 +290,10 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("RGB", "RGBX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) self.assert_unpack("RGB", "RGBX;L", 4, (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_unpack("RGB", "BGRX", 4, (3, 2, 1), (7, 6, 5), (11, 10, 9)) - self.assert_unpack("RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12)) - self.assert_unpack("RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10)) + self.assert_unpack( + "RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12)) + self.assert_unpack( + "RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10)) self.assert_unpack( "RGB", "YCC;P", b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data @@ -280,7 +303,8 @@ class TestLibUnpack(PillowTestCase): self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3)) def test_RGBA(self): - self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) + self.assert_unpack( + "RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) self.assert_unpack( "RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11)) self.assert_unpack( @@ -322,9 +346,12 @@ class TestLibUnpack(PillowTestCase): "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) self.assert_unpack("RGBA", "RGBA;15", 2, (8, 131, 0, 0), (24, 0, 8, 0)) self.assert_unpack("RGBA", "BGRA;15", 2, (0, 131, 8, 0), (8, 0, 24, 0)) - self.assert_unpack("RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0)) - self.assert_unpack("RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) - self.assert_unpack("RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) + self.assert_unpack( + "RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0)) + self.assert_unpack( + "RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) + self.assert_unpack( + "RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) self.assert_unpack( "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) self.assert_unpack( @@ -335,10 +362,14 @@ class TestLibUnpack(PillowTestCase): "RGBA", "YCCA;P", b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data (0, 161, 0, 4), (255, 255, 255, 237), (27, 158, 0, 206), (0, 118, 0, 17)) - self.assert_unpack("RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) - self.assert_unpack("RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) - self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) - self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) + self.assert_unpack( + "RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) + self.assert_unpack( + "RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) + self.assert_unpack( + "RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) + self.assert_unpack( + "RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) def test_RGBa(self): self.assert_unpack( @@ -351,10 +382,13 @@ class TestLibUnpack(PillowTestCase): "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) def test_RGBX(self): - self.assert_unpack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) - self.assert_unpack("RGBX", "RGB;L", 3, (1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X)) + self.assert_unpack("RGBX", "RGB", 3, + (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) + self.assert_unpack("RGBX", "RGB;L", 3, + (1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X)) self.assert_unpack("RGBX", "RGB;16B", 6, (1, 3, 5, X), (7, 9, 11, X)) - self.assert_unpack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) + self.assert_unpack("RGBX", "BGR", 3, + (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) self.assert_unpack("RGBX", "RGB;15", 2, (8, 131, 0, X), (24, 0, 8, X)) self.assert_unpack("RGBX", "BGR;15", 2, (0, 131, 8, X), (8, 0, 24, X)) self.assert_unpack("RGBX", "RGB;4B", 2, (17, 0, 34, X), (51, 0, 68, X)) @@ -366,19 +400,28 @@ class TestLibUnpack(PillowTestCase): "RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) self.assert_unpack( "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_unpack("RGBX", "RGBX;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) - self.assert_unpack("RGBX", "RGBX;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) - self.assert_unpack("RGBX", "BGRX", 4, (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) - self.assert_unpack("RGBX", "XRGB", 4, (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X)) - self.assert_unpack("RGBX", "XBGR", 4, (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) + self.assert_unpack("RGBX", "RGBX;16L", 8, + (2, 4, 6, 8), (10, 12, 14, 16)) + self.assert_unpack("RGBX", "RGBX;16B", 8, + (1, 3, 5, 7), (9, 11, 13, 15)) + self.assert_unpack("RGBX", "BGRX", 4, + (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) + self.assert_unpack("RGBX", "XRGB", 4, + (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X)) + self.assert_unpack("RGBX", "XBGR", 4, + (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) self.assert_unpack( "RGBX", "YCC;P", b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data (127, 102, 0, X), (192, 227, 0, X), (213, 255, 170, X), (98, 255, 133, X)) - self.assert_unpack("RGBX", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) - self.assert_unpack("RGBX", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) - self.assert_unpack("RGBX", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) - self.assert_unpack("RGBX", "X", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) + self.assert_unpack("RGBX", "R", 1, + (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) + self.assert_unpack("RGBX", "G", 1, + (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) + self.assert_unpack("RGBX", "B", 1, + (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) + self.assert_unpack("RGBX", "X", 1, + (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) def test_CMYK(self): self.assert_unpack( @@ -392,10 +435,14 @@ class TestLibUnpack(PillowTestCase): (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) self.assert_unpack( "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_unpack("CMYK", "C", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) - self.assert_unpack("CMYK", "M", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) - self.assert_unpack("CMYK", "Y", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) - self.assert_unpack("CMYK", "K", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) + self.assert_unpack("CMYK", "C", 1, + (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) + self.assert_unpack("CMYK", "M", 1, + (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) + self.assert_unpack("CMYK", "Y", 1, + (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) + self.assert_unpack("CMYK", "K", 1, + (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) self.assert_unpack( "CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0)) self.assert_unpack( @@ -451,7 +498,8 @@ class TestLibUnpack(PillowTestCase): if sys.byteorder == 'little': self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403) - self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) + self.assert_unpack("I", "I;16NS", + b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605) self.assert_unpack( "I", "I;32NS", @@ -459,7 +507,8 @@ class TestLibUnpack(PillowTestCase): else: self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708) self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304) - self.assert_unpack("I", "I;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) + self.assert_unpack("I", "I;16NS", + b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708) self.assert_unpack( "I", "I;32NS", @@ -483,14 +532,18 @@ class TestLibUnpack(PillowTestCase): if sys.byteorder == 'little': self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403) - self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', 0x0183, -31999) + self.assert_unpack( + "F", "F;16NS", + b'\x83\x01\x01\x83', 0x0183, -31999) self.assert_unpack("F", "F;32N", 4, 67305984, 134678016) self.assert_unpack( "F", "F;32NS", b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000) else: self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304) - self.assert_unpack("F", "F;16NS", b'\x83\x01\x01\x83', -31999, 0x0183) + self.assert_unpack( + "F", "F;16NS", + b'\x83\x01\x01\x83', -31999, 0x0183) self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708) self.assert_unpack( "F", "F;32NS", diff --git a/Tests/test_map.py b/Tests/test_map.py index 14bd835a2..8e3916d27 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -4,7 +4,8 @@ import sys from PIL import Image -@unittest.skipIf(sys.platform.startswith('win32'), "Win32 does not call map_buffer") +@unittest.skipIf(sys.platform.startswith('win32'), + "Win32 does not call map_buffer") class TestMap(PillowTestCase): def test_overflow(self): # There is the potential to overflow comparisons in map.c diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 1501e54bb..4efcd2c51 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -124,7 +124,9 @@ class TestNumpy(PillowTestCase): def test_save_tiff_uint16(self): # Tests that we're getting the pixel value in the right byte order. pixel_value = 0x1234 - a = numpy.array([pixel_value] * TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1], dtype=numpy.uint16) + a = numpy.array( + [pixel_value] * TEST_IMAGE_SIZE[0] * TEST_IMAGE_SIZE[1], + dtype=numpy.uint16) a.shape = TEST_IMAGE_SIZE img = Image.fromarray(a) diff --git a/Tests/test_pdfparser.py b/Tests/test_pdfparser.py index db97c97dd..42c813520 100644 --- a/Tests/test_pdfparser.py +++ b/Tests/test_pdfparser.py @@ -1,6 +1,8 @@ from helper import unittest, PillowTestCase -from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, PdfDict, PdfFormatError, PdfName, PdfParser, PdfStream, decode_text, encode_text, pdf_repr +from PIL.PdfParser import IndirectObjectDef, IndirectReference, PdfBinary, \ + PdfDict, PdfFormatError, PdfName, PdfParser, \ + PdfStream, decode_text, encode_text, pdf_repr class TestPdfParser(PillowTestCase): @@ -22,23 +24,35 @@ class TestPdfParser(PillowTestCase): self.assertNotEqual(IndirectObjectDef(1, 2), (1, 2)) def test_parsing(self): - self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"), b"Name#Hash") + self.assertEqual(PdfParser.interpret_name(b"Name#23Hash"), + b"Name#Hash") self.assertEqual(PdfParser.interpret_name(b"Name#23Hash", as_text=True), "Name#Hash") - self.assertEqual(PdfParser.get_value(b"1 2 R ", 0), (IndirectReference(1, 2), 5)) + self.assertEqual(PdfParser.get_value(b"1 2 R ", 0), + (IndirectReference(1, 2), 5)) self.assertEqual(PdfParser.get_value(b"true[", 0), (True, 4)) self.assertEqual(PdfParser.get_value(b"false%", 0), (False, 5)) self.assertEqual(PdfParser.get_value(b"null<", 0), (None, 4)) - self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0), (123, 15)) - self.assertEqual(PdfParser.get_value(b"<901FA3>", 0), (b"\x90\x1F\xA3", 8)) - self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), (b"\x90\x1F\xA0", 17)) + self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0), + (123, 15)) + self.assertEqual(PdfParser.get_value(b"<901FA3>", 0), + (b"\x90\x1F\xA3", 8)) + self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), + (b"\x90\x1F\xA0", 17)) self.assertEqual(PdfParser.get_value(b"(asd)", 0), (b"asd", 5)) - self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), (b"asd(qwe)zxc", 13)) - self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0), (b"Two words.", 14)) - self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), (b"Two\nlines.", 12)) - self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0), (b"Two\nlines.", 13)) - self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0), (b"Two\nlines.", 13)) - self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), (b"One(paren", 12)) - self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), (b"One)paren", 12)) + self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), + (b"asd(qwe)zxc", 13)) + self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0), + (b"Two words.", 14)) + self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), + (b"Two\nlines.", 12)) + self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0), + (b"Two\nlines.", 13)) + self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0), + (b"Two\nlines.", 13)) + self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), + (b"One(paren", 12)) + self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), + (b"One)paren", 12)) self.assertEqual(PdfParser.get_value(b"(\\0053)", 0), (b"\x053", 7)) self.assertEqual(PdfParser.get_value(b"(\\053)", 0), (b"\x2B", 6)) self.assertEqual(PdfParser.get_value(b"(\\53)", 0), (b"\x2B", 5)) @@ -65,23 +79,30 @@ class TestPdfParser(PillowTestCase): def test_pdf_repr(self): self.assertEqual(bytes(IndirectReference(1, 2)), b"1 2 R") - self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj") + self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), + b"1 2 obj") self.assertEqual(bytes(PdfName(b"Name#Hash")), b"/Name#23Hash") self.assertEqual(bytes(PdfName("Name#Hash")), b"/Name#23Hash") - self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") - self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") + self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})), + b"<<\n/Name 1 2 R\n>>") + self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})), + b"<<\n/Name 1 2 R\n>>") self.assertEqual(pdf_repr(IndirectReference(1, 2)), b"1 2 R") - self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj") + self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), + b"1 2 obj") self.assertEqual(pdf_repr(PdfName(b"Name#Hash")), b"/Name#23Hash") self.assertEqual(pdf_repr(PdfName("Name#Hash")), b"/Name#23Hash") - self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") - self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>") + self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), + b"<<\n/Name 1 2 R\n>>") + self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), + b"<<\n/Name 1 2 R\n>>") self.assertEqual(pdf_repr(123), b"123") self.assertEqual(pdf_repr(True), b"true") self.assertEqual(pdf_repr(False), b"false") self.assertEqual(pdf_repr(None), b"null") self.assertEqual(pdf_repr(b"a)/b\\(c"), br"(a\)/b\\\(c)") - self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]), b"[ 123 true <<\n/a /b\n>> ]") + self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]), + b"[ 123 true <<\n/a /b\n>> ]") self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>") diff --git a/setup.py b/setup.py index 761d552cc..9529787f9 100755 --- a/setup.py +++ b/setup.py @@ -424,7 +424,8 @@ class pil_build_ext(build_ext): best_path = None for name in os.listdir(program_files): if name.startswith('OpenJPEG '): - version = tuple(int(x) for x in name[9:].strip().split('.')) + version = tuple(int(x) for x in + name[9:].strip().split('.')) if version > best_version: best_version = version best_path = os.path.join(program_files, name) @@ -501,7 +502,8 @@ class pil_build_ext(build_ext): # possible. _add_directory(self.compiler.include_dirs, best_path, 0) feature.jpeg2000 = 'openjp2' - feature.openjpeg_version = '.'.join(str(x) for x in best_version) + feature.openjpeg_version = '.'.join(str(x) for x in + best_version) if feature.want('imagequant'): _dbg('Looking for imagequant') @@ -516,7 +518,8 @@ class pil_build_ext(build_ext): if _find_include_file(self, 'tiff.h'): if _find_library_file(self, "tiff"): feature.tiff = "tiff" - if sys.platform == "win32" and _find_library_file(self, "libtiff"): + if (sys.platform == "win32" and + _find_library_file(self, "libtiff")): feature.tiff = "libtiff" if (sys.platform == "darwin" and _find_library_file(self, "libtiff")): @@ -528,14 +531,16 @@ class pil_build_ext(build_ext): # look for freetype2 include files freetype_version = 0 for subdir in self.compiler.include_dirs: - _dbg('Checking for include file %s in %s', ("ft2build.h", subdir)) + _dbg('Checking for include file %s in %s', + ("ft2build.h", subdir)) if os.path.isfile(os.path.join(subdir, "ft2build.h")): _dbg('Found %s in %s', ("ft2build.h", subdir)) freetype_version = 21 subdir = os.path.join(subdir, "freetype2") break subdir = os.path.join(subdir, "freetype2") - _dbg('Checking for include file %s in %s', ("ft2build.h", subdir)) + _dbg('Checking for include file %s in %s', + ("ft2build.h", subdir)) if os.path.isfile(os.path.join(subdir, "ft2build.h")): _dbg('Found %s in %s', ("ft2build.h", subdir)) freetype_version = 21 diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index f9e5ee5c8..8b86f9a00 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -60,7 +60,14 @@ class BmpImageFile(ImageFile.ImageFile): format_description = "Windows Bitmap" format = "BMP" # --------------------------------------------------- BMP Compression values - COMPRESSIONS = {'RAW': 0, 'RLE8': 1, 'RLE4': 2, 'BITFIELDS': 3, 'JPEG': 4, 'PNG': 5} + COMPRESSIONS = { + 'RAW': 0, + 'RLE8': 1, + 'RLE4': 2, + 'BITFIELDS': 3, + 'JPEG': 4, + 'PNG': 5 + } RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 def _bitmap(self, header=0, offset=0): @@ -69,10 +76,13 @@ class BmpImageFile(ImageFile.ImageFile): if header: seek(header) file_info = {} - file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) + # read bmp header size @offset 14 (this is part of the header size) + file_info['header_size'] = i32(read(4)) file_info['direction'] = -1 # --------------------- If requested, read header at a specific position - header_data = ImageFile._safe_read(self.fp, file_info['header_size'] - 4) # read the rest of the bmp header, without its size + # read the rest of the bmp header, without its size + header_data = ImageFile._safe_read(self.fp, + file_info['header_size'] - 4) # --------------------------------------------------- IBM OS/2 Bitmap v1 # ------ This format has different offsets because of width/height types if file_info['header_size'] == 12: @@ -88,12 +98,16 @@ class BmpImageFile(ImageFile.ImageFile): file_info['y_flip'] = i8(header_data[7]) == 0xff file_info['direction'] = 1 if file_info['y_flip'] else -1 file_info['width'] = i32(header_data[0:4]) - file_info['height'] = i32(header_data[4:8]) if not file_info['y_flip'] else 2**32 - i32(header_data[4:8]) + file_info['height'] = (i32(header_data[4:8]) + if not file_info['y_flip'] + else 2**32 - i32(header_data[4:8])) file_info['planes'] = i16(header_data[8:10]) file_info['bits'] = i16(header_data[10:12]) file_info['compression'] = i32(header_data[12:16]) - file_info['data_size'] = i32(header_data[16:20]) # byte size of pixel data - file_info['pixels_per_meter'] = (i32(header_data[20:24]), i32(header_data[24:28])) + # byte size of pixel data + file_info['data_size'] = i32(header_data[16:20]) + file_info['pixels_per_meter'] = (i32(header_data[20:24]), + i32(header_data[24:28])) file_info['colors'] = i32(header_data[28:32]) file_info['palette_padding'] = 4 self.info["dpi"] = tuple( @@ -101,21 +115,32 @@ class BmpImageFile(ImageFile.ImageFile): file_info['pixels_per_meter'])) if file_info['compression'] == self.BITFIELDS: if len(header_data) >= 52: - for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']): + for idx, mask in enumerate(['r_mask', + 'g_mask', + 'b_mask', + 'a_mask']): file_info[mask] = i32(header_data[36+idx*4:40+idx*4]) else: - # 40 byte headers only have the three components in the bitfields masks, + # 40 byte headers only have the three components in the + # bitfields masks, # ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx # See also https://github.com/python-pillow/Pillow/issues/1293 - # There is a 4th component in the RGBQuad, in the alpha location, but it - # is listed as a reserved component, and it is not generally an alpha channel + # There is a 4th component in the RGBQuad, in the alpha + # location, but it is listed as a reserved component, + # and it is not generally an alpha channel file_info['a_mask'] = 0x0 for mask in ['r_mask', 'g_mask', 'b_mask']: file_info[mask] = i32(read(4)) - file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask']) - file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask']) + file_info['rgb_mask'] = (file_info['r_mask'], + file_info['g_mask'], + file_info['b_mask']) + file_info['rgba_mask'] = (file_info['r_mask'], + file_info['g_mask'], + file_info['b_mask'], + file_info['a_mask']) else: - raise IOError("Unsupported BMP header type (%d)" % file_info['header_size']) + raise IOError("Unsupported BMP header type (%d)" % + file_info['header_size']) # ------------------ Special case : header is reported 40, which # ---------------------- is shorter than real size for bpp >= 16 self.size = file_info['width'], file_info['height'] @@ -127,11 +152,15 @@ class BmpImageFile(ImageFile.ImageFile): # ----------------------- Check bit depth for unusual unsupported values self.mode, raw_mode = BIT2MODE.get(file_info['bits'], (None, None)) if self.mode is None: - raise IOError("Unsupported BMP pixel depth (%d)" % file_info['bits']) + raise IOError("Unsupported BMP pixel depth (%d)" + % file_info['bits']) # ----------------- Process BMP with Bitfields compression (not palette) if file_info['compression'] == self.BITFIELDS: SUPPORTED = { - 32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0)], + 32: [(0xff0000, 0xff00, 0xff, 0x0), + (0xff0000, 0xff00, 0xff, 0xff000000), + (0x0, 0x0, 0x0, 0x0), + (0xff000000, 0xff0000, 0xff00, 0x0)], 24: [(0xff0000, 0xff00, 0xff)], 16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)] } @@ -145,11 +174,15 @@ class BmpImageFile(ImageFile.ImageFile): (16, (0x7c00, 0x3e0, 0x1f)): "BGR;15" } if file_info['bits'] in SUPPORTED: - if file_info['bits'] == 32 and file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: + if file_info['bits'] == 32 and \ + file_info['rgba_mask'] in SUPPORTED[file_info['bits']]: raw_mode = MASK_MODES[(file_info['bits'], file_info['rgba_mask'])] self.mode = "RGBA" if raw_mode in ("BGRA",) else self.mode - elif file_info['bits'] in (24, 16) and file_info['rgb_mask'] in SUPPORTED[file_info['bits']]: - raw_mode = MASK_MODES[(file_info['bits'], file_info['rgb_mask'])] + elif (file_info['bits'] in (24, 16) and + file_info['rgb_mask'] in SUPPORTED[file_info['bits']]): + raw_mode = MASK_MODES[ + (file_info['bits'], file_info['rgb_mask']) + ] else: raise IOError("Unsupported BMP bitfields layout") else: @@ -158,17 +191,20 @@ class BmpImageFile(ImageFile.ImageFile): if file_info['bits'] == 32 and header == 22: # 32-bit .cur offset raw_mode, self.mode = "BGRA", "RGBA" else: - raise IOError("Unsupported BMP compression (%d)" % file_info['compression']) + raise IOError("Unsupported BMP compression (%d)" % + file_info['compression']) # ---------------- Once the header is processed, process the palette/LUT if self.mode == "P": # Paletted for 1, 4 and 8 bit images # ----------------------------------------------------- 1-bit images if not (0 < file_info['colors'] <= 65536): - raise IOError("Unsupported BMP Palette size (%d)" % file_info['colors']) + raise IOError("Unsupported BMP Palette size (%d)" % + file_info['colors']) else: padding = file_info['palette_padding'] palette = read(padding * file_info['colors']) greyscale = True - indices = (0, 255) if file_info['colors'] == 2 else list(range(file_info['colors'])) + indices = (0, 255) if file_info['colors'] == 2 else \ + list(range(file_info['colors'])) # ------------------ Check if greyscale and ignore palette if so for ind, val in enumerate(indices): rgb = palette[ind*padding:ind*padding + 3] @@ -180,13 +216,19 @@ class BmpImageFile(ImageFile.ImageFile): raw_mode = self.mode else: self.mode = "P" - self.palette = ImagePalette.raw("BGRX" if padding == 4 else "BGR", palette) + self.palette = ImagePalette.raw( + "BGRX" if padding == 4 else "BGR", palette) # ----------------------------- Finally set the tile data for the plugin self.info['compression'] = file_info['compression'] - self.tile = [('raw', (0, 0, file_info['width'], file_info['height']), offset or self.fp.tell(), - (raw_mode, ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), file_info['direction']) - )] + self.tile = [ + ('raw', + (0, 0, file_info['width'], file_info['height']), + offset or self.fp.tell(), + (raw_mode, + ((file_info['width'] * file_info['bits'] + 31) >> 3) & (~3), + file_info['direction'])) + ] def _open(self): """ Open file, check magic number and read header """ diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index e755f94b9..3bd65c93d 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -142,7 +142,8 @@ class DdsImageFile(ImageFile.ImageFile): # ignoring flags which pertain to volume textures and cubemaps dxt10 = BytesIO(self.fp.read(20)) dxgi_format, dimension = struct.unpack("= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2) + return len(prefix) >= 8 and \ + i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2) ## @@ -54,7 +55,8 @@ class GbrImageFile(ImageFile.ImageFile): if width <= 0 or height <= 0: raise SyntaxError("not a GIMP brush") if color_depth not in (1, 4): - raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth) + raise SyntaxError( + "Unsupported GIMP brush color depth: %s" % color_depth) if version == 1: comment_length = header_size-20 diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index e0da01129..fec2f7663 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -397,7 +397,8 @@ def _write_multiple_frames(im, fp, palette): im_frames = [] frame_count = 0 - for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): + for imSequence in itertools.chain([im], + im.encoderinfo.get("append_images", [])): for im_frame in ImageSequence.Iterator(imSequence): # a copy is required here since seek can still mutate the image im_frame = _normalize_mode(im_frame.copy()) @@ -413,17 +414,19 @@ def _write_multiple_frames(im, fp, palette): if im_frames: # delta frame previous = im_frames[-1] - if _get_palette_bytes(im_frame) == _get_palette_bytes(previous['im']): + if _get_palette_bytes(im_frame) == \ + _get_palette_bytes(previous['im']): delta = ImageChops.subtract_modulo(im_frame, previous['im']) else: - delta = ImageChops.subtract_modulo(im_frame.convert('RGB'), - previous['im'].convert('RGB')) + delta = ImageChops.subtract_modulo( + im_frame.convert('RGB'), previous['im'].convert('RGB')) bbox = delta.getbbox() if not bbox: # This frame is identical to the previous frame if duration: - previous['encoderinfo']['duration'] += encoderinfo['duration'] + previous['encoderinfo']['duration'] += \ + encoderinfo['duration'] continue else: bbox = None @@ -525,7 +528,8 @@ def _write_local_header(fp, im, offset, flags): o8(transparency) + # transparency index o8(0)) - if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]) <= 255: + if "comment" in im.encoderinfo and \ + 1 <= len(im.encoderinfo["comment"]) <= 255: fp.write(b"!" + o8(254) + # extension intro o8(len(im.encoderinfo["comment"])) + @@ -691,7 +695,8 @@ def _get_global_header(im, info): for extensionKey in ["transparency", "duration", "loop", "comment"]: if info and extensionKey in info: if ((extensionKey == "duration" and info[extensionKey] == 0) or - (extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))): + (extensionKey == "comment" and + not (1 <= len(info[extensionKey]) <= 255))): continue version = b"89a" break diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index d82e30efc..4b6281f13 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -305,10 +305,10 @@ def profileToProfile( :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 see the pyCMS documentation for details on rendering intents and what they do. @@ -424,10 +424,10 @@ def buildTransform( :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 see the pyCMS documentation for details on rendering intents and what they do. @@ -512,20 +512,20 @@ def buildProofTransform( :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the input->proof (simulated) transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 see the pyCMS documentation for details on rendering intents and what they do. - :param proofRenderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for proof->output transform + :param proofRenderingIntent: Integer (0-3) specifying the rendering intent + you wish to use for proof->output transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 see the pyCMS documentation for details on rendering intents and what they do. @@ -875,10 +875,10 @@ def getDefaultIntent(profile): :returns: Integer 0-3 specifying the default rendering intent for this profile. - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 see the pyCMS documentation for details on rendering intents and what they do. @@ -913,15 +913,15 @@ def isIntentSupported(profile, intent, direction): :param intent: Integer (0-3) specifying the rendering intent you wish to use with this profile - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) + ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 + ImageCms.INTENT_SATURATION = 2 + ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 see the pyCMS documentation for details on rendering intents and what they do. - :param direction: Integer specifying if the profile is to be used for input, - output, or proof + :param direction: Integer specifying if the profile is to be used for + input, output, or proof INPUT = 0 (or use ImageCms.DIRECTION_INPUT) OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 920b977f4..bc8c0cf38 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -217,7 +217,8 @@ class ImageDraw(object): ink = fill if ink is not None: try: - mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs) + mask, offset = font.getmask2(text, self.fontmode, + *args, **kwargs) xy = xy[0] + offset[0], xy[1] + offset[1] except AttributeError: try: diff --git a/src/PIL/ImageEnhance.py b/src/PIL/ImageEnhance.py index 11c9c3a06..1b78bfd9b 100644 --- a/src/PIL/ImageEnhance.py +++ b/src/PIL/ImageEnhance.py @@ -51,7 +51,8 @@ class Color(_Enhance): if 'A' in image.getbands(): self.intermediate_mode = 'LA' - self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) + self.degenerate = image.convert( + self.intermediate_mode).convert(image.mode) class Contrast(_Enhance): diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 681dee524..bdcc43d1f 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -166,8 +166,9 @@ class ImageFile(Image.Image): if use_mmap: # try memory mapping decoder_name, extents, offset, args = self.tile[0] - if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \ - and args[0] in Image._MAPMODES: + if decoder_name == "raw" and len(args) >= 3 and \ + args[0] == self.mode and \ + args[0] in Image._MAPMODES: try: if hasattr(Image.core, "map"): # use built-in mapper WIN32 only @@ -180,12 +181,14 @@ class ImageFile(Image.Image): # use mmap, if possible import mmap with open(self.filename, "r") as fp: - self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) + self.map = mmap.mmap(fp.fileno(), 0, + access=mmap.ACCESS_READ) self.im = Image.core.map_buffer( - self.map, self.size, decoder_name, extents, offset, args - ) + self.map, self.size, decoder_name, extents, + offset, args) readonly = 1 - # After trashing self.im, we might need to reload the palette data. + # After trashing self.im, + # we might need to reload the palette data. if self.palette: self.palette.dirty = 1 except (AttributeError, EnvironmentError, ImportError): @@ -217,7 +220,8 @@ class ImageFile(Image.Image): while True: try: s = read(self.decodermaxblock) - except (IndexError, struct.error): # truncated png/gif + except (IndexError, struct.error): + # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: @@ -229,7 +233,8 @@ class ImageFile(Image.Image): else: self.tile = [] raise IOError("image file is truncated " - "(%d bytes not processed)" % len(b)) + "(%d bytes not processed)" % + len(b)) b = b + s n, err_code = decoder.decode(b) @@ -588,10 +593,12 @@ class PyDecoder(object): """ Override to perform the decoding process. - :param buffer: A bytes object with the data to be decoded. If `handles_eof` - is set, then `buffer` will be empty and `self.fd` will be set. - :returns: A tuple of (bytes consumed, errcode). If finished with decoding - return <0 for the bytes consumed. Err codes are from `ERRORS` + :param buffer: A bytes object with the data to be decoded. + If `handles_eof` is set, then `buffer` will be empty and `self.fd` + will be set. + :returns: A tuple of (bytes consumed, errcode). + If finished with decoding return <0 for the bytes consumed. + Err codes are from `ERRORS` """ raise NotImplementedError() @@ -650,8 +657,8 @@ class PyDecoder(object): Convenience method to set the internal image from a stream of raw data :param data: Bytes to be set - :param rawmode: The rawmode to be used for the decoder. If not specified, - it will default to the mode of the image + :param rawmode: The rawmode to be used for the decoder. + If not specified, it will default to the mode of the image :returns: None """ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 3ac29e8f6..099ccc4ff 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -141,7 +141,8 @@ class FreeTypeFont(object): self.layout_engine = layout_engine if isPath(font): - self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine) + self.font = core.getfont(font, size, index, encoding, + layout_engine=layout_engine) else: self.font_bytes = font.read() self.font = core.getfont( @@ -175,9 +176,11 @@ class FreeTypeFont(object): return self.font.getsize(text)[1] def getmask(self, text, mode="", direction=None, features=None): - return self.getmask2(text, mode, direction=direction, features=features)[0] + return self.getmask2(text, mode, direction=direction, + features=features)[0] - def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs): + 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) @@ -194,12 +197,13 @@ class FreeTypeFont(object): :return: A FreeTypeFont object. """ - return FreeTypeFont(font=self.path if font is None else font, - size=self.size if size is None else size, - index=self.index if index is None else index, - encoding=self.encoding if encoding is None else encoding, - layout_engine=self.layout_engine if layout_engine is None else layout_engine - ) + return FreeTypeFont( + font=self.path if font is None else font, + size=self.size if size is None else size, + index=self.index if index is None else index, + encoding=self.encoding if encoding is None else encoding, + layout_engine=self.layout_engine if layout_engine is None else layout_engine + ) class TransposedFont(object): @@ -303,12 +307,16 @@ def truetype(font=None, size=10, index=0, encoding="", for walkfilename in walkfilenames: if ext and walkfilename == ttf_filename: fontpath = os.path.join(walkroot, walkfilename) - return FreeTypeFont(fontpath, size, index, encoding, layout_engine) - elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: + return FreeTypeFont(fontpath, size, index, + encoding, layout_engine) + elif (not ext and + os.path.splitext(walkfilename)[0] == ttf_filename): fontpath = os.path.join(walkroot, walkfilename) if os.path.splitext(fontpath)[1] == '.ttf': - return FreeTypeFont(fontpath, size, index, encoding, layout_engine) - if not ext and first_font_with_a_different_extension is None: + return FreeTypeFont(fontpath, size, index, + encoding, layout_engine) + if not ext \ + and first_font_with_a_different_extension is None: first_font_with_a_different_extension = fontpath if first_font_with_a_different_extension: return FreeTypeFont(first_font_with_a_different_extension, size, diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index b227f2127..2b3377a14 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -42,7 +42,8 @@ def getmode(mode): for m, (basemode, basetype, bands) in Image._MODEINFO.items(): modes[m] = ModeDescriptor(m, bands, basemode, basetype) # extra experimental modes - modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L") + modes["RGBa"] = ModeDescriptor("RGBa", + ("R", "G", "B", "a"), "RGB", "L") modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index c9dc36312..2930c1d9c 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -119,7 +119,8 @@ def align8to32(bytes, width, mode): new_data = [] for i in range(len(bytes) // bytes_per_line): - new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + b'\x00' * extra_padding) + new_data.append(bytes[i*bytes_per_line:(i+1)*bytes_per_line] + + b'\x00' * extra_padding) return b''.join(new_data) diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index b5ad53df7..17bf32f62 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -195,7 +195,8 @@ class PhotoImage(object): # Pypy is using a ffi cdata element # (Pdb) self.tk.interp # - _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) + _imagingtk.tkinit( + int(ffi.cast("uintptr_t", tk.interp)), 1) else: _imagingtk.tkinit(tk.interpaddr(), 1) except AttributeError: diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index 25fbefbca..7b170fe16 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -270,7 +270,8 @@ def _save(im, fp, filename): Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) Image.register_save(Jpeg2KImageFile.format, _save) -Image.register_extensions(Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]) +Image.register_extensions(Jpeg2KImageFile.format, + [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"]) Image.register_mime(Jpeg2KImageFile.format, 'image/jp2') Image.register_mime(Jpeg2KImageFile.format, 'image/jpx') diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 97ef834d3..98c27010c 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -799,6 +799,7 @@ def jpeg_factory(fp=None, filename=None): Image.register_open(JpegImageFile.format, jpeg_factory, _accept) Image.register_save(JpegImageFile.format, _save) -Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) +Image.register_extensions(JpegImageFile.format, + [".jfif", ".jpe", ".jpg", ".jpeg"]) Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 9692d1162..b2c7a3d79 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -126,8 +126,9 @@ class MspDecoder(ImageFile.PyDecoder): continue row = self.fd.read(rowlen) if len(row) != rowlen: - raise IOError("Truncated MSP file, expected %d bytes on row %s", - (rowlen, x)) + raise IOError( + "Truncated MSP file, expected %d bytes on row %s", + (rowlen, x)) idx = 0 while idx < rowlen: runtype = i8(row[idx]) diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 8538bcd49..d411bfc41 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -98,7 +98,8 @@ def _save(im, fp, filename, save_all=False): try: im_numberOfPages = im.n_frames except AttributeError: - # Image format does not have n_frames. It is a single frame image + # Image format does not have n_frames. + # It is a single frame image pass numberOfPages += im_numberOfPages for i in range(im_numberOfPages): @@ -115,9 +116,9 @@ def _save(im, fp, filename, save_all=False): for imSequence in ims: im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence] for im in im_pages: - # FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits) - # or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports - # Flatedecode (zip compression). + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode + # (packbits) or LZWDecode (tiff/lzw compression). Note that + # PDF 1.2 also supports Flatedecode (zip compression). bits = 8 params = None @@ -135,7 +136,12 @@ def _save(im, fp, filename, save_all=False): elif im.mode == "P": filter = "ASCIIHexDecode" palette = im.im.getpalette("RGB") - colorspace = [PdfParser.PdfName("Indexed"), PdfParser.PdfName("DeviceRGB"), 255, PdfParser.PdfBinary(palette)] + colorspace = [ + PdfParser.PdfName("Indexed"), + PdfParser.PdfName("DeviceRGB"), + 255, + PdfParser.PdfBinary(palette) + ] procset = "ImageI" # indexed color elif im.mode == "RGB": filter = "DCTDecode" @@ -166,7 +172,8 @@ def _save(im, fp, filename, save_all=False): elif filter == "FlateDecode": ImageFile._save(im, op, [("zip", (0, 0)+im.size, 0, im.mode)]) elif filter == "RunLengthDecode": - ImageFile._save(im, op, [("packbits", (0, 0)+im.size, 0, im.mode)]) + ImageFile._save(im, op, + [("packbits", (0, 0)+im.size, 0, im.mode)]) else: raise ValueError("unsupported PDF filter (%s)" % filter) @@ -175,26 +182,37 @@ def _save(im, fp, filename, save_all=False): width, height = im.size - existing_pdf.write_obj(image_refs[pageNumber], stream=op.getvalue(), - Type=PdfParser.PdfName("XObject"), - Subtype=PdfParser.PdfName("Image"), - Width=width, # * 72.0 / resolution, - Height=height, # * 72.0 / resolution, - Filter=PdfParser.PdfName(filter), - BitsPerComponent=bits, - DecodeParams=params, - ColorSpace=colorspace) + existing_pdf.write_obj(image_refs[pageNumber], + stream=op.getvalue(), + Type=PdfParser.PdfName("XObject"), + Subtype=PdfParser.PdfName("Image"), + Width=width, # * 72.0 / resolution, + Height=height, # * 72.0 / resolution, + Filter=PdfParser.PdfName(filter), + BitsPerComponent=bits, + DecodeParams=params, + ColorSpace=colorspace) # # page existing_pdf.write_page(page_refs[pageNumber], - Resources=PdfParser.PdfDict( - ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], - XObject=PdfParser.PdfDict(image=image_refs[pageNumber])), - MediaBox=[0, 0, int(width * 72.0 / resolution), int(height * 72.0 / resolution)], - Contents=contents_refs[pageNumber] - ) + Resources=PdfParser.PdfDict( + ProcSet=[ + PdfParser.PdfName("PDF"), + PdfParser.PdfName(procset) + ], + XObject=PdfParser.PdfDict( + image=image_refs[pageNumber] + ) + ), + MediaBox=[ + 0, + 0, + int(width * 72.0 / resolution), + int(height * 72.0 / resolution) + ], + Contents=contents_refs[pageNumber]) # # page contents @@ -204,7 +222,8 @@ def _save(im, fp, filename, save_all=False): int(width * 72.0 / resolution), int(height * 72.0 / resolution))) - existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents) + existing_pdf.write_obj(contents_refs[pageNumber], + stream=page_contents) pageNumber += 1 diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 9031330da..971f44514 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -20,7 +20,8 @@ else: # Python 2.x return s # pragma: no cover -# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set on page 656 +# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set +# on page 656 def encode_text(s): return codecs.BOM_UTF16_BE + s.encode("utf_16_be") @@ -80,7 +81,8 @@ def decode_text(b): class PdfFormatError(RuntimeError): - """An error that probably indicates a syntactic or semantic error in the PDF file structure""" + """An error that probably indicates a syntactic or semantic error in the + PDF file structure""" pass @@ -89,7 +91,8 @@ def check_format_condition(condition, error_message): raise PdfFormatError(error_message) -class IndirectReference(collections.namedtuple("IndirectReferenceTuple", ["object_id", "generation"])): +class IndirectReference(collections.namedtuple("IndirectReferenceTuple", + ["object_id", "generation"])): def __str__(self): return "%s %s R" % self @@ -97,7 +100,9 @@ class IndirectReference(collections.namedtuple("IndirectReferenceTuple", ["objec return self.__str__().encode("us-ascii") def __eq__(self, other): - return other.__class__ is self.__class__ and other.object_id == self.object_id and other.generation == self.generation + return other.__class__ is self.__class__ and \ + other.object_id == self.object_id and \ + other.generation == self.generation def __ne__(self, other): return not (self == other) @@ -143,19 +148,26 @@ class XrefTable: elif key in self.deleted_entries: generation = self.deleted_entries[key] else: - raise IndexError("object ID " + str(key) + " cannot be deleted because it doesn't exist") + raise IndexError("object ID " + str(key) + + " cannot be deleted because it doesn't exist") def __contains__(self, key): return key in self.existing_entries or key in self.new_entries def __len__(self): - return len(set(self.existing_entries.keys()) | set(self.new_entries.keys()) | set(self.deleted_entries.keys())) + return len(set(self.existing_entries.keys()) | + set(self.new_entries.keys()) | + set(self.deleted_entries.keys())) def keys(self): - return (set(self.existing_entries.keys()) - set(self.deleted_entries.keys())) | set(self.new_entries.keys()) + return ( + set(self.existing_entries.keys()) - + set(self.deleted_entries.keys()) + ) | set(self.new_entries.keys()) def write(self, f): - keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) + keys = sorted(set(self.new_entries.keys()) | + set(self.deleted_entries.keys())) deleted_keys = sorted(set(self.deleted_entries.keys())) startxref = f.tell() f.write(b"xref\n") @@ -172,10 +184,12 @@ class XrefTable: else: contiguous_keys = keys keys = None - f.write(make_bytes("%d %d\n" % (contiguous_keys[0], len(contiguous_keys)))) + f.write(make_bytes("%d %d\n" % + (contiguous_keys[0], len(contiguous_keys)))) for object_id in contiguous_keys: if object_id in self.new_entries: - f.write(make_bytes("%010d %05d n \n" % self.new_entries[object_id])) + f.write(make_bytes("%010d %05d n \n" % + self.new_entries[object_id])) else: this_deleted_object_id = deleted_keys.pop(0) check_format_condition(object_id == this_deleted_object_id, @@ -186,7 +200,9 @@ class XrefTable: next_in_linked_list = deleted_keys[0] except IndexError: next_in_linked_list = 0 - f.write(make_bytes("%010d %05d f \n" % (next_in_linked_list, self.deleted_entries[object_id]))) + f.write(make_bytes("%010d %05d f \n" % + (next_in_linked_list, + self.deleted_entries[object_id]))) return startxref @@ -203,7 +219,8 @@ class PdfName: return self.name.decode("us-ascii") def __eq__(self, other): - return (isinstance(other, PdfName) and other.name == self.name) or other == self.name + return (isinstance(other, PdfName) and other.name == self.name) or \ + other == self.name def __hash__(self): return hash(self.name) @@ -313,7 +330,9 @@ class PdfStream: expected_length = self.dictionary.Length return zlib.decompress(self.buf, bufsize=int(expected_length)) else: - raise NotImplementedError("stream filter %s unknown/unsupported" % repr(self.dictionary.Filter)) + raise NotImplementedError( + "stream filter %s unknown/unsupported" % + repr(self.dictionary.Filter)) def pdf_repr(x): @@ -323,7 +342,8 @@ def pdf_repr(x): return b"false" elif x is None: return b"null" - elif isinstance(x, PdfName) or isinstance(x, PdfDict) or isinstance(x, PdfArray) or isinstance(x, PdfBinary): + elif (isinstance(x, PdfName) or isinstance(x, PdfDict) or + isinstance(x, PdfArray) or isinstance(x, PdfBinary)): return bytes(x) elif isinstance(x, int): return str(x).encode("us-ascii") @@ -331,10 +351,15 @@ def pdf_repr(x): return bytes(PdfDict(x)) elif isinstance(x, list): return bytes(PdfArray(x)) - elif (py3 and isinstance(x, str)) or (not py3 and isinstance(x, unicode)): + elif ((py3 and isinstance(x, str)) or + (not py3 and isinstance(x, unicode))): return pdf_repr(encode_text(x)) elif isinstance(x, bytes): - return b"(" + x.replace(b"\\", b"\\\\").replace(b"(", b"\\(").replace(b")", b"\\)") + b")" # XXX escape more chars? handle binary garbage + # XXX escape more chars? handle binary garbage + x = x.replace(b"\\", b"\\\\") + x = x.replace(b"(", b"\\(") + x = x.replace(b")", b"\\)") + return b"(" + x + b")" else: return bytes(x) @@ -344,10 +369,13 @@ class PdfParser: Supports PDF up to 1.4 """ - def __init__(self, filename=None, f=None, buf=None, start_offset=0, mode="rb"): - # type: (PdfParser, str, file, Union[bytes, bytearray], int, str) -> None + def __init__(self, filename=None, f=None, + buf=None, start_offset=0, mode="rb"): + # type: (PdfParser, str, file, Union[bytes, bytearray], int, str) + # -> None if buf and f: - raise RuntimeError("specify buf or f or filename, but not both buf and f") + raise RuntimeError( + "specify buf or f or filename, but not both buf and f") self.filename = filename self.buf = buf self.f = f @@ -473,7 +501,8 @@ class PdfParser: if self.info: trailer_dict[b"Info"] = self.info_ref self.last_xref_section_offset = start_xref - self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) + make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref)) + self.f.write(b"trailer\n" + bytes(PdfDict(trailer_dict)) + + make_bytes("\nstartxref\n%d\n%%%%EOF" % start_xref)) def write_page(self, ref, *objs, **dict_obj): if isinstance(ref, int): @@ -535,13 +564,18 @@ class PdfParser: else: self.info = PdfDict(self.read_indirect(self.info_ref)) check_format_condition(b"Type" in self.root, "/Type missing in Root") - check_format_condition(self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog") + check_format_condition(self.root[b"Type"] == b"Catalog", + "/Type in Root is not /Catalog") check_format_condition(b"Pages" in self.root, "/Pages missing in Root") - check_format_condition(isinstance(self.root[b"Pages"], IndirectReference), "/Pages in Root is not an indirect reference") + check_format_condition(isinstance(self.root[b"Pages"], + IndirectReference), + "/Pages in Root is not an indirect reference") self.pages_ref = self.root[b"Pages"] self.page_tree_root = self.read_indirect(self.pages_ref) self.pages = self.linearize_page_tree(self.page_tree_root) - # save the original list of page references in case the user modifies, adds or deletes some pages and we need to rewrite the pages and their list + # save the original list of page references + # in case the user modifies, adds or deletes some pages + # and we need to rewrite the pages and their list self.orig_pages = self.pages[:] def next_object_id(self, offset=None): @@ -562,10 +596,14 @@ class PdfParser: whitespace_mandatory = whitespace + b"+" newline_only = br"[\r\n]+" newline = whitespace_optional + newline_only + whitespace_optional - re_trailer_end = re.compile(whitespace_mandatory + br"trailer" + whitespace_optional + br"\<\<(.*\>\>)" + newline - + br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL) - re_trailer_prev = re.compile(whitespace_optional + br"trailer" + whitespace_optional + br"\<\<(.*?\>\>)" + newline - + br"startxref" + newline + br"([0-9]+)" + newline + br"%%EOF" + whitespace_optional, re.DOTALL) + re_trailer_end = re.compile( + whitespace_mandatory + br"trailer" + whitespace_optional + + br"\<\<(.*\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" + + newline + br"%%EOF" + whitespace_optional + br"$", re.DOTALL) + re_trailer_prev = re.compile( + whitespace_optional + br"trailer" + whitespace_optional + + br"\<\<(.*?\>\>)" + newline + br"startxref" + newline + br"([0-9]+)" + + newline + br"%%EOF" + whitespace_optional, re.DOTALL) def read_trailer(self): search_start_offset = len(self.buf) - 16384 @@ -589,19 +627,26 @@ class PdfParser: self.read_prev_trailer(self.trailer_dict[b"Prev"]) def read_prev_trailer(self, xref_section_offset): - trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) - m = self.re_trailer_prev.search(self.buf[trailer_offset:trailer_offset+16384]) + trailer_offset = self.read_xref_table( + xref_section_offset=xref_section_offset) + m = self.re_trailer_prev.search( + self.buf[trailer_offset:trailer_offset+16384]) check_format_condition(m, "previous trailer not found") trailer_data = m.group(1) - check_format_condition(int(m.group(2)) == xref_section_offset, "xref section offset in previous trailer doesn't match what was expected") + check_format_condition(int(m.group(2)) == xref_section_offset, + "xref section offset in previous trailer " + "doesn't match what was expected") trailer_dict = self.interpret_trailer(trailer_data) if b"Prev" in trailer_dict: self.read_prev_trailer(trailer_dict[b"Prev"]) re_whitespace_optional = re.compile(whitespace_optional) - re_name = re.compile(whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + delimiter_or_ws + br")") + re_name = re.compile( + whitespace_optional + br"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" + + delimiter_or_ws + br")") re_dict_start = re.compile(whitespace_optional + br"\<\<") - re_dict_end = re.compile(whitespace_optional + br"\>\>" + whitespace_optional) + re_dict_end = re.compile( + whitespace_optional + br"\>\>" + whitespace_optional) @classmethod def interpret_trailer(cls, trailer_data): @@ -611,13 +656,21 @@ class PdfParser: m = cls.re_name.match(trailer_data, offset) if not m: m = cls.re_dict_end.match(trailer_data, offset) - check_format_condition(m and m.end() == len(trailer_data), "name not found in trailer, remaining data: " + repr(trailer_data[offset:])) + check_format_condition( + m and m.end() == len(trailer_data), + "name not found in trailer, remaining data: " + + repr(trailer_data[offset:])) break key = cls.interpret_name(m.group(1)) value, offset = cls.get_value(trailer_data, m.end()) trailer[key] = value - check_format_condition(b"Size" in trailer and isinstance(trailer[b"Size"], int), "/Size not in trailer or not an integer") - check_format_condition(b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), "/Root not in trailer or not an indirect reference") + check_format_condition( + b"Size" in trailer and isinstance(trailer[b"Size"], int), + "/Size not in trailer or not an integer") + check_format_condition( + b"Root" in trailer and + isinstance(trailer[b"Root"], IndirectReference), + "/Root not in trailer or not an indirect reference") return trailer re_hashes_in_name = re.compile(br"([^#]*)(#([0-9a-fA-F]{2}))?") @@ -627,7 +680,8 @@ class PdfParser: name = b"" for m in cls.re_hashes_in_name.finditer(raw): if m.group(3): - name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) + name += m.group(1) + \ + bytearray.fromhex(m.group(3).decode("us-ascii")) else: name += m.group(1) if as_text: @@ -635,21 +689,37 @@ class PdfParser: else: return bytes(name) - re_null = re.compile(whitespace_optional + br"null(?=" + delimiter_or_ws + br")") - re_true = re.compile(whitespace_optional + br"true(?=" + delimiter_or_ws + br")") - re_false = re.compile(whitespace_optional + br"false(?=" + delimiter_or_ws + br")") - re_int = re.compile(whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")") - re_real = re.compile(whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + delimiter_or_ws + br")") + re_null = re.compile( + whitespace_optional + br"null(?=" + delimiter_or_ws + br")") + re_true = re.compile( + whitespace_optional + br"true(?=" + delimiter_or_ws + br")") + re_false = re.compile( + whitespace_optional + br"false(?=" + delimiter_or_ws + br")") + re_int = re.compile( + whitespace_optional + br"([-+]?[0-9]+)(?=" + delimiter_or_ws + br")") + re_real = re.compile( + whitespace_optional + br"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" + + delimiter_or_ws + br")") re_array_start = re.compile(whitespace_optional + br"\[") re_array_end = re.compile(whitespace_optional + br"]") - re_string_hex = re.compile(whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>") + re_string_hex = re.compile( + whitespace_optional + br"\<(" + whitespace_or_hex + br"*)\>") re_string_lit = re.compile(whitespace_optional + br"\(") - re_indirect_reference = re.compile(whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws + br")") - re_indirect_def_start = re.compile(whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" + delimiter_or_ws + br")") - re_indirect_def_end = re.compile(whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")") - re_comment = re.compile(br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*") + re_indirect_reference = re.compile( + whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + + br"([-+]?[0-9]+)" + whitespace_mandatory + br"R(?=" + delimiter_or_ws + + br")") + re_indirect_def_start = re.compile( + whitespace_optional + br"([-+]?[0-9]+)" + whitespace_mandatory + + br"([-+]?[0-9]+)" + whitespace_mandatory + br"obj(?=" + + delimiter_or_ws + br")") + re_indirect_def_end = re.compile( + whitespace_optional + br"endobj(?=" + delimiter_or_ws + br")") + re_comment = re.compile( + br"(" + whitespace_optional + br"%[^\r\n]*" + newline + br")*") re_stream_start = re.compile(whitespace_optional + br"stream\r?\n") - re_stream_end = re.compile(whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")") + re_stream_end = re.compile( + whitespace_optional + br"endstream(?=" + delimiter_or_ws + br")") @classmethod def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): @@ -660,21 +730,34 @@ class PdfParser: offset = m.end() m = cls.re_indirect_def_start.match(data, offset) if m: - check_format_condition(int(m.group(1)) > 0, "indirect object definition: object ID must be greater than 0") - check_format_condition(int(m.group(2)) >= 0, "indirect object definition: generation must be non-negative") - check_format_condition(expect_indirect is None or expect_indirect == IndirectReference(int(m.group(1)), int(m.group(2))), + check_format_condition( + int(m.group(1)) > 0, + "indirect object definition: object ID must be greater than 0") + check_format_condition( + int(m.group(2)) >= 0, + "indirect object definition: generation must be non-negative") + check_format_condition( + expect_indirect is None or expect_indirect == + IndirectReference(int(m.group(1)), int(m.group(2))), "indirect object definition different than expected") - object, offset = cls.get_value(data, m.end(), max_nesting=max_nesting-1) + object, offset = cls.get_value( + data, m.end(), max_nesting=max_nesting-1) if offset is None: return object, None m = cls.re_indirect_def_end.match(data, offset) - check_format_condition(m, "indirect object definition end not found") + check_format_condition( + m, "indirect object definition end not found") return object, m.end() - check_format_condition(not expect_indirect, "indirect object definition not found") + check_format_condition( + not expect_indirect, "indirect object definition not found") m = cls.re_indirect_reference.match(data, offset) if m: - check_format_condition(int(m.group(1)) > 0, "indirect object reference: object ID must be greater than 0") - check_format_condition(int(m.group(2)) >= 0, "indirect object reference: generation must be non-negative") + check_format_condition( + int(m.group(1)) > 0, + "indirect object reference: object ID must be greater than 0") + check_format_condition( + int(m.group(2)) >= 0, + "indirect object reference: generation must be non-negative") return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() m = cls.re_dict_start.match(data, offset) if m: @@ -682,10 +765,12 @@ class PdfParser: result = {} m = cls.re_dict_end.match(data, offset) while not m: - key, offset = cls.get_value(data, offset, max_nesting=max_nesting-1) + key, offset = cls.get_value( + data, offset, max_nesting=max_nesting-1) if offset is None: return result, None - value, offset = cls.get_value(data, offset, max_nesting=max_nesting-1) + value, offset = cls.get_value( + data, offset, max_nesting=max_nesting-1) result[key] = value if offset is None: return result, None @@ -696,7 +781,9 @@ class PdfParser: try: stream_len = int(result[b"Length"]) except (TypeError, KeyError, ValueError): - raise PdfFormatError("bad or missing Length in stream dict (%r)" % result.get(b"Length", None)) + raise PdfFormatError( + "bad or missing Length in stream dict (%r)" % + result.get(b"Length", None)) stream_data = data[m.end():m.end() + stream_len] m = cls.re_stream_end.match(data, m.end() + stream_len) check_format_condition(m, "stream end not found") @@ -711,7 +798,8 @@ class PdfParser: result = [] m = cls.re_array_end.match(data, offset) while not m: - value, offset = cls.get_value(data, offset, max_nesting=max_nesting-1) + value, offset = cls.get_value( + data, offset, max_nesting=max_nesting-1) result.append(value) if offset is None: return result, None @@ -734,18 +822,25 @@ class PdfParser: return int(m.group(1)), m.end() m = cls.re_real.match(data, offset) if m: - return float(m.group(1)), m.end() # XXX Decimal instead of float??? + # XXX Decimal instead of float??? + return float(m.group(1)), m.end() m = cls.re_string_hex.match(data, offset) if m: - hex_string = bytearray([b for b in m.group(1) if b in b"0123456789abcdefABCDEF"]) # filter out whitespace + # filter out whitespace + hex_string = bytearray([ + b for b in m.group(1) + if b in b"0123456789abcdefABCDEF" + ]) if len(hex_string) % 2 == 1: - hex_string.append(ord(b"0")) # append a 0 if the length is not even - yes, at the end + # append a 0 if the length is not even - yes, at the end + hex_string.append(ord(b"0")) return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() m = cls.re_string_lit.match(data, offset) if m: return cls.get_literal_string(data, m.end()) # return None, offset # fallback (only for debugging) - raise PdfFormatError("unrecognized object: " + repr(data[offset:offset+32])) + raise PdfFormatError( + "unrecognized object: " + repr(data[offset:offset+32])) re_lit_str_token = re.compile(br"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))") escaped_chars = { @@ -792,19 +887,24 @@ class PdfParser: offset = m.end() raise PdfFormatError("unfinished literal string") - re_xref_section_start = re.compile(whitespace_optional + br"xref" + newline) - re_xref_subsection_start = re.compile(whitespace_optional + br"([0-9]+)" + whitespace_mandatory + br"([0-9]+)" + whitespace_optional + newline_only) + re_xref_section_start = re.compile( + whitespace_optional + br"xref" + newline) + re_xref_subsection_start = re.compile( + whitespace_optional + br"([0-9]+)" + whitespace_mandatory + + br"([0-9]+)" + whitespace_optional + newline_only) re_xref_entry = re.compile(br"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") def read_xref_table(self, xref_section_offset): subsection_found = False - m = self.re_xref_section_start.match(self.buf, xref_section_offset + self.start_offset) + m = self.re_xref_section_start.match( + self.buf, xref_section_offset + self.start_offset) check_format_condition(m, "xref section start not found") offset = m.end() while True: m = self.re_xref_subsection_start.match(self.buf, offset) if not m: - check_format_condition(subsection_found, "xref subsection start not found") + check_format_condition( + subsection_found, "xref subsection start not found") break subsection_found = True offset = m.end() @@ -818,22 +918,31 @@ class PdfParser: generation = int(m.group(2)) if not is_free: new_entry = (int(m.group(1)), generation) - check_format_condition(i not in self.xref_table or self.xref_table[i] == new_entry, "xref entry duplicated (and not identical)") + check_format_condition( + i not in self.xref_table or + self.xref_table[i] == new_entry, + "xref entry duplicated (and not identical)") self.xref_table[i] = new_entry return offset def read_indirect(self, ref, max_nesting=-1): offset, generation = self.xref_table[ref[0]] - check_format_condition(generation == ref[1], "expected to find generation %s for object ID %s in xref table, instead found generation %s at offset %s" + check_format_condition( + generation == ref[1], + "expected to find generation %s for object ID %s in xref table, " + "instead found generation %s at offset %s" % (ref[1], ref[0], generation, offset)) - value = self.get_value(self.buf, offset + self.start_offset, expect_indirect=IndirectReference(*ref), max_nesting=max_nesting)[0] + value = self.get_value(self.buf, offset + self.start_offset, + expect_indirect=IndirectReference(*ref), + max_nesting=max_nesting)[0] self.cached_objects[ref] = value return value def linearize_page_tree(self, node=None): if node is None: node = self.page_tree_root - check_format_condition(node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages") + check_format_condition( + node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages") pages = [] for kid in node[b"Kids"]: kid_object = self.read_indirect(kid) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 826061990..4f1f0f9cf 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -142,7 +142,8 @@ class ChunkStream(object): def crc(self, cid, data): "Read and verify checksum" - # Skip CRC checks for ancillary chunks if allowed to load truncated images + # Skip CRC checks for ancillary chunks if allowed to load truncated + # images # 5th byte of first char is 1 [specs, section 5.4] if ImageFile.LOAD_TRUNCATED_IMAGES and (i8(cid[0]) >> 5 & 1): self.crc_skip(cid, data) @@ -301,8 +302,8 @@ class PngStream(ChunkStream): def check_text_memory(self, chunklen): self.text_memory += chunklen if self.text_memory > MAX_TEXT_MEMORY: - raise ValueError("Too much memory used in text chunks: %s>MAX_TEXT_MEMORY" % - self.text_memory) + raise ValueError("Too much memory used in text chunks: " + "%s>MAX_TEXT_MEMORY" % self.text_memory) def chunk_iCCP(self, pos, length): diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index c599ba8d5..9866c9040 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -83,7 +83,8 @@ class PpmImageFile(ImageFile.ImageFile): if s not in b_whitespace: break if s == b"": - raise ValueError("File does not extend beyond magic number") + raise ValueError( + "File does not extend beyond magic number") if s != b"#": break s = self.fp.readline() diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index ef0f40ebd..113d44fac 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -222,6 +222,7 @@ Image.register_save(SgiImageFile.format, _save) Image.register_mime(SgiImageFile.format, "image/sgi") Image.register_mime(SgiImageFile.format, "image/rgb") -Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) +Image.register_extensions(SgiImageFile.format, + [".bw", ".rgb", ".rgba", ".sgi"]) # End of file diff --git a/src/PIL/SunImagePlugin.py b/src/PIL/SunImagePlugin.py index 03b266bc4..3126bd9d6 100644 --- a/src/PIL/SunImagePlugin.py +++ b/src/PIL/SunImagePlugin.py @@ -94,7 +94,8 @@ class SunImageFile(ImageFile.ImageFile): raise SyntaxError("Unsupported Palette Type") offset = offset + palette_length - self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) + self.palette = ImagePalette.raw("RGB;L", + self.fp.read(palette_length)) if self.mode == "L": self.mode = "P" rawmode = rawmode.replace('L', 'P') diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 14b66a1b9..94a028afa 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -431,7 +431,8 @@ class ImageFileDirectory_v2(MutableMapping): * self.tagtype = {} * Key: numerical tiff tag number - * Value: integer corresponding to the data type from `~PIL.TiffTags.TYPES` + * Value: integer corresponding to the data type from + ~PIL.TiffTags.TYPES` .. versionadded:: 3.0.0 """ diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 427f3a489..ef19ee66e 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -23,7 +23,8 @@ from collections import namedtuple class TagInfo(namedtuple("_TagInfo", "value name type length enum")): __slots__ = [] - def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): + def __new__(cls, value=None, name="unknown", + type=None, length=None, enum=None): return super(TagInfo, cls).__new__( cls, value, name, type, length, enum or {}) @@ -72,8 +73,8 @@ TAGS_V2 = { 257: ("ImageLength", LONG, 1), 258: ("BitsPerSample", SHORT, 0), 259: ("Compression", SHORT, 1, - {"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3, "Group 4 Fax": 4, - "LZW": 5, "JPEG": 6, "PackBits": 32773}), + {"Uncompressed": 1, "CCITT 1d": 2, "Group 3 Fax": 3, + "Group 4 Fax": 4, "LZW": 5, "JPEG": 6, "PackBits": 32773}), 262: ("PhotometricInterpretation", SHORT, 1, {"WhiteIsZero": 0, "BlackIsZero": 1, "RGB": 2, "RGB Palette": 3, diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 213584497..aeb19374d 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -22,7 +22,8 @@ from __future__ import print_function from . import Image, ImageFile -from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long +from ._binary import i16le as word, si16le as short, \ + i32le as dword, si32le as _long from ._util import py3 From 2630054266a3c2f069c6b1d8a4ce93dc777cc78b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 2 Jul 2018 19:26:07 +1000 Subject: [PATCH 24/87] Removed unused import --- src/PIL/TiffImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 94a028afa..66b211cbf 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -45,7 +45,6 @@ from . import Image, ImageFile, ImagePalette, TiffTags from ._binary import i8, o8 from ._util import py3 -import collections from fractions import Fraction from numbers import Number, Rational From 4407cb65079a7d1150277e3b9a144996f56357c9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 2 Jul 2018 22:41:16 +0300 Subject: [PATCH 25/87] Update CHANGES.rst --- CHANGES.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0c31eb3c7..a6018ffb6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,21 @@ Changelog (Pillow) ================== +5.3.0 (unreleased) +------------------ + +- Tests: Add LA to TGA test modes #3222 + [danpla] + +- Skip outline if the draw operation fills with the same colour #2922 + [radarhere] + +- Flake8 fixes #3173 + [radarhere] + +- Avoid deprecated 'U' mode when opening files #2187 + [jdufresne] + 5.2.0 (2018-07-01) ------------------ From 3b74a4c8ce112251fca255da345040931dd4c00f Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 4 Jul 2018 11:55:58 +0300 Subject: [PATCH 26/87] Test ImageChops --- Tests/test_imagechops.py | 204 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 4e30dc175..ad8a43a44 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -3,6 +3,16 @@ from helper import unittest, PillowTestCase, hopper from PIL import Image from PIL import ImageChops +BLACK = (0, 0, 0) +BROWN = (127, 64, 0) +CYAN = (0, 255, 255) +DARK_GREEN = (0, 128, 0) +GREEN = (0, 255, 0) +ORANGE = (255, 128, 0) +WHITE = (255, 255, 255) + +GREY = 128 + class TestImageChops(PillowTestCase): @@ -35,6 +45,200 @@ class TestImageChops(PillowTestCase): ImageChops.offset(im, 10) ImageChops.offset(im, 10, 20) + def test_add(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") + im2 = Image.open("Tests/images/imagedraw_floodfill.png") + + # Act + new = ImageChops.add(im1, im2) + + # Assert + self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + self.assertEqual(new.getpixel((50, 50)), ORANGE) + + def test_add_modulo(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") + im2 = Image.open("Tests/images/imagedraw_floodfill.png") + + # Act + new = ImageChops.add_modulo(im1, im2) + + # Assert + self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + self.assertEqual(new.getpixel((50, 50)), ORANGE) + + def test_blend(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") + im2 = Image.open("Tests/images/imagedraw_floodfill.png") + + # Act + new = ImageChops.blend(im1, im2, 0.5) + + # Assert + self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + self.assertEqual(new.getpixel((50, 50)), BROWN) + + def test_constant(self): + # Arrange + im = Image.new("RGB", (20, 10)) + + # Act + new = ImageChops.constant(im, GREY) + + # Assert + self.assertEqual(new.size, im.size) + self.assertEqual(new.getpixel((0, 0)), GREY) + self.assertEqual(new.getpixel((19, 9)), GREY) + + def test_darker(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") + im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + + # Act + new = ImageChops.darker(im1, im2) + + # Assert + self.assert_image_equal(new, im2) + + def test_difference(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_arc_end_le_start.png") + im2 = Image.open("Tests/images/imagedraw_arc_no_loops.png") + + # Act + new = ImageChops.difference(im1, im2) + + # Assert + self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + + def test_duplicate(self): + # Arrange + im = hopper() + + # Act + new = ImageChops.duplicate(im) + + # Assert + self.assert_image_equal(new, im) + + def test_invert(self): + # Arrange + im = Image.open("Tests/images/imagedraw_floodfill.png") + + # Act + new = ImageChops.invert(im) + + # Assert + self.assertEqual(new.getbbox(), (0, 0, 100, 100)) + self.assertEqual(new.getpixel((0, 0)), WHITE) + self.assertEqual(new.getpixel((50, 50)), CYAN) + + def test_lighter(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") + im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + + # Act + new = ImageChops.darker(im1, im2) + + # Assert + self.assert_image_equal(new, im2) + + def test_multiply_black(self): + """If you multiply an image with a solid black image, + the result is black.""" + # Arrange + im1 = hopper() + black = Image.new("RGB", im1.size, "black") + + # Act + new = ImageChops.multiply(im1, black) + + # Assert + self.assert_image_equal(new, black) + + def test_multiply_green(self): + # Arrange + im = Image.open("Tests/images/imagedraw_floodfill.png") + green = Image.new("RGB", im.size, "green") + + # Act + new = ImageChops.multiply(im, green) + + # Assert + self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + self.assertEqual(new.getpixel((25, 25)), DARK_GREEN) + self.assertEqual(new.getpixel((50, 50)), BLACK) + + def test_multiply_white(self): + """If you multiply with a solid white image, + the image is unaffected.""" + # Arrange + im1 = hopper() + white = Image.new("RGB", im1.size, "white") + + # Act + new = ImageChops.multiply(im1, white) + + # Assert + self.assert_image_equal(new, im1) + + def test_offset(self): + # Arrange + im = Image.open("Tests/images/imagedraw_ellipse_RGB.png") + xoffset = 45 + yoffset = 20 + + # Act + new = ImageChops.offset(im, xoffset, yoffset) + + # Assert + self.assertEqual(new.getbbox(), (0, 45, 100, 96)) + self.assertEqual(new.getpixel((50, 50)), BLACK) + self.assertEqual(new.getpixel((50+xoffset, 50+yoffset)), DARK_GREEN) + + def test_screen(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") + im2 = Image.open("Tests/images/imagedraw_floodfill.png") + + # Act + new = ImageChops.screen(im1, im2) + + # Assert + self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + self.assertEqual(new.getpixel((50, 50)), ORANGE) + + def test_subtract(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") + im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + + # Act + new = ImageChops.subtract(im1, im2) + + # Assert + self.assertEqual(new.getbbox(), (25, 50, 76, 76)) + self.assertEqual(new.getpixel((50, 50)), GREEN) + self.assertEqual(new.getpixel((50, 51)), BLACK) + + def test_subtract_modulo(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") + im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + + # Act + new = ImageChops.subtract_modulo(im1, im2) + + # Assert + self.assertEqual(new.getbbox(), (25, 50, 76, 76)) + self.assertEqual(new.getpixel((50, 50)), GREEN) + self.assertEqual(new.getpixel((50, 51)), BLACK) + def test_logical(self): def table(op, a, b): From b565f45d7787bcd0823c6885e3f5cf17d7db770c Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Sun, 1 Jul 2018 12:50:33 +0200 Subject: [PATCH 27/87] avoid invalid free if out of memory The surrounding code suggests this should only be freed if it was allocated locally. --- src/_imaging.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/_imaging.c b/src/_imaging.c index 5004becee..0cb2f56b9 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -448,7 +448,7 @@ float16tofloat32(const FLOAT16 in) { t1 = in & 0x7fff; // Non-sign bits t2 = in & 0x8000; // Sign bit t3 = in & 0x7c00; // Exponent - + t1 <<= 13; // Align mantissa on MSB t2 <<= 16; // Shift sign bit into position @@ -778,7 +778,8 @@ _prepare_lut_table(PyObject* table, Py_ssize_t table_size) /* malloc check ok, max is 2 * 4 * 65**3 = 2197000 */ prepared = (INT16*) malloc(sizeof(INT16) * table_size); if ( ! prepared) { - free(table_data); + if (free_table_data) + free(table_data); return (INT16*) PyErr_NoMemory(); } From 937443f2a64564335b6e7439160742bc5939fd0c Mon Sep 17 00:00:00 2001 From: Alex Clark Date: Thu, 5 Jul 2018 18:27:01 -0400 Subject: [PATCH 28/87] =?UTF-8?q?Update=20examples=20=E2=80=A6=20[ci=20ski?= =?UTF-8?q?p]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update examples and remove "hide old releases" step, N/A on pypi.org. --- RELEASING.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 6541d69d5..77c866cd4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,7 +4,7 @@ Released quarterly on the first day of January, April, July, October. -* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174 +* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154 * [ ] Develop and prepare release in ``master`` branch. * [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch. * [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in TravisCI. @@ -13,8 +13,8 @@ Released quarterly on the first day of January, April, July, October. * [ ] Run pre-release check via `make release-test` in a freshly cloned repo. * [ ] Create branch and tag for release e.g.: ``` - $ git branch 2.9.x - $ git tag 2.9.0 + $ git branch 5.2.x + $ git tag 5.2.0 $ git push --all $ git push --tags ``` @@ -23,8 +23,7 @@ Released quarterly on the first day of January, April, July, October. $ make sdist ``` * [ ] Create [binary distributions](#binary-distributions) -* [ ] Upload all binaries and source distributions with ``twine upload dist/Pillow-4.1.0-*`` -* [ ] Manually hide old versions on PyPI such that only the latest major release is visible when viewing https://pypi.org/project/Pillow/ (https://pypi.org/manage/project/Pillow/releases/) +* [ ] Upload all binaries and source distributions e.g. ``twine upload dist/Pillow-5.2.0-*`` ## Point Release @@ -32,17 +31,17 @@ Released as needed for security, installation or critical bug fixes. * [ ] Make necessary changes in ``master`` branch. * [ ] Update `CHANGES.rst`. -* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``. -* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``. +* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``5.2.x``. +* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``5.2.x``. * [ ] Checkout release branch e.g.: ``` - git checkout -t remotes/origin/2.9.x + git checkout -t remotes/origin/5.2.x ``` * [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `src/PIL/_version.py` * [ ] Run pre-release check via `make release-test`. * [ ] Create tag for release e.g.: ``` - $ git tag 2.9.1 + $ git tag 5.2.1 $ git push --tags ``` * [ ] Create source distributions e.g.: @@ -99,8 +98,8 @@ Released as needed privately to individual vendors for critical security-related ## Publicize Release -* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/aclark4life/status/583366798302691328. +* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010 ## Documentation -* [ ] Make sure the default version for Read the Docs is the latest release version, e.g. ``3.1.x`` rather than ``latest``: https://readthedocs.org/projects/pillow/versions/ +* [ ] Make sure the default version for Read the Docs is the latest release version, i.e. ``5.2.0`` rather than ``latest`` e.g. https://pillow.readthedocs.io/en/5.2.x/ From 63d8637bb8cbd90ce0372a221b2acdae505b053c Mon Sep 17 00:00:00 2001 From: tsennott Date: Fri, 6 Jul 2018 18:18:06 -0700 Subject: [PATCH 29/87] adding three-color feature to ImageOps.colorize --- src/PIL/ImageOps.py | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 25d491aff..33196d5ed 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -136,28 +136,50 @@ def autocontrast(image, cutoff=0, ignore=None): return _lut(image, lut) -def colorize(image, black, white): +def colorize(image, black, white, mid=None, midpoint=128): """ - Colorize grayscale image. The **black** and **white** - arguments should be RGB tuples; this function calculates a color - wedge mapping all black pixels in the source image to the first - color, and all white pixels to the second color. + Colorize grayscale image. + This function calculates a color wedge mapping all + black pixels in the source image to the first + color, and all white pixels to the second color. If + mid is specified, it uses three color mapping. + The **black** and **white** + arguments should be RGB tuples; optionally you can use + three color mapping by also specifying **mid**, and + optionally, **midpoint** (which is the integer value + in [0, 255] corresponding to the midpoint color, + default 128). :param image: The image to colorize. :param black: The color to use for black input pixels. :param white: The color to use for white input pixels. + :param mid: The color to use for midtone input pixels. + :param midpoint: the int value [0, 255] for the mid color. :return: An image. """ assert image.mode == "L" black = _color(black, "RGB") + if mid is not None: + mid = _color(mid, "RGB") white = _color(white, "RGB") red = [] green = [] blue = [] - for i in range(256): - red.append(black[0]+i*(white[0]-black[0])//255) - green.append(black[1]+i*(white[1]-black[1])//255) - blue.append(black[2]+i*(white[2]-black[2])//255) + if mid is None: + for i in range(256): + red.append(black[0] + i * (white[0] - black[0]) // 255) + green.append(black[1] + i * (white[1] - black[1]) // 255) + blue.append(black[2] + i * (white[2] - black[2]) // 255) + else: + for i in range(0, midpoint): + red.append(black[0] + i * (mid[0] - black[0]) // 255) + green.append(black[1] + i * (mid[1] - black[1]) // 255) + blue.append(black[2] + i * (mid[2] - black[2]) // 255) + for i in range(0, 256 - midpoint): + red.append(mid[0] + i * (white[0] - mid[0]) // 255) + green.append(mid[1] + i * (white[1] - mid[1]) // 255) + blue.append(mid[2] + i * (white[2] - mid[2]) // 255) + image = image.convert("RGB") return _lut(image, red + green + blue) From adf570a77ea802d86a59199a165aef6c053b852b Mon Sep 17 00:00:00 2001 From: tsennott Date: Fri, 6 Jul 2018 18:42:16 -0700 Subject: [PATCH 30/87] adding tests, updated docstring and comments --- Tests/test_imageops.py | 16 ++++++++++++++++ src/PIL/ImageOps.py | 8 ++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 11cf3619d..63ec0d2e4 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -94,6 +94,22 @@ class TestImageOps(PillowTestCase): newimg = ImageOps.scale(i, 0.5) self.assertEqual(newimg.size, (25, 25)) + def test_colorize(self): + # Test the colorizing function + + # Grab test image + i = hopper("L").resize((15, 16)) + + # Test original 2-color functionality + ImageOps.colorize(i, 'green', 'red') + + # Test new three color functionality (cyanotype colors) + ImageOps.colorize(i, + (32, 37, 79), + (255, 255, 255), + (35, 52, 121), + 40) + if __name__ == '__main__': unittest.main() diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 33196d5ed..29dec124d 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -147,8 +147,8 @@ def colorize(image, black, white, mid=None, midpoint=128): arguments should be RGB tuples; optionally you can use three color mapping by also specifying **mid**, and optionally, **midpoint** (which is the integer value - in [0, 255] corresponding to the midpoint color, - default 128). + in [1, 254] corresponding to where the midpoint color + should be mapped (0 being black and 255 being white). :param image: The image to colorize. :param black: The color to use for black input pixels. @@ -158,10 +158,14 @@ def colorize(image, black, white, mid=None, midpoint=128): :return: An image. """ assert image.mode == "L" + + # Define colors from arguments black = _color(black, "RGB") if mid is not None: mid = _color(mid, "RGB") white = _color(white, "RGB") + + # Create the mapping red = [] green = [] blue = [] From 3c6fd275c8965d85ee89dfff608420e638feec21 Mon Sep 17 00:00:00 2001 From: tsennott Date: Fri, 6 Jul 2018 19:09:57 -0700 Subject: [PATCH 31/87] added assert for midpoint range --- src/PIL/ImageOps.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 29dec124d..133f9a8b8 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -154,10 +154,11 @@ def colorize(image, black, white, mid=None, midpoint=128): :param black: The color to use for black input pixels. :param white: The color to use for white input pixels. :param mid: The color to use for midtone input pixels. - :param midpoint: the int value [0, 255] for the mid color. + :param midpoint: the int value in [1, 254] for the mid color. :return: An image. """ assert image.mode == "L" + assert 1 <= midpoint <= 254 # Define colors from arguments black = _color(black, "RGB") From b19c460568675268145ce9f8ea2cfb42c357a5f0 Mon Sep 17 00:00:00 2001 From: tsennott Date: Fri, 6 Jul 2018 19:49:07 -0700 Subject: [PATCH 32/87] fixed mapping function, now smooth --- src/PIL/ImageOps.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 133f9a8b8..e5ce71060 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -136,7 +136,7 @@ def autocontrast(image, cutoff=0, ignore=None): return _lut(image, lut) -def colorize(image, black, white, mid=None, midpoint=128): +def colorize(image, black, white, mid=None, midpoint=127): """ Colorize grayscale image. This function calculates a color wedge mapping all @@ -176,14 +176,16 @@ def colorize(image, black, white, mid=None, midpoint=128): green.append(black[1] + i * (white[1] - black[1]) // 255) blue.append(black[2] + i * (white[2] - black[2]) // 255) else: - for i in range(0, midpoint): - red.append(black[0] + i * (mid[0] - black[0]) // 255) - green.append(black[1] + i * (mid[1] - black[1]) // 255) - blue.append(black[2] + i * (mid[2] - black[2]) // 255) - for i in range(0, 256 - midpoint): - red.append(mid[0] + i * (white[0] - mid[0]) // 255) - green.append(mid[1] + i * (white[1] - mid[1]) // 255) - blue.append(mid[2] + i * (white[2] - mid[2]) // 255) + range1 = range(0, midpoint) + range2 = range(0, 256 - midpoint) + for i in range1: + red.append(black[0] + i * (mid[0] - black[0]) // len(range1)) + green.append(black[1] + i * (mid[1] - black[1]) // len(range1)) + blue.append(black[2] + i * (mid[2] - black[2]) // len(range1)) + for i in range2: + red.append(mid[0] + i * (white[0] - mid[0]) // len(range2)) + green.append(mid[1] + i * (white[1] - mid[1]) // len(range2)) + blue.append(mid[2] + i * (white[2] - mid[2]) // len(range2)) image = image.convert("RGB") return _lut(image, red + green + blue) From 837d8683332a14cdd5234cfebe026235417d1a6a Mon Sep 17 00:00:00 2001 From: tsennott Date: Sat, 7 Jul 2018 02:40:25 -0700 Subject: [PATCH 33/87] updated test to assert equality with reference images --- Tests/images/bw_gradient.png | Bin 0 -> 1926 bytes Tests/images/bw_gradient_2color.png | Bin 0 -> 179 bytes Tests/images/bw_gradient_3color.png | Bin 0 -> 246 bytes Tests/test_imageops.py | 24 +++++++++++++++--------- 4 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 Tests/images/bw_gradient.png create mode 100644 Tests/images/bw_gradient_2color.png create mode 100644 Tests/images/bw_gradient_3color.png diff --git a/Tests/images/bw_gradient.png b/Tests/images/bw_gradient.png new file mode 100644 index 0000000000000000000000000000000000000000..0b7275dc19cb188d0b1c65334593fe5adc948476 GIT binary patch literal 1926 zcmb_dU5MO798WEN9aa&2Qjm~?Qqf6fl592^ZauEqv%AvVo!g~7-#R-pyBlwlNt4{| z-aUx=B2^#zAmWqagCHXKpwb76S_Sb%DiqN-K@cp0FQS4t*^gN3T|Ew$-RxxM_y79+ ze=~D_ZRNy~xd-MHMLE)J)LV*j=zck#yWM?EcF?C^trGtp)L>)zgIK!iwHuj6_M*$cAdw(~$%13VKvEA*vd%Vn7E#grSQ~q)tDYjFz%4Zq*;3#*%L}t)J%!HjLqL zs1Ge&q&)+9o@YSQFijv4Alr^}G6HcnKl4!M8BN0^4@ImN9!W=R<~2=bIw>JaO1E(~ zrAZQIj7VZ29Tp{xfsB^8WHTL%gEMOI0grf`XA+A_tYj$&O8BmBqo_n@`SO;`Xj`_O#Tvn4{KjkDBX&+J#zP;MhK zIJlp(P1qL$kp_ZEmMu;Fd%o%WYpLjl1G$j3P8?U8%f5p=#{s5}3bl__hZ|ugTf05o zKd1P)>1YtDW1(R}i@i|OXdwbFDVR)$yHL143MTf$K9^2!qKgSE6Wx4BQohuabnj1m z#$FXk@OR65VY&xrdxBM$6uE3d851jv$VA>~f zkIN(0&< zi|41OKRzult$wq<)b2lb?Fzd4l6sf_)zM!U^Q&`>cTaxv=Y!g7Z@UjI)!XmA_{kp^ zzx%BF+e=@5z4~o%-{mXU-hbxRH$HfB<%J8Yt;;{G>fggh-nwx6rI&wEj(&0Ym1Fmu ieSP8C^Vh%pcAY} zZ+$AZ@2z(&tK_?Kool@2KC}~K_+Z|`uE6<_ l(Zi{NVUmDcg9;iq{@%pDQw3)GKLC1!!PC{xWt~$(698i!XOaK_ literal 0 HcmV?d00001 diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 63ec0d2e4..c63e75574 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,6 +1,7 @@ from helper import unittest, PillowTestCase, hopper from PIL import ImageOps +from PIL import Image class TestImageOps(PillowTestCase): @@ -97,18 +98,23 @@ class TestImageOps(PillowTestCase): def test_colorize(self): # Test the colorizing function - # Grab test image - i = hopper("L").resize((15, 16)) + # Grab test image (10px black, 256px gradient, 10px white) + im = Image.open("Tests/images/bw_gradient.png") + im = im.convert("L") # Test original 2-color functionality - ImageOps.colorize(i, 'green', 'red') + out_2color = ImageOps.colorize(im, 'red', 'green') - # Test new three color functionality (cyanotype colors) - ImageOps.colorize(i, - (32, 37, 79), - (255, 255, 255), - (35, 52, 121), - 40) + # Test new three color functionality, with midpoint offset + out_3color = ImageOps.colorize(im, 'red', 'green', 'yellow', 100) + + # Assert 2-color + ref_2color = Image.open("Tests/images/bw_gradient_2color.png") + self.assert_image_equal(out_2color, ref_2color) + + # Assert 3-color + ref_3color = Image.open("Tests/images/bw_gradient_3color.png") + self.assert_image_equal(out_3color, ref_3color) if __name__ == '__main__': From 4a6ec5ca72ed832578aeff7b80a5254dc6214801 Mon Sep 17 00:00:00 2001 From: tsennott Date: Sat, 7 Jul 2018 18:19:26 -0700 Subject: [PATCH 34/87] updated colorize to allow optional black/white positions; enhanced tests --- Tests/images/bw_gradient.png | Bin 1926 -> 102 bytes Tests/images/bw_gradient_2color.png | Bin 179 -> 0 bytes Tests/images/bw_gradient_3color.png | Bin 246 -> 0 bytes Tests/test_imageops.py | 84 +++++++++++++++++++--- src/PIL/ImageOps.py | 106 ++++++++++++++++++++-------- 5 files changed, 148 insertions(+), 42 deletions(-) delete mode 100644 Tests/images/bw_gradient_2color.png delete mode 100644 Tests/images/bw_gradient_3color.png diff --git a/Tests/images/bw_gradient.png b/Tests/images/bw_gradient.png index 0b7275dc19cb188d0b1c65334593fe5adc948476..79c921486f8dc91f7c6f6bab36ba8ba302ed9b6f 100644 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5xHv#$-PijWKuXus#WAE}&f5!)f(#5i%^S-8 px%#v93$e=hZjU`?*}%5|8QZ?gVHD1NS6l`%z|+;wWt~$(69Dsx9MAv& literal 1926 zcmb_dU5MO798WEN9aa&2Qjm~?Qqf6fl592^ZauEqv%AvVo!g~7-#R-pyBlwlNt4{| z-aUx=B2^#zAmWqagCHXKpwb76S_Sb%DiqN-K@cp0FQS4t*^gN3T|Ew$-RxxM_y79+ ze=~D_ZRNy~xd-MHMLE)J)LV*j=zck#yWM?EcF?C^trGtp)L>)zgIK!iwHuj6_M*$cAdw(~$%13VKvEA*vd%Vn7E#grSQ~q)tDYjFz%4Zq*;3#*%L}t)J%!HjLqL zs1Ge&q&)+9o@YSQFijv4Alr^}G6HcnKl4!M8BN0^4@ImN9!W=R<~2=bIw>JaO1E(~ zrAZQIj7VZ29Tp{xfsB^8WHTL%gEMOI0grf`XA+A_tYj$&O8BmBqo_n@`SO;`Xj`_O#Tvn4{KjkDBX&+J#zP;MhK zIJlp(P1qL$kp_ZEmMu;Fd%o%WYpLjl1G$j3P8?U8%f5p=#{s5}3bl__hZ|ugTf05o zKd1P)>1YtDW1(R}i@i|OXdwbFDVR)$yHL143MTf$K9^2!qKgSE6Wx4BQohuabnj1m z#$FXk@OR65VY&xrdxBM$6uE3d851jv$VA>~f zkIN(0&< zi|41OKRzult$wq<)b2lb?Fzd4l6sf_)zM!U^Q&`>cTaxv=Y!g7Z@UjI)!XmA_{kp^ zzx%BF+e=@5z4~o%-{mXU-hbxRH$HfB<%J8Yt;;{G>fggh-nwx6rI&wEj(&0Ym1Fmu ieSP8C^Vh%pcAY} zZ+$AZ@2z(&tK_?Kool@2KC}~K_+Z|`uE6<_ l(Zi{NVUmDcg9;iq{@%pDQw3)GKLC1!!PC{xWt~$(698i!XOaK_ diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index c63e75574..d1be04046 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -98,23 +98,85 @@ class TestImageOps(PillowTestCase): def test_colorize(self): # Test the colorizing function - # Grab test image (10px black, 256px gradient, 10px white) + # Open test image (256px by 10px, black to white) im = Image.open("Tests/images/bw_gradient.png") im = im.convert("L") - # Test original 2-color functionality - out_2color = ImageOps.colorize(im, 'red', 'green') + # Create image with original 2-color functionality + im_2c = ImageOps.colorize(im, 'red', 'green') - # Test new three color functionality, with midpoint offset - out_3color = ImageOps.colorize(im, 'red', 'green', 'yellow', 100) + # Create image with original 2-color functionality with offsets + im_2c_offset = ImageOps.colorize(im, + black='red', + white='green', + blackpoint=50, + whitepoint=200) - # Assert 2-color - ref_2color = Image.open("Tests/images/bw_gradient_2color.png") - self.assert_image_equal(out_2color, ref_2color) + # Create image with new three color functionality with offsets + im_3c_offset = ImageOps.colorize(im, + black='red', + white='green', + mid='blue', + blackpoint=50, + whitepoint=200, + midpoint=100) - # Assert 3-color - ref_3color = Image.open("Tests/images/bw_gradient_3color.png") - self.assert_image_equal(out_3color, ref_3color) + # Define function for approximate equality of tuples + def tuple_approx_equal(actual, target, thresh): + value = True + for i, target in enumerate(target): + value *= (target - thresh <= actual[i] <= target + thresh) + return value + + # Test output image (2-color) + left = (0, 1) + middle = (127, 1) + right = (255, 1) + self.assertTrue(tuple_approx_equal(im_2c.getpixel(left), + (255, 0, 0), thresh=1), + '2-color image black incorrect') + self.assertTrue(tuple_approx_equal(im_2c.getpixel(middle), + (127, 63, 0), thresh=1), + '2-color image mid incorrect') + self.assertTrue(tuple_approx_equal(im_2c.getpixel(right), + (0, 127, 0), thresh=1), + '2-color image white incorrect') + + # Test output image (2-color) with offsets + left = (25, 1) + middle = (125, 1) + right = (225, 1) + self.assertTrue(tuple_approx_equal(im_2c_offset.getpixel(left), + (255, 0, 0), thresh=1), + '2-color image (with offset) black incorrect') + self.assertTrue(tuple_approx_equal(im_2c_offset.getpixel(middle), + (127, 63, 0), thresh=1), + '2-color image (with offset) mid incorrect') + self.assertTrue(tuple_approx_equal(im_2c_offset.getpixel(right), + (0, 127, 0), thresh=1), + '2-color image (with offset) white incorrect') + + # Test output image (3-color) with offsets + left = (25, 1) + left_middle = (75, 1) + middle = (100, 1) + right_middle = (150, 1) + right = (225, 1) + self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(left), + (255, 0, 0), thresh=1), + '3-color image (with offset) black incorrect') + self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(left_middle), + (127, 0, 127), thresh=1), + '3-color image (with offset) low-mid incorrect') + self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(middle), + (0, 0, 255), thresh=1), + '3-color image (with offset) mid incorrect') + self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(right_middle), + (0, 63, 127), thresh=1), + '3-color image (with offset) high-mid incorrect') + self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(right), + (0, 127, 0), thresh=1), + '3-color image (with offset) white incorrect') if __name__ == '__main__': diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index e5ce71060..4e6baf8c9 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -136,57 +136,101 @@ def autocontrast(image, cutoff=0, ignore=None): return _lut(image, lut) -def colorize(image, black, white, mid=None, midpoint=127): +def colorize(image, black, white, mid=None, blackpoint=0, + whitepoint=255, midpoint=127): """ Colorize grayscale image. - This function calculates a color wedge mapping all - black pixels in the source image to the first - color, and all white pixels to the second color. If - mid is specified, it uses three color mapping. - The **black** and **white** - arguments should be RGB tuples; optionally you can use - three color mapping by also specifying **mid**, and - optionally, **midpoint** (which is the integer value - in [1, 254] corresponding to where the midpoint color - should be mapped (0 being black and 255 being white). + This function calculates a color wedge which maps all black pixels in + the source image to the first color and all white pixels to the + second color. If **mid** is specified, it uses three-color mapping. + The **black** and **white** arguments should be RGB tuples or color names; + optionally you can use three-color mapping by also specifying **mid**. + Mapping positions for any of the colors can be specified + (e.g. **blackpoint**), where these parameters are the integer + value in [0, 255] corresponding to where the corresponding color + should be mapped. :param image: The image to colorize. :param black: The color to use for black input pixels. :param white: The color to use for white input pixels. :param mid: The color to use for midtone input pixels. - :param midpoint: the int value in [1, 254] for the mid color. + :param blackpoint: an int value [0, 255] for the black mapping. + :param whitepoint: an int value [0, 255] for the white mapping. + :param midpoint: an int value [0, 255] for the midtone mapping. :return: An image. """ + + # Initial asserts assert image.mode == "L" - assert 1 <= midpoint <= 254 + assert 0 <= whitepoint <= 255 + assert 0 <= blackpoint <= 255 + assert 0 <= midpoint <= 255 + assert blackpoint <= whitepoint + if mid is not None: + assert blackpoint <= midpoint + assert whitepoint >= midpoint # Define colors from arguments black = _color(black, "RGB") + white = _color(white, "RGB") if mid is not None: mid = _color(mid, "RGB") - white = _color(white, "RGB") - # Create the mapping + # Empty lists for the mapping red = [] green = [] blue = [] - if mid is None: - for i in range(256): - red.append(black[0] + i * (white[0] - black[0]) // 255) - green.append(black[1] + i * (white[1] - black[1]) // 255) - blue.append(black[2] + i * (white[2] - black[2]) // 255) - else: - range1 = range(0, midpoint) - range2 = range(0, 256 - midpoint) - for i in range1: - red.append(black[0] + i * (mid[0] - black[0]) // len(range1)) - green.append(black[1] + i * (mid[1] - black[1]) // len(range1)) - blue.append(black[2] + i * (mid[2] - black[2]) // len(range1)) - for i in range2: - red.append(mid[0] + i * (white[0] - mid[0]) // len(range2)) - green.append(mid[1] + i * (white[1] - mid[1]) // len(range2)) - blue.append(mid[2] + i * (white[2] - mid[2]) // len(range2)) + # Create the mapping (2-color) + if mid is None: + + # Define ranges + range_low = range(0, blackpoint) + range_map = range(0, whitepoint - blackpoint) + range_high = range(0, 256 - whitepoint) + + # Map + for i in range_low: + red.append(black[0]) + green.append(black[1]) + blue.append(black[2]) + for i in range_map: + red.append(black[0] + i * (white[0] - black[0]) // len(range_map)) + green.append(black[1] + i * (white[1] - black[1]) // len(range_map)) + blue.append(black[2] + i * (white[2] - black[2]) // len(range_map)) + for i in range_high: + red.append(white[0]) + green.append(white[1]) + blue.append(white[2]) + + # Create the mapping (3-color) + else: + + # Define ranges + range_low = range(0, blackpoint) + range_map1 = range(0, midpoint - blackpoint) + range_map2 = range(0, whitepoint - midpoint) + range_high = range(0, 256 - whitepoint) + + # Map + for i in range_low: + red.append(black[0]) + green.append(black[1]) + blue.append(black[2]) + for i in range_map1: + red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1)) + green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1)) + blue.append(black[2] + i * (mid[2] - black[2]) // len(range_map1)) + for i in range_map2: + red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2)) + green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2)) + blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2)) + for i in range_high: + red.append(white[0]) + green.append(white[1]) + blue.append(white[2]) + + # Return converted image image = image.convert("RGB") return _lut(image, red + green + blue) From 1eed17c70e4a37039e30c78dfb472af7bc47c667 Mon Sep 17 00:00:00 2001 From: tsennott Date: Sun, 8 Jul 2018 20:09:39 -0700 Subject: [PATCH 35/87] tightened up colorize(); split tests; moved tuple comparison fcn to helper.py --- Tests/helper.py | 9 ++++ Tests/test_imageops.py | 109 ++++++++++++++++++++++------------------- src/PIL/ImageOps.py | 47 ++++++------------ 3 files changed, 83 insertions(+), 82 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 207f497d2..834589723 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -307,6 +307,15 @@ def hopper(mode=None, cache={}): return im.copy() +def tuple_approx_equal(actual, target, threshold): + """Tests if tuple actual has values within threshold from tuple target""" + + value = True + for i, target in enumerate(target): + value *= (target - threshold <= actual[i] <= target + threshold) + return value + + def command_succeeds(cmd): """ Runs the command, which must be a list of strings. Returns True if the diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index d1be04046..07e4dd343 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper +from helper import unittest, PillowTestCase, hopper, tuple_approx_equal from PIL import ImageOps from PIL import Image @@ -95,87 +95,94 @@ class TestImageOps(PillowTestCase): newimg = ImageOps.scale(i, 0.5) self.assertEqual(newimg.size, (25, 25)) - def test_colorize(self): - # Test the colorizing function + def test_colorize_2color(self): + # Test the colorizing function with 2-color functionality # Open test image (256px by 10px, black to white) im = Image.open("Tests/images/bw_gradient.png") im = im.convert("L") # Create image with original 2-color functionality - im_2c = ImageOps.colorize(im, 'red', 'green') - - # Create image with original 2-color functionality with offsets - im_2c_offset = ImageOps.colorize(im, - black='red', - white='green', - blackpoint=50, - whitepoint=200) - - # Create image with new three color functionality with offsets - im_3c_offset = ImageOps.colorize(im, - black='red', - white='green', - mid='blue', - blackpoint=50, - whitepoint=200, - midpoint=100) - - # Define function for approximate equality of tuples - def tuple_approx_equal(actual, target, thresh): - value = True - for i, target in enumerate(target): - value *= (target - thresh <= actual[i] <= target + thresh) - return value + im_test = ImageOps.colorize(im, 'red', 'green') # Test output image (2-color) left = (0, 1) middle = (127, 1) right = (255, 1) - self.assertTrue(tuple_approx_equal(im_2c.getpixel(left), - (255, 0, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(left), + (255, 0, 0), threshold=1), '2-color image black incorrect') - self.assertTrue(tuple_approx_equal(im_2c.getpixel(middle), - (127, 63, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(middle), + (127, 63, 0), threshold=1), '2-color image mid incorrect') - self.assertTrue(tuple_approx_equal(im_2c.getpixel(right), - (0, 127, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(right), + (0, 127, 0), threshold=1), '2-color image white incorrect') + def test_colorize_2color_offset(self): + # Test the colorizing function with 2-color functionality and offset + + # Open test image (256px by 10px, black to white) + im = Image.open("Tests/images/bw_gradient.png") + im = im.convert("L") + + # Create image with original 2-color functionality with offsets + im_test = ImageOps.colorize(im, + black='red', + white='green', + blackpoint=50, + whitepoint=100) + # Test output image (2-color) with offsets left = (25, 1) - middle = (125, 1) - right = (225, 1) - self.assertTrue(tuple_approx_equal(im_2c_offset.getpixel(left), - (255, 0, 0), thresh=1), + middle = (75, 1) + right = (125, 1) + self.assertTrue(tuple_approx_equal(im_test.getpixel(left), + (255, 0, 0), threshold=1), '2-color image (with offset) black incorrect') - self.assertTrue(tuple_approx_equal(im_2c_offset.getpixel(middle), - (127, 63, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(middle), + (127, 63, 0), threshold=1), '2-color image (with offset) mid incorrect') - self.assertTrue(tuple_approx_equal(im_2c_offset.getpixel(right), - (0, 127, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(right), + (0, 127, 0), threshold=1), '2-color image (with offset) white incorrect') + def test_colorize_3color_offset(self): + # Test the colorizing function with 3-color functionality and offset + + # Open test image (256px by 10px, black to white) + im = Image.open("Tests/images/bw_gradient.png") + im = im.convert("L") + + # Create image with new three color functionality with offsets + im_test = ImageOps.colorize(im, + black='red', + white='green', + mid='blue', + blackpoint=50, + whitepoint=200, + midpoint=100) + # Test output image (3-color) with offsets left = (25, 1) left_middle = (75, 1) middle = (100, 1) right_middle = (150, 1) right = (225, 1) - self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(left), - (255, 0, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(left), + (255, 0, 0), threshold=1), '3-color image (with offset) black incorrect') - self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(left_middle), - (127, 0, 127), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(left_middle), + (127, 0, 127), threshold=1), '3-color image (with offset) low-mid incorrect') - self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(middle), - (0, 0, 255), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(middle), + (0, 0, 255), threshold=1), '3-color image (with offset) mid incorrect') - self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(right_middle), - (0, 63, 127), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(right_middle), + (0, 63, 127), threshold=1), '3-color image (with offset) high-mid incorrect') - self.assertTrue(tuple_approx_equal(im_3c_offset.getpixel(right), - (0, 127, 0), thresh=1), + self.assertTrue(tuple_approx_equal(im_test.getpixel(right), + (0, 127, 0), threshold=1), '3-color image (with offset) white incorrect') diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 4e6baf8c9..0ba7ce069 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -162,13 +162,10 @@ def colorize(image, black, white, mid=None, blackpoint=0, # Initial asserts assert image.mode == "L" - assert 0 <= whitepoint <= 255 - assert 0 <= blackpoint <= 255 - assert 0 <= midpoint <= 255 - assert blackpoint <= whitepoint - if mid is not None: - assert blackpoint <= midpoint - assert whitepoint >= midpoint + if mid is None: + assert 0 <= blackpoint <= whitepoint <= 255 + else: + assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 # Define colors from arguments black = _color(black, "RGB") @@ -181,42 +178,28 @@ def colorize(image, black, white, mid=None, blackpoint=0, green = [] blue = [] + # Create the low-end values + for i in range(0, blackpoint): + red.append(black[0]) + green.append(black[1]) + blue.append(black[2]) + # Create the mapping (2-color) if mid is None: - # Define ranges - range_low = range(0, blackpoint) range_map = range(0, whitepoint - blackpoint) - range_high = range(0, 256 - whitepoint) - # Map - for i in range_low: - red.append(black[0]) - green.append(black[1]) - blue.append(black[2]) for i in range_map: red.append(black[0] + i * (white[0] - black[0]) // len(range_map)) green.append(black[1] + i * (white[1] - black[1]) // len(range_map)) blue.append(black[2] + i * (white[2] - black[2]) // len(range_map)) - for i in range_high: - red.append(white[0]) - green.append(white[1]) - blue.append(white[2]) # Create the mapping (3-color) else: - # Define ranges - range_low = range(0, blackpoint) range_map1 = range(0, midpoint - blackpoint) range_map2 = range(0, whitepoint - midpoint) - range_high = range(0, 256 - whitepoint) - # Map - for i in range_low: - red.append(black[0]) - green.append(black[1]) - blue.append(black[2]) for i in range_map1: red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1)) green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1)) @@ -225,10 +208,12 @@ def colorize(image, black, white, mid=None, blackpoint=0, red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2)) green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2)) blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2)) - for i in range_high: - red.append(white[0]) - green.append(white[1]) - blue.append(white[2]) + + # Create the high-end values + for i in range(0, 256 - whitepoint): + red.append(white[0]) + green.append(white[1]) + blue.append(white[2]) # Return converted image image = image.convert("RGB") From 50d6611587424394be1fdd93255b32f96ea7e34f Mon Sep 17 00:00:00 2001 From: tsennott Date: Mon, 9 Jul 2018 07:04:48 -0700 Subject: [PATCH 36/87] moved tuple test to assert method in PillowTestCase; added docs --- Tests/helper.py | 19 ++++----- Tests/test_imageops.py | 79 +++++++++++++++++++++---------------- docs/releasenotes/5.3.0.rst | 39 ++++++++++++++++++ docs/releasenotes/index.rst | 1 + src/PIL/ImageOps.py | 5 ++- 5 files changed, 98 insertions(+), 45 deletions(-) create mode 100644 docs/releasenotes/5.3.0.rst diff --git a/Tests/helper.py b/Tests/helper.py index 834589723..b6ef6dc13 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -192,6 +192,16 @@ class PillowTestCase(unittest.TestCase): def assert_not_all_same(self, items, msg=None): self.assertFalse(items.count(items[0]) == len(items), msg) + def assert_tuple_approx_equal(self, actuals, targets, threshold, msg): + """Tests if actuals has values within threshold from targets""" + + value = True + for i, target in enumerate(targets): + value *= (target - threshold <= actuals[i] <= target + threshold) + + self.assertTrue(value, + msg + ': ' + repr(actuals) + ' != ' + repr(targets)) + def skipKnownBadTest(self, msg=None, platform=None, travis=None, interpreter=None): # Skip if platform/travis matches, and @@ -307,15 +317,6 @@ def hopper(mode=None, cache={}): return im.copy() -def tuple_approx_equal(actual, target, threshold): - """Tests if tuple actual has values within threshold from tuple target""" - - value = True - for i, target in enumerate(target): - value *= (target - threshold <= actual[i] <= target + threshold) - return value - - def command_succeeds(cmd): """ Runs the command, which must be a list of strings. Returns True if the diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 07e4dd343..9c4da2463 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,4 +1,4 @@ -from helper import unittest, PillowTestCase, hopper, tuple_approx_equal +from helper import unittest, PillowTestCase, hopper from PIL import ImageOps from PIL import Image @@ -109,15 +109,18 @@ class TestImageOps(PillowTestCase): left = (0, 1) middle = (127, 1) right = (255, 1) - self.assertTrue(tuple_approx_equal(im_test.getpixel(left), - (255, 0, 0), threshold=1), - '2-color image black incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(middle), - (127, 63, 0), threshold=1), - '2-color image mid incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(right), - (0, 127, 0), threshold=1), - '2-color image white incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg='black test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(middle), + (127, 63, 0), + threshold=1, + msg='mid test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg='white test pixel incorrect') def test_colorize_2color_offset(self): # Test the colorizing function with 2-color functionality and offset @@ -137,15 +140,18 @@ class TestImageOps(PillowTestCase): left = (25, 1) middle = (75, 1) right = (125, 1) - self.assertTrue(tuple_approx_equal(im_test.getpixel(left), - (255, 0, 0), threshold=1), - '2-color image (with offset) black incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(middle), - (127, 63, 0), threshold=1), - '2-color image (with offset) mid incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(right), - (0, 127, 0), threshold=1), - '2-color image (with offset) white incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg='black test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(middle), + (127, 63, 0), + threshold=1, + msg='mid test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg='white test pixel incorrect') def test_colorize_3color_offset(self): # Test the colorizing function with 3-color functionality and offset @@ -169,21 +175,26 @@ class TestImageOps(PillowTestCase): middle = (100, 1) right_middle = (150, 1) right = (225, 1) - self.assertTrue(tuple_approx_equal(im_test.getpixel(left), - (255, 0, 0), threshold=1), - '3-color image (with offset) black incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(left_middle), - (127, 0, 127), threshold=1), - '3-color image (with offset) low-mid incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(middle), - (0, 0, 255), threshold=1), - '3-color image (with offset) mid incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(right_middle), - (0, 63, 127), threshold=1), - '3-color image (with offset) high-mid incorrect') - self.assertTrue(tuple_approx_equal(im_test.getpixel(right), - (0, 127, 0), threshold=1), - '3-color image (with offset) white incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg='black test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(left_middle), + (127, 0, 127), + threshold=1, + msg='low-mid test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(middle), + (0, 0, 255), + threshold=1, + msg='mid incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(right_middle), + (0, 63, 127), + threshold=1, + msg='high-mid test pixel incorrect') + self.assert_tuple_approx_equal(im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg='white test pixel incorrect') if __name__ == '__main__': diff --git a/docs/releasenotes/5.3.0.rst b/docs/releasenotes/5.3.0.rst new file mode 100644 index 000000000..9869b6a7a --- /dev/null +++ b/docs/releasenotes/5.3.0.rst @@ -0,0 +1,39 @@ +5.3.0 +----- + +API Changes +=========== + +Deprecations +^^^^^^^^^^^^ + +These version constants have been deprecated. ``VERSION`` will be removed in +Pillow 6.0.0, and ``PILLOW_VERSION`` will be removed after that. + +* ``PIL.VERSION`` (old PIL version 1.1.7) +* ``PIL.PILLOW_VERSION`` +* ``PIL.Image.VERSION`` +* ``PIL.Image.PILLOW_VERSION`` + +Use ``PIL.__version__`` instead. + +API Additions +============= + +ImageOps.colorize +^^^^^^^^^^^^^^^^^ + +Previously ``ImageOps.colorize`` only supported two-color mapping with +``black`` and ``white`` arguments being mapped to 0 and 255 respectively. +Now it supports three-color mapping with the optional ``mid`` parameter, and +the positions for all three color arguments can each be optionally specified +(``blackpoint``, ``whitepoint`` and ``midpoint``). +For example, with all optional arguments:: + ImageOps.colorize(im, black=(32, 37, 79), white='white', mid=(59, 101, 175), + blackpoint=15, whitepoint=240, midpoint=100) + + + +Other Changes +============= + diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 16e5c1d85..fc8d686eb 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 5.3.0 5.2.0 5.1.0 5.0.0 diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 0ba7ce069..9b470062a 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -147,8 +147,9 @@ def colorize(image, black, white, mid=None, blackpoint=0, optionally you can use three-color mapping by also specifying **mid**. Mapping positions for any of the colors can be specified (e.g. **blackpoint**), where these parameters are the integer - value in [0, 255] corresponding to where the corresponding color - should be mapped. + value corresponding to where the corresponding color should be mapped. + These parameters must have logical order, such that + **blackpoint** <= **midpoint** <= **whitepoint** (if **mid** is specified). :param image: The image to colorize. :param black: The color to use for black input pixels. From 87f042626f91c344efd669c76a243b74e76fe7c9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 11 Jul 2018 20:28:23 +0300 Subject: [PATCH 37/87] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a6018ffb6..b4bbfbb60 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 5.3.0 (unreleased) ------------------ +- Add three-color support to ImageOps.colorize #3242 + [tsennott] + - Tests: Add LA to TGA test modes #3222 [danpla] From 8253c2cc269f7f7d79c0fafa9d71cbd0b4692445 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 12 Jul 2018 06:05:08 +1000 Subject: [PATCH 38/87] Removed 5.2.0 changes [ci skip] --- docs/releasenotes/5.3.0.rst | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/docs/releasenotes/5.3.0.rst b/docs/releasenotes/5.3.0.rst index 9869b6a7a..a5591b98d 100644 --- a/docs/releasenotes/5.3.0.rst +++ b/docs/releasenotes/5.3.0.rst @@ -1,33 +1,17 @@ 5.3.0 ----- -API Changes -=========== - -Deprecations -^^^^^^^^^^^^ - -These version constants have been deprecated. ``VERSION`` will be removed in -Pillow 6.0.0, and ``PILLOW_VERSION`` will be removed after that. - -* ``PIL.VERSION`` (old PIL version 1.1.7) -* ``PIL.PILLOW_VERSION`` -* ``PIL.Image.VERSION`` -* ``PIL.Image.PILLOW_VERSION`` - -Use ``PIL.__version__`` instead. - API Additions ============= ImageOps.colorize ^^^^^^^^^^^^^^^^^ -Previously ``ImageOps.colorize`` only supported two-color mapping with -``black`` and ``white`` arguments being mapped to 0 and 255 respectively. -Now it supports three-color mapping with the optional ``mid`` parameter, and -the positions for all three color arguments can each be optionally specified -(``blackpoint``, ``whitepoint`` and ``midpoint``). +Previously ``ImageOps.colorize`` only supported two-color mapping with +``black`` and ``white`` arguments being mapped to 0 and 255 respectively. +Now it supports three-color mapping with the optional ``mid`` parameter, and +the positions for all three color arguments can each be optionally specified +(``blackpoint``, ``whitepoint`` and ``midpoint``). For example, with all optional arguments:: ImageOps.colorize(im, black=(32, 37, 79), white='white', mid=(59, 101, 175), blackpoint=15, whitepoint=240, midpoint=100) From 448beaa9aac674ec06c15a81ad6582ca34e623e9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 12 Jul 2018 19:48:59 +1000 Subject: [PATCH 39/87] Improved wording [ci skip] --- src/libImaging/Draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 7b7c5fac0..a3bb92309 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -434,7 +434,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, } for (i = 0; i < n; i++) { - /* This causes that the pixels of horizontal edges are drawn twice :( + /* This causes the pixels of horizontal edges to be drawn twice :( * but without it there are inconsistencies in ellipses */ if (e[i].ymin == e[i].ymax) { (*hline)(im, e[i].xmin, e[i].ymin, e[i].xmax, ink); From 44a4219283a0c38e3eb5be7fae7c0715794c312a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 14 Jul 2018 18:55:13 +1000 Subject: [PATCH 40/87] Added test for converting GIF with RGBA palette to P --- Tests/test_image_convert.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index ed971e698..1e208d80c 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -136,6 +136,17 @@ class TestImageConvert(PillowTestCase): self.assertNotIn('transparency', im_p.info) im_p.save(f) + def test_gif_with_rgba_palette_to_p(self): + # See https://github.com/python-pillow/Pillow/issues/2433 + im = Image.open('Tests/images/hopper.gif') + im.info['transparency'] = 255 + im.load() + self.assertEqual(im.palette.mode, 'RGBA') + im_p = im.convert('P') + + # Should not raise ValueError: unrecognized raw mode + im_p.load() + def test_p_la(self): im = hopper('RGBA') alpha = hopper('L') From 47c4647a9b05ac0bae9b6e3c3950685de1bec94e Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 14 Jul 2018 23:33:54 +0300 Subject: [PATCH 41/87] Remove redundant module skipping The skipping originally applied to ImageFileIO (#375), and ImageFileIO was later removed (#1343) but this line was missed. --- docs/PIL.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 67edb9901..fe69fed62 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -62,8 +62,6 @@ can be found here. :undoc-members: :show-inheritance: -.. intentionally skipped documenting this because it's deprecated - :mod:`ImageShow` Module ----------------------- From 1e37125a23996614f1f4fb05e2c60cd3bb1fdf16 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 19 Jul 2018 06:36:19 +1000 Subject: [PATCH 42/87] Added GitHub release to release checklist [ci skip] --- RELEASING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASING.md b/RELEASING.md index 3c0e50457..5a6214810 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -24,6 +24,7 @@ Released quarterly on the first day of January, April, July, October. ``` * [ ] Create [binary distributions](#binary-distributions) * [ ] Upload all binaries and source distributions e.g. ``twine upload dist/Pillow-5.2.0-*`` +* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) * [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), append `.dev0` to version identifier in `src/PIL/_version.py` ## Point Release @@ -50,6 +51,7 @@ Released as needed for security, installation or critical bug fixes. $ make sdist ``` * [ ] Create [binary distributions](#binary-distributions) +* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) ## Embargoed Release @@ -73,6 +75,7 @@ Released as needed privately to individual vendors for critical security-related $ make sdist ``` * [ ] Create [binary distributions](#binary-distributions) +* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new) ## Binary Distributions From 88fdf504b3706f9f91344c5b78fec3fb7d1577b1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 Jul 2018 14:38:47 +1000 Subject: [PATCH 43/87] Removed duplicate test --- Tests/test_imagegrab.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index b2edffa57..87a6956f6 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -12,11 +12,6 @@ try: im = ImageGrab.grab() self.assert_image(im, im.mode, im.size) - @unittest.skipIf(on_appveyor(), "Test fails on appveyor") - def test_grab2(self): - im = ImageGrab.grab() - self.assert_image(im, im.mode, im.size) - except ImportError: class TestImageGrab(PillowTestCase): def test_skip(self): From 639f7c1462110792ae6f4256cfbe4b3e4c168cd2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 Jul 2018 20:40:13 +1000 Subject: [PATCH 44/87] Include further instructions from ISSUE_TEMPLATE [ci skip] --- .github/CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 8d0d7ff45..eef2a30bd 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -21,7 +21,9 @@ Please send a pull request to the master branch. Please include [documentation]( ## Reporting Issues -When reporting issues, please include code that reproduces the issue and whenever possible, an image that demonstrates the issue. The best reproductions are self-contained scripts with minimal dependencies. +When reporting issues, please include code that reproduces the issue and whenever possible, an image that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive. + +The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as plone, Django, or buildout, try to replicate the issue just using Pillow. ### Provide details From d372837b08aa6b2806433fd485d2846c1ed0c670 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 19 Jun 2018 20:33:38 +1000 Subject: [PATCH 45/87] Revert "Temporarily use --no-cache-dir for pip on mingw32" --- winbuild/appveyor_install_msys2_deps.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/appveyor_install_msys2_deps.sh b/winbuild/appveyor_install_msys2_deps.sh index 5cbc53ac3..fbab280b3 100644 --- a/winbuild/appveyor_install_msys2_deps.sh +++ b/winbuild/appveyor_install_msys2_deps.sh @@ -6,7 +6,7 @@ pacman -S --noconfirm mingw32/mingw-w64-i686-python3-pip \ mingw32/mingw-w64-i686-python2-setuptools \ mingw-w64-i686-libjpeg-turbo -C:/msys64/mingw32/bin/python3 -m pip install --upgrade --no-cache-dir pip +C:/msys64/mingw32/bin/python3 -m pip install --upgrade pip -/mingw32/bin/pip install --no-cache-dir pytest pytest-cov olefile -/mingw32/bin/pip3 install --no-cache-dir pytest pytest-cov olefile +/mingw32/bin/pip install pytest pytest-cov olefile +/mingw32/bin/pip3 install pytest pytest-cov olefile From 8038e3888e5d815569a2889fa8644a0340702181 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Aug 2018 21:44:21 +1000 Subject: [PATCH 46/87] Changed Pillow Python support notes to table [ci skip] --- docs/installation.rst | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 1501d1de1..782ee7e75 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -13,15 +13,21 @@ Warnings Notes ----- -.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. +.. note:: Pillow is supported on the following Python versions -.. note:: Pillow >= 2.0.0 < 4.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4, 3.5 - -.. note:: Pillow >= 4.0.0 < 5.0.0 supports Python versions 2.7, 3.3, 3.4, 3.5, 3.6 - -.. note:: Pillow >= 5.0.0 < 5.2.0 supports Python versions 2.7, 3.4, 3.5, 3.6 - -.. note:: Pillow >= 5.2.0 supports Python versions 2.7, 3.4, 3.5, 3.6, 3.7 ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|**Python** |**2.4**|**2.5**|**2.6**|**2.7**|**3.2**|**3.3**|**3.4**|**3.5**|**3.6**|**3.7**| ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow < 2.0.0 | Yes | Yes | Yes | Yes | | | | | | | ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 2.x - 3.x | | | Yes | Yes | Yes | Yes | Yes | Yes | | | ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 4.x | | | | Yes | | Yes | Yes | Yes | Yes | | ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow 5.0.x - 5.1.x| | | | Yes | | | Yes | Yes | Yes | | ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +|Pillow >= 5.2.0 | | | | Yes | | | Yes | Yes | Yes | Yes | ++--------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ Basic Installation ------------------ From d2c9825cf03e0c23aa4db636a72e64f43375f828 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Aug 2018 23:41:14 +1000 Subject: [PATCH 47/87] Removed old build flags from documentation [ci skip] --- docs/installation.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 782ee7e75..fc3f9b32c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -215,17 +215,15 @@ Build Options parallel building. * Build flags: ``--disable-zlib``, ``--disable-jpeg``, - ``--disable-tiff``, ``--disable-freetype``, ``--disable-tcl``, - ``--disable-tk``, ``--disable-lcms``, ``--disable-webp``, - ``--disable-webpmux``, ``--disable-jpeg2000``, + ``--disable-tiff``, ``--disable-freetype``, ``--disable-lcms``, + ``--disable-webp``, ``--disable-webpmux``, ``--disable-jpeg2000``, ``--disable-imagequant``. Disable building the corresponding feature even if the development libraries are present on the building machine. * Build flags: ``--enable-zlib``, ``--enable-jpeg``, - ``--enable-tiff``, ``--enable-freetype``, ``--enable-tcl``, - ``--enable-tk``, ``--enable-lcms``, ``--enable-webp``, - ``--enable-webpmux``, ``--enable-jpeg2000``, + ``--enable-tiff``, ``--enable-freetype``, ``--enable-lcms``, + ``--enable-webp``, ``--enable-webpmux``, ``--enable-jpeg2000``, ``--enable-imagequant``. Require that the corresponding feature is built. The build will raise an exception if the libraries are not found. Webpmux (WebP metadata) From f5f2d850015ae04aeef0c9cd952d1c08b613eeea Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Aug 2018 13:22:08 +1000 Subject: [PATCH 48/87] Added 3.7 to list of OS X CI tested Python versions [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index fc3f9b32c..fd44e15d2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -394,7 +394,7 @@ These platforms are built and tested for every change. +----------------------------------+-------------------------------+-----------------------+ | Fedora 26 | 2.7 |x86-64 | +----------------------------------+-------------------------------+-----------------------+ -| Mac OS X 10.10 Yosemite* | 2.7, 3.4, 3.5, 3.6 |x86-64 | +| Mac OS X 10.10 Yosemite* | 2.7, 3.4, 3.5, 3.6, 3.7 |x86-64 | +----------------------------------+-------------------------------+-----------------------+ | Ubuntu Linux 16.04 LTS | 2.7 |x86-64 | +----------------------------------+-------------------------------+-----------------------+ From 58c0e54334fc7efa5535ff34fc62a7ad500333d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Aug 2018 13:28:12 +1000 Subject: [PATCH 49/87] Added 9c to list of tested libjpeg versions [ci skip] --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index fd44e15d2..1093bf6ba 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -128,8 +128,8 @@ Many of Pillow's features require external libraries: * **libjpeg** provides JPEG functionality. - * Pillow has been tested with libjpeg versions **6b**, **8**, **9**, **9a**, - and **9b** and libjpeg-turbo version **8**. + * Pillow has been tested with libjpeg versions **6b**, **8**, **9-9c** and + libjpeg-turbo version **8**. * Starting with Pillow 3.0.0, libjpeg is required by default, but may be disabled with the ``--disable-jpeg`` flag. From a91cb4b24ae300d90ece3b8419b8d4532c4a2223 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Aug 2018 22:35:48 +1000 Subject: [PATCH 50/87] Improved GIF documentation [ci skip] --- docs/handbook/image-file-formats.rst | 64 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 1b0661df1..37c6c0e72 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -84,7 +84,14 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following of the GIF, in milliseconds. **loop** - May not be present. The number of times the GIF should loop. + May not be present. The number of times the GIF should loop. 0 means that + it will loop forever. + +**comment** + May not be present. A comment about the image. + +**extension** + May not be present. Contains application specific information. Reading sequences ~~~~~~~~~~~~~~~~~ @@ -115,25 +122,12 @@ are available:: It is also supported for ICNS. If images are passed in of relevant sizes, they will be used instead of scaling down the main image. -**duration** - The display duration of each frame of the multiframe gif, in - milliseconds. Pass a single integer for a constant duration, or a - list or tuple to set the duration for each frame separately. +**include_color_table** + Whether or not to include local color table. -**loop** - Integer number of times the GIF should loop. - -**optimize** - If present and true, attempt to compress the palette by - eliminating unused colors. This is only useful if the palette can - be compressed to the next smaller power of 2 elements. - -**palette** - Use the specified palette for the saved image. The palette should - be a bytes or bytearray object containing the palette entries in - RGBRGB... form. It should be no more than 768 bytes. Alternately, - the palette can be passed in as an - :py:class:`PIL.ImagePalette.ImagePalette` object. +**interlace** + Whether or not the image is interlaced. By default, it is, unless the image + is less than 16 pixels in width or height. **disposal** Indicates the way in which the graphic is to be treated after being displayed. @@ -146,6 +140,38 @@ are available:: Pass a single integer for a constant disposal, or a list or tuple to set the disposal for each frame separately. +**palette** + Use the specified palette for the saved image. The palette should + be a bytes or bytearray object containing the palette entries in + RGBRGB... form. It should be no more than 768 bytes. Alternately, + the palette can be passed in as an + :py:class:`PIL.ImagePalette.ImagePalette` object. + +**optimize** + If present and true, attempt to compress the palette by + eliminating unused colors. This is only useful if the palette can + be compressed to the next smaller power of 2 elements. + +Note that if the image you are saving comes from an existing GIF, it may have +the following properties in its :py:attr:`~PIL.Image.Image.info` dictionary. +For these options, if you do not pass them in, they will default to +their :py:attr:`~PIL.Image.Image.info` values. + +**transparency** + Transparency color index. + +**duration** + The display duration of each frame of the multiframe gif, in + milliseconds. Pass a single integer for a constant duration, or a + list or tuple to set the duration for each frame separately. + +**loop** + Integer number of times the GIF should loop. 0 means that it will loop + forever. By default, the image will not loop. + +**comment** + A comment about the image. + Reading local images ~~~~~~~~~~~~~~~~~~~~ From de9171e6c7ac2cd1d9157858d91cda7c29a5fabb Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 4 Aug 2018 22:19:28 +0300 Subject: [PATCH 51/87] Temporarily pin pytest to prevent scandir non-compilation --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 95d816a98..629641947 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -71,7 +71,7 @@ build_script: test_script: - cd c:\pillow -- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov' +- '%PYTHON%\%PIP_DIR%\pip.exe install "pytest<3.7" pytest-cov' - '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests' #- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest? From d7044350d5b029e7426d481968745d24d55c77cb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Aug 2018 13:47:42 +1000 Subject: [PATCH 52/87] Updated libimagequant to 2.12.1 --- depends/install_imagequant.sh | 2 +- docs/installation.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index ce5f66d8d..56dfabf8f 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -archive=libimagequant-2.11.10 +archive=libimagequant-2.12.1 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/master/$archive.tar.gz diff --git a/docs/installation.rst b/docs/installation.rst index 1093bf6ba..f8a9f5e5a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -165,7 +165,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-2.11** + * Pillow has been tested with libimagequant **2.6-2.12.1** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. From 5c2e3253534ea0bd5ec84c6c94c3bbc72042913a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Aug 2018 19:16:33 +1000 Subject: [PATCH 53/87] Changed order of tests --- Tests/{test_image_fromqpixmap.py => test_qt_image_fromqpixmap.py} | 0 Tests/{test_image_toqimage.py => test_qt_image_toqimage.py} | 0 Tests/{test_image_toqpixmap.py => test_qt_image_toqpixmap.py} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename Tests/{test_image_fromqpixmap.py => test_qt_image_fromqpixmap.py} (100%) rename Tests/{test_image_toqimage.py => test_qt_image_toqimage.py} (100%) rename Tests/{test_image_toqpixmap.py => test_qt_image_toqpixmap.py} (100%) diff --git a/Tests/test_image_fromqpixmap.py b/Tests/test_qt_image_fromqpixmap.py similarity index 100% rename from Tests/test_image_fromqpixmap.py rename to Tests/test_qt_image_fromqpixmap.py diff --git a/Tests/test_image_toqimage.py b/Tests/test_qt_image_toqimage.py similarity index 100% rename from Tests/test_image_toqimage.py rename to Tests/test_qt_image_toqimage.py diff --git a/Tests/test_image_toqpixmap.py b/Tests/test_qt_image_toqpixmap.py similarity index 100% rename from Tests/test_image_toqpixmap.py rename to Tests/test_qt_image_toqpixmap.py From de06db637bfb91528cad8c6825c21f8626b65145 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 5 Aug 2018 10:31:33 +0300 Subject: [PATCH 54/87] Update CHANGES.rst --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b4bbfbb60..972092010 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 5.3.0 (unreleased) ------------------ +- Depends: Update libimagequant to 2.12.1 #3281 + [radarhere] + - Add three-color support to ImageOps.colorize #3242 [tsennott] From 164867643507f4334b80b8747321c76373a50a52 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 9 Aug 2018 20:54:16 +1000 Subject: [PATCH 55/87] Fixed typo [ci skip] --- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MpoImagePlugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 98c27010c..a75e3d428 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -793,7 +793,7 @@ def jpeg_factory(fp=None, filename=None): return im -# -------------------------------------------------------------------q- +# --------------------------------------------------------------------- # Registry stuff Image.register_open(JpegImageFile.format, jpeg_factory, _accept) diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 460ccec27..a1a8d655a 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -85,7 +85,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): return self.__frame -# -------------------------------------------------------------------q- +# --------------------------------------------------------------------- # Registry stuff # Note that since MPO shares a factory with JPEG, we do not need to do a From 9fcfdb4d4ee39afba500a7f507fcd6e2b85fd4c7 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 10 Aug 2018 20:15:21 +0300 Subject: [PATCH 56/87] Revert "CI: Temporarily pin pytest to prevent scandir non-compilation" --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 629641947..95d816a98 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -71,7 +71,7 @@ build_script: test_script: - cd c:\pillow -- '%PYTHON%\%PIP_DIR%\pip.exe install "pytest<3.7" pytest-cov' +- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov' - '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests' #- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest? From 046df78448972b9a619aef63b18531d356c35712 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Aug 2018 16:39:49 +1000 Subject: [PATCH 57/87] Fixed typos --- Tests/test_file_jpeg2k.py | 4 ++-- src/PIL/ImagePalette.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 71f15f7b8..be49c99bf 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -146,13 +146,13 @@ class TestFileJpeg2k(PillowTestCase): self.assertEqual(j2k.mode, 'I;16') self.assertEqual(jp2.mode, 'I;16') - def test_16bit_monchrome_jp2_like_tiff(self): + def test_16bit_monochrome_jp2_like_tiff(self): tiff_16bit = Image.open('Tests/images/16bit.cropped.tif') jp2 = Image.open('Tests/images/16bit.cropped.jp2') self.assert_image_similar(jp2, tiff_16bit, 1e-3) - def test_16bit_monchrome_j2k_like_tiff(self): + def test_16bit_monochrome_j2k_like_tiff(self): tiff_16bit = Image.open('Tests/images/16bit.cropped.tif') j2k = Image.open('Tests/images/16bit.cropped.j2k') diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index cecc64583..81e99abbf 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -59,7 +59,7 @@ class ImagePalette(object): def getdata(self): """ - Get palette contents in format suitable # for the low-level + Get palette contents in format suitable for the low-level ``im.putpalette`` primitive. .. warning:: This method is experimental. From e3aaa80c0690676286b408759449c9219fd54146 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 12 Aug 2018 13:58:26 +1000 Subject: [PATCH 58/87] Added NumPy to documentation --- src/PIL/Image.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c58952657..f13a98276 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2440,9 +2440,20 @@ def fromarray(obj, mode=None): Creates an image memory from an object exporting the array interface (using the buffer protocol). - If obj is not contiguous, then the tobytes method is called + If **obj** is not contiguous, then the tobytes method is called and :py:func:`~PIL.Image.frombuffer` is used. + If you have an image in NumPy:: + + from PIL import Image + import numpy as np + im = Image.open('hopper.jpg') + a = numpy.asarray(im) + + Then this can be used to convert it to a Pillow image:: + + im = Image.fromarray(a) + :param obj: Object with array interface :param mode: Mode to use (will be determined from type if None) See: :ref:`concept-modes`. From 26d1eb4b058c6485acb87bf2aaee613c334236f6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 14 Aug 2018 19:37:16 +1000 Subject: [PATCH 59/87] Updated StackOverflow links [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 36b7c5e95..8a33f18dc 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, Gitter or Stack Overflow here and here. Please report issues on GitHub. + You can get help via IRC at irc://irc.freenode.net#pil, Gitter or Stack Overflow. Please report issues on GitHub.

From 4d1cebd9a889c662217e8a0e2275fa1d45c39674 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 28 Jun 2018 17:26:00 +0300 Subject: [PATCH 60/87] Use Ubuntu 16.04 LTS (Xenial Xerus) as CI base --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 30c32cb76..a79b8dc53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ matrix: - env: DOCKER="fedora-26-amd64" DOCKER_TAG="pytest" - env: DOCKER="fedora-27-amd64" DOCKER_TAG="pytest" -dist: trusty +dist: xenial sudo: required From 825817efe58ff6f10add0dd6609a3f0783d42aa4 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 28 Jun 2018 17:53:18 +0300 Subject: [PATCH 61/87] Use Ubuntu 14.04 LTS (Trusty Tahr) for PyPy2/3 and Py3.4, not available on Xenial --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a79b8dc53..ad385a507 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +dist: xenial language: python cache: pip @@ -12,13 +13,16 @@ matrix: fast_finish: true include: - python: "pypy" + dist: trusty - python: "pypy3" + dist: trusty - python: '3.7-dev' - python: '2.7' - python: "2.7_with_system_site_packages" # For PyQt4 - python: '3.6' - python: '3.5' - python: '3.4' + dist: trusty - env: DOCKER="alpine" DOCKER_TAG="pytest" - env: DOCKER="arch" DOCKER_TAG="pytest" # contains PyQt5 - env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="pytest" @@ -31,8 +35,6 @@ matrix: - env: DOCKER="fedora-26-amd64" DOCKER_TAG="pytest" - env: DOCKER="fedora-27-amd64" DOCKER_TAG="pytest" -dist: xenial - sudo: required services: From b2599042a27a76327dead2f3c29c4c3e5401c59f Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 3 Jul 2018 16:40:18 +0300 Subject: [PATCH 62/87] Add Ubuntu 14.04 LTS (Trusty Tahr) jobs for those that can use it --- .travis.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad385a507..23225dbbb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ dist: xenial +sudo: required language: python cache: pip @@ -16,11 +17,19 @@ matrix: dist: trusty - python: "pypy3" dist: trusty - - python: '3.7-dev' + - python: '3.7' - python: '2.7' + - python: '2.7' + dist: trusty - python: "2.7_with_system_site_packages" # For PyQt4 + - python: "2.7_with_system_site_packages" # For PyQt4 + dist: trusty - python: '3.6' + - python: '3.6' + dist: trusty - python: '3.5' + - python: '3.5' + dist: trusty - python: '3.4' dist: trusty - env: DOCKER="alpine" DOCKER_TAG="pytest" @@ -35,8 +44,6 @@ matrix: - env: DOCKER="fedora-26-amd64" DOCKER_TAG="pytest" - env: DOCKER="fedora-27-amd64" DOCKER_TAG="pytest" -sudo: required - services: - docker From 6a7c7783eb97761c64a5464775085011e342538f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Aug 2018 19:40:13 +1000 Subject: [PATCH 63/87] Corrected wording --- Tests/test_file_tar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index 3dd075042..9e02ab1a5 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -12,7 +12,7 @@ class TestFileTar(PillowTestCase): def setUp(self): if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: - self.skipTest("neither jpeg nor zip support not available") + self.skipTest("neither jpeg nor zip support available") def test_sanity(self): if "zip_decoder" in codecs: From e5e5cb11432ddff2e52748317cc260b2e2c3493f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 18 Aug 2018 19:13:37 +1000 Subject: [PATCH 64/87] Update example [ci skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 5a6214810..7794612fa 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -94,7 +94,7 @@ Released as needed privately to individual vendors for critical security-related $ git fetch --all $ git checkout [[release tag]] $ cd .. - $ git commit -m "Pillow -> 2.9.0" Pillow + $ git commit -m "Pillow -> 5.2.0" Pillow $ git push ``` * [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/). From de7b43397afe6b44b56622b2300ccdb4b6e7636e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Aug 2018 20:36:05 +1000 Subject: [PATCH 65/87] Added links to installation documentation [ci skip] --- docs/handbook/image-file-formats.rst | 4 ++-- docs/releasenotes/4.2.0.rst | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 37c6c0e72..b48788e8b 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -529,8 +529,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: .. note:: To enable PNG support, you need to build and install the ZLIB compression - library before building the Python Imaging Library. See the installation - documentation for details. + library before building the Python Imaging Library. See the `installation + documentation <../installation.rst>`_ for details. PPM ^^^ diff --git a/docs/releasenotes/4.2.0.rst b/docs/releasenotes/4.2.0.rst index 1b41580a7..9919a5db1 100644 --- a/docs/releasenotes/4.2.0.rst +++ b/docs/releasenotes/4.2.0.rst @@ -6,9 +6,10 @@ Added Complex Text Rendering Pillow now supports complex text rendering for scripts requiring glyph composition and bidirectional flow. This optional feature adds three -dependencies: harfbuzz, fribidi, and raqm. See the install -documentation for further details. This feature is tested and works on -Unix and Mac, but has not yet been built on Windows platforms. +dependencies: harfbuzz, fribidi, and raqm. See the `install +documentation <../installation.rst>`_ for further details. This feature is +tested and works on Unix and Mac, but has not yet been built on Windows +platforms. New Optional Parameters ======================= From af817c83ad1a3d5504afd742d02d26e7b9ea726f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Aug 2018 21:07:25 +1000 Subject: [PATCH 66/87] Corrected links for Read The Docs [ci skip] --- docs/handbook/image-file-formats.rst | 2 +- docs/releasenotes/4.2.0.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index b48788e8b..eb50ff23d 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -530,7 +530,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: To enable PNG support, you need to build and install the ZLIB compression library before building the Python Imaging Library. See the `installation - documentation <../installation.rst>`_ for details. + documentation <../installation.html>`_ for details. PPM ^^^ diff --git a/docs/releasenotes/4.2.0.rst b/docs/releasenotes/4.2.0.rst index 9919a5db1..e07fd9071 100644 --- a/docs/releasenotes/4.2.0.rst +++ b/docs/releasenotes/4.2.0.rst @@ -7,7 +7,7 @@ Added Complex Text Rendering Pillow now supports complex text rendering for scripts requiring glyph composition and bidirectional flow. This optional feature adds three dependencies: harfbuzz, fribidi, and raqm. See the `install -documentation <../installation.rst>`_ for further details. This feature is +documentation <../installation.html>`_ for further details. This feature is tested and works on Unix and Mac, but has not yet been built on Windows platforms. From a6a3a21f476fe9cfb98dcc328ac314c6c10a73ba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Aug 2018 21:15:16 +1000 Subject: [PATCH 67/87] Improved wording [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index f8a9f5e5a..9f0393563 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -178,7 +178,7 @@ Many of Pillow's features require external libraries: shaping (using HarfBuzz), and proper script itemization. As a result, Raqm can support most writing systems covered by Unicode. * libraqm depends on the following libraries: FreeType, HarfBuzz, - FriBiDi, make sure that you install them before install libraqm + FriBiDi, make sure that you install them before installing libraqm if not available as package in your system. * setting text direction or font features is not supported without libraqm. From fb4d762a2f91c69f7fa9e7b9dc8ac0e4480fe2a6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Aug 2018 20:12:12 +1000 Subject: [PATCH 68/87] Download lib if not present in pillow-depends --- winbuild/build_dep.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index a059b1ee8..6ac3baa47 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -2,6 +2,7 @@ from unzip import unzip from untar import untar import os +from fetch import fetch from config import compilers, compiler_from_env, libs @@ -44,6 +45,8 @@ def extract(src, dest): def extract_libs(): for name, lib in libs.items(): filename = lib['filename'] + if not os.path.exists(filename): + filename = fetch(lib['url']) if name == 'openjpeg': for compiler in compilers.values(): if not os.path.exists(os.path.join( From c8e00203ece19ae73d797a089bf935dda5eed6a4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Aug 2018 20:18:36 +1000 Subject: [PATCH 69/87] Removed unnecessary setUp calls --- Tests/test_qt_image_fromqpixmap.py | 3 +-- Tests/test_qt_image_toqimage.py | 3 --- Tests/test_qt_image_toqpixmap.py | 4 +--- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Tests/test_qt_image_fromqpixmap.py b/Tests/test_qt_image_fromqpixmap.py index 543b74bbf..0127f77e2 100644 --- a/Tests/test_qt_image_fromqpixmap.py +++ b/Tests/test_qt_image_fromqpixmap.py @@ -1,5 +1,5 @@ from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase +from test_imageqt import PillowQPixmapTestCase from PIL import ImageQt @@ -7,7 +7,6 @@ from PIL import ImageQt class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase): def roundtrip(self, expected): - PillowQtTestCase.setUp(self) result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected)) # Qt saves all pixmaps as rgb self.assert_image_equal(result, expected.convert('RGB')) diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index c9971cf73..896cf13fb 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -25,7 +25,6 @@ if ImageQt.qt_is_installed: class TestToQImage(PillowQtTestCase, PillowTestCase): def test_sanity(self): - PillowQtTestCase.setUp(self) for mode in ('RGB', 'RGBA', 'L', 'P', '1'): src = hopper(mode) data = ImageQt.toqimage(src) @@ -61,8 +60,6 @@ class TestToQImage(PillowQtTestCase, PillowTestCase): self.assert_image_equal(reloaded, src) def test_segfault(self): - PillowQtTestCase.setUp(self) - app = QApplication([]) ex = Example() assert(app) # Silence warning diff --git a/Tests/test_qt_image_toqpixmap.py b/Tests/test_qt_image_toqpixmap.py index c6555d7ff..5de7810f5 100644 --- a/Tests/test_qt_image_toqpixmap.py +++ b/Tests/test_qt_image_toqpixmap.py @@ -1,5 +1,5 @@ from helper import unittest, PillowTestCase, hopper -from test_imageqt import PillowQtTestCase, PillowQPixmapTestCase +from test_imageqt import PillowQPixmapTestCase from PIL import ImageQt @@ -10,8 +10,6 @@ if ImageQt.qt_is_installed: class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): def test_sanity(self): - PillowQtTestCase.setUp(self) - for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): data = ImageQt.toqpixmap(hopper(mode)) From 0a1fae8c2dc08aebbccd9c57d75ead681d23ed14 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Aug 2018 08:56:41 +1000 Subject: [PATCH 70/87] Added tests --- Tests/test_image_filter.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 3636a73f7..75665d20a 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -94,6 +94,15 @@ class TestImageFilter(PillowTestCase): self.assertEqual(rankfilter.size, 1) self.assertEqual(rankfilter.rank, 2) + def test_kernel_not_enough_coefficients(self): + self.assertRaises(ValueError, + lambda: ImageFilter.Kernel((3, 3), (0, 0))) + + def test_kernel_filter_p(self): + kernel = ImageFilter.Kernel((2, 2), (0, 0, 0, 0)) + + self.assertRaises(ValueError, kernel.filter, hopper("P")) + def test_consistency_3x3(self): source = Image.open("Tests/images/hopper.bmp") reference = Image.open("Tests/images/hopper_emboss.bmp") From 9c5c66cc8fe6c807520a523994996e8ff0a51b97 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Aug 2018 11:59:27 +1000 Subject: [PATCH 71/87] Improved ImageChops tests --- Tests/test_imagechops.py | 111 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index ad8a43a44..3714675cf 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -57,6 +57,28 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getbbox(), (25, 25, 76, 76)) self.assertEqual(new.getpixel((50, 50)), ORANGE) + def test_add_scale_offset(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") + im2 = Image.open("Tests/images/imagedraw_floodfill.png") + + # Act + new = ImageChops.add(im1, im2, scale=2.5, offset=100) + + # Assert + self.assertEqual(new.getbbox(), (0, 0, 100, 100)) + self.assertEqual(new.getpixel((50, 50)), (202, 151, 100)) + + def test_add_clip(self): + # Arrange + im = hopper() + + # Act + new = ImageChops.add(im, im) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (255, 255, 254)) + def test_add_modulo(self): # Arrange im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") @@ -69,6 +91,16 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getbbox(), (25, 25, 76, 76)) self.assertEqual(new.getpixel((50, 50)), ORANGE) + def test_add_modulo_no_clip(self): + # Arrange + im = hopper() + + # Act + new = ImageChops.add_modulo(im, im) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (224, 76, 254)) + def test_blend(self): # Arrange im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") @@ -93,7 +125,7 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getpixel((0, 0)), GREY) self.assertEqual(new.getpixel((19, 9)), GREY) - def test_darker(self): + def test_darker_image(self): # Arrange im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") @@ -104,6 +136,17 @@ class TestImageChops(PillowTestCase): # Assert self.assert_image_equal(new, im2) + def test_darker_pixel(self): + # Arrange + im1 = hopper() + im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + + # Act + new = ImageChops.darker(im1, im2) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (240, 166, 0)) + def test_difference(self): # Arrange im1 = Image.open("Tests/images/imagedraw_arc_end_le_start.png") @@ -115,6 +158,17 @@ class TestImageChops(PillowTestCase): # Assert self.assertEqual(new.getbbox(), (25, 25, 76, 76)) + def test_difference_pixel(self): + # Arrange + im1 = hopper() + im2 = Image.open("Tests/images/imagedraw_polygon_kite_RGB.png") + + # Act + new = ImageChops.difference(im1, im2) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (240, 166, 128)) + def test_duplicate(self): # Arrange im = hopper() @@ -137,16 +191,27 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getpixel((0, 0)), WHITE) self.assertEqual(new.getpixel((50, 50)), CYAN) - def test_lighter(self): + def test_lighter_image(self): # Arrange im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") # Act - new = ImageChops.darker(im1, im2) + new = ImageChops.lighter(im1, im2) # Assert - self.assert_image_equal(new, im2) + self.assert_image_equal(new, im1) + + def test_lighter_pixel(self): + # Arrange + im1 = hopper() + im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + + # Act + new = ImageChops.lighter(im1, im2) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (255, 255, 127)) def test_multiply_black(self): """If you multiply an image with a solid black image, @@ -201,6 +266,10 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getpixel((50, 50)), BLACK) self.assertEqual(new.getpixel((50+xoffset, 50+yoffset)), DARK_GREEN) + # Test no yoffset + self.assertEqual(ImageChops.offset(im, xoffset), + ImageChops.offset(im, xoffset, xoffset)) + def test_screen(self): # Arrange im1 = Image.open("Tests/images/imagedraw_ellipse_RGB.png") @@ -226,6 +295,29 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getpixel((50, 50)), GREEN) self.assertEqual(new.getpixel((50, 51)), BLACK) + def test_subtract_scale_offset(self): + # Arrange + im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") + im2 = Image.open("Tests/images/imagedraw_outline_chord_RGB.png") + + # Act + new = ImageChops.subtract(im1, im2, scale=2.5, offset=100) + + # Assert + self.assertEqual(new.getbbox(), (0, 0, 100, 100)) + self.assertEqual(new.getpixel((50, 50)), (100, 202, 100)) + + def test_subtract_clip(self): + # Arrange + im1 = hopper() + im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + + # Act + new = ImageChops.subtract(im1, im2) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (0, 0, 127)) + def test_subtract_modulo(self): # Arrange im1 = Image.open("Tests/images/imagedraw_chord_RGB.png") @@ -239,6 +331,17 @@ class TestImageChops(PillowTestCase): self.assertEqual(new.getpixel((50, 50)), GREEN) self.assertEqual(new.getpixel((50, 51)), BLACK) + def test_subtract_modulo_no_clip(self): + # Arrange + im1 = hopper() + im2 = Image.open("Tests/images/imagedraw_chord_RGB.png") + + # Act + new = ImageChops.subtract_modulo(im1, im2) + + # Assert + self.assertEqual(new.getpixel((50, 50)), (241, 167, 127)) + def test_logical(self): def table(op, a, b): From 9d5db408c5f8093c048a3336c174c4a703d6970e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Aug 2018 13:57:24 +1000 Subject: [PATCH 72/87] Changed rm command to use force --- depends/install_extra_test_images.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depends/install_extra_test_images.sh b/depends/install_extra_test_images.sh index 667c74e6d..089a5e4d7 100755 --- a/depends/install_extra_test_images.sh +++ b/depends/install_extra_test_images.sh @@ -1,7 +1,7 @@ #!/bin/bash # install extra test images -rm -r test_images +rm -rf test_images # Use SVN to just fetch a single git subdirectory svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images From f115546b56a54b6458f189df0348806cfdf569bd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Aug 2018 14:57:15 +1000 Subject: [PATCH 73/87] Update CHANGES.rst [ci skip] --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 972092010..1daa50703 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,18 @@ Changelog (Pillow) 5.3.0 (unreleased) ------------------ +- Tests: Added ImageChops tests #3230 + [hugovk, radarhere] + +- AppVeyor: Download lib if not present in pillow-depends #3316 + [radarhere] + +- Travis CI: Add Python 3.7 and Xenial #3234 + [hugovk] + +- Docs: Added documentation for NumPy conversion #3301 + [radarhere] + - Depends: Update libimagequant to 2.12.1 #3281 [radarhere] From 3ae5f054107f640383ec5ebb219dbf76f5b14b21 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Aug 2018 23:30:47 +1000 Subject: [PATCH 74/87] Changed Kernel to subclass BuiltinFilter, instead of the other way around --- Tests/test_image_filter.py | 10 +++++----- src/PIL/ImageFilter.py | 19 ++++++++----------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 75665d20a..6936a84f0 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -94,15 +94,15 @@ class TestImageFilter(PillowTestCase): self.assertEqual(rankfilter.size, 1) self.assertEqual(rankfilter.rank, 2) + def test_builtinfilter_p(self): + builtinFilter = ImageFilter.BuiltinFilter() + + self.assertRaises(ValueError, builtinFilter.filter, hopper("P")) + def test_kernel_not_enough_coefficients(self): self.assertRaises(ValueError, lambda: ImageFilter.Kernel((3, 3), (0, 0))) - def test_kernel_filter_p(self): - kernel = ImageFilter.Kernel((2, 2), (0, 0, 0, 0)) - - self.assertRaises(ValueError, kernel.filter, hopper("P")) - def test_consistency_3x3(self): source = Image.open("Tests/images/hopper.bmp") reference = Image.open("Tests/images/hopper_emboss.bmp") diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index e77349df0..de99e6410 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -33,7 +33,14 @@ class MultibandFilter(Filter): pass -class Kernel(MultibandFilter): +class BuiltinFilter(MultibandFilter): + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + return image.filter(*self.filterargs) + + +class Kernel(BuiltinFilter): """ Create a convolution kernel. The current version only supports 3x3 and 5x5 integer and floating point kernels. @@ -60,16 +67,6 @@ class Kernel(MultibandFilter): raise ValueError("not enough coefficients in kernel") self.filterargs = size, scale, offset, kernel - def filter(self, image): - if image.mode == "P": - raise ValueError("cannot filter palette images") - return image.filter(*self.filterargs) - - -class BuiltinFilter(Kernel): - def __init__(self): - pass - class RankFilter(Filter): """ From f4d39c95573cf5e25a87221e9a63c8a2987a1588 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Aug 2018 23:33:12 +1000 Subject: [PATCH 75/87] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1daa50703..f77b4137b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 5.3.0 (unreleased) ------------------ +- Tests: Added ImageFilter tests #3295 + [radarhere] + - Tests: Added ImageChops tests #3230 [hugovk, radarhere] From bdf2705cd3a76f03d0530d1b90b552d42cd16114 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 4 Jul 2018 12:15:06 +0300 Subject: [PATCH 76/87] Remove ununsed draw_line. Only draw_lines is used by ImageDraw.py --- src/_imaging.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/_imaging.c b/src/_imaging.c index 0cb2f56b9..4ff277b0c 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -2697,22 +2697,6 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* -_draw_line(ImagingDrawObject* self, PyObject* args) -{ - int x0, y0, x1, y1; - int ink; - if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink)) - return NULL; - - if (ImagingDrawLine(self->image->image, x0, y0, x1, y1, - &ink, self->blend) < 0) - return NULL; - - Py_INCREF(Py_None); - return Py_None; -} - static PyObject* _draw_lines(ImagingDrawObject* self, PyObject* args) { @@ -2961,7 +2945,6 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args) static struct PyMethodDef _draw_methods[] = { #ifdef WITH_IMAGEDRAW /* Graphics (ImageDraw) */ - {"draw_line", (PyCFunction)_draw_line, 1}, {"draw_lines", (PyCFunction)_draw_lines, 1}, #ifdef WITH_ARROW {"draw_outline", (PyCFunction)_draw_outline, 1}, From 6fc1e79e9601ff0990d93c22caf413d0fd51b11b Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 4 Jul 2018 12:19:32 +0300 Subject: [PATCH 77/87] Remove ununsed draw_point. Only draw_points is used by ImageDraw.py --- src/_imaging.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/_imaging.c b/src/_imaging.c index 4ff277b0c..3acbdb01c 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -2750,21 +2750,6 @@ _draw_lines(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* -_draw_point(ImagingDrawObject* self, PyObject* args) -{ - int x, y; - int ink; - if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink)) - return NULL; - - if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0) - return NULL; - - Py_INCREF(Py_None); - return Py_None; -} - static PyObject* _draw_points(ImagingDrawObject* self, PyObject* args) { @@ -2951,7 +2936,6 @@ static struct PyMethodDef _draw_methods[] = { #endif {"draw_polygon", (PyCFunction)_draw_polygon, 1}, {"draw_rectangle", (PyCFunction)_draw_rectangle, 1}, - {"draw_point", (PyCFunction)_draw_point, 1}, {"draw_points", (PyCFunction)_draw_points, 1}, {"draw_arc", (PyCFunction)_draw_arc, 1}, {"draw_bitmap", (PyCFunction)_draw_bitmap, 1}, From 3cf6ad1895ce6a77245d3bc96c8d19c440ad6132 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Jul 2018 00:54:52 +0300 Subject: [PATCH 78/87] Remove ununsed font_getabc --- src/_imagingft.c | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/_imagingft.c b/src/_imagingft.c index 86e0fe45b..f94e55803 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -674,47 +674,6 @@ font_getsize(FontObject* self, PyObject* args) ); } -static PyObject* -font_getabc(FontObject* self, PyObject* args) -{ - FT_ULong ch; - FT_Face face; - double a, b, c; - - /* calculate ABC values for a given string */ - - PyObject* string; - if (!PyArg_ParseTuple(args, "O:getabc", &string)) - return NULL; - -#if PY_VERSION_HEX >= 0x03000000 - if (!PyUnicode_Check(string)) { -#else - if (!PyUnicode_Check(string) && !PyString_Check(string)) { -#endif - PyErr_SetString(PyExc_TypeError, "expected string"); - return NULL; - } - - if (font_getchar(string, 0, &ch)) { - int index, error; - face = self->face; - index = FT_Get_Char_Index(face, ch); - /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */ - error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); - if (error) - return geterror(error); - a = face->glyph->metrics.horiBearingX / 64.0; - b = face->glyph->metrics.width / 64.0; - c = (face->glyph->metrics.horiAdvance - - face->glyph->metrics.horiBearingX - - face->glyph->metrics.width) / 64.0; - } else - a = b = c = 0.0; - - return Py_BuildValue("ddd", a, b, c); -} - static PyObject* font_render(FontObject* self, PyObject* args) { @@ -854,7 +813,6 @@ font_dealloc(FontObject* self) static PyMethodDef font_methods[] = { {"render", (PyCFunction) font_render, METH_VARARGS}, {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, - {"getabc", (PyCFunction) font_getabc, METH_VARARGS}, {NULL, NULL} }; From 9c4370e5ef0631e201f5a61e35e2a2181bd95ebf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 Aug 2018 20:16:41 +1000 Subject: [PATCH 79/87] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f77b4137b..785d41f6a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 5.3.0 (unreleased) ------------------ +- Remove unused draw.draw_line, draw.draw_point and font.getabc methods #3232 + [hugovk] + - Tests: Added ImageFilter tests #3295 [radarhere] From 353159c74f94e15b68ee141677f84a2c5e5b5d52 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 28 Aug 2018 20:41:47 +1000 Subject: [PATCH 80/87] Only update the Pillow submodule of pillow-wheels [ci skip] --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 7794612fa..b5e548e06 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -89,7 +89,7 @@ Released as needed privately to individual vendors for critical security-related $ git clone https://github.com/python-pillow/pillow-wheels $ cd pillow-wheels $ git submodule init - $ git submodule update + $ git submodule update Pillow $ cd Pillow $ git fetch --all $ git checkout [[release tag]] From c6ea6a1cc2fd6a13357d09776dd878bfb0a84444 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 28 Aug 2018 20:42:29 +1000 Subject: [PATCH 81/87] Add updated submodule in pillow-wheels [ci skip] --- RELEASING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASING.md b/RELEASING.md index b5e548e06..8812318fc 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -94,6 +94,7 @@ Released as needed privately to individual vendors for critical security-related $ git fetch --all $ git checkout [[release tag]] $ cd .. + $ git add Pillow $ git commit -m "Pillow -> 5.2.0" Pillow $ git push ``` From 63fb52219cf2d7011b520ad6e0d99013bc486ba2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Aug 2018 19:21:00 +1000 Subject: [PATCH 82/87] Revert "Add updated submodule in pillow-wheels [ci skip]" This reverts commit c6ea6a1cc2fd6a13357d09776dd878bfb0a84444. --- RELEASING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index 8812318fc..b5e548e06 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -94,7 +94,6 @@ Released as needed privately to individual vendors for critical security-related $ git fetch --all $ git checkout [[release tag]] $ cd .. - $ git add Pillow $ git commit -m "Pillow -> 5.2.0" Pillow $ git push ``` From 4ec322aa7d35f632db5da102c5f7ceb1cbc9609f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 1 Sep 2018 09:50:49 +1000 Subject: [PATCH 83/87] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 785d41f6a..7efb178af 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 5.3.0 (unreleased) ------------------ +- Changed ImageFilter.Kernel to subclass ImageFilter.BuiltinFilter, instead of the other way around #3273 + [radarhere] + - Remove unused draw.draw_line, draw.draw_point and font.getabc methods #3232 [hugovk] From 0411caba67b8127694be4dabbdedd1dc65d12576 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 1 Sep 2018 17:18:13 +1000 Subject: [PATCH 84/87] Catch ValueError when processing the edge of an image --- Tests/test_imagedraw.py | 5 +++++ src/PIL/ImageDraw.py | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 33e7f8477..5f9649a78 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -366,6 +366,11 @@ class TestImageDraw(PillowTestCase): ImageDraw.floodfill(im, (W, H), red) self.assert_image_equal(im, im_floodfill) + # Test filling at the edge of an image + im = Image.new("RGB", (1, 1)) + ImageDraw.floodfill(im, (0, 0), red) + self.assert_image_equal(im, Image.new("RGB", (1, 1), red)) + def test_floodfill_border(self): # floodfill() is experimental diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index ca8c1d17b..428174784 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -358,7 +358,7 @@ def floodfill(image, xy, value, border=None, thresh=0): for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): try: p = pixel[s, t] - except IndexError: + except (ValueError, IndexError): pass else: if _color_diff(p, background) <= thresh: @@ -372,7 +372,7 @@ def floodfill(image, xy, value, border=None, thresh=0): for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): try: p = pixel[s, t] - except IndexError: + except (ValueError, IndexError): pass else: if p != value and p != border: From 4a34116be5e800d05bed62ef7c2e588f7e686a07 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 1 Sep 2018 18:30:04 +1000 Subject: [PATCH 85/87] Removed duplicate code --- src/PIL/ImageDraw.py | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 428174784..02fa865aa 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -351,34 +351,23 @@ def floodfill(image, xy, value, border=None, thresh=0): except (ValueError, IndexError): return # seed point outside image edge = [(x, y)] - if border is None: - while edge: - newedge = [] - for (x, y) in edge: - for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): - try: - p = pixel[s, t] - except (ValueError, IndexError): - pass + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except (ValueError, IndexError): + pass + else: + if border is None: + fill = _color_diff(p, background) <= thresh else: - if _color_diff(p, background) <= thresh: - pixel[s, t] = value - newedge.append((s, t)) - edge = newedge - else: - while edge: - newedge = [] - for (x, y) in edge: - for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): - try: - p = pixel[s, t] - except (ValueError, IndexError): - pass - else: - if p != value and p != border: - pixel[s, t] = value - newedge.append((s, t)) - edge = newedge + fill = p != value and p != border + if fill: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge def _color_diff(rgb1, rgb2): From d86fa316ca328f1d6647afb06cf89f5fddd45a33 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Sep 2018 21:02:32 +1000 Subject: [PATCH 86/87] Fixed typo [ci skip] --- docs/handbook/writing-your-own-file-decoder.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index aa2463bd1..258e9af3e 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -204,7 +204,7 @@ table describes some commonly used **raw modes**: +-----------+-----------------------------------------------------------------+ | ``RGBX`` | 24-bit true colour, stored as (red, green, blue, pad). | +-----------+-----------------------------------------------------------------+ -| ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, the | +| ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, then| | | all green pixels, finally all blue pixels). | +-----------+-----------------------------------------------------------------+ From f4fd517373d5a36887d0cd20fcc16de39b85275e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 5 Sep 2018 21:05:09 +1000 Subject: [PATCH 87/87] Removed blank line after heading for consistency [ci skip] --- docs/handbook/writing-your-own-file-decoder.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 258e9af3e..107e25f36 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -171,7 +171,6 @@ The fields are used as follows: stride defaults to 0. **orientation** - Whether the first line in the image is the top line on the screen (1), or the bottom line (-1). If omitted, the orientation defaults to 1.