From bed2ed7438b512e4cfb18bc3a3bb18f4a53d8a87 Mon Sep 17 00:00:00 2001 From: Meithal Date: Fri, 12 Jun 2020 09:08:20 +0200 Subject: [PATCH 0001/1186] Fixes BLP1 picture handling --- src/PIL/BlpImagePlugin.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 5ccba37dbd8..8cdf0aae1b6 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -245,7 +245,7 @@ def _open(self): if self.magic == b"BLP1": decoder = "BLP1" - self.mode = "RGB" + self.mode = "RGBA" elif self.magic == b"BLP2": decoder = "BLP2" self.mode = "RGBA" if self._blp_alpha_depth else "RGB" @@ -331,7 +331,7 @@ def _load(self): except struct.error: break b, g, r, a = palette[offset] - data.extend([r, g, b]) + data.extend([r, g, b, 0xFF]) # is there a case where alpha is used? self.set_as_raw(bytes(data)) else: @@ -353,9 +353,19 @@ def _decode_jpeg_stream(self): data = jpeg_header + data data = BytesIO(data) image = JpegImageFile(data) - self.tile = image.tile # :/ - self.fd = image.fp - self.mode = image.mode + image.mode = "RGBA" + image.tile = [("jpeg", (0, 0) + self.size, 0, ("RGBA", ""))] + + b, g, r, a = image.split() + if not any( + [a.getpixel((x, y)) for x in range(a.width) for y in range(a.height)] + ): + # try to unprotect completely transparent pictures + from PIL import ImageOps + + a = ImageOps.invert(a) + image = Image.merge("RGBA", (r, g, b, a)) + self.set_as_raw(image.tobytes()) class BLP2Decoder(_BLPBaseDecoder): From 096d2bf64a6d5d738c84cca7b57e19a0c468f14f Mon Sep 17 00:00:00 2001 From: Meithal Date: Fri, 12 Jun 2020 18:48:15 +0200 Subject: [PATCH 0002/1186] Coverage change fix --- src/PIL/BlpImagePlugin.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 8cdf0aae1b6..bb3a90bb84c 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -357,13 +357,6 @@ def _decode_jpeg_stream(self): image.tile = [("jpeg", (0, 0) + self.size, 0, ("RGBA", ""))] b, g, r, a = image.split() - if not any( - [a.getpixel((x, y)) for x in range(a.width) for y in range(a.height)] - ): - # try to unprotect completely transparent pictures - from PIL import ImageOps - - a = ImageOps.invert(a) image = Image.merge("RGBA", (r, g, b, a)) self.set_as_raw(image.tobytes()) From 6b81e34d676070e6fe519b525c27621ea6f5fe68 Mon Sep 17 00:00:00 2001 From: Piolie Date: Mon, 21 Dec 2020 00:56:30 -0300 Subject: [PATCH 0003/1186] Improve handling of PPM headers --- src/PIL/PpmImagePlugin.py | 76 +++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index abf4d651dc5..05993a7d15e 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -20,7 +20,7 @@ # # -------------------------------------------------------------------- -b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" +B_WHITESPACE = b"\x20\x09\x0a\x0b\x0c\x0d" MODES = { # standard @@ -49,25 +49,39 @@ class PpmImageFile(ImageFile.ImageFile): format = "PPM" format_description = "Pbmplus image" - def _token(self, s=b""): + def _read_token(self, token=b""): + def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF + while True: + c = self.fp.read(1) + if c in b"\r\n": + break + + while True: # read until non-whitespace is found + c = self.fp.read(1) + if c == b"#": # found comment, ignore it + _ignore_comment() + continue + if c in B_WHITESPACE: # found whitespace, ignore it + if c == b"": # reached EOF + raise EOFError("Reached EOF while reading header") + continue + break + + token += c + while True: # read until next whitespace c = self.fp.read(1) - if not c or c in b_whitespace: + if c == b"#": + _ignore_comment() + continue + if c in B_WHITESPACE: # token ended break - if c > b"\x79": - raise ValueError("Expected ASCII value, found binary") - s = s + c - if len(s) > 9: - raise ValueError("Expected int, got > 9 digits") - return s + token += c + return token def _open(self): - - # check magic - s = self.fp.read(1) - if s != b"P": - raise SyntaxError("not a PPM file") - magic_number = self._token(s) + P = self.fp.read(1) + magic_number = self._read_token(P) mode = MODES[magic_number] self.custom_mimetype = { @@ -83,29 +97,21 @@ def _open(self): self.mode = rawmode = mode for ix in range(3): - while True: - while True: - s = self.fp.read(1) - if s not in b_whitespace: - break - if s == b"": - raise ValueError("File does not extend beyond magic number") - if s != b"#": - break - s = self.fp.readline() - s = int(self._token(s)) - if ix == 0: - xsize = s - elif ix == 1: - ysize = s + try: # check token sanity + token = int(self._read_token()) + except ValueError: + raise SyntaxError("Non-decimal-ASCII found in header") + if ix == 0: # token is the x size + xsize = token + elif ix == 1: # token is the y size + ysize = token if mode == "1": break - elif ix == 2: - # maxgrey - if s > 255: + elif ix == 2: # token is maxval + if token > 255: if not mode == "L": - raise ValueError(f"Too many colors for band: {s}") - if s < 2 ** 16: + raise SyntaxError(f"Too many colors for band: {token}") + if token < 2 ** 16: self.mode = "I" rawmode = "I;16B" else: From 699afe1e8915c8332e1116fbd08ef972deb9af35 Mon Sep 17 00:00:00 2001 From: Piolie Date: Mon, 21 Dec 2020 01:15:49 -0300 Subject: [PATCH 0004/1186] Improve PPM tests --- Tests/test_file_ppm.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index e7c3fb06ff7..9429f1e2a63 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -1,6 +1,6 @@ import pytest -from PIL import Image +from PIL import Image, UnidentifiedImageError from .helper import assert_image_equal, assert_image_similar, hopper @@ -50,12 +50,30 @@ def test_pnm(tmp_path): assert_image_equal(im, reloaded) +def test_header_with_comments(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P6 #comment\n#comment\r12#comment\r8\n128 #comment\n255\n") + + with Image.open(path) as im: + assert im.size == (128, 128) + + +def test_nondecimal_header(tmp_path): + path = str(tmp_path / "temp.djvurle") + with open(path, "wb") as f: + f.write(b"P6\n128\x00") + + with pytest.raises(UnidentifiedImageError): + Image.open(path) + + def test_truncated_file(tmp_path): path = str(tmp_path / "temp.pgm") with open(path, "w") as f: f.write("P6") - with pytest.raises(ValueError): + with pytest.raises(UnidentifiedImageError): Image.open(path) @@ -65,7 +83,7 @@ def test_neg_ppm(): # has been removed. The default opener doesn't accept negative # sizes. - with pytest.raises(OSError): + with pytest.raises(UnidentifiedImageError): Image.open("Tests/images/negative_size.ppm") From d2ad27d70a00efa98560cf5036326abcae850d5e Mon Sep 17 00:00:00 2001 From: Piolie Date: Mon, 4 Jan 2021 01:49:19 -0300 Subject: [PATCH 0005/1186] Correctly check magic number --- src/PIL/PpmImagePlugin.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 05993a7d15e..b9837a0fe84 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -49,6 +49,16 @@ class PpmImageFile(ImageFile.ImageFile): format = "PPM" format_description = "Pbmplus image" + def _read_magic(self, s=b""): + while True: # read until next whitespace + c = self.fp.read(1) + if c in B_WHITESPACE: + break + s = s + c + if len(s) > 6: # exceeded max magic number length + break + return s + def _read_token(self, token=b""): def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF while True: @@ -80,9 +90,11 @@ def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF return token def _open(self): - P = self.fp.read(1) - magic_number = self._read_token(P) - mode = MODES[magic_number] + magic_number = self._read_magic() + try: + mode = MODES[magic_number] + except KeyError: + raise SyntaxError("Not a PPM image file") from None self.custom_mimetype = { b"P4": "image/x-portable-bitmap", From 4dbe244e42cd7225ef3b77b3d592e3dd522e4386 Mon Sep 17 00:00:00 2001 From: Piolie Date: Wed, 6 Jan 2021 01:07:14 -0300 Subject: [PATCH 0006/1186] Add token limit --- src/PIL/PpmImagePlugin.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index b9837a0fe84..6ece914c350 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -87,6 +87,8 @@ def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF if c in B_WHITESPACE: # token ended break token += c + if len(token) > 9: + raise ValueError(f"Token too long: {token}") return token def _open(self): @@ -109,8 +111,9 @@ def _open(self): self.mode = rawmode = mode for ix in range(3): + token = self._read_token() try: # check token sanity - token = int(self._read_token()) + token = int(token) except ValueError: raise SyntaxError("Non-decimal-ASCII found in header") if ix == 0: # token is the x size From 5d0ad5e2e9a5065d69a00890342334f374277d1c Mon Sep 17 00:00:00 2001 From: Piolie Date: Wed, 6 Jan 2021 01:15:07 -0300 Subject: [PATCH 0007/1186] Revert exception types to `ValueError` --- Tests/test_file_ppm.py | 6 +++--- src/PIL/PpmImagePlugin.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 9429f1e2a63..21a810e3037 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -64,7 +64,7 @@ def test_nondecimal_header(tmp_path): with open(path, "wb") as f: f.write(b"P6\n128\x00") - with pytest.raises(UnidentifiedImageError): + with pytest.raises(ValueError): Image.open(path) @@ -73,7 +73,7 @@ def test_truncated_file(tmp_path): with open(path, "w") as f: f.write("P6") - with pytest.raises(UnidentifiedImageError): + with pytest.raises(ValueError): Image.open(path) @@ -83,7 +83,7 @@ def test_neg_ppm(): # has been removed. The default opener doesn't accept negative # sizes. - with pytest.raises(UnidentifiedImageError): + with pytest.raises(OSError): Image.open("Tests/images/negative_size.ppm") diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 6ece914c350..efd845d3b47 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -73,7 +73,7 @@ def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF continue if c in B_WHITESPACE: # found whitespace, ignore it if c == b"": # reached EOF - raise EOFError("Reached EOF while reading header") + raise ValueError("Reached EOF while reading header") continue break @@ -115,7 +115,7 @@ def _open(self): try: # check token sanity token = int(token) except ValueError: - raise SyntaxError("Non-decimal-ASCII found in header") + raise ValueError(f"Non-decimal-ASCII found in header: {token}") if ix == 0: # token is the x size xsize = token elif ix == 1: # token is the y size @@ -125,7 +125,7 @@ def _open(self): elif ix == 2: # token is maxval if token > 255: if not mode == "L": - raise SyntaxError(f"Too many colors for band: {token}") + raise ValueError(f"Too many colors for band: {token}") if token < 2 ** 16: self.mode = "I" rawmode = "I;16B" @@ -156,7 +156,7 @@ def _save(im, fp, filename): elif im.mode == "RGBA": rawmode, head = "RGB", b"P6" else: - raise OSError(f"cannot write mode {im.mode} as PPM") + raise OSError(f"Cannot write mode {im.mode} as PPM") fp.write(head + ("\n%d %d\n" % im.size).encode("ascii")) if head == b"P6": fp.write(b"255\n") From 002e0bd6975974288eb46e1609ba319699dc44ba Mon Sep 17 00:00:00 2001 From: Piolie Date: Wed, 6 Jan 2021 01:21:35 -0300 Subject: [PATCH 0008/1186] Add tests for token size and wrong magic number --- Tests/test_file_ppm.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 21a810e3037..0ea6b277eda 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -49,6 +49,13 @@ def test_pnm(tmp_path): with Image.open(f) as reloaded: assert_image_equal(im, reloaded) +def test_not_ppm(tmp_path): + path = str(tmp_path / "temp.djvurle") + with open(path, "wb") as f: + f.write(b"PyXX") + + with pytest.raises(UnidentifiedImageError): + Image.open(path) def test_header_with_comments(tmp_path): path = str(tmp_path / "temp.ppm") @@ -67,6 +74,13 @@ def test_nondecimal_header(tmp_path): with pytest.raises(ValueError): Image.open(path) +def test_token_too_long(tmp_path): + path = str(tmp_path / "temp.djvurle") + with open(path, "wb") as f: + f.write(b"P6\n 0123456789") + + with pytest.raises(ValueError): + Image.open(path) def test_truncated_file(tmp_path): path = str(tmp_path / "temp.pgm") From 73fed77c0c58efd2ce11b8c0e96858bd03655163 Mon Sep 17 00:00:00 2001 From: Piolie Date: Wed, 6 Jan 2021 14:46:30 -0300 Subject: [PATCH 0009/1186] Suppress exception context --- src/PIL/PpmImagePlugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index efd845d3b47..8bd7d16db39 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -115,7 +115,9 @@ def _open(self): try: # check token sanity token = int(token) except ValueError: - raise ValueError(f"Non-decimal-ASCII found in header: {token}") + raise ValueError( + f"Non-decimal-ASCII found in header: {token}" + ) from None if ix == 0: # token is the x size xsize = token elif ix == 1: # token is the y size From bc5ecfb79c3dca0553e2b863ba7d306f2e0fd434 Mon Sep 17 00:00:00 2001 From: Piolie Date: Wed, 6 Jan 2021 14:53:30 -0300 Subject: [PATCH 0010/1186] Make minor changes to tests - add test for maxcolors; - extend coverage for wrong magic number; - fix linting. --- Tests/test_file_ppm.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 0ea6b277eda..77798514dac 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -49,14 +49,16 @@ def test_pnm(tmp_path): with Image.open(f) as reloaded: assert_image_equal(im, reloaded) + def test_not_ppm(tmp_path): path = str(tmp_path / "temp.djvurle") with open(path, "wb") as f: - f.write(b"PyXX") + f.write(b"PyInvalid") with pytest.raises(UnidentifiedImageError): Image.open(path) + def test_header_with_comments(tmp_path): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: @@ -74,6 +76,7 @@ def test_nondecimal_header(tmp_path): with pytest.raises(ValueError): Image.open(path) + def test_token_too_long(tmp_path): path = str(tmp_path / "temp.djvurle") with open(path, "wb") as f: @@ -82,6 +85,16 @@ def test_token_too_long(tmp_path): with pytest.raises(ValueError): Image.open(path) + +def test_too_many_colors(tmp_path): + path = str(tmp_path / "temp.djvurle") + with open(path, "wb") as f: + f.write(b"P6\n1 1\n1000\n") + + with pytest.raises(ValueError): + Image.open(path) + + def test_truncated_file(tmp_path): path = str(tmp_path / "temp.pgm") with open(path, "w") as f: From 50522d932ea93dc09db0fae52a0ebe80a5f4eb08 Mon Sep 17 00:00:00 2001 From: Piolie Date: Sun, 31 Jan 2021 00:31:32 -0300 Subject: [PATCH 0011/1186] Change max token size to 10 - ...so as not to reject "2,147,483,647" (2 ** 31 - 1); - reword exception message. --- src/PIL/PpmImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 8bd7d16db39..5ff98e346de 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -87,8 +87,8 @@ def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF if c in B_WHITESPACE: # token ended break token += c - if len(token) > 9: - raise ValueError(f"Token too long: {token}") + if len(token) > 10: + raise ValueError(f"Token too long in file header: {token}") return token def _open(self): From 39288f0fb0755b8ef9bbc4867bda6a19e91287fd Mon Sep 17 00:00:00 2001 From: Piolie Date: Sun, 31 Jan 2021 00:51:39 -0300 Subject: [PATCH 0012/1186] Create `maxval` variable --- src/PIL/PpmImagePlugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 5ff98e346de..93ce3a4d221 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -125,7 +125,8 @@ def _open(self): if mode == "1": break elif ix == 2: # token is maxval - if token > 255: + maxval = token + if maxval > 255: if not mode == "L": raise ValueError(f"Too many colors for band: {token}") if token < 2 ** 16: From b43654d15954a5af46ad618291b7c859549ccc63 Mon Sep 17 00:00:00 2001 From: Piolie Date: Sun, 10 Jan 2021 18:45:46 -0300 Subject: [PATCH 0013/1186] Change variable name in `_read_magic()` --- src/PIL/PpmImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 93ce3a4d221..e25b4bcec02 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -49,15 +49,15 @@ class PpmImageFile(ImageFile.ImageFile): format = "PPM" format_description = "Pbmplus image" - def _read_magic(self, s=b""): + def _read_magic(self, magic=b""): while True: # read until next whitespace c = self.fp.read(1) if c in B_WHITESPACE: break - s = s + c - if len(s) > 6: # exceeded max magic number length + magic += c + if len(magic) > 6: # exceeded max magic number length break - return s + return magic def _read_token(self, token=b""): def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF From b6f6fba8cf170e60619da81ff1a24c2ef4364be5 Mon Sep 17 00:00:00 2001 From: Piolie Date: Wed, 13 Jan 2021 18:45:29 -0300 Subject: [PATCH 0014/1186] Rewrite `_ignore_comment` as one-liner --- src/PIL/PpmImagePlugin.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index e25b4bcec02..95987b4a45d 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -61,10 +61,8 @@ def _read_magic(self, magic=b""): def _read_token(self, token=b""): def _ignore_comment(): # ignores rest of the line; stops at CR, LF or EOF - while True: - c = self.fp.read(1) - if c in b"\r\n": - break + while self.fp.read(1) not in b"\r\n": + pass while True: # read until non-whitespace is found c = self.fp.read(1) From 41a439da7d7c38e54660011abee948d177f67126 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Mar 2021 14:42:36 +1100 Subject: [PATCH 0015/1186] Added context managers --- Tests/test_file_ppm.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 77798514dac..94fbc62a2ff 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -56,7 +56,8 @@ def test_not_ppm(tmp_path): f.write(b"PyInvalid") with pytest.raises(UnidentifiedImageError): - Image.open(path) + with Image.open(path): + pass def test_header_with_comments(tmp_path): @@ -74,7 +75,8 @@ def test_nondecimal_header(tmp_path): f.write(b"P6\n128\x00") with pytest.raises(ValueError): - Image.open(path) + with Image.open(path): + pass def test_token_too_long(tmp_path): @@ -83,7 +85,8 @@ def test_token_too_long(tmp_path): f.write(b"P6\n 0123456789") with pytest.raises(ValueError): - Image.open(path) + with Image.open(path): + pass def test_too_many_colors(tmp_path): @@ -92,7 +95,8 @@ def test_too_many_colors(tmp_path): f.write(b"P6\n1 1\n1000\n") with pytest.raises(ValueError): - Image.open(path) + with Image.open(path): + pass def test_truncated_file(tmp_path): From 8ad5172e8858b5dd023612bbacb37f066b577012 Mon Sep 17 00:00:00 2001 From: Piolie Date: Sun, 21 Mar 2021 02:16:39 -0300 Subject: [PATCH 0016/1186] Fix wrong extension in temp test files --- Tests/test_file_ppm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 94fbc62a2ff..ecc3401b5fa 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -51,7 +51,7 @@ def test_pnm(tmp_path): def test_not_ppm(tmp_path): - path = str(tmp_path / "temp.djvurle") + path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: f.write(b"PyInvalid") @@ -70,7 +70,7 @@ def test_header_with_comments(tmp_path): def test_nondecimal_header(tmp_path): - path = str(tmp_path / "temp.djvurle") + path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: f.write(b"P6\n128\x00") @@ -80,7 +80,7 @@ def test_nondecimal_header(tmp_path): def test_token_too_long(tmp_path): - path = str(tmp_path / "temp.djvurle") + path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: f.write(b"P6\n 0123456789") @@ -90,7 +90,7 @@ def test_token_too_long(tmp_path): def test_too_many_colors(tmp_path): - path = str(tmp_path / "temp.djvurle") + path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: f.write(b"P6\n1 1\n1000\n") From 9c2cbcf4520fe92d3d6b70bda13e3b1078ff73b1 Mon Sep 17 00:00:00 2001 From: Piolie Date: Mon, 22 Mar 2021 13:06:16 -0300 Subject: [PATCH 0017/1186] Keep case consistency in error messages Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/PpmImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 95987b4a45d..725ddec10f3 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -157,7 +157,7 @@ def _save(im, fp, filename): elif im.mode == "RGBA": rawmode, head = "RGB", b"P6" else: - raise OSError(f"Cannot write mode {im.mode} as PPM") + raise OSError(f"cannot write mode {im.mode} as PPM") fp.write(head + ("\n%d %d\n" % im.size).encode("ascii")) if head == b"P6": fp.write(b"255\n") From 919f38e3d9b13aef38b87a7e08ce8ba879be7cf3 Mon Sep 17 00:00:00 2001 From: Meithal Date: Mon, 5 Jul 2021 00:20:15 +0200 Subject: [PATCH 0018/1186] Try test --- Tests/images/blp/blp1_jpeg.jpg | Bin 0 -> 1651 bytes Tests/images/blp/blp1_jpeg.png | Bin 0 -> 270 bytes Tests/images/blp/war3mapMap.blp | Bin 0 -> 19430 bytes Tests/images/blp/war3mapMap.jpg | Bin 0 -> 1651 bytes Tests/images/blp/war3mapMap.png | Bin 0 -> 270 bytes Tests/test_file_blp.py | 6 ++++++ src/PIL/BlpImagePlugin.py | 14 ++++++++++++-- 7 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Tests/images/blp/blp1_jpeg.jpg create mode 100644 Tests/images/blp/blp1_jpeg.png create mode 100644 Tests/images/blp/war3mapMap.blp create mode 100644 Tests/images/blp/war3mapMap.jpg create mode 100644 Tests/images/blp/war3mapMap.png diff --git a/Tests/images/blp/blp1_jpeg.jpg b/Tests/images/blp/blp1_jpeg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c045c5945c1071f3d4bd4e3f0d878cec6899bc17 GIT binary patch literal 1651 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<uA z0}~@NGZPClD=P~NP<1U(o`FS>RY=j$kxe)-kzJ`!#HexNLJno8jR!@8E`CrkPAY2R z|V^&07y2J$~}^+4C1KUw!=a`ODXD-+%o41@afjpD+ON7@EHXf&OA*VPR%r2lxYE#}NLy#lXYN2#h>tK?Zwfl gP32wyWDa4_AkC=Dz<6e<1v|(Sp00i_>zopr0LjD?Hvj+t literal 0 HcmV?d00001 diff --git a/Tests/images/blp/war3mapMap.blp b/Tests/images/blp/war3mapMap.blp new file mode 100644 index 0000000000000000000000000000000000000000..03afb3aa5919a856946c916056f89a522ad82692 GIT binary patch literal 19430 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 Tgm1M#U*!PD{hz-9NCW>1<>tIM literal 0 HcmV?d00001 diff --git a/Tests/images/blp/war3mapMap.jpg b/Tests/images/blp/war3mapMap.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c045c5945c1071f3d4bd4e3f0d878cec6899bc17 GIT binary patch literal 1651 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<uA z0}~@NGZPClD=P~NP<1U(o`FS>RY=j$kxe)-kzJ`!#HexNLJno8jR!@8E`CrkPAY2R z|V^&07y2J$~}^+4C1KUw!=a`ODXD-+%o41@afjpD+ON7@EHXf&OA*VPR%r2lxYE#}NLy#lXYN2#h>tK?Zwfl gP32wyWDa4_AkC=Dz<6e<1v|(Sp00i_>zopr0LjD?Hvj+t literal 0 HcmV?d00001 diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index 15bd7e4f8ef..3e4401996ec 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -20,6 +20,12 @@ def test_load_blp2_dxt1a(): assert_image_equal_tofile(im, "Tests/images/blp/blp2_dxt1a.png") +def test_load_blp1(): + with Image.open("Tests/images/blp/war3mapMap.blp") as im: + im.convert("RGB") + assert_image_equal_tofile(im, "Tests/images/blp/war3mapMap.png") + + @pytest.mark.parametrize( "test_file", [ diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index f62b1bebe9b..c286a8197d8 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -28,7 +28,7 @@ - DXT3 compression is used if alpha_encoding == 1. - DXT5 compression is used if alpha_encoding == 7. """ - +print("foo") import struct from io import BytesIO @@ -245,7 +245,7 @@ def _open(self): if self.magic == b"BLP1": decoder = "BLP1" - self.mode = "RGBA" + self.mode = "BGRA" elif self.magic == b"BLP2": decoder = "BLP2" self.mode = "RGBA" if self._blp_alpha_depth else "RGB" @@ -361,6 +361,16 @@ def _decode_jpeg_stream(self): image.tile = [("jpeg", (0, 0) + self.size, 0, ("RGBA", ""))] b, g, r, a = image.split() + print(b, g, r, a) + if not any( + [a.getpixel((x, y)) for x in range(a.width) for y in range(a.height)] + ): + # try to unprotect completely transparent pictures + from PIL import ImageOps + + a = ImageOps.invert(a) + + image = Image.merge("RGBA", (r, g, b, a)) self.set_as_raw(image.tobytes()) From 3fbc9eb2290a9fcaba31f972dc8aaf09358a2e45 Mon Sep 17 00:00:00 2001 From: Meithal Date: Thu, 15 Jul 2021 20:33:35 +0200 Subject: [PATCH 0019/1186] self.mode = "BGRA" wasn't correct and captured by #affa059 --- Tests/images/blp/war3mapMap.jpg | Bin 1651 -> 0 bytes Tests/images/blp/war3mapMap.png | Bin 270 -> 0 bytes Tests/test_file_blp.py | 3 ++- src/PIL/BlpImagePlugin.py | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 Tests/images/blp/war3mapMap.jpg delete mode 100644 Tests/images/blp/war3mapMap.png diff --git a/Tests/images/blp/war3mapMap.jpg b/Tests/images/blp/war3mapMap.jpg deleted file mode 100644 index c045c5945c1071f3d4bd4e3f0d878cec6899bc17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1651 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<uA z0}~@NGZPClD=P~NP<1U(o`FS>RY=j$kxe)-kzJ`!#HexNLJno8jR!@8E`CrkPAY2R z|V^&07y2J$~}^+4C1KUw!=a`ODXD-+%o41@afjpD+ON7@EHXf&OA*VPR%r2lxYE#}NLy#lXYN2#h>tK?Zwfl gP32wyWDa4_AkC=Dz<6e<1v|(Sp00i_>zopr0LjD?Hvj+t diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index 3e4401996ec..c3540f4fab3 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -22,7 +22,8 @@ def test_load_blp2_dxt1a(): def test_load_blp1(): with Image.open("Tests/images/blp/war3mapMap.blp") as im: - im.convert("RGB") + png = im.copy() + png.save(fp="Tests/images/blp/war3mapMap.png") assert_image_equal_tofile(im, "Tests/images/blp/war3mapMap.png") diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index c286a8197d8..907d33a20ef 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -245,7 +245,7 @@ def _open(self): if self.magic == b"BLP1": decoder = "BLP1" - self.mode = "BGRA" + self.mode = "RGBA" elif self.magic == b"BLP2": decoder = "BLP2" self.mode = "RGBA" if self._blp_alpha_depth else "RGB" From a139b9784541659c86ba9994563f07a3ba3ef4bc Mon Sep 17 00:00:00 2001 From: Meithal Date: Thu, 15 Jul 2021 20:48:47 +0200 Subject: [PATCH 0020/1186] Cleanup --- src/PIL/BlpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 907d33a20ef..6909599c149 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -28,7 +28,7 @@ - DXT3 compression is used if alpha_encoding == 1. - DXT5 compression is used if alpha_encoding == 7. """ -print("foo") + import struct from io import BytesIO From fdbb1870d44b2a831d37b0545f5bdba2d147518e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 08:52:46 -0500 Subject: [PATCH 0021/1186] CI: Add a cygwin run to GitHub Actions. Requested for the _imagingtk fix. --- .github/workflows/test-cygwin.yml | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/test-cygwin.yml diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml new file mode 100644 index 00000000000..4275e65a67b --- /dev/null +++ b/.github/workflows/test-cygwin.yml @@ -0,0 +1,48 @@ +name: Test Cygwin + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: windows-2019 + name: Cygwin Python 3.8 + + steps: + - name: Checkout Pillow + uses: actions/checkout@v2 + - name: Set up Cygwin + uses: egor-tensin/setup-cygwin@v3 + with: + platform: x64 + packages: > + python38-devel python38-tkinter libfreetype-devel + libimagequant-devel libjpeg-devel liblcms2-devel + libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel + libxcb-devel python38-olefile python38-pathlib + python38-numpy netpbm ImageMagick jpeg xorg-server-extra + xorg-server-common xinit python38-setuptools + python38-wheel + install-dir: 'C:\tools\cygwin' + - name: Clean up path + uses: egor-tensin/cleanup-path@v2 + with: + dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Build Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 setup.py bdist_wheel + - name: Install Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + - name: Test Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 selftest.py + xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 67bef87a070af4e239707de64670ac833031e46c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 18:19:22 -0500 Subject: [PATCH 0022/1186] CI: Specify where the command line goes in the shell. I think this is how specifying the shell works. The documentation isn't terribly clear. --- .github/workflows/test-cygwin.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 4275e65a67b..c22fe2bec70 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -34,15 +34,15 @@ jobs: with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - name: Build Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 setup.py bdist_wheel - name: Install Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl - name: Test Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 selftest.py xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From b798989f33b4ef275360cb491d0c555079fd899e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:08:28 -0500 Subject: [PATCH 0023/1186] CI: Make sure all python requirements are installed in Cygwin CI. For some reason wheel wasn't installed properly. --- .github/workflows/test-cygwin.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c22fe2bec70..cb0c84ef53c 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -21,18 +21,24 @@ jobs: with: platform: x64 packages: > - python38-devel python38-tkinter libfreetype-devel - libimagequant-devel libjpeg-devel liblcms2-devel - libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel - libxcb-devel python38-olefile python38-pathlib - python38-numpy netpbm ImageMagick jpeg xorg-server-extra - xorg-server-common xinit python38-setuptools - python38-wheel + libfreetype-devel libimagequant-devel libjpeg-devel + liblcms2-devel libopenjp2-devel libraqm-devel + libtiff-devel libwebp-devel libxcb-devel netpbm + ImageMagick jpeg xorg-server-extra xorg-server-common + xinit python38-setuptools python38-devel python38-tkinter + python38-wheel python38-pip python38-olefile + python38-numpy python38-pytest python38-sphinx + python38-packaging python38-cffi install-dir: 'C:\tools\cygwin' - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Ensure python dependencies installed + shell: 'C:\tools\cygwin\bin\dash.exe {0}' + run: | + /usr/bin/python3.8 -m pip install pip wheel setuptools + /usr/bin/python3.8 -m pip install -r requirements.txt - name: Build Pillow shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | From a4495decf878298abb8b379251c6cf98aa662736 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:57:44 -0500 Subject: [PATCH 0024/1186] CI: Stop using dash so the DOS line endings don't throw things off. --- .github/workflows/test-cygwin.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index cb0c84ef53c..904658f80a2 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -29,26 +29,23 @@ jobs: python38-wheel python38-pip python38-olefile python38-numpy python38-pytest python38-sphinx python38-packaging python38-cffi + libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel install-dir: 'C:\tools\cygwin' - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - name: Ensure python dependencies installed - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 -m pip install pip wheel setuptools - /usr/bin/python3.8 -m pip install -r requirements.txt + C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools + C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt - name: Build Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 setup.py bdist_wheel + C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + C:\tools\cygwin\bin\python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl - name: Test Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 selftest.py - xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\python3.8 selftest.py + C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 8ebac45f24fa53bcdb148b418222b41db0455626 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:14:41 -0500 Subject: [PATCH 0025/1186] CI: Explicitly use dash to get shell globbing. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 904658f80a2..c1404b2c68b 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow run: | - C:\tools\cygwin\bin\python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install *.whl' - name: Test Pillow run: | C:\tools\cygwin\bin\python3.8 selftest.py From cd087c600775c52af65a1a3ea6ff9818718f63a9 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:20:47 -0500 Subject: [PATCH 0026/1186] CI: Fix the path to the build wheels. setup.py bdist_wheel goes to dist/*.whl pip wheel goes to *.whl --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c1404b2c68b..ff496443b00 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow run: | - C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install *.whl' + C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - name: Test Pillow run: | C:\tools\cygwin\bin\python3.8 selftest.py From 1daaf9273ceb94fd15e84cbe4f9f095ed7f6669c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:53:43 -0500 Subject: [PATCH 0027/1186] CI: Split the config check from the actual test. I'm using selftest.py to check whether I've installed everything. Pytest actually finds and runs the tests. For some reason that wasn't running earlier. --- .github/workflows/test-cygwin.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index ff496443b00..94711051441 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -45,7 +45,9 @@ jobs: - name: Install Pillow run: | C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - - name: Test Pillow + - name: Check Pillow configuration run: | C:\tools\cygwin\bin\python3.8 selftest.py + - name: Test Pillow + run: | C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 9ec4034bff2f03bac37ac0ec0e60f4c0bbaf3313 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 08:26:24 -0500 Subject: [PATCH 0028/1186] CI: Incorporate suggestions from PR. Upload coverage information, add Cygwin to the list of systems with CI, space out entries. --- .github/workflows/test-cygwin.yml | 28 ++++++++++++++++++++-------- docs/installation.rst | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 94711051441..d7c74e7bc65 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -1,12 +1,6 @@ name: Test Cygwin -on: - push: - branches: - - main - pull_request: - branches: - - main +on: [push, pull_request, workflow_dispatch] jobs: build: @@ -16,6 +10,7 @@ jobs: steps: - name: Checkout Pillow uses: actions/checkout@v2 + - name: Set up Cygwin uses: egor-tensin/setup-cygwin@v3 with: @@ -31,23 +26,40 @@ jobs: python38-packaging python38-cffi libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel install-dir: 'C:\tools\cygwin' + - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - - name: Ensure python dependencies installed + + - name: Build system information + run: C:\tools\cygwin\bin\python3.8 .github/workflows/system-info.py + + - name: Ensure Python dependencies are installed run: | C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt + - name: Build Pillow run: | C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel + - name: Install Pillow run: | C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' + - name: Check Pillow configuration run: | C:\tools\cygwin\bin\python3.8 selftest.py + - name: Test Pillow run: | C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + + - name: After success + run: | + C:\tools\cygwin\bin\dash .ci/after_success.sh + + - name: Upload coverage + run: | + C:\tools\cygwin\bin\bash -c 'bash <(curl -s https://codecov.io/bash) -F' diff --git a/docs/installation.rst b/docs/installation.rst index 1fb6897d279..3e5123c7a53 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,6 +460,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| Cygwin | 3.8 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | From 407abbfa186d545cd2dc4850c0f6f4d29032c386 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:12:31 -0500 Subject: [PATCH 0029/1186] CI: Try to get tests fully working The actual test step wasn't running, so try to run that as a shell script rather than an executable. Also get more of the dependencies installed. --- .github/workflows/test-cygwin.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d7c74e7bc65..24bd6a2be20 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -23,8 +23,7 @@ jobs: xinit python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy python38-pytest python38-sphinx - python38-packaging python38-cffi - libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel + python38-packaging python38-cffi python38-requests install-dir: 'C:\tools\cygwin' - name: Clean up path @@ -54,11 +53,11 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh - name: After success run: | - C:\tools\cygwin\bin\dash .ci/after_success.sh + C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage run: | From 4cfb1a854f531afdab4c0c4bb7a4fc6fc754e69a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:13:56 -0500 Subject: [PATCH 0030/1186] CI: Use the new GitHub Action for codecov The old bash downloader will be removed soon. --- .github/workflows/test-cygwin.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 24bd6a2be20..8bb3e4cdd4d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,5 +60,8 @@ jobs: C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage - run: | - C:\tools\cygwin\bin\bash -c 'bash <(curl -s https://codecov.io/bash) -F' + uses: codecov/codecov-actions@v2 + with: + file: ./coverage.xml + flags: GHA_Cygwin + name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} From 90cf149cbe9449934767eba376f690d37b1cc53a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:17:28 -0500 Subject: [PATCH 0031/1186] CI: Fix the spelling on the codecov repository. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 8bb3e4cdd4d..6215b72f8f5 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,7 +60,7 @@ jobs: C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-actions@v2 + uses: codecov/codecov-action@v2 with: file: ./coverage.xml flags: GHA_Cygwin From 7fd3e9977e7dd0c04e4e72fdc65f59253f0e6281 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:59:45 -0500 Subject: [PATCH 0032/1186] CI: Make sure correct version of python is used for testing. Cygwin is trying to use the highest-available Python version. One of the Python packages has scripts in /usr/bin that should be in the python39- subpackage. --- .ci/test.sh | 4 ++-- .github/workflows/test-cygwin.yml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index 8ff7c5f6483..b0f019d6607 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,6 +2,6 @@ set -e -python3 -c "from PIL import Image" +python3$1 -c "from PIL import Image" -python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE +python3$1 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6215b72f8f5..bbe5eccee72 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,8 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\dash -c "mv .ci/test.sh .ci/test.sh.dos && /bin/tr -d '\r' <.ci/test.sh.dos >test.sh" + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success run: | From b3cb55f82360bf39eebb363d8f65cfd97bebf5fe Mon Sep 17 00:00:00 2001 From: Alexey Shamrin Date: Wed, 15 Dec 2021 22:35:32 +0200 Subject: [PATCH 0033/1186] keep IPython/Jupyter text/plain output stable --- src/PIL/Image.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0fca3fa5cc4..2653c6274c5 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -644,6 +644,19 @@ def __repr__(self): id(self), ) + def _repr_pretty_(self, p, cycle): + """IPython plain text display support""" + + # Same as __repr__ but without unpredicatable id(self), + # to keep Jupyter notebook `text/plain` output stable. + p.text("<%s.%s image mode=%s size=%dx%d>" % ( + self.__class__.__module__, + self.__class__.__name__, + self.mode, + self.size[0], + self.size[1], + )) + def _repr_png_(self): """iPython display hook support From 56d630294c31ea7c1360a9830ad82d9303ea6602 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 15 Dec 2021 20:39:38 +0000 Subject: [PATCH 0034/1186] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/Image.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2653c6274c5..3677cccc3f2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -649,13 +649,16 @@ def _repr_pretty_(self, p, cycle): # Same as __repr__ but without unpredicatable id(self), # to keep Jupyter notebook `text/plain` output stable. - p.text("<%s.%s image mode=%s size=%dx%d>" % ( - self.__class__.__module__, - self.__class__.__name__, - self.mode, - self.size[0], - self.size[1], - )) + p.text( + "<%s.%s image mode=%s size=%dx%d>" + % ( + self.__class__.__module__, + self.__class__.__name__, + self.mode, + self.size[0], + self.size[1], + ) + ) def _repr_png_(self): """iPython display hook support From 1c25d95d5abf3ae8b4de6b4e55d42c8f8593cf80 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 09:55:24 -0500 Subject: [PATCH 0035/1186] CI: Get cygwin tests running They segfault in one of the NumPy tests, but they run. --- .github/workflows/test-cygwin.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index bbe5eccee72..d3ab0a36149 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,9 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\dash -c "mv .ci/test.sh .ci/test.sh.dos && /bin/tr -d '\r' <.ci/test.sh.dos >test.sh" + C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos + C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" + C:\tools\cygwin\bin\chmod u+x .ci/test.sh C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success @@ -66,3 +68,10 @@ jobs: file: ./coverage.xml flags: GHA_Cygwin name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} + + - name: After failure + if: failure() + run: | + C:\tools\cygwin\bin\uname -a + C:\tools\cygwin\bin\python3.8 -m pip list + C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow From b9fee08c5911dcdb38d320260589cce3229c5c72 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 10:00:14 -0500 Subject: [PATCH 0036/1186] TST: Parametrize numpy roundtrip to find failing case Segfaults are annoying to debug. --- .github/workflows/test-cygwin.yml | 2 ++ Tests/test_numpy.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d3ab0a36149..b6a305b7151 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -38,6 +38,7 @@ jobs: run: | C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt + C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked - name: Build Pillow run: | @@ -56,6 +57,7 @@ jobs: C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" C:\tools\cygwin\bin\chmod u+x .ci/test.sh + C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index def7adf3f02..936474fe8d2 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -189,8 +189,9 @@ def test_putdata(): assert len(im.getdata()) == len(arr) -def test_roundtrip_eye(): - for dtype in ( +@pytest.mark.parametrize( + "dtype", + ( bool, numpy.bool8, numpy.int8, @@ -202,9 +203,11 @@ def test_roundtrip_eye(): float, numpy.float32, numpy.float64, - ): - arr = numpy.eye(10, dtype=dtype) - numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) + ), +) +def test_roundtrip_eye(dtype): + arr = numpy.eye(10, dtype=dtype) + numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) def test_zero_size(): From 77c8a07f1c6f5330146a31023de06d6f541e4735 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:28:30 -0500 Subject: [PATCH 0037/1186] DOC: Rearrange the Cygwin entries on the support matrix. From a suggestion on the PR, make it clearer that Cygwin runs on Windows. Also record the Cygwin versions in the check against Python versions. --- docs/installation.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 3e5123c7a53..d77e2742f2c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,8 +460,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Cygwin | 3.8 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | @@ -482,6 +480,8 @@ These platforms are built and tested for every change. | Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 | | +----------------------------+---------------------+ | | 3.9/MinGW | x86, x86-64 | +| +----------------------------+---------------------+ +| | 3.8/Cygwin | x86-64 | +----------------------------------+----------------------------+---------------------+ @@ -560,6 +560,8 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+---------------------------+------------------+--------------+ | Windows 10 | 3.7 | 7.1.0 |x86-64 | +----------------------------------+---------------------------+------------------+--------------+ +| Windows 10/Cygwin 3.3 | 3.6, 3.7, 3.8, 3.9 | 8.4.0 |x86-64 | ++----------------------------------+---------------------------+------------------+--------------+ | Windows 8.1 Pro | 2.6, 2.7, 3.2, 3.3, 3.4 | 2.4.0 |x86,x86-64 | +----------------------------------+---------------------------+------------------+--------------+ | Windows 8 Pro | 2.6, 2.7, 3.2, 3.3, 3.4a3 | 2.2.0 |x86,x86-64 | From 8e9da1559b71a01380d3324e3ae12396fd07e395 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Dec 2021 14:47:59 +1100 Subject: [PATCH 0038/1186] Lint fix --- src/PIL/BlpImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 6909599c149..f968ed85195 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -370,7 +370,6 @@ def _decode_jpeg_stream(self): a = ImageOps.invert(a) - image = Image.merge("RGBA", (r, g, b, a)) self.set_as_raw(image.tobytes()) From 05b63366df05ebe8dd182f4e1596b23582631b3d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 3 Jan 2022 09:22:31 +1100 Subject: [PATCH 0039/1186] 9.1.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 0ee7ac8c58b..62c7dba5570 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "9.0.0" +__version__ = "9.1.0.dev0" From 3446537c60a0678761dc916603c6220370ad3834 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 17:10:44 +0000 Subject: [PATCH 0040/1186] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 911470a610e47d9da5ea938b0887c3df62819b85 → f1d4e742c91dd5179d742b0db9293c4472b765f8](https://github.com/psf/black/compare/911470a610e47d9da5ea938b0887c3df62819b85...f1d4e742c91dd5179d742b0db9293c4472b765f8) - [github.com/PyCQA/isort: fd5ba70665a37ec301a1f714ed09336048b3be63 → c5e8fa75dda5f764d20f66a215d71c21cfa198e1](https://github.com/PyCQA/isort/compare/fd5ba70665a37ec301a1f714ed09336048b3be63...c5e8fa75dda5f764d20f66a215d71c21cfa198e1) - [github.com/asottile/yesqa: 644ede78511c02fc6f8e03e014cc1ddcfbf1e1f5 → 35cf7dc24fa922927caded7a21b2a8cb04bf8e10](https://github.com/asottile/yesqa/compare/644ede78511c02fc6f8e03e014cc1ddcfbf1e1f5...35cf7dc24fa922927caded7a21b2a8cb04bf8e10) - [github.com/PyCQA/flake8: dcd740bc0ebaf2b3d43e59a0060d157c97de13f3 → cbeb4c9c4137cff1568659fcc48e8b85cddd0c8d](https://github.com/PyCQA/flake8/compare/dcd740bc0ebaf2b3d43e59a0060d157c97de13f3...cbeb4c9c4137cff1568659fcc48e8b85cddd0c8d) - [github.com/pre-commit/pre-commit-hooks: 38b88246ccc552bffaaf54259d064beeee434539 → 8fe62d14e0b4d7d845a7022c5c2c3ae41bdd3f26](https://github.com/pre-commit/pre-commit-hooks/compare/38b88246ccc552bffaaf54259d064beeee434539...8fe62d14e0b4d7d845a7022c5c2c3ae41bdd3f26) --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5f1d16709c1..822fa43ca33 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 911470a610e47d9da5ea938b0887c3df62819b85 # frozen: 21.9b0 + rev: f1d4e742c91dd5179d742b0db9293c4472b765f8 # frozen: 21.12b0 hooks: - id: black args: ["--target-version", "py37"] @@ -9,12 +9,12 @@ repos: types: [] - repo: https://github.com/PyCQA/isort - rev: fd5ba70665a37ec301a1f714ed09336048b3be63 # frozen: 5.9.3 + rev: c5e8fa75dda5f764d20f66a215d71c21cfa198e1 # frozen: 5.10.1 hooks: - id: isort - repo: https://github.com/asottile/yesqa - rev: 644ede78511c02fc6f8e03e014cc1ddcfbf1e1f5 # frozen: v1.2.3 + rev: 35cf7dc24fa922927caded7a21b2a8cb04bf8e10 # frozen: v1.3.0 hooks: - id: yesqa @@ -25,7 +25,7 @@ repos: exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) - repo: https://github.com/PyCQA/flake8 - rev: dcd740bc0ebaf2b3d43e59a0060d157c97de13f3 # frozen: 3.9.2 + rev: cbeb4c9c4137cff1568659fcc48e8b85cddd0c8d # frozen: 4.0.1 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] @@ -37,7 +37,7 @@ repos: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: 38b88246ccc552bffaaf54259d064beeee434539 # frozen: v4.0.1 + rev: 8fe62d14e0b4d7d845a7022c5c2c3ae41bdd3f26 # frozen: v4.1.0 hooks: - id: check-merge-conflict - id: check-yaml From 52f29a537d5d979d7aa5e30a629beb67139fde20 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 17:11:30 +0000 Subject: [PATCH 0041/1186] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/helper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 8504993fb5d..feccce6bcf9 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -30,7 +30,6 @@ def upload(a, b): a.show() b.show() - elif "GITHUB_ACTIONS" in os.environ: HAS_UPLOADER = True @@ -44,7 +43,6 @@ def upload(a, b): b.save(os.path.join(tmpdir, "b.png")) return tmpdir - else: try: import test_image_results From e077229d7a165cade8c3538b3371f344fa28fc38 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 4 Jan 2022 11:32:15 +0200 Subject: [PATCH 0042/1186] Remove readonly from Image.__eq__ --- src/PIL/Image.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index e5ea25fc44f..0bafd39076c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -627,7 +627,6 @@ def __eq__(self, other): and self.size == other.size and self.info == other.info and self._category == other._category - and self.readonly == other.readonly and self.getpalette() == other.getpalette() and self.tobytes() == other.tobytes() ) From 5a35e7baee6875123459e1f40827565d65e28416 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 6 Jan 2022 06:19:51 +1100 Subject: [PATCH 0043/1186] Replaced direct invocation of setup.py --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0dac63d3961..44ab2ef1c50 100644 --- a/Makefile +++ b/Makefile @@ -112,7 +112,7 @@ valgrind: .PHONY: readme readme: - python3 setup.py --long-description | markdown2 > .long-description.html && open .long-description.html + markdown2 README.md > .long-description.html && open .long-description.html .PHONY: lint From f40c005399e393eab5fec960247afe454fa072a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Wallk=C3=B6tter?= Date: Thu, 6 Jan 2022 07:58:57 +0100 Subject: [PATCH 0044/1186] Add missing ImageModes The modes are mentioned in the docs, but weren't part of ImageMode. --- src/PIL/ImageMode.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 0afcf9fe129..ae4ada75a66 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -52,6 +52,11 @@ def getmode(mode): "HSV": ("RGB", "L", ("H", "S", "V")), # extra experimental modes "RGBa": ("RGB", "L", ("R", "G", "B", "a")), + "BGR": ("BGR", "L", ("B", "G", "R")), + "BGR;15": ("RGB", "L", ("B", "G", "R")), + "BGR;16": ("RGB", "L", ("B", "G", "R")), + "BGR;24": ("RGB", "L", ("B", "G", "R")), + "BGR;32": ("RGB", "L", ("B", "G", "R")), "LA": ("L", "L", ("L", "A")), "La": ("L", "L", ("L", "a")), "PA": ("RGB", "L", ("P", "A")), From fbe396f22aeb87c1987287681035714d348485ee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 7 Jan 2022 12:34:58 +1100 Subject: [PATCH 0045/1186] Removed duplicate check --- Tests/test_image_mode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py index 7f92c226416..0232a5536d0 100644 --- a/Tests/test_image_mode.py +++ b/Tests/test_image_mode.py @@ -65,6 +65,5 @@ def check(mode, *result): check("RGB", "RGB", "L", 3, ("R", "G", "B")) check("RGBA", "RGB", "L", 4, ("R", "G", "B", "A")) check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) - check("RGBX", "RGB", "L", 4, ("R", "G", "B", "X")) check("CMYK", "RGB", "L", 4, ("C", "M", "Y", "K")) check("YCbCr", "RGB", "L", 3, ("Y", "Cb", "Cr")) From b5160591bc9da2425ff15b632215139eafc2cce6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 7 Jan 2022 16:29:38 +1100 Subject: [PATCH 0046/1186] Return an empty bytestring from tobytes() for an empty image --- Tests/test_image.py | 5 +++++ src/PIL/Image.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index 4dde66f11a1..2d46d760db4 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -786,6 +786,11 @@ def test_exif_load_from_fp(self): 34665: 196, } + @pytest.mark.parametrize("size", ((1, 0), (0, 1), (0, 0))) + def test_zero_tobytes(self, size): + im = Image.new("RGB", size) + assert im.tobytes() == b"" + def test_categories_deprecation(self): with pytest.warns(DeprecationWarning): assert hopper().category == 0 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0bafd39076c..69089a29063 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -716,6 +716,9 @@ def tobytes(self, encoder_name="raw", *args): self.load() + if self.width == 0 or self.height == 0: + return b"" + # unpack data e = _getencoder(self.mode, encoder_name, args) e.setimage(self.im) From 7ad8fdb67796300ab52e97e432bfe34d17860ad7 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:34:30 -0500 Subject: [PATCH 0047/1186] CI: Get all the tests running on Cygwin. --- .github/workflows/test-cygwin.yml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b6a305b7151..f43460293b8 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -18,12 +18,13 @@ jobs: packages: > libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel - libtiff-devel libwebp-devel libxcb-devel netpbm - ImageMagick jpeg xorg-server-extra xorg-server-common - xinit python38-setuptools python38-devel python38-tkinter - python38-wheel python38-pip python38-olefile - python38-numpy python38-pytest python38-sphinx - python38-packaging python38-cffi python38-requests + libtiff-devel libwebp-devel libxcb-devel libpng-devel + netpbm ImageMagick jpeg xorg-server-extra + xorg-server-common xinit subversion python38-setuptools + python38-devel python38-tkinter python38-wheel + python38-pip python38-olefile python38-numpy + python38-pytest python38-sphinx python38-packaging + python38-cffi python38-requests install-dir: 'C:\tools\cygwin' - name: Clean up path @@ -40,6 +41,12 @@ jobs: C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked + - name: Download extra test images + run: | + C:\tools\cygwin\bin\dash -c "tr -d '\r' install_extra_test_images.sh" + C:\tools\cygwin\bin\dash -c "mv install_extra_test_images.sh depends/install_extra_test_images.sh" + C:\tools\cygwin\bin\dash -c "cd depends; dash install_extra_test_images.sh" + - name: Build Pillow run: | C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel @@ -57,11 +64,16 @@ jobs: C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" C:\tools\cygwin\bin\chmod u+x .ci/test.sh + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 --ignore=Tests/test_numpy.py + + - name: Test the possibly-segfaulting NumPy tests separately + run: | C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success run: | + C:\tools\cygwin\bin\mv .ci/after_success.sh .ci/after_success.sh.dos + C:\tools\cygwin\bin\dash -c "tr -d '\r' <.ci/after_success.sh.dos >.ci/after_success.sh" C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage From a23131c22e18d51b25398334f8f1e7b07a90d8ac Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:49:45 -0500 Subject: [PATCH 0048/1186] CI: Stop trying to run the netpbm tests. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f43460293b8..10b83fe880a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -19,7 +19,7 @@ jobs: libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libpng-devel - netpbm ImageMagick jpeg xorg-server-extra + ImageMagick jpeg xorg-server-extra xorg-server-common xinit subversion python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy From b2c6db8d3b0bf759a8611300a55f3d509d7b23e6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 7 Jan 2022 22:48:26 +0200 Subject: [PATCH 0049/1186] Add CVE IDs --- CHANGES.rst | 4 ++-- docs/releasenotes/9.0.0.rst | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c2d4892cb2f..de3e9b9caae 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,13 +5,13 @@ Changelog (Pillow) 9.0.0 (2022-01-02) ------------------ -- Restrict builtins for ImageMath.eval(). CVE TBD #5923 +- Restrict builtins for ImageMath.eval(). CVE-2022-22817 #5923 [radarhere] - Ensure JpegImagePlugin stops at the end of a truncated file #5921 [radarhere] -- Fixed ImagePath.Path array handling. CVEs TBD #5920 +- Fixed ImagePath.Path array handling. CVE-2022-22815, CVE-2022-22816 #5920 [radarhere] - Remove consecutive duplicate tiles that only differ by their offset #5919 diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index f2be128bb90..fbf2e7ce4ff 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -119,15 +119,16 @@ Google's `OSS-Fuzz`_ project for finding this issue. Restrict builtins available to ImageMath.eval ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To limit :py:class:`PIL.ImageMath` to working with images, Pillow will now restrict the -builtins available to :py:meth:`PIL.ImageMath.eval`. This will help prevent problems -arising if users evaluate arbitrary expressions, such as -``ImageMath.eval("exec(exit())")``. CVE TBD +:cve:`CVE-2022-22817`: To limit :py:class:`PIL.ImageMath` to working with images, Pillow +will now restrict the builtins available to :py:meth:`PIL.ImageMath.eval`. This will +help prevent problems arising if users evaluate arbitrary expressions, such as +``ImageMath.eval("exec(exit())")``. Fixed ImagePath.Path array handling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -CWE-126 and CWE-665 were found when initializing ``ImagePath.Path``. CVEs TBD +:cve:`CVE-2022-22815` (CWE-126) and :cve:`CVE-2022-22816` (CWE-665) were found when +initializing ``ImagePath.Path``. .. _OSS-Fuzz: https://github.com/google/oss-fuzz From 2d9dfefe6ecdd783fb8713b0810c851ef9a364bd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 8 Jan 2022 13:47:51 +1100 Subject: [PATCH 0050/1186] Added specific error if coordinate type is incorrect --- Tests/test_imagepath.py | 4 ++- src/path.c | 62 +++++++++++++++-------------------------- 2 files changed, 25 insertions(+), 41 deletions(-) diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index b18271cc5a1..e38a2068a4a 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -70,9 +70,11 @@ def test_invalid_coords(): coords = ["a", "b"] # Act / Assert - with pytest.raises(SystemError): + with pytest.raises(ValueError) as e: ImagePath.Path(coords) + assert str(e.value) == "incorrect coordinate type" + def test_path_odd_number_of_coordinates(): # Arrange diff --git a/src/path.c b/src/path.c index dea274ee336..e2bdc9419ea 100644 --- a/src/path.c +++ b/src/path.c @@ -162,42 +162,37 @@ PyPath_Flatten(PyObject *data, double **pxy) { return -1; } +#define assign_item_to_array(op, decref) \ +if (PyFloat_Check(op)) { \ + xy[j++] = PyFloat_AS_DOUBLE(op); \ +} else if (PyLong_Check(op)) { \ + xy[j++] = (float)PyLong_AS_LONG(op); \ +} else if (PyNumber_Check(op)) { \ + xy[j++] = PyFloat_AsDouble(op); \ +} else if (PyArg_ParseTuple(op, "dd", &x, &y)) { \ + xy[j++] = x; \ + xy[j++] = y; \ +} else { \ + PyErr_SetString(PyExc_ValueError, "incorrect coordinate type"); \ + if (decref) { \ + Py_DECREF(op); \ + } \ + free(xy); \ + return -1; \ +} + /* Copy table to path array */ if (PyList_Check(data)) { for (i = 0; i < n; i++) { double x, y; PyObject *op = PyList_GET_ITEM(data, i); - if (PyFloat_Check(op)) { - xy[j++] = PyFloat_AS_DOUBLE(op); - } else if (PyLong_Check(op)) { - xy[j++] = (float)PyLong_AS_LONG(op); - } else if (PyNumber_Check(op)) { - xy[j++] = PyFloat_AsDouble(op); - } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { - xy[j++] = x; - xy[j++] = y; - } else { - free(xy); - return -1; - } + assign_item_to_array(op, 0); } } else if (PyTuple_Check(data)) { for (i = 0; i < n; i++) { double x, y; PyObject *op = PyTuple_GET_ITEM(data, i); - if (PyFloat_Check(op)) { - xy[j++] = PyFloat_AS_DOUBLE(op); - } else if (PyLong_Check(op)) { - xy[j++] = (float)PyLong_AS_LONG(op); - } else if (PyNumber_Check(op)) { - xy[j++] = PyFloat_AsDouble(op); - } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { - xy[j++] = x; - xy[j++] = y; - } else { - free(xy); - return -1; - } + assign_item_to_array(op, 0); } } else { for (i = 0; i < n; i++) { @@ -213,20 +208,7 @@ PyPath_Flatten(PyObject *data, double **pxy) { return -1; } } - if (PyFloat_Check(op)) { - xy[j++] = PyFloat_AS_DOUBLE(op); - } else if (PyLong_Check(op)) { - xy[j++] = (float)PyLong_AS_LONG(op); - } else if (PyNumber_Check(op)) { - xy[j++] = PyFloat_AsDouble(op); - } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { - xy[j++] = x; - xy[j++] = y; - } else { - Py_DECREF(op); - free(xy); - return -1; - } + assign_item_to_array(op, 1); Py_DECREF(op); } } From 4e7e70fd7982c6cf478b20bb1d11e9140283dccc Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 8 Jan 2022 10:08:18 -0500 Subject: [PATCH 0051/1186] CI: Install netpbm and dependencies on Cygwin CI. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 10b83fe880a..162522c0aa3 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -19,7 +19,7 @@ jobs: libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libpng-devel - ImageMagick jpeg xorg-server-extra + ImageMagick jpeg netpbm perl xorg-server-extra xorg-server-common xinit subversion python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy From 6e5e45a9ecb119c1c80f84ae32cb1fdb9c9e578d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 9 Jan 2022 16:05:48 +1100 Subject: [PATCH 0052/1186] Ensure duplicated file pointer is closed --- src/PIL/TiffImagePlugin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 5df5c4f4cd5..e54082feceb 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1234,6 +1234,12 @@ def _load_libtiff(self): # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) + if fp: + try: + os.close(fp) + except OSError: + pass + self.tile = [] self.readonly = 0 From 2d7c1bcc9d01f7424e9d10b0df659354734fe40b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jan 2022 16:42:26 +1100 Subject: [PATCH 0053/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index de3e9b9caae..49ab72962de 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,21 @@ Changelog (Pillow) ================== +9.1.0 (unreleased) +------------------ + +- Ensure duplicated file pointer is closed #5946 + [radarhere] + +- Added specific error if ImagePath coordinate type is incorrect #5942 + [radarhere] + +- Return an empty bytestring from tobytes() for an empty image #5938 + [radarhere] + +- Remove readonly from Image.__eq__ #5930 + [hugovk] + 9.0.0 (2022-01-02) ------------------ From 11dd54383777c53dae5564e35f4e38476f161b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Wallk=C3=B6tter?= Date: Mon, 10 Jan 2022 08:17:53 +0100 Subject: [PATCH 0054/1186] remove mode BGR --- src/PIL/ImageMode.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index ae4ada75a66..e6bf0bb100b 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -52,7 +52,6 @@ def getmode(mode): "HSV": ("RGB", "L", ("H", "S", "V")), # extra experimental modes "RGBa": ("RGB", "L", ("R", "G", "B", "a")), - "BGR": ("BGR", "L", ("B", "G", "R")), "BGR;15": ("RGB", "L", ("B", "G", "R")), "BGR;16": ("RGB", "L", ("B", "G", "R")), "BGR;24": ("RGB", "L", ("B", "G", "R")), From 90f3f72e1925622690d3d1e18dcae71ae794ba3e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Jan 2022 13:30:51 +1100 Subject: [PATCH 0055/1186] Removed unused constant --- src/PIL/ImageMath.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 06bea800de2..b4579081bc2 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -19,8 +19,6 @@ from . import Image, _imagingmath -VERBOSE = 0 - def _isconstant(v): return isinstance(v, (int, float)) From 9c6df29b4ddc9a065d6c7d5c451f21d9bb9af34a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Jan 2022 13:42:58 +1100 Subject: [PATCH 0056/1186] Removed unreachable error --- src/PIL/ImageMath.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index b4579081bc2..8396148e827 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -67,8 +67,6 @@ def apply(self, op, im1, im2=None, mode=None): im1 = im1.convert("F") if im2.mode != "F": im2 = im2.convert("F") - if im1.mode != im2.mode: - raise ValueError("mode mismatch") if im1.size != im2.size: # crop both arguments to a common size size = (min(im1.size[0], im2.size[0]), min(im1.size[1], im2.size[1])) From 01e7b3943d6ac4c208c3f6da8df27178e1e09155 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 11 Jan 2022 13:50:00 +1100 Subject: [PATCH 0057/1186] Simplified code --- src/PIL/ImageMath.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 8396148e827..4b6e4ccda3a 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -74,9 +74,7 @@ def apply(self, op, im1, im2=None, mode=None): im1 = im1.crop((0, 0) + size) if im2.size != size: im2 = im2.crop((0, 0) + size) - out = Image.new(mode or im1.mode, size, None) - else: - out = Image.new(mode or im1.mode, im1.size, None) + out = Image.new(mode or im1.mode, im1.size, None) im1.load() im2.load() try: From 47fe0b1b9ff8cfe6de1d111437988264700310ee Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 11 Jan 2022 18:48:56 +0200 Subject: [PATCH 0058/1186] Autolink CWE numbers with sphinx-issues --- .github/workflows/test.yml | 2 +- docs/releasenotes/9.0.0.rst | 4 ++-- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 414c7e94edd..82518f556e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -93,7 +93,7 @@ jobs: - name: Docs if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.9 run: | - python3 -m pip install sphinx-copybutton sphinx-issues sphinx-removed-in sphinx-rtd-theme sphinxext-opengraph + python3 -m pip install sphinx-copybutton sphinx-issues>=3.0.1 sphinx-removed-in sphinx-rtd-theme sphinxext-opengraph make doccheck - name: After success diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index fbf2e7ce4ff..947ccd849e3 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -127,8 +127,8 @@ help prevent problems arising if users evaluate arbitrary expressions, such as Fixed ImagePath.Path array handling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -:cve:`CVE-2022-22815` (CWE-126) and :cve:`CVE-2022-22816` (CWE-665) were found when -initializing ``ImagePath.Path``. +:cve:`CVE-2022-22815` (:cwe:`CWE-126`) and :cve:`CVE-2022-22816` (:cwe:`CWE-665`) were +found when initializing ``ImagePath.Path``. .. _OSS-Fuzz: https://github.com/google/oss-fuzz diff --git a/requirements.txt b/requirements.txt index feaa2f718b4..1e150e304b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pytest-cov pytest-timeout sphinx>=2.4 sphinx-copybutton -sphinx-issues +sphinx-issues>=3.0.1 sphinx-removed-in sphinx-rtd-theme>=1.0 sphinxext-opengraph From 2885b7f3ff6bb021d9646a609686c0f52d776904 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 12 Jan 2022 01:25:58 +0200 Subject: [PATCH 0059/1186] Dont need to pin for GHA Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82518f556e9..414c7e94edd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -93,7 +93,7 @@ jobs: - name: Docs if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.9 run: | - python3 -m pip install sphinx-copybutton sphinx-issues>=3.0.1 sphinx-removed-in sphinx-rtd-theme sphinxext-opengraph + python3 -m pip install sphinx-copybutton sphinx-issues sphinx-removed-in sphinx-rtd-theme sphinxext-opengraph make doccheck - name: After success From ba86d90a009621c052e0f268bb0062932b2096f3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 12 Jan 2022 15:17:04 +1100 Subject: [PATCH 0060/1186] Simplified code --- src/PIL/SpiderImagePlugin.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 062af9f983e..061efe8eceb 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -238,14 +238,14 @@ def makeSpiderHeader(im): if 1024 % lenbyt != 0: labrec += 1 labbyt = labrec * lenbyt - hdr = [] nvalues = int(labbyt / 4) + if nvalues < 23: + return [] + + hdr = [] for i in range(nvalues): hdr.append(0.0) - if len(hdr) < 23: - return [] - # NB these are Fortran indices hdr[1] = 1.0 # nslice (=1 for an image) hdr[2] = float(nrow) # number of rows per slice @@ -259,10 +259,7 @@ def makeSpiderHeader(im): hdr = hdr[1:] hdr.append(0.0) # pack binary data into a string - hdrstr = [] - for v in hdr: - hdrstr.append(struct.pack("f", v)) - return hdrstr + return [struct.pack("f", v) for v in hdr] def _save(im, fp, filename): From a9441a475d4c4133f161b7927352b4bda5cc9b6f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 12 Jan 2022 17:29:25 +1100 Subject: [PATCH 0061/1186] Added IREC header entry for use with Bio-formats library --- src/PIL/SpiderImagePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 061efe8eceb..11c706b4d2c 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -249,6 +249,7 @@ def makeSpiderHeader(im): # NB these are Fortran indices hdr[1] = 1.0 # nslice (=1 for an image) hdr[2] = float(nrow) # number of rows per slice + hdr[3] = float(nrow) # number of records in the image hdr[5] = 1.0 # iform for 2D image hdr[12] = float(nsam) # number of pixels per line hdr[13] = float(labrec) # number of records in file header From fe32501922ef5e1be9a7d307132719bd5d52ca35 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 14 Jan 2022 10:16:35 +1100 Subject: [PATCH 0062/1186] Corrected allocation --- src/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/path.c b/src/path.c index e2bdc9419ea..3e3431575ec 100644 --- a/src/path.c +++ b/src/path.c @@ -57,7 +57,7 @@ alloc_array(Py_ssize_t count) { if ((unsigned long long)count > (SIZE_MAX / (2 * sizeof(double))) - 1) { return ImagingError_MemoryError(); } - xy = calloc(2 * count * sizeof(double) + 1, sizeof(double)); + xy = calloc(2 * count + 1, sizeof(double)); if (!xy) { ImagingError_MemoryError(); } From f8e4e9c2dd94c6f4789639dd891b8a6d5fb16e14 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jan 2022 09:02:31 +1100 Subject: [PATCH 0063/1186] Added enums --- Tests/test_color_lut.py | 168 +++++++++++---- Tests/test_file_apng.py | 74 +++---- Tests/test_file_gif.py | 6 +- Tests/test_file_ico.py | 12 +- Tests/test_file_jpeg.py | 2 +- Tests/test_file_jpeg2k.py | 2 +- Tests/test_file_libtiff.py | 2 +- Tests/test_format_hsv.py | 2 +- Tests/test_image_convert.py | 6 +- Tests/test_image_getdata.py | 2 +- Tests/test_image_paste.py | 18 +- Tests/test_image_quantize.py | 12 +- Tests/test_image_reduce.py | 12 +- Tests/test_image_resample.py | 134 ++++++------ Tests/test_image_resize.py | 98 +++++---- Tests/test_image_rotate.py | 14 +- Tests/test_image_thumbnail.py | 14 +- Tests/test_image_transform.py | 78 ++++--- Tests/test_image_transpose.py | 53 ++--- Tests/test_imagecms.py | 4 +- Tests/test_imagedraw.py | 4 +- Tests/test_imagefile.py | 2 +- Tests/test_imagefont.py | 20 +- Tests/test_mode_i16.py | 2 +- docs/handbook/image-file-formats.rst | 10 +- docs/handbook/tutorial.rst | 12 +- docs/reference/Image.rst | 114 +++++----- docs/reference/ImageCms.rst | 42 ++-- docs/reference/ImageFont.rst | 4 +- docs/reference/features.rst | 2 +- docs/reference/plugins.rst | 3 +- docs/releasenotes/2.7.0.rst | 16 +- selftest.py | 4 +- src/PIL/BlpImagePlugin.py | 40 ++-- src/PIL/FtexImagePlugin.py | 15 +- src/PIL/GifImagePlugin.py | 6 +- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/Image.py | 302 ++++++++++++++++----------- src/PIL/ImageCms.py | 89 ++++---- src/PIL/ImageFilter.py | 2 +- src/PIL/ImageFont.py | 29 ++- src/PIL/ImageOps.py | 32 +-- src/PIL/ImageTransform.py | 8 +- src/PIL/PngImagePlugin.py | 88 ++++---- src/PIL/SpiderImagePlugin.py | 2 +- src/PIL/TgaImagePlugin.py | 2 +- src/PIL/TiffImagePlugin.py | 14 +- 47 files changed, 907 insertions(+), 672 deletions(-) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 99776ce58cf..120ff777e77 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -43,107 +43,158 @@ def test_wrong_args(self): im = Image.new("RGB", (10, 10), 0) with pytest.raises(ValueError, match="filter"): - im.im.color_lut_3d("RGB", Image.CUBIC, *self.generate_identity_table(3, 3)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BICUBIC, *self.generate_identity_table(3, 3) + ) with pytest.raises(ValueError, match="image mode"): im.im.color_lut_3d( - "wrong", Image.LINEAR, *self.generate_identity_table(3, 3) + "wrong", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) with pytest.raises(ValueError, match="table_channels"): - im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(5, 3)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(5, 3) + ) with pytest.raises(ValueError, match="table_channels"): - im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(1, 3)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(1, 3) + ) with pytest.raises(ValueError, match="table_channels"): - im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(2, 3)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(2, 3) + ) with pytest.raises(ValueError, match="Table size"): im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (1, 3, 3)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (1, 3, 3)), ) with pytest.raises(ValueError, match="Table size"): im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (66, 3, 3)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (66, 3, 3)), ) with pytest.raises(ValueError, match=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.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, 0] * 7 + ) with pytest.raises(ValueError, match=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.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, 0] * 9 + ) with pytest.raises(TypeError): - im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, "0"] * 8) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, 3, 2, 2, 2, [0, 0, "0"] * 8 + ) with pytest.raises(TypeError): - im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, 16) + im.im.color_lut_3d("RGB", Image.Resampling.BILINEAR, 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)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) + ) - im.im.color_lut_3d("CMYK", Image.LINEAR, *self.generate_identity_table(4, 3)) + im.im.color_lut_3d( + "CMYK", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3) + ) im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (2, 3, 3)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (2, 3, 3)), ) im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (65, 3, 3)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (65, 3, 3)), ) im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (3, 65, 3)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (3, 65, 3)), ) im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (3, 3, 65)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (3, 3, 65)), ) def test_wrong_mode(self): with pytest.raises(ValueError, match="wrong mode"): im = Image.new("L", (10, 10), 0) - im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(3, 3)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) + ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("RGB", (10, 10), 0) - im.im.color_lut_3d("L", Image.LINEAR, *self.generate_identity_table(3, 3)) + im.im.color_lut_3d( + "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) + ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("L", (10, 10), 0) - im.im.color_lut_3d("L", Image.LINEAR, *self.generate_identity_table(3, 3)) + im.im.color_lut_3d( + "L", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) + ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("RGB", (10, 10), 0) im.im.color_lut_3d( - "RGBA", Image.LINEAR, *self.generate_identity_table(3, 3) + "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(3, 3) ) with pytest.raises(ValueError, match="wrong mode"): im = Image.new("RGB", (10, 10), 0) - im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(4, 3)) + im.im.color_lut_3d( + "RGB", Image.Resampling.BILINEAR, *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)) + im.im.color_lut_3d( + "RGBA", Image.Resampling.BILINEAR, *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)) + im.im.color_lut_3d( + "RGBA", Image.Resampling.BILINEAR, *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)) + im.im.color_lut_3d( + "HSV", Image.Resampling.BILINEAR, *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)) + im.im.color_lut_3d( + "RGBA", Image.Resampling.BILINEAR, *self.generate_identity_table(4, 3) + ) def test_identities(self): g = Image.linear_gradient("L") im = Image.merge( - "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + "RGB", + [ + g, + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + ], ) # Fast test with small cubes @@ -152,7 +203,9 @@ def test_identities(self): im, im._new( im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, size) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, size), ) ), ) @@ -162,7 +215,9 @@ def test_identities(self): im, im._new( im.im.color_lut_3d( - "RGB", Image.LINEAR, *self.generate_identity_table(3, (2, 2, 65)) + "RGB", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, (2, 2, 65)), ) ), ) @@ -170,7 +225,12 @@ def test_identities(self): def test_identities_4_channels(self): g = Image.linear_gradient("L") im = Image.merge( - "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + "RGB", + [ + g, + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + ], ) # Red channel copied to alpha @@ -178,7 +238,9 @@ def test_identities_4_channels(self): Image.merge("RGBA", (im.split() * 2)[:4]), im._new( im.im.color_lut_3d( - "RGBA", Image.LINEAR, *self.generate_identity_table(4, 17) + "RGBA", + Image.Resampling.BILINEAR, + *self.generate_identity_table(4, 17), ) ), ) @@ -189,9 +251,9 @@ def test_copy_alpha_channel(self): "RGBA", [ g, - g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180), - g.transpose(Image.ROTATE_270), + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + g.transpose(Image.Transpose.ROTATE_270), ], ) @@ -199,7 +261,9 @@ def test_copy_alpha_channel(self): im, im._new( im.im.color_lut_3d( - "RGBA", Image.LINEAR, *self.generate_identity_table(3, 17) + "RGBA", + Image.Resampling.BILINEAR, + *self.generate_identity_table(3, 17), ) ), ) @@ -207,14 +271,19 @@ def test_copy_alpha_channel(self): def test_channels_order(self): g = Image.linear_gradient("L") im = Image.merge( - "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + "RGB", + [ + g, + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + ], ) # Reverse channels by splitting and using table # fmt: off assert_image_equal( Image.merge('RGB', im.split()[::-1]), - im._new(im.im.color_lut_3d('RGB', Image.LINEAR, + im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, 3, 2, 2, 2, [ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, @@ -227,11 +296,16 @@ def test_channels_order(self): def test_overflow(self): g = Image.linear_gradient("L") im = Image.merge( - "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + "RGB", + [ + g, + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + ], ) # fmt: off - transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, + transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, 3, 2, 2, 2, [ -1, -1, -1, 2, -1, -1, @@ -251,7 +325,7 @@ def test_overflow(self): assert transformed[205, 205] == (255, 255, 0) # fmt: off - transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, + transformed = im._new(im.im.color_lut_3d('RGB', Image.Resampling.BILINEAR, 3, 2, 2, 2, [ -3, -3, -3, 5, -3, -3, @@ -354,7 +428,12 @@ def test_numpy_sources(self): def test_numpy_formats(self): g = Image.linear_gradient("L") im = Image.merge( - "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + "RGB", + [ + g, + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + ], ) lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) @@ -445,7 +524,12 @@ def test_apply(self): g = Image.linear_gradient("L") im = Image.merge( - "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + "RGB", + [ + g, + g.transpose(Image.Transpose.ROTATE_90), + g.transpose(Image.Transpose.ROTATE_180), + ], ) assert im == im.filter(lut) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index d48e5ce07f3..f5c76708201 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -120,9 +120,9 @@ def test_apng_dispose_op_previous_frame(): # save_all=True, # append_images=[green, blue], # disposal=[ - # PngImagePlugin.APNG_DISPOSE_OP_NONE, - # PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS, - # PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS + # PngImagePlugin.Disposal.OP_NONE, + # PngImagePlugin.Disposal.OP_PREVIOUS, + # PngImagePlugin.Disposal.OP_PREVIOUS # ], # ) with Image.open("Tests/images/apng/dispose_op_previous_frame.png") as im: @@ -455,31 +455,31 @@ def test_apng_save_disposal(tmp_path): green = Image.new("RGBA", size, (0, 255, 0, 255)) transparent = Image.new("RGBA", size, (0, 0, 0, 0)) - # test APNG_DISPOSE_OP_NONE + # test OP_NONE red.save( test_file, save_all=True, append_images=[green, transparent], - disposal=PngImagePlugin.APNG_DISPOSE_OP_NONE, - blend=PngImagePlugin.APNG_BLEND_OP_OVER, + disposal=PngImagePlugin.Disposal.OP_NONE, + blend=PngImagePlugin.Blend.OP_OVER, ) with Image.open(test_file) as im: im.seek(2) assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) - # test APNG_DISPOSE_OP_BACKGROUND + # test OP_BACKGROUND disposal = [ - PngImagePlugin.APNG_DISPOSE_OP_NONE, - PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND, - PngImagePlugin.APNG_DISPOSE_OP_NONE, + PngImagePlugin.Disposal.OP_NONE, + PngImagePlugin.Disposal.OP_BACKGROUND, + PngImagePlugin.Disposal.OP_NONE, ] red.save( test_file, save_all=True, append_images=[red, transparent], disposal=disposal, - blend=PngImagePlugin.APNG_BLEND_OP_OVER, + blend=PngImagePlugin.Blend.OP_OVER, ) with Image.open(test_file) as im: im.seek(2) @@ -487,26 +487,26 @@ def test_apng_save_disposal(tmp_path): assert im.getpixel((64, 32)) == (0, 0, 0, 0) disposal = [ - PngImagePlugin.APNG_DISPOSE_OP_NONE, - PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND, + PngImagePlugin.Disposal.OP_NONE, + PngImagePlugin.Disposal.OP_BACKGROUND, ] red.save( test_file, save_all=True, append_images=[green], disposal=disposal, - blend=PngImagePlugin.APNG_BLEND_OP_OVER, + blend=PngImagePlugin.Blend.OP_OVER, ) with Image.open(test_file) as im: im.seek(1) assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) - # test APNG_DISPOSE_OP_PREVIOUS + # test OP_PREVIOUS disposal = [ - PngImagePlugin.APNG_DISPOSE_OP_NONE, - PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS, - PngImagePlugin.APNG_DISPOSE_OP_NONE, + PngImagePlugin.Disposal.OP_NONE, + PngImagePlugin.Disposal.OP_PREVIOUS, + PngImagePlugin.Disposal.OP_NONE, ] red.save( test_file, @@ -514,7 +514,7 @@ def test_apng_save_disposal(tmp_path): append_images=[green, red, transparent], default_image=True, disposal=disposal, - blend=PngImagePlugin.APNG_BLEND_OP_OVER, + blend=PngImagePlugin.Blend.OP_OVER, ) with Image.open(test_file) as im: im.seek(3) @@ -522,15 +522,15 @@ def test_apng_save_disposal(tmp_path): assert im.getpixel((64, 32)) == (0, 255, 0, 255) disposal = [ - PngImagePlugin.APNG_DISPOSE_OP_NONE, - PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS, + PngImagePlugin.Disposal.OP_NONE, + PngImagePlugin.Disposal.OP_PREVIOUS, ] red.save( test_file, save_all=True, append_images=[green], disposal=disposal, - blend=PngImagePlugin.APNG_BLEND_OP_OVER, + blend=PngImagePlugin.Blend.OP_OVER, ) with Image.open(test_file) as im: im.seek(1) @@ -538,7 +538,7 @@ def test_apng_save_disposal(tmp_path): assert im.getpixel((64, 32)) == (0, 255, 0, 255) # test info disposal - red.info["disposal"] = PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND + red.info["disposal"] = PngImagePlugin.Disposal.OP_BACKGROUND red.save( test_file, save_all=True, @@ -556,12 +556,12 @@ def test_apng_save_disposal_previous(tmp_path): red = Image.new("RGBA", size, (255, 0, 0, 255)) green = Image.new("RGBA", size, (0, 255, 0, 255)) - # test APNG_DISPOSE_OP_NONE + # test OP_NONE transparent.save( test_file, save_all=True, append_images=[red, green], - disposal=PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS, + disposal=PngImagePlugin.Disposal.OP_PREVIOUS, ) with Image.open(test_file) as im: im.seek(2) @@ -576,17 +576,17 @@ def test_apng_save_blend(tmp_path): green = Image.new("RGBA", size, (0, 255, 0, 255)) transparent = Image.new("RGBA", size, (0, 0, 0, 0)) - # test APNG_BLEND_OP_SOURCE on solid color + # test OP_SOURCE on solid color blend = [ - PngImagePlugin.APNG_BLEND_OP_OVER, - PngImagePlugin.APNG_BLEND_OP_SOURCE, + PngImagePlugin.Blend.OP_OVER, + PngImagePlugin.Blend.OP_SOURCE, ] red.save( test_file, save_all=True, append_images=[red, green], default_image=True, - disposal=PngImagePlugin.APNG_DISPOSE_OP_NONE, + disposal=PngImagePlugin.Disposal.OP_NONE, blend=blend, ) with Image.open(test_file) as im: @@ -594,17 +594,17 @@ def test_apng_save_blend(tmp_path): assert im.getpixel((0, 0)) == (0, 255, 0, 255) assert im.getpixel((64, 32)) == (0, 255, 0, 255) - # test APNG_BLEND_OP_SOURCE on transparent color + # test OP_SOURCE on transparent color blend = [ - PngImagePlugin.APNG_BLEND_OP_OVER, - PngImagePlugin.APNG_BLEND_OP_SOURCE, + PngImagePlugin.Blend.OP_OVER, + PngImagePlugin.Blend.OP_SOURCE, ] red.save( test_file, save_all=True, append_images=[red, transparent], default_image=True, - disposal=PngImagePlugin.APNG_DISPOSE_OP_NONE, + disposal=PngImagePlugin.Disposal.OP_NONE, blend=blend, ) with Image.open(test_file) as im: @@ -612,14 +612,14 @@ def test_apng_save_blend(tmp_path): assert im.getpixel((0, 0)) == (0, 0, 0, 0) assert im.getpixel((64, 32)) == (0, 0, 0, 0) - # test APNG_BLEND_OP_OVER + # test OP_OVER red.save( test_file, save_all=True, append_images=[green, transparent], default_image=True, - disposal=PngImagePlugin.APNG_DISPOSE_OP_NONE, - blend=PngImagePlugin.APNG_BLEND_OP_OVER, + disposal=PngImagePlugin.Disposal.OP_NONE, + blend=PngImagePlugin.Blend.OP_OVER, ) with Image.open(test_file) as im: im.seek(1) @@ -630,7 +630,7 @@ def test_apng_save_blend(tmp_path): assert im.getpixel((64, 32)) == (0, 255, 0, 255) # test info blend - red.info["blend"] = PngImagePlugin.APNG_BLEND_OP_OVER + red.info["blend"] = PngImagePlugin.Blend.OP_OVER red.save(test_file, save_all=True, append_images=[green, transparent]) with Image.open(test_file) as im: im.seek(2) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 00bf582fafb..8b0306db892 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -210,8 +210,8 @@ def test_palette_handling(tmp_path): with Image.open(TEST_GIF) as im: im = im.convert("RGB") - im = im.resize((100, 100), Image.LANCZOS) - im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256) + im = im.resize((100, 100), Image.Resampling.LANCZOS) + im2 = im.convert("P", palette=Image.Palette.ADAPTIVE, colors=256) f = str(tmp_path / "temp.gif") im2.save(f, optimize=True) @@ -911,7 +911,7 @@ def test_save_I(tmp_path): def test_getdata(): # Test getheader/getdata against legacy values. # Create a 'P' image with holes in the palette. - im = Image._wedge().resize((16, 16), Image.NEAREST) + im = Image._wedge().resize((16, 16), Image.Resampling.NEAREST) im.putpalette(ImagePalette.ImagePalette("RGB")) im.info = {"background": 0} diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index 317264db646..8eff2cc439f 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -48,7 +48,9 @@ def test_save_to_bytes(): assert im.mode == reloaded.mode assert (64, 64) == reloaded.size assert reloaded.format == "ICO" - assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) + assert_image_equal( + reloaded, hopper().resize((64, 64), Image.Resampling.LANCZOS) + ) # The other one output.seek(0) @@ -58,7 +60,9 @@ def test_save_to_bytes(): assert im.mode == reloaded.mode assert (32, 32) == reloaded.size assert reloaded.format == "ICO" - assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS)) + assert_image_equal( + reloaded, hopper().resize((32, 32), Image.Resampling.LANCZOS) + ) @pytest.mark.parametrize("mode", ("1", "L", "P", "RGB", "RGBA")) @@ -75,7 +79,7 @@ def test_save_to_bytes_bmp(mode): assert "RGBA" == reloaded.mode assert (64, 64) == reloaded.size assert reloaded.format == "ICO" - im = hopper(mode).resize((64, 64), Image.LANCZOS).convert("RGBA") + im = hopper(mode).resize((64, 64), Image.Resampling.LANCZOS).convert("RGBA") assert_image_equal(reloaded, im) # The other one @@ -86,7 +90,7 @@ def test_save_to_bytes_bmp(mode): assert "RGBA" == reloaded.mode assert (32, 32) == reloaded.size assert reloaded.format == "ICO" - im = hopper(mode).resize((32, 32), Image.LANCZOS).convert("RGBA") + im = hopper(mode).resize((32, 32), Image.Resampling.LANCZOS).convert("RGBA") assert_image_equal(reloaded, im) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 4b2ffe70d0c..f0cfb7811c4 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -271,7 +271,7 @@ def test_empty_exif_gps(self): del exif[0x8769] # Assert that it needs to be transposed - assert exif[0x0112] == Image.TRANSVERSE + assert exif[0x0112] == Image.Transpose.TRANSVERSE # Assert that the GPS IFD is present and empty assert exif.get_ifd(0x8825) == {} diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index ca410162a1c..b5ea6d0a026 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -291,7 +291,7 @@ def test_subsampling_decode(name): # RGB reference images are downscaled epsilon = 3e-3 width, height = width * 2, height * 2 - expected = im2.resize((width, height), Image.NEAREST) + expected = im2.resize((width, height), Image.Resampling.NEAREST) assert_image_similar(im, expected, epsilon) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index e40a19394bf..53ed2520ae0 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -112,7 +112,7 @@ def test_g4_write(self, tmp_path): test_file = "Tests/images/hopper_g4_500.tif" with Image.open(test_file) as orig: out = str(tmp_path / "temp.tif") - rot = orig.transpose(Image.ROTATE_90) + rot = orig.transpose(Image.Transpose.ROTATE_90) assert rot.size == (500, 500) rot.save(out) diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index 3b9c8b07146..b485e854f52 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -77,7 +77,7 @@ def to_rgb_colorsys(im): def test_wedge(): - src = wedge().resize((3 * 32, 32), Image.BILINEAR) + src = wedge().resize((3 * 32, 32), Image.Resampling.BILINEAR) im = src.convert("HSV") comparable = to_hsv_colorsys(src) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index a5a95e96255..75af92427d7 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -136,7 +136,7 @@ def test_trns_l(tmp_path): assert "transparency" in im_p.info im_p.save(f) - im_p = im.convert("P", palette=Image.ADAPTIVE) + im_p = im.convert("P", palette=Image.Palette.ADAPTIVE) assert "transparency" in im_p.info im_p.save(f) @@ -159,13 +159,13 @@ def test_trns_RGB(tmp_path): assert "transparency" not in im_rgba.info im_rgba.save(f) - im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.ADAPTIVE) + im_p = pytest.warns(UserWarning, im.convert, "P", palette=Image.Palette.ADAPTIVE) assert "transparency" not in im_p.info im_p.save(f) im = Image.new("RGB", (1, 1)) im.info["transparency"] = im.getpixel((0, 0)) - im_p = im.convert("P", palette=Image.ADAPTIVE) + im_p = im.convert("P", palette=Image.Palette.ADAPTIVE) assert im_p.info["transparency"] == im_p.getpixel((0, 0)) im_p.save(f) diff --git a/Tests/test_image_getdata.py b/Tests/test_image_getdata.py index 159efd78aa2..36c81b40f07 100644 --- a/Tests/test_image_getdata.py +++ b/Tests/test_image_getdata.py @@ -14,7 +14,7 @@ def test_sanity(): def test_roundtrip(): def getdata(mode): - im = hopper(mode).resize((32, 30), Image.NEAREST) + im = hopper(mode).resize((32, 30), Image.Resampling.NEAREST) data = im.getdata() return data[0], len(data), len(list(data)) diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index 1d3ca813550..dc3caef017b 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -45,7 +45,7 @@ def mask_1(self): @cached_property def mask_L(self): - return self.gradient_L.transpose(Image.ROTATE_270) + return self.gradient_L.transpose(Image.Transpose.ROTATE_270) @cached_property def gradient_L(self): @@ -62,8 +62,8 @@ def gradient_RGB(self): "RGB", [ self.gradient_L, - self.gradient_L.transpose(Image.ROTATE_90), - self.gradient_L.transpose(Image.ROTATE_180), + self.gradient_L.transpose(Image.Transpose.ROTATE_90), + self.gradient_L.transpose(Image.Transpose.ROTATE_180), ], ) @@ -73,9 +73,9 @@ def gradient_RGBA(self): "RGBA", [ self.gradient_L, - self.gradient_L.transpose(Image.ROTATE_90), - self.gradient_L.transpose(Image.ROTATE_180), - self.gradient_L.transpose(Image.ROTATE_270), + self.gradient_L.transpose(Image.Transpose.ROTATE_90), + self.gradient_L.transpose(Image.Transpose.ROTATE_180), + self.gradient_L.transpose(Image.Transpose.ROTATE_270), ], ) @@ -85,9 +85,9 @@ def gradient_RGBa(self): "RGBa", [ self.gradient_L, - self.gradient_L.transpose(Image.ROTATE_90), - self.gradient_L.transpose(Image.ROTATE_180), - self.gradient_L.transpose(Image.ROTATE_270), + self.gradient_L.transpose(Image.Transpose.ROTATE_90), + self.gradient_L.transpose(Image.Transpose.ROTATE_180), + self.gradient_L.transpose(Image.Transpose.ROTATE_270), ], ) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 53b6c900793..6ce39e0ac95 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -21,7 +21,7 @@ def test_sanity(): def test_libimagequant_quantize(): image = hopper() try: - converted = image.quantize(100, Image.LIBIMAGEQUANT) + converted = image.quantize(100, Image.Quantize.LIBIMAGEQUANT) except ValueError as ex: # pragma: no cover if "dependency" in str(ex).lower(): pytest.skip("libimagequant support not available") @@ -34,7 +34,7 @@ def test_libimagequant_quantize(): def test_octree_quantize(): image = hopper() - converted = image.quantize(100, Image.FASTOCTREE) + converted = image.quantize(100, Image.Quantize.FASTOCTREE) assert converted.mode == "P" assert_image_similar(converted.convert("RGB"), image, 20) assert len(converted.getcolors()) == 100 @@ -97,10 +97,10 @@ def test_transparent_colors_equal(): @pytest.mark.parametrize( "method, color", ( - (Image.MEDIANCUT, (0, 0, 0)), - (Image.MAXCOVERAGE, (0, 0, 0)), - (Image.FASTOCTREE, (0, 0, 0)), - (Image.FASTOCTREE, (0, 0, 0, 0)), + (Image.Quantize.MEDIANCUT, (0, 0, 0)), + (Image.Quantize.MAXCOVERAGE, (0, 0, 0)), + (Image.Quantize.FASTOCTREE, (0, 0, 0)), + (Image.Quantize.FASTOCTREE, (0, 0, 0, 0)), ), ) def test_palette(method, color): diff --git a/Tests/test_image_reduce.py b/Tests/test_image_reduce.py index b4eebc14218..70dc87f0a86 100644 --- a/Tests/test_image_reduce.py +++ b/Tests/test_image_reduce.py @@ -97,7 +97,7 @@ def get_image(mode): bands = [gradients_image] for _ in mode_info.bands[1:]: # rotate previous image - band = bands[-1].transpose(Image.ROTATE_90) + band = bands[-1].transpose(Image.Transpose.ROTATE_90) bands.append(band) # Correct alpha channel by transforming completely transparent pixels. # Low alpha values also emphasize error after alpha multiplication. @@ -138,24 +138,26 @@ def compare_reduce_with_reference(im, factor, average_diff=0.4, max_diff=1): reference = Image.new(im.mode, reduced.size) area_size = (im.size[0] // factor[0], im.size[1] // factor[1]) area_box = (0, 0, area_size[0] * factor[0], area_size[1] * factor[1]) - area = im.resize(area_size, Image.BOX, area_box) + area = im.resize(area_size, Image.Resampling.BOX, area_box) reference.paste(area, (0, 0)) if area_size[0] < reduced.size[0]: assert reduced.size[0] - area_size[0] == 1 last_column_box = (area_box[2], 0, im.size[0], area_box[3]) - last_column = im.resize((1, area_size[1]), Image.BOX, last_column_box) + last_column = im.resize( + (1, area_size[1]), Image.Resampling.BOX, last_column_box + ) reference.paste(last_column, (area_size[0], 0)) if area_size[1] < reduced.size[1]: assert reduced.size[1] - area_size[1] == 1 last_row_box = (0, area_box[3], area_box[2], im.size[1]) - last_row = im.resize((area_size[0], 1), Image.BOX, last_row_box) + last_row = im.resize((area_size[0], 1), Image.Resampling.BOX, last_row_box) reference.paste(last_row, (0, area_size[1])) if area_size[0] < reduced.size[0] and area_size[1] < reduced.size[1]: last_pixel_box = (area_box[2], area_box[3], im.size[0], im.size[1]) - last_pixel = im.resize((1, 1), Image.BOX, last_pixel_box) + last_pixel = im.resize((1, 1), Image.Resampling.BOX, last_pixel_box) reference.paste(last_pixel, area_size) assert_compare_images(reduced, reference, average_diff, max_diff) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 8bf2ce916dd..12542233773 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -24,7 +24,7 @@ def test_overflow(self): ): with pytest.raises(MemoryError): # any resampling filter will do here - im.im.resize((xsize, ysize), Image.BILINEAR) + im.im.resize((xsize, ysize), Image.Resampling.BILINEAR) def test_invalid_size(self): im = hopper() @@ -103,7 +103,7 @@ def serialize_image(self, image): def test_reduce_box(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.BOX) + case = case.resize((4, 4), Image.Resampling.BOX) # fmt: off data = ("e1 e1" "e1 e1") @@ -114,7 +114,7 @@ def test_reduce_box(self): def test_reduce_bilinear(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.BILINEAR) + case = case.resize((4, 4), Image.Resampling.BILINEAR) # fmt: off data = ("e1 c9" "c9 b7") @@ -125,7 +125,7 @@ def test_reduce_bilinear(self): def test_reduce_hamming(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.HAMMING) + case = case.resize((4, 4), Image.Resampling.HAMMING) # fmt: off data = ("e1 da" "da d3") @@ -136,7 +136,7 @@ def test_reduce_hamming(self): def test_reduce_bicubic(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (12, 12), 0xE1) - case = case.resize((6, 6), Image.BICUBIC) + case = case.resize((6, 6), Image.Resampling.BICUBIC) # fmt: off data = ("e1 e3 d4" "e3 e5 d6" @@ -148,7 +148,7 @@ def test_reduce_bicubic(self): def test_reduce_lanczos(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (16, 16), 0xE1) - case = case.resize((8, 8), Image.LANCZOS) + case = case.resize((8, 8), Image.Resampling.LANCZOS) # fmt: off data = ("e1 e0 e4 d7" "e0 df e3 d6" @@ -161,7 +161,7 @@ def test_reduce_lanczos(self): def test_enlarge_box(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.BOX) + case = case.resize((4, 4), Image.Resampling.BOX) # fmt: off data = ("e1 e1" "e1 e1") @@ -172,7 +172,7 @@ def test_enlarge_box(self): def test_enlarge_bilinear(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.BILINEAR) + case = case.resize((4, 4), Image.Resampling.BILINEAR) # fmt: off data = ("e1 b0" "b0 98") @@ -183,7 +183,7 @@ def test_enlarge_bilinear(self): def test_enlarge_hamming(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.HAMMING) + case = case.resize((4, 4), Image.Resampling.HAMMING) # fmt: off data = ("e1 d2" "d2 c5") @@ -194,7 +194,7 @@ def test_enlarge_hamming(self): def test_enlarge_bicubic(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (4, 4), 0xE1) - case = case.resize((8, 8), Image.BICUBIC) + case = case.resize((8, 8), Image.Resampling.BICUBIC) # fmt: off data = ("e1 e5 ee b9" "e5 e9 f3 bc" @@ -207,7 +207,7 @@ def test_enlarge_bicubic(self): def test_enlarge_lanczos(self): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (6, 6), 0xE1) - case = case.resize((12, 12), Image.LANCZOS) + case = case.resize((12, 12), Image.Resampling.LANCZOS) data = ( "e1 e0 db ed f5 b8" "e0 df da ec f3 b7" @@ -220,7 +220,9 @@ def test_enlarge_lanczos(self): self.check_case(channel, self.make_sample(data, (12, 12))) def test_box_filter_correct_range(self): - im = Image.new("RGB", (8, 8), "#1688ff").resize((100, 100), Image.BOX) + im = Image.new("RGB", (8, 8), "#1688ff").resize( + (100, 100), Image.Resampling.BOX + ) ref = Image.new("RGB", (100, 100), "#1688ff") assert_image_equal(im, ref) @@ -228,7 +230,7 @@ def test_box_filter_correct_range(self): class TestCoreResampleConsistency: def make_case(self, mode, fill): im = Image.new(mode, (512, 9), fill) - return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0] + return im.resize((9, 512), Image.Resampling.LANCZOS), im.load()[0, 0] def run_case(self, case): channel, color = case @@ -283,20 +285,20 @@ def run_levels_case(self, i): @pytest.mark.xfail(reason="Current implementation isn't precise enough") def test_levels_rgba(self): case = self.make_levels_case("RGBA") - self.run_levels_case(case.resize((512, 32), Image.BOX)) - self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) - self.run_levels_case(case.resize((512, 32), Image.HAMMING)) - self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) - self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.BOX)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.BILINEAR)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.HAMMING)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.BICUBIC)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.LANCZOS)) @pytest.mark.xfail(reason="Current implementation isn't precise enough") def test_levels_la(self): case = self.make_levels_case("LA") - self.run_levels_case(case.resize((512, 32), Image.BOX)) - self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) - self.run_levels_case(case.resize((512, 32), Image.HAMMING)) - self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) - self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.BOX)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.BILINEAR)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.HAMMING)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.BICUBIC)) + self.run_levels_case(case.resize((512, 32), Image.Resampling.LANCZOS)) def make_dirty_case(self, mode, clean_pixel, dirty_pixel): i = Image.new(mode, (64, 64), dirty_pixel) @@ -321,19 +323,27 @@ def run_dirty_case(self, i, clean_pixel): 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.Resampling.BOX), (255, 255, 0)) + self.run_dirty_case( + case.resize((20, 20), Image.Resampling.BILINEAR), (255, 255, 0) + ) + self.run_dirty_case( + case.resize((20, 20), Image.Resampling.HAMMING), (255, 255, 0) + ) + self.run_dirty_case( + case.resize((20, 20), Image.Resampling.BICUBIC), (255, 255, 0) + ) + self.run_dirty_case( + case.resize((20, 20), Image.Resampling.LANCZOS), (255, 255, 0) + ) def test_dirty_pixels_la(self): case = self.make_dirty_case("LA", (255, 128), (0, 0)) - self.run_dirty_case(case.resize((20, 20), Image.BOX), (255,)) - self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255,)) - self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255,)) - self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), (255,)) - self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,)) + self.run_dirty_case(case.resize((20, 20), Image.Resampling.BOX), (255,)) + self.run_dirty_case(case.resize((20, 20), Image.Resampling.BILINEAR), (255,)) + self.run_dirty_case(case.resize((20, 20), Image.Resampling.HAMMING), (255,)) + self.run_dirty_case(case.resize((20, 20), Image.Resampling.BICUBIC), (255,)) + self.run_dirty_case(case.resize((20, 20), Image.Resampling.LANCZOS), (255,)) class TestCoreResamplePasses: @@ -346,26 +356,26 @@ def count(self, diff): def test_horizontal(self): im = hopper("L") with self.count(1): - im.resize((im.size[0] - 10, im.size[1]), Image.BILINEAR) + im.resize((im.size[0] - 10, im.size[1]), Image.Resampling.BILINEAR) def test_vertical(self): im = hopper("L") with self.count(1): - im.resize((im.size[0], im.size[1] - 10), Image.BILINEAR) + im.resize((im.size[0], im.size[1] - 10), Image.Resampling.BILINEAR) def test_both(self): im = hopper("L") with self.count(2): - im.resize((im.size[0] - 10, im.size[1] - 10), Image.BILINEAR) + im.resize((im.size[0] - 10, im.size[1] - 10), Image.Resampling.BILINEAR) def test_box_horizontal(self): im = hopper("L") box = (20, 0, im.size[0] - 20, im.size[1]) with self.count(1): # the same size, but different box - with_box = im.resize(im.size, Image.BILINEAR, box) + with_box = im.resize(im.size, Image.Resampling.BILINEAR, box) with self.count(2): - cropped = im.crop(box).resize(im.size, Image.BILINEAR) + cropped = im.crop(box).resize(im.size, Image.Resampling.BILINEAR) assert_image_similar(with_box, cropped, 0.1) def test_box_vertical(self): @@ -373,9 +383,9 @@ def test_box_vertical(self): box = (0, 20, im.size[0], im.size[1] - 20) with self.count(1): # the same size, but different box - with_box = im.resize(im.size, Image.BILINEAR, box) + with_box = im.resize(im.size, Image.Resampling.BILINEAR, box) with self.count(2): - cropped = im.crop(box).resize(im.size, Image.BILINEAR) + cropped = im.crop(box).resize(im.size, Image.Resampling.BILINEAR) assert_image_similar(with_box, cropped, 0.1) @@ -388,7 +398,7 @@ def test_reduce(self): draw = ImageDraw.Draw(i) draw.rectangle((0, 0, i.size[0] // 2 - 1, 0), test_color) - px = i.resize((5, i.size[1]), Image.BICUBIC).load() + px = i.resize((5, i.size[1]), Image.Resampling.BICUBIC).load() if px[2, 0] != test_color // 2: assert test_color // 2 == px[2, 0] @@ -396,7 +406,7 @@ def test_nonzero_coefficients(self): # regression test for the wrong coefficients calculation # due to bug https://github.com/python-pillow/Pillow/issues/2161 im = Image.new("RGBA", (1280, 1280), (0x20, 0x40, 0x60, 0xFF)) - histogram = im.resize((256, 256), Image.BICUBIC).histogram() + histogram = im.resize((256, 256), Image.Resampling.BICUBIC).histogram() # first channel assert histogram[0x100 * 0 + 0x20] == 0x10000 @@ -412,12 +422,12 @@ class TestCoreResampleBox: def test_wrong_arguments(self): im = hopper() for resample in ( - Image.NEAREST, - Image.BOX, - Image.BILINEAR, - Image.HAMMING, - Image.BICUBIC, - Image.LANCZOS, + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, ): im.resize((32, 32), resample, (0, 0, im.width, im.height)) im.resize((32, 32), resample, (20, 20, im.width, im.height)) @@ -456,7 +466,7 @@ def split_range(size, tiles): for y0, y1 in split_range(dst_size[1], ytiles): for x0, x1 in split_range(dst_size[0], xtiles): box = (x0 * scale[0], y0 * scale[1], x1 * scale[0], y1 * scale[1]) - tile = im.resize((x1 - x0, y1 - y0), Image.BICUBIC, box) + tile = im.resize((x1 - x0, y1 - y0), Image.Resampling.BICUBIC, box) tiled.paste(tile, (x0, y0)) return tiled @@ -467,7 +477,7 @@ def test_tiles(self): with Image.open("Tests/images/flower.jpg") as im: assert im.size == (480, 360) dst_size = (251, 188) - reference = im.resize(dst_size, Image.BICUBIC) + reference = im.resize(dst_size, Image.Resampling.BICUBIC) for tiles in [(1, 1), (3, 3), (9, 7), (100, 100)]: tiled = self.resize_tiled(im, dst_size, *tiles) @@ -483,12 +493,16 @@ def test_subsample(self): assert im.size == (480, 360) dst_size = (48, 36) # Reference is cropped image resized to destination - reference = im.crop((0, 0, 473, 353)).resize(dst_size, Image.BICUBIC) - # Image.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45) - supersampled = im.resize((60, 45), Image.BOX) + reference = im.crop((0, 0, 473, 353)).resize( + dst_size, Image.Resampling.BICUBIC + ) + # Image.Resampling.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45) + supersampled = im.resize((60, 45), Image.Resampling.BOX) - with_box = supersampled.resize(dst_size, Image.BICUBIC, (0, 0, 59.125, 44.125)) - without_box = supersampled.resize(dst_size, Image.BICUBIC) + with_box = supersampled.resize( + dst_size, Image.Resampling.BICUBIC, (0, 0, 59.125, 44.125) + ) + without_box = supersampled.resize(dst_size, Image.Resampling.BICUBIC) # error with box should be much smaller than without assert_image_similar(reference, with_box, 6) @@ -496,7 +510,7 @@ def test_subsample(self): assert_image_similar(reference, without_box, 5) def test_formats(self): - for resample in [Image.NEAREST, Image.BILINEAR]: + for resample in [Image.Resampling.NEAREST, Image.Resampling.BILINEAR]: for mode in ["RGB", "L", "RGBA", "LA", "I", ""]: im = hopper(mode) box = (20, 20, im.size[0] - 20, im.size[1] - 20) @@ -514,7 +528,7 @@ def test_passthrough(self): ((40, 50), (10, 0, 50, 50)), ((40, 50), (10, 20, 50, 70)), ]: - res = im.resize(size, Image.LANCZOS, box) + res = im.resize(size, Image.Resampling.LANCZOS, box) assert res.size == size assert_image_equal(res, im.crop(box), f">>> {size} {box}") @@ -528,7 +542,7 @@ def test_no_passthrough(self): ((40, 50), (10.4, 0.4, 50.4, 50.4)), ((40, 50), (10.4, 20.4, 50.4, 70.4)), ]: - res = im.resize(size, Image.LANCZOS, box) + res = im.resize(size, Image.Resampling.LANCZOS, box) assert res.size == size with pytest.raises(AssertionError, match=r"difference \d"): # check that the difference at least that much @@ -538,7 +552,7 @@ def test_skip_horizontal(self): # Can skip resize for one dimension im = hopper() - for flt in [Image.NEAREST, Image.BICUBIC]: + for flt in [Image.Resampling.NEAREST, Image.Resampling.BICUBIC]: for size, box in [ ((40, 50), (0, 0, 40, 90)), ((40, 50), (0, 20, 40, 90)), @@ -559,7 +573,7 @@ def test_skip_vertical(self): # Can skip resize for one dimension im = hopper() - for flt in [Image.NEAREST, Image.BICUBIC]: + for flt in [Image.Resampling.NEAREST, Image.Resampling.BICUBIC]: for size, box in [ ((40, 50), (0, 0, 90, 50)), ((40, 50), (20, 0, 90, 50)), diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 1fe278052fa..04b7c8c97db 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -35,33 +35,33 @@ def test_nearest_mode(self): "I;16", ]: # exotic mode im = hopper(mode) - r = self.resize(im, (15, 12), Image.NEAREST) + r = self.resize(im, (15, 12), Image.Resampling.NEAREST) assert r.mode == mode assert r.size == (15, 12) assert r.im.bands == im.im.bands def test_convolution_modes(self): with pytest.raises(ValueError): - self.resize(hopper("1"), (15, 12), Image.BILINEAR) + self.resize(hopper("1"), (15, 12), Image.Resampling.BILINEAR) with pytest.raises(ValueError): - self.resize(hopper("P"), (15, 12), Image.BILINEAR) + self.resize(hopper("P"), (15, 12), Image.Resampling.BILINEAR) with pytest.raises(ValueError): - self.resize(hopper("I;16"), (15, 12), Image.BILINEAR) + self.resize(hopper("I;16"), (15, 12), Image.Resampling.BILINEAR) for mode in ["L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr"]: im = hopper(mode) - r = self.resize(im, (15, 12), Image.BILINEAR) + r = self.resize(im, (15, 12), Image.Resampling.BILINEAR) assert r.mode == mode assert r.size == (15, 12) assert 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, + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, ]: r = self.resize(hopper("RGB"), (15, 12), f) assert r.mode == "RGB" @@ -69,12 +69,12 @@ def test_reduce_filters(self): def test_enlarge_filters(self): for f in [ - Image.NEAREST, - Image.BOX, - Image.BILINEAR, - Image.HAMMING, - Image.BICUBIC, - Image.LANCZOS, + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, ]: r = self.resize(hopper("RGB"), (212, 195), f) assert r.mode == "RGB" @@ -95,12 +95,12 @@ def test_endianness(self): samples["dirty"].putpixel((1, 1), 128) for f in [ - Image.NEAREST, - Image.BOX, - Image.BILINEAR, - Image.HAMMING, - Image.BICUBIC, - Image.LANCZOS, + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, ]: # samples resized with current filter references = { @@ -124,12 +124,12 @@ def test_endianness(self): def test_enlarge_zero(self): for f in [ - Image.NEAREST, - Image.BOX, - Image.BILINEAR, - Image.HAMMING, - Image.BICUBIC, - Image.LANCZOS, + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, ]: r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), f) assert r.mode == "RGB" @@ -164,15 +164,19 @@ def gradients_image(): class TestReducingGapResize: def test_reducing_gap_values(self, gradients_image): - ref = gradients_image.resize((52, 34), Image.BICUBIC, reducing_gap=None) - im = gradients_image.resize((52, 34), Image.BICUBIC) + ref = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, reducing_gap=None + ) + im = gradients_image.resize((52, 34), Image.Resampling.BICUBIC) assert_image_equal(ref, im) with pytest.raises(ValueError): - gradients_image.resize((52, 34), Image.BICUBIC, reducing_gap=0) + gradients_image.resize((52, 34), Image.Resampling.BICUBIC, reducing_gap=0) with pytest.raises(ValueError): - gradients_image.resize((52, 34), Image.BICUBIC, reducing_gap=0.99) + gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, reducing_gap=0.99 + ) def test_reducing_gap_1(self, gradients_image): for box, epsilon in [ @@ -180,9 +184,9 @@ def test_reducing_gap_1(self, gradients_image): ((1.1, 2.2, 510.8, 510.9), 4), ((3, 10, 410, 256), 10), ]: - ref = gradients_image.resize((52, 34), Image.BICUBIC, box=box) + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) im = gradients_image.resize( - (52, 34), Image.BICUBIC, box=box, reducing_gap=1.0 + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=1.0 ) with pytest.raises(AssertionError): @@ -196,9 +200,9 @@ def test_reducing_gap_2(self, gradients_image): ((1.1, 2.2, 510.8, 510.9), 1.5), ((3, 10, 410, 256), 1), ]: - ref = gradients_image.resize((52, 34), Image.BICUBIC, box=box) + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) im = gradients_image.resize( - (52, 34), Image.BICUBIC, box=box, reducing_gap=2.0 + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=2.0 ) with pytest.raises(AssertionError): @@ -212,9 +216,9 @@ def test_reducing_gap_3(self, gradients_image): ((1.1, 2.2, 510.8, 510.9), 1), ((3, 10, 410, 256), 0.5), ]: - ref = gradients_image.resize((52, 34), Image.BICUBIC, box=box) + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) im = gradients_image.resize( - (52, 34), Image.BICUBIC, box=box, reducing_gap=3.0 + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=3.0 ) with pytest.raises(AssertionError): @@ -224,9 +228,9 @@ def test_reducing_gap_3(self, gradients_image): def test_reducing_gap_8(self, gradients_image): for box in [None, (1.1, 2.2, 510.8, 510.9), (3, 10, 410, 256)]: - ref = gradients_image.resize((52, 34), Image.BICUBIC, box=box) + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) im = gradients_image.resize( - (52, 34), Image.BICUBIC, box=box, reducing_gap=8.0 + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=8.0 ) assert_image_equal(ref, im) @@ -236,8 +240,10 @@ def test_box_filter(self, gradients_image): ((0, 0, 512, 512), 5.5), ((0.9, 1.7, 128, 128), 9.5), ]: - ref = gradients_image.resize((52, 34), Image.BOX, box=box) - im = gradients_image.resize((52, 34), Image.BOX, box=box, reducing_gap=1.0) + ref = gradients_image.resize((52, 34), Image.Resampling.BOX, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BOX, box=box, reducing_gap=1.0 + ) assert_image_similar(ref, im, epsilon) @@ -261,12 +267,12 @@ def resize(mode, size): def test_default_filter(self): for mode in "L", "RGB", "I", "F": im = hopper(mode) - assert im.resize((20, 20), Image.BICUBIC) == im.resize((20, 20)) + assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20)) for mode in "1", "P": im = hopper(mode) - assert im.resize((20, 20), Image.NEAREST) == im.resize((20, 20)) + assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) for mode in "I;16", "I;16L", "I;16B", "BGR;15", "BGR;16": im = hopper(mode) - assert im.resize((20, 20), Image.NEAREST) == im.resize((20, 20)) + assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 2d72ffa684c..f96864c53df 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -46,14 +46,14 @@ def test_zero(): def test_resample(): # Target image creation, inspected by eye. # >>> im = Image.open('Tests/images/hopper.ppm') - # >>> im = im.rotate(45, resample=Image.BICUBIC, expand=True) + # >>> im = im.rotate(45, resample=Image.Resampling.BICUBIC, expand=True) # >>> im.save('Tests/images/hopper_45.png') with Image.open("Tests/images/hopper_45.png") as target: for (resample, epsilon) in ( - (Image.NEAREST, 10), - (Image.BILINEAR, 5), - (Image.BICUBIC, 0), + (Image.Resampling.NEAREST, 10), + (Image.Resampling.BILINEAR, 5), + (Image.Resampling.BICUBIC, 0), ): im = hopper() im = im.rotate(45, resample=resample, expand=True) @@ -62,7 +62,7 @@ def test_resample(): def test_center_0(): im = hopper() - im = im.rotate(45, center=(0, 0), resample=Image.BICUBIC) + im = im.rotate(45, center=(0, 0), resample=Image.Resampling.BICUBIC) with Image.open("Tests/images/hopper_45.png") as target: target_origin = target.size[1] / 2 @@ -73,7 +73,7 @@ def test_center_0(): def test_center_14(): im = hopper() - im = im.rotate(45, center=(14, 14), resample=Image.BICUBIC) + im = im.rotate(45, center=(14, 14), resample=Image.Resampling.BICUBIC) with Image.open("Tests/images/hopper_45.png") as target: target_origin = target.size[1] / 2 - 14 @@ -90,7 +90,7 @@ def test_translate(): (target_origin, target_origin, target_origin + 128, target_origin + 128) ) - im = im.rotate(45, translate=(5, 5), resample=Image.BICUBIC) + im = im.rotate(45, translate=(5, 5), resample=Image.Resampling.BICUBIC) assert_image_similar(im, target, 1) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index dd140955dee..6d4eb4cd132 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -97,24 +97,24 @@ def test_DCT_scaling_edges(): thumb = fromstring(tostring(im, "JPEG", quality=99, subsampling=0)) # small reducing_gap to amplify the effect - thumb.thumbnail((32, 32), Image.BICUBIC, reducing_gap=1.0) + thumb.thumbnail((32, 32), Image.Resampling.BICUBIC, reducing_gap=1.0) - ref = im.resize((32, 32), Image.BICUBIC) + ref = im.resize((32, 32), Image.Resampling.BICUBIC) # This is still JPEG, some error is present. Without the fix it is 11.5 assert_image_similar(thumb, ref, 1.5) def test_reducing_gap_values(): im = hopper() - im.thumbnail((18, 18), Image.BICUBIC) + im.thumbnail((18, 18), Image.Resampling.BICUBIC) ref = hopper() - ref.thumbnail((18, 18), Image.BICUBIC, reducing_gap=2.0) + ref.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=2.0) # reducing_gap=2.0 should be the default assert_image_equal(ref, im) ref = hopper() - ref.thumbnail((18, 18), Image.BICUBIC, reducing_gap=None) + ref.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=None) with pytest.raises(AssertionError): assert_image_equal(ref, im) @@ -125,9 +125,9 @@ def test_reducing_gap_for_DCT_scaling(): with Image.open("Tests/images/hopper.jpg") as ref: # thumbnail should call draft with reducing_gap scale ref.draft(None, (18 * 3, 18 * 3)) - ref = ref.resize((18, 18), Image.BICUBIC) + ref = ref.resize((18, 18), Image.Resampling.BICUBIC) with Image.open("Tests/images/hopper.jpg") as im: - im.thumbnail((18, 18), Image.BICUBIC, reducing_gap=3.0) + im.thumbnail((18, 18), Image.Resampling.BICUBIC, reducing_gap=3.0) assert_image_equal(ref, im) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index ea208362b2a..ac0e74969b0 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -34,20 +34,22 @@ def test_info(self): def test_palette(self): with Image.open("Tests/images/hopper.gif") as im: - transformed = im.transform(im.size, Image.AFFINE, [1, 0, 0, 0, 1, 0]) + transformed = im.transform( + im.size, Image.Transform.AFFINE, [1, 0, 0, 0, 1, 0] + ) assert im.palette.palette == transformed.palette.palette def test_extent(self): im = hopper("RGB") (w, h) = im.size # fmt: off - transformed = im.transform(im.size, Image.EXTENT, + transformed = im.transform(im.size, Image.Transform.EXTENT, (0, 0, w//2, h//2), # ul -> lr - Image.BILINEAR) + Image.Resampling.BILINEAR) # fmt: on - scaled = im.resize((w * 2, h * 2), Image.BILINEAR).crop((0, 0, w, h)) + scaled = im.resize((w * 2, h * 2), Image.Resampling.BILINEAR).crop((0, 0, w, h)) # undone -- precision? assert_image_similar(transformed, scaled, 23) @@ -57,15 +59,18 @@ def test_quad(self): im = hopper("RGB") (w, h) = im.size # fmt: off - transformed = im.transform(im.size, Image.QUAD, + transformed = im.transform(im.size, Image.Transform.QUAD, (0, 0, 0, h//2, # ul -> ccw around quad: w//2, h//2, w//2, 0), - Image.BILINEAR) + Image.Resampling.BILINEAR) # fmt: on scaled = im.transform( - (w, h), Image.AFFINE, (0.5, 0, 0, 0, 0.5, 0), Image.BILINEAR + (w, h), + Image.Transform.AFFINE, + (0.5, 0, 0, 0, 0.5, 0), + Image.Resampling.BILINEAR, ) assert_image_equal(transformed, scaled) @@ -80,9 +85,9 @@ def test_fill(self): (w, h) = im.size transformed = im.transform( im.size, - Image.EXTENT, + Image.Transform.EXTENT, (0, 0, w * 2, h * 2), - Image.BILINEAR, + Image.Resampling.BILINEAR, fillcolor="red", ) @@ -93,18 +98,21 @@ def test_mesh(self): im = hopper("RGBA") (w, h) = im.size # fmt: off - transformed = im.transform(im.size, Image.MESH, + transformed = im.transform(im.size, Image.Transform.MESH, [((0, 0, w//2, h//2), # box (0, 0, 0, h, w, h, w, 0)), # ul -> ccw around quad ((w//2, h//2, w, h), # box (0, 0, 0, h, w, h, w, 0))], # ul -> ccw around quad - Image.BILINEAR) + Image.Resampling.BILINEAR) # fmt: on scaled = im.transform( - (w // 2, h // 2), Image.AFFINE, (2, 0, 0, 0, 2, 0), Image.BILINEAR + (w // 2, h // 2), + Image.Transform.AFFINE, + (2, 0, 0, 0, 2, 0), + Image.Resampling.BILINEAR, ) checker = Image.new("RGBA", im.size) @@ -137,14 +145,16 @@ def _test_alpha_premult(self, op): def test_alpha_premult_resize(self): def op(im, sz): - return im.resize(sz, Image.BILINEAR) + return im.resize(sz, Image.Resampling.BILINEAR) self._test_alpha_premult(op) def test_alpha_premult_transform(self): def op(im, sz): (w, h) = im.size - return im.transform(sz, Image.EXTENT, (0, 0, w, h), Image.BILINEAR) + return im.transform( + sz, Image.Transform.EXTENT, (0, 0, w, h), Image.Resampling.BILINEAR + ) self._test_alpha_premult(op) @@ -171,7 +181,7 @@ def _test_nearest(self, op, mode): @pytest.mark.parametrize("mode", ("RGBA", "LA")) def test_nearest_resize(self, mode): def op(im, sz): - return im.resize(sz, Image.NEAREST) + return im.resize(sz, Image.Resampling.NEAREST) self._test_nearest(op, mode) @@ -179,7 +189,9 @@ def op(im, sz): def test_nearest_transform(self, mode): def op(im, sz): (w, h) = im.size - return im.transform(sz, Image.EXTENT, (0, 0, w, h), Image.NEAREST) + return im.transform( + sz, Image.Transform.EXTENT, (0, 0, w, h), Image.Resampling.NEAREST + ) self._test_nearest(op, mode) @@ -213,13 +225,15 @@ def test_missing_method_data(self): def test_unknown_resampling_filter(self): with hopper() as im: (w, h) = im.size - for resample in (Image.BOX, "unknown"): + for resample in (Image.Resampling.BOX, "unknown"): with pytest.raises(ValueError): - im.transform((100, 100), Image.EXTENT, (0, 0, w, h), resample) + im.transform( + (100, 100), Image.Transform.EXTENT, (0, 0, w, h), resample + ) class TestImageTransformAffine: - transform = Image.AFFINE + transform = Image.Transform.AFFINE def _test_image(self): im = hopper("RGB") @@ -247,7 +261,11 @@ def _test_rotate(self, deg, transpose): else: transposed = im - for resample in [Image.NEAREST, Image.BILINEAR, Image.BICUBIC]: + for resample in [ + Image.Resampling.NEAREST, + Image.Resampling.BILINEAR, + Image.Resampling.BICUBIC, + ]: transformed = im.transform( transposed.size, self.transform, matrix, resample ) @@ -257,13 +275,13 @@ def test_rotate_0_deg(self): self._test_rotate(0, None) def test_rotate_90_deg(self): - self._test_rotate(90, Image.ROTATE_90) + self._test_rotate(90, Image.Transpose.ROTATE_90) def test_rotate_180_deg(self): - self._test_rotate(180, Image.ROTATE_180) + self._test_rotate(180, Image.Transpose.ROTATE_180) def test_rotate_270_deg(self): - self._test_rotate(270, Image.ROTATE_270) + self._test_rotate(270, Image.Transpose.ROTATE_270) def _test_resize(self, scale, epsilonscale): im = self._test_image() @@ -273,9 +291,9 @@ def _test_resize(self, scale, epsilonscale): matrix_down = [scale, 0, 0, 0, scale, 0, 0, 0] for resample, epsilon in [ - (Image.NEAREST, 0), - (Image.BILINEAR, 2), - (Image.BICUBIC, 1), + (Image.Resampling.NEAREST, 0), + (Image.Resampling.BILINEAR, 2), + (Image.Resampling.BICUBIC, 1), ]: transformed = im.transform(size_up, self.transform, matrix_up, resample) transformed = transformed.transform( @@ -306,9 +324,9 @@ def _test_translate(self, x, y, epsilonscale): matrix_down = [1, 0, x, 0, 1, y, 0, 0] for resample, epsilon in [ - (Image.NEAREST, 0), - (Image.BILINEAR, 1.5), - (Image.BICUBIC, 1), + (Image.Resampling.NEAREST, 0), + (Image.Resampling.BILINEAR, 1.5), + (Image.Resampling.BICUBIC, 1), ]: transformed = im.transform(size_up, self.transform, matrix_up, resample) transformed = transformed.transform( @@ -328,4 +346,4 @@ def test_translate_50(self): class TestImageTransformPerspective(TestImageTransformAffine): # Repeat all tests for AFFINE transformations with PERSPECTIVE - transform = Image.PERSPECTIVE + transform = Image.Transform.PERSPECTIVE diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index a004434dae7..6408e156491 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,12 +1,4 @@ -from PIL.Image import ( - FLIP_LEFT_RIGHT, - FLIP_TOP_BOTTOM, - ROTATE_90, - ROTATE_180, - ROTATE_270, - TRANSPOSE, - TRANSVERSE, -) +from PIL.Image import Transpose from . import helper from .helper import assert_image_equal @@ -20,7 +12,7 @@ def test_flip_left_right(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(FLIP_LEFT_RIGHT) + out = im.transpose(Transpose.FLIP_LEFT_RIGHT) assert out.mode == mode assert out.size == im.size @@ -37,7 +29,7 @@ def transpose(mode): def test_flip_top_bottom(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(FLIP_TOP_BOTTOM) + out = im.transpose(Transpose.FLIP_TOP_BOTTOM) assert out.mode == mode assert out.size == im.size @@ -54,7 +46,7 @@ def transpose(mode): def test_rotate_90(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(ROTATE_90) + out = im.transpose(Transpose.ROTATE_90) assert out.mode == mode assert out.size == im.size[::-1] @@ -71,7 +63,7 @@ def transpose(mode): def test_rotate_180(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(ROTATE_180) + out = im.transpose(Transpose.ROTATE_180) assert out.mode == mode assert out.size == im.size @@ -88,7 +80,7 @@ def transpose(mode): def test_rotate_270(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(ROTATE_270) + out = im.transpose(Transpose.ROTATE_270) assert out.mode == mode assert out.size == im.size[::-1] @@ -105,7 +97,7 @@ def transpose(mode): def test_transpose(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(TRANSPOSE) + out = im.transpose(Transpose.TRANSPOSE) assert out.mode == mode assert out.size == im.size[::-1] @@ -122,7 +114,7 @@ def transpose(mode): def test_tranverse(): def transpose(mode): im = HOPPER[mode] - out = im.transpose(TRANSVERSE) + out = im.transpose(Transpose.TRANSVERSE) assert out.mode == mode assert out.size == im.size[::-1] @@ -143,20 +135,31 @@ def test_roundtrip(): def transpose(first, second): return im.transpose(first).transpose(second) - assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) - assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) - assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) - assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) assert_image_equal( - im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM) + im, transpose(Transpose.FLIP_LEFT_RIGHT, Transpose.FLIP_LEFT_RIGHT) ) assert_image_equal( - im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT) + im, transpose(Transpose.FLIP_TOP_BOTTOM, Transpose.FLIP_TOP_BOTTOM) ) + assert_image_equal(im, transpose(Transpose.ROTATE_90, Transpose.ROTATE_270)) + assert_image_equal(im, transpose(Transpose.ROTATE_180, Transpose.ROTATE_180)) assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT) + im.transpose(Transpose.TRANSPOSE), + transpose(Transpose.ROTATE_90, Transpose.FLIP_TOP_BOTTOM), ) assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM) + im.transpose(Transpose.TRANSPOSE), + transpose(Transpose.ROTATE_270, Transpose.FLIP_LEFT_RIGHT), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_90, Transpose.FLIP_LEFT_RIGHT), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_270, Transpose.FLIP_TOP_BOTTOM), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_180, Transpose.TRANSPOSE), ) - assert_image_equal(im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 99f3b4e0329..09af1138989 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -140,7 +140,7 @@ def test_intent(): skip_missing() assert ImageCms.getDefaultIntent(SRGB) == 0 support = ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT + SRGB, ImageCms.Intent.ABSOLUTE_COLORIMETRIC, ImageCms.Direction.INPUT ) assert support == 1 @@ -153,7 +153,7 @@ def test_profile_object(): # ["sRGB built-in", "", "WhitePoint : D65 (daylight)", "", ""] assert ImageCms.getDefaultIntent(p) == 0 support = ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT + p, ImageCms.Intent.ABSOLUTE_COLORIMETRIC, ImageCms.Direction.INPUT ) assert support == 1 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index b661494c733..3cd755cb4d3 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -183,7 +183,7 @@ def test_bitmap(): im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) with Image.open("Tests/images/pil123rgba.png") as small: - small = small.resize((50, 50), Image.NEAREST) + small = small.resize((50, 50), Image.Resampling.NEAREST) # Act draw.bitmap((10, 10), small) @@ -319,7 +319,7 @@ def test_ellipse_symmetric(): im = Image.new("RGB", (width, 100)) draw = ImageDraw.Draw(im) draw.ellipse(bbox, fill="green", outline="blue") - assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT)) + assert_image_equal(im, im.transpose(Image.Transpose.FLIP_LEFT_RIGHT)) def test_ellipse_width(): diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index a5c76700d65..3e86477c526 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -23,7 +23,7 @@ class TestImageFile: def test_parser(self): def roundtrip(format): - im = hopper("L").resize((1000, 1000), Image.NEAREST) + im = hopper("L").resize((1000, 1000), Image.Resampling.NEAREST) if format in ("MSP", "XBM"): im = im.convert("1") diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0d423aab7be..8e2c61848dd 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -29,7 +29,7 @@ class TestImageFont: - LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC + LAYOUT_ENGINE = ImageFont.Layout.BASIC def get_font(self): return ImageFont.truetype( @@ -94,12 +94,12 @@ def test_unavailable_layout_engine(self): try: ttf = ImageFont.truetype( - FONT_PATH, FONT_SIZE, layout_engine=ImageFont.LAYOUT_RAQM + FONT_PATH, FONT_SIZE, layout_engine=ImageFont.Layout.RAQM ) finally: ImageFont.core.HAVE_RAQM = have_raqm - assert ttf.layout_engine == ImageFont.LAYOUT_BASIC + assert ttf.layout_engine == ImageFont.Layout.BASIC def _render(self, font): txt = "Hello World!" @@ -182,7 +182,7 @@ def test_getlength(self, text, mode, font, size, length_basic, length_raqm): im = Image.new(mode, (1, 1), 0) d = ImageDraw.Draw(im) - if self.LAYOUT_ENGINE == ImageFont.LAYOUT_BASIC: + if self.LAYOUT_ENGINE == ImageFont.Layout.BASIC: length = d.textlength(text, f) assert length == length_basic else: @@ -294,7 +294,7 @@ def test_rotated_transposed_font(self): word = "testing" font = self.get_font() - orientation = Image.ROTATE_90 + orientation = Image.Transpose.ROTATE_90 transposed_font = ImageFont.TransposedFont(font, orientation=orientation) # Original font @@ -333,7 +333,7 @@ def test_rotated_transposed_font_get_mask(self): # Arrange text = "mask this" font = self.get_font() - orientation = Image.ROTATE_90 + orientation = Image.Transpose.ROTATE_90 transposed_font = ImageFont.TransposedFont(font, orientation=orientation) # Act @@ -604,7 +604,7 @@ def test_complex_font_settings(self): # Arrange t = self.get_font() # Act / Assert - if t.layout_engine == ImageFont.LAYOUT_BASIC: + if t.layout_engine == ImageFont.Layout.BASIC: with pytest.raises(KeyError): t.getmask("абвг", direction="rtl") with pytest.raises(KeyError): @@ -753,7 +753,7 @@ def test_anchor(self, anchor, left, top): name, text = "quick", "Quick" path = f"Tests/images/test_anchor_{name}_{anchor}.png" - if self.LAYOUT_ENGINE == ImageFont.LAYOUT_RAQM: + if self.LAYOUT_ENGINE == ImageFont.Layout.RAQM: width, height = (129, 44) else: width, height = (128, 44) @@ -993,7 +993,7 @@ def test_colr_mask(self): @skip_unless_feature("raqm") class TestImageFont_RaqmLayout(TestImageFont): - LAYOUT_ENGINE = ImageFont.LAYOUT_RAQM + LAYOUT_ENGINE = ImageFont.Layout.RAQM def test_render_mono_size(): @@ -1004,7 +1004,7 @@ def test_render_mono_size(): ttf = ImageFont.truetype( "Tests/fonts/DejaVuSans/DejaVuSans.ttf", 18, - layout_engine=ImageFont.LAYOUT_BASIC, + layout_engine=ImageFont.Layout.BASIC, ) draw.text((10, 10), "r" * 10, "black", ttf) diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 0571aabf495..688af7113cc 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -34,7 +34,7 @@ def basic(mode): imOut = imIn.copy() verify(imOut) # copy - imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) + imOut = imIn.transform((w, h), Image.Transform.EXTENT, (0, 0, w, h)) verify(imOut) # transform filename = str(tmp_path / "temp.im") diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index bd44f63a3bb..04966d5dfad 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -696,12 +696,12 @@ parameter must be set to ``True``. The following parameters can also be set: operation to be used for this frame before rendering the next frame. Defaults to 0. - * 0 (:py:data:`~PIL.PngImagePlugin.APNG_DISPOSE_OP_NONE`, default) - + * 0 (:py:data:`~PIL.PngImagePlugin.Disposal.OP_NONE`, default) - No disposal is done on this frame before rendering the next frame. - * 1 (:py:data:`PIL.PngImagePlugin.APNG_DISPOSE_OP_BACKGROUND`) - + * 1 (:py:data:`PIL.PngImagePlugin.Disposal.OP_BACKGROUND`) - This frame's modified region is cleared to fully transparent black before rendering the next frame. - * 2 (:py:data:`~PIL.PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS`) - + * 2 (:py:data:`~PIL.PngImagePlugin.Disposal.OP_PREVIOUS`) - This frame's modified region is reverted to the previous frame's contents before rendering the next frame. @@ -710,10 +710,10 @@ parameter must be set to ``True``. The following parameters can also be set: operation to be used for this frame before rendering the next frame. Defaults to 0. - * 0 (:py:data:`~PIL.PngImagePlugin.APNG_BLEND_OP_SOURCE`) - + * 0 (:py:data:`~PIL.PngImagePlugin.Blend.OP_SOURCE`) - All color components of this frame, including alpha, overwrite the previous output image contents. - * 1 (:py:data:`~PIL.PngImagePlugin.APNG_BLEND_OP_OVER`) - + * 1 (:py:data:`~PIL.PngImagePlugin.Blend.OP_OVER`) - This frame should be alpha composited with the previous output image contents. .. note:: diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst index aa9efe19261..b0dbffda4a6 100644 --- a/docs/handbook/tutorial.rst +++ b/docs/handbook/tutorial.rst @@ -155,7 +155,7 @@ Processing a subrectangle, and pasting it back :: - region = region.transpose(Image.ROTATE_180) + region = region.transpose(Image.Transpose.ROTATE_180) im.paste(region, box) When pasting regions back, the size of the region must match the given region @@ -238,11 +238,11 @@ Transposing an image :: - out = im.transpose(Image.FLIP_LEFT_RIGHT) - out = im.transpose(Image.FLIP_TOP_BOTTOM) - out = im.transpose(Image.ROTATE_90) - out = im.transpose(Image.ROTATE_180) - out = im.transpose(Image.ROTATE_270) + out = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + out = im.transpose(Image.Transpose.FLIP_TOP_BOTTOM) + out = im.transpose(Image.Transpose.ROTATE_90) + out = im.transpose(Image.Transpose.ROTATE_180) + out = im.transpose(Image.Transpose.ROTATE_270) ``transpose(ROTATE)`` operations can also be performed identically with :py:meth:`~PIL.Image.Image.rotate` operations, provided the ``expand`` flag is diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index c80b28a984b..2613b6585b3 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -254,7 +254,8 @@ This rotates the input image by ``theta`` degrees counter clockwise: .. automethod:: PIL.Image.Image.transform .. automethod:: PIL.Image.Image.transpose -This flips the input image by using the :data:`FLIP_LEFT_RIGHT` method. +This flips the input image by using the :data:`PIL.Image.Transpose.FLIP_LEFT_RIGHT` +method. .. code-block:: python @@ -263,9 +264,9 @@ This flips the input image by using the :data:`FLIP_LEFT_RIGHT` method. with Image.open("hopper.jpg") as im: # Flip the image from left to right - im_flipped = im.transpose(method=Image.FLIP_LEFT_RIGHT) + im_flipped = im.transpose(method=Image.Transpose.FLIP_LEFT_RIGHT) # To flip the image from top to bottom, - # use the method "Image.FLIP_TOP_BOTTOM" + # use the method "Image.Transpose.FLIP_TOP_BOTTOM" .. automethod:: PIL.Image.Image.verify @@ -389,68 +390,57 @@ Transpose methods Used to specify the :meth:`Image.transpose` method to use. -.. data:: FLIP_LEFT_RIGHT -.. data:: FLIP_TOP_BOTTOM -.. data:: ROTATE_90 -.. data:: ROTATE_180 -.. data:: ROTATE_270 -.. data:: TRANSPOSE -.. data:: TRANSVERSE +.. autoclass:: Transpose + :members: + :undoc-members: Transform methods ^^^^^^^^^^^^^^^^^ Used to specify the :meth:`Image.transform` method to use. -.. data:: AFFINE +.. py:class:: Transform + + .. py:attribute:: AFFINE - Affine transform + Affine transform -.. data:: EXTENT + .. py:attribute:: EXTENT - Cut out a rectangular subregion + Cut out a rectangular subregion -.. data:: PERSPECTIVE + .. py:attribute:: PERSPECTIVE - Perspective transform + Perspective transform -.. data:: QUAD + .. py:attribute:: QUAD - Map a quadrilateral to a rectangle + Map a quadrilateral to a rectangle -.. data:: MESH + .. py:attribute:: MESH - Map a number of source quadrilaterals in one operation + Map a number of source quadrilaterals in one operation Resampling filters ^^^^^^^^^^^^^^^^^^ See :ref:`concept-filters` for details. -.. data:: NEAREST - :noindex: -.. data:: BOX - :noindex: -.. data:: BILINEAR - :noindex: -.. data:: HAMMING - :noindex: -.. data:: BICUBIC - :noindex: -.. data:: LANCZOS - :noindex: +.. autoclass:: Resampling + :members: + :undoc-members: -Some filters are also available under the following names for backwards compatibility: +Some deprecated filters are also available under the following names: .. data:: NONE :noindex: - :value: NEAREST + :value: Resampling.NEAREST .. data:: LINEAR - :value: BILINEAR + :value: Resampling.BILINEAR .. data:: CUBIC - :value: BICUBIC + :value: Resampling.BICUBIC .. data:: ANTIALIAS - :value: LANCZOS + :value: Resampling.LANCZOS Dither modes ^^^^^^^^^^^^ @@ -458,48 +448,56 @@ Dither modes Used to specify the dithering method to use for the :meth:`~Image.convert` and :meth:`~Image.quantize` methods. -.. data:: NONE - :noindex: +.. py:class:: Dither + + .. py:attribute:: NONE + + No dither - No dither + .. py:attribute:: ORDERED -.. comment: (not implemented) - .. data:: ORDERED - .. data:: RASTERIZE + Not implemented -.. data:: FLOYDSTEINBERG + .. py:attribute:: RASTERIZE - Floyd-Steinberg dither + Not implemented + + .. py:attribute:: FLOYDSTEINBERG + + Floyd-Steinberg dither Palettes ^^^^^^^^ Used to specify the pallete to use for the :meth:`~Image.convert` method. -.. data:: WEB -.. data:: ADAPTIVE +.. autoclass:: Palette + :members: + :undoc-members: Quantization methods ^^^^^^^^^^^^^^^^^^^^ Used to specify the quantization method to use for the :meth:`~Image.quantize` method. -.. data:: MEDIANCUT +.. py:class:: Quantize + + .. py:attribute:: MEDIANCUT - Median cut. Default method, except for RGBA images. This method does not support - RGBA images. + Median cut. Default method, except for RGBA images. This method does not support + RGBA images. -.. data:: MAXCOVERAGE + .. py:attribute:: MAXCOVERAGE - Maximum coverage. This method does not support RGBA images. + Maximum coverage. This method does not support RGBA images. -.. data:: FASTOCTREE + .. py:attribute:: FASTOCTREE - Fast octree. Default method for RGBA images. + Fast octree. Default method for RGBA images. -.. data:: LIBIMAGEQUANT + .. py:attribute:: LIBIMAGEQUANT - libimagequant + libimagequant - Check support using :py:func:`PIL.features.check_feature` - with ``feature="libimagequant"``. + Check support using :py:func:`PIL.features.check_feature` with + ``feature="libimagequant"``. diff --git a/docs/reference/ImageCms.rst b/docs/reference/ImageCms.rst index f938e63a0bc..9b9b5e7b29e 100644 --- a/docs/reference/ImageCms.rst +++ b/docs/reference/ImageCms.rst @@ -118,8 +118,8 @@ can be easily displayed in a chromaticity diagram, for example). another profile (usually overridden at run-time, but provided here for DeviceLink and embedded source profiles, see 7.2.15 of ICC.1:2010). - One of ``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC``, ``ImageCms.INTENT_PERCEPTUAL``, - ``ImageCms.INTENT_RELATIVE_COLORIMETRIC`` and ``ImageCms.INTENT_SATURATION``. + One of ``ImageCms.Intent.ABSOLUTE_COLORIMETRIC``, ``ImageCms.Intent.PERCEPTUAL``, + ``ImageCms.Intent.RELATIVE_COLORIMETRIC`` and ``ImageCms.Intent.SATURATION``. .. py:attribute:: profile_id :type: bytes @@ -313,14 +313,14 @@ can be easily displayed in a chromaticity diagram, for example). the CLUT model. The dictionary is indexed by intents - (``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC``, - ``ImageCms.INTENT_PERCEPTUAL``, - ``ImageCms.INTENT_RELATIVE_COLORIMETRIC`` and - ``ImageCms.INTENT_SATURATION``). + (``ImageCms.Intent.ABSOLUTE_COLORIMETRIC``, + ``ImageCms.Intent.PERCEPTUAL``, + ``ImageCms.Intent.RELATIVE_COLORIMETRIC`` and + ``ImageCms.Intent.SATURATION``). The values are 3-tuples indexed by directions - (``ImageCms.DIRECTION_INPUT``, ``ImageCms.DIRECTION_OUTPUT``, - ``ImageCms.DIRECTION_PROOF``). + (``ImageCms.Direction.INPUT``, ``ImageCms.Direction.OUTPUT``, + ``ImageCms.Direction.PROOF``). The elements of the tuple are booleans. If the value is ``True``, that intent is supported for that direction. @@ -331,14 +331,14 @@ can be easily displayed in a chromaticity diagram, for example). Returns a dictionary of all supported intents and directions. The dictionary is indexed by intents - (``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC``, - ``ImageCms.INTENT_PERCEPTUAL``, - ``ImageCms.INTENT_RELATIVE_COLORIMETRIC`` and - ``ImageCms.INTENT_SATURATION``). + (``ImageCms.Intent.ABSOLUTE_COLORIMETRIC``, + ``ImageCms.Intent.PERCEPTUAL``, + ``ImageCms.Intent.RELATIVE_COLORIMETRIC`` and + ``ImageCms.Intent.SATURATION``). The values are 3-tuples indexed by directions - (``ImageCms.DIRECTION_INPUT``, ``ImageCms.DIRECTION_OUTPUT``, - ``ImageCms.DIRECTION_PROOF``). + (``ImageCms.Direction.INPUT``, ``ImageCms.Direction.OUTPUT``, + ``ImageCms.Direction.PROOF``). The elements of the tuple are booleans. If the value is ``True``, that intent is supported for that direction. @@ -352,11 +352,11 @@ can be easily displayed in a chromaticity diagram, for example). Note that you can also get this information for all intents and directions with :py:attr:`.intent_supported`. - :param intent: One of ``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC``, - ``ImageCms.INTENT_PERCEPTUAL``, - ``ImageCms.INTENT_RELATIVE_COLORIMETRIC`` - and ``ImageCms.INTENT_SATURATION``. - :param direction: One of ``ImageCms.DIRECTION_INPUT``, - ``ImageCms.DIRECTION_OUTPUT`` - and ``ImageCms.DIRECTION_PROOF`` + :param intent: One of ``ImageCms.Intent.ABSOLUTE_COLORIMETRIC``, + ``ImageCms.Intent.PERCEPTUAL``, + ``ImageCms.Intent.RELATIVE_COLORIMETRIC`` + and ``ImageCms.Intent.SATURATION``. + :param direction: One of ``ImageCms.Direction.INPUT``, + ``ImageCms.Direction.OUTPUT`` + and ``ImageCms.Direction.PROOF`` :return: Boolean if the intent and direction is supported. diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index 5f718ce19e4..8efef7cfd5c 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -60,12 +60,12 @@ Methods Constants --------- -.. data:: PIL.ImageFont.LAYOUT_BASIC +.. data:: PIL.ImageFont.Layout.BASIC Use basic text layout for TrueType font. Advanced features such as text direction are not supported. -.. data:: PIL.ImageFont.LAYOUT_RAQM +.. data:: PIL.ImageFont.Layout.RAQM Use Raqm text layout for TrueType font. Advanced features are supported. diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 0a6381098da..c6619306186 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -57,7 +57,7 @@ Support for the following features can be checked: * ``transp_webp``: Support for transparency in WebP images. * ``webp_mux``: (compile time) Support for EXIF data in WebP images. * ``webp_anim``: (compile time) Support for animated WebP images. -* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`. Run-time version number is available for Raqm 0.7.0 or newer. +* ``raqm``: Raqm library, required for ``ImageFont.Layout.RAQM`` in :py:func:`PIL.ImageFont.truetype`. Run-time version number is available for Raqm 0.7.0 or newer. * ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available. * ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library. diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index 7094f87846c..5c833a35f22 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -230,8 +230,7 @@ Plugin reference .. automodule:: PIL.PngImagePlugin :members: ChunkStream, PngImageFile, PngStream, getchunks, is_cid, putchunk, - MAX_TEXT_CHUNK, MAX_TEXT_MEMORY, APNG_BLEND_OP_SOURCE, APNG_BLEND_OP_OVER, - APNG_DISPOSE_OP_NONE, APNG_DISPOSE_OP_BACKGROUND, APNG_DISPOSE_OP_PREVIOUS + Blend, Disposal, MAX_TEXT_CHUNK, MAX_TEXT_MEMORY :undoc-members: :show-inheritance: :member-order: groupwise diff --git a/docs/releasenotes/2.7.0.rst b/docs/releasenotes/2.7.0.rst index 660d331640c..dda814c1f7d 100644 --- a/docs/releasenotes/2.7.0.rst +++ b/docs/releasenotes/2.7.0.rst @@ -111,16 +111,14 @@ downscaling with libjpeg, which uses supersampling internally, not convolutions. Image transposition ------------------- -A new method :py:data:`PIL.Image.TRANSPOSE` has been added for the +A new method ``TRANSPOSE`` has been added for the :py:meth:`~PIL.Image.Image.transpose` operation in addition to -:py:data:`~PIL.Image.FLIP_LEFT_RIGHT`, :py:data:`~PIL.Image.FLIP_TOP_BOTTOM`, -:py:data:`~PIL.Image.ROTATE_90`, :py:data:`~PIL.Image.ROTATE_180`, -:py:data:`~PIL.Image.ROTATE_270`. :py:data:`~PIL.Image.TRANSPOSE` is an algebra -transpose, with an image reflected across its main diagonal. - -The speed of :py:data:`~PIL.Image.ROTATE_90`, :py:data:`~PIL.Image.ROTATE_270` -and :py:data:`~PIL.Image.TRANSPOSE` has been significantly improved for large -images which don't fit in the processor cache. +``FLIP_LEFT_RIGHT``, ``FLIP_TOP_BOTTOM``, ``ROTATE_90``, ``ROTATE_180``, +``ROTATE_270``. ``TRANSPOSE`` is an algebra transpose, with an image reflected +across its main diagonal. + +The speed of ``ROTATE_90``, ``ROTATE_270`` and ``TRANSPOSE`` has been significantly +improved for large images which don't fit in the processor cache. Gaussian blur and unsharp mask ------------------------------ diff --git a/selftest.py b/selftest.py index 4ebd7cc00da..5010c121380 100755 --- a/selftest.py +++ b/selftest.py @@ -97,9 +97,9 @@ def testimage(): 10456 >>> len(im.tobytes()) 49152 - >>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0))) + >>> _info(im.transform((512, 512), Image.Transform.AFFINE, (1,0,0,0,1,0))) (None, 'RGB', (512, 512)) - >>> _info(im.transform((512, 512), Image.EXTENT, (32,32,96,96))) + >>> _info(im.transform((512, 512), Image.Transform.EXTENT, (32,32,96,96))) (None, 'RGB', (512, 512)) The ImageDraw module lets you draw stuff in raster images: diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 7b78597b443..111f03ae63f 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -30,19 +30,33 @@ """ import struct +from enum import IntEnum from io import BytesIO from . import Image, ImageFile -BLP_FORMAT_JPEG = 0 -BLP_ENCODING_UNCOMPRESSED = 1 -BLP_ENCODING_DXT = 2 -BLP_ENCODING_UNCOMPRESSED_RAW_BGRA = 3 +class Format(IntEnum): + JPEG = 0 -BLP_ALPHA_ENCODING_DXT1 = 0 -BLP_ALPHA_ENCODING_DXT3 = 1 -BLP_ALPHA_ENCODING_DXT5 = 7 + +class Encoding(IntEnum): + UNCOMPRESSED = 1 + DXT = 2 + UNCOMPRESSED_RAW_BGRA = 3 + + +class AlphaEncoding(IntEnum): + DXT1 = 0 + DXT3 = 1 + DXT5 = 7 + + +globals().update({"BLP_FORMAT_" + k: v for k, v in Format.__members__.items()}) +globals().update({"BLP_ENCODING_" + k: v for k, v in Encoding.__members__.items()}) +globals().update( + {"BLP_ALPHA_ENCODING_" + k: v for k, v in AlphaEncoding.__members__.items()} +) def unpack_565(i): @@ -320,7 +334,7 @@ def _read_blp_header(self): class BLP1Decoder(_BLPBaseDecoder): def _load(self): - if self._blp_compression == BLP_FORMAT_JPEG: + if self._blp_compression == Format.JPEG: self._decode_jpeg_stream() elif self._blp_compression == 1: @@ -372,7 +386,7 @@ def _load(self): if self._blp_compression == 1: # Uncompressed or DirectX compression - if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED: + if self._blp_encoding == Encoding.UNCOMPRESSED: _data = BytesIO(self._safe_read(self._blp_lengths[0])) while True: try: @@ -382,8 +396,8 @@ def _load(self): b, g, r, a = palette[offset] data.extend((r, g, b)) - elif self._blp_encoding == BLP_ENCODING_DXT: - if self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT1: + elif self._blp_encoding == Encoding.DXT: + if self._blp_alpha_encoding == AlphaEncoding.DXT1: linesize = (self.size[0] + 3) // 4 * 8 for yb in range((self.size[1] + 3) // 4): for d in decode_dxt1( @@ -391,13 +405,13 @@ def _load(self): ): data += d - elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3: + elif self._blp_alpha_encoding == AlphaEncoding.DXT3: linesize = (self.size[0] + 3) // 4 * 16 for yb in range((self.size[1] + 3) // 4): for d in decode_dxt3(self._safe_read(linesize)): data += d - elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5: + elif self._blp_alpha_encoding == AlphaEncoding.DXT5: linesize = (self.size[0] + 3) // 4 * 16 for yb in range((self.size[1] + 3) // 4): for d in decode_dxt5(self._safe_read(linesize)): diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index 3b169038c89..de5859ba53d 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -52,13 +52,20 @@ """ import struct +from enum import IntEnum from io import BytesIO from . import Image, ImageFile MAGIC = b"FTEX" -FORMAT_DXT1 = 0 -FORMAT_UNCOMPRESSED = 1 + + +class Format(IntEnum): + DXT1 = 0 + UNCOMPRESSED = 1 + + +globals().update({"FORMAT_" + k: v for k, v in Format.__members__.items()}) class FtexImageFile(ImageFile.ImageFile): @@ -83,10 +90,10 @@ def _open(self): data = self.fp.read(mipmap_size) - if format == FORMAT_DXT1: + if format == Format.DXT1: self.mode = "RGBA" self.tile = [("bcn", (0, 0) + self.size, 0, (1))] - elif format == FORMAT_UNCOMPRESSED: + elif format == Format.UNCOMPRESSED: self.tile = [("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))] else: raise ValueError(f"Invalid texture compression format: {repr(format)}") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 8c2180bc115..4f8ea209dfc 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -169,12 +169,12 @@ def _seek(self, frame): if "transparency" in self.info: self.mode = "RGBA" self.im.putpalettealpha(self.info["transparency"], 0) - self.im = self.im.convert("RGBA", Image.FLOYDSTEINBERG) + self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) del self.info["transparency"] else: self.mode = "RGB" - self.im = self.im.convert("RGB", Image.FLOYDSTEINBERG) + self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) if self.dispose: self.im.paste(self.dispose, self.dispose_extent) @@ -425,7 +425,7 @@ def _normalize_mode(im, initial_call=False): palette_size = 256 if im.palette: palette_size = len(im.palette.getdata()[1]) // 3 - im = im.convert("P", palette=Image.ADAPTIVE, colors=palette_size) + im = im.convert("P", palette=Image.Palette.ADAPTIVE, colors=palette_size) if im.palette.mode == "RGBA": for rgba in im.palette.colors.keys(): if rgba[3] == 0: diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index d9ff9b5e731..07ba12a094c 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -69,7 +69,7 @@ def _save(im, fp, filename): if not tmp: # TODO: invent a more convenient method for proportional scalings tmp = im.copy() - tmp.thumbnail(size, Image.LANCZOS, reducing_gap=None) + tmp.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None) bits = BmpImagePlugin.SAVE[tmp.mode][1] if bmp else 32 fp.write(struct.pack(" 1 or factor_y > 1: @@ -2017,7 +2057,7 @@ def reduce(self, factor, box=None): def rotate( self, angle, - resample=NEAREST, + resample=Resampling.NEAREST, expand=0, center=None, translate=None, @@ -2030,12 +2070,12 @@ def rotate( :param angle: In degrees counter clockwise. :param resample: An optional resampling filter. This can be - one of :py:data:`PIL.Image.NEAREST` (use nearest neighbour), + one of :py:data:`PIL.Image.Resampling.NEAREST` (use nearest neighbour), :py:data:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:data:`PIL.Image.BICUBIC` + environment), or :py:data:`PIL.Image.Resampling.BICUBIC` (cubic spline interpolation in a 4x4 environment). If omitted, or if the image has mode "1" or "P", it is - set to :py:data:`PIL.Image.NEAREST`. See :ref:`concept-filters`. + set to :py:data:`PIL.Image.Resampling.NEAREST`. See :ref:`concept-filters`. :param expand: Optional expansion flag. If true, expands the output image to make it large enough to hold the entire rotated image. If false or omitted, make the output image the same size as the @@ -2056,9 +2096,11 @@ def rotate( if angle == 0: return self.copy() if angle == 180: - return self.transpose(ROTATE_180) + return self.transpose(Transpose.ROTATE_180) if angle in (90, 270) and (expand or self.width == self.height): - return self.transpose(ROTATE_90 if angle == 90 else ROTATE_270) + return self.transpose( + Transpose.ROTATE_90 if angle == 90 else Transpose.ROTATE_270 + ) # Calculate the affine matrix. Note that this is the reverse # transformation (from destination image to source) because we @@ -2127,7 +2169,9 @@ def transform(x, y, matrix): matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) w, h = nw, nh - return self.transform((w, h), AFFINE, matrix, resample, fillcolor=fillcolor) + return self.transform( + (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor + ) def save(self, fp, format=None, **params): """ @@ -2313,7 +2357,7 @@ def tell(self): """ return 0 - def thumbnail(self, size, resample=BICUBIC, reducing_gap=2.0): + def thumbnail(self, size, resample=Resampling.BICUBIC, reducing_gap=2.0): """ Make this image into a thumbnail. This method modifies the image to contain a thumbnail version of itself, no larger than @@ -2329,11 +2373,14 @@ def thumbnail(self, size, resample=BICUBIC, reducing_gap=2.0): :param size: Requested size. :param resample: Optional resampling filter. This can be one - of :py:data:`PIL.Image.NEAREST`, :py:data:`PIL.Image.BOX`, - :py:data:`PIL.Image.BILINEAR`, :py:data:`PIL.Image.HAMMING`, - :py:data:`PIL.Image.BICUBIC` or :py:data:`PIL.Image.LANCZOS`. - If omitted, it defaults to :py:data:`PIL.Image.BICUBIC`. - (was :py:data:`PIL.Image.NEAREST` prior to version 2.5.0). + of :py:data:`PIL.Image.Resampling.NEAREST`, + :py:data:`PIL.Image.Resampling.BOX`, + :py:data:`PIL.Image.Resampling.BILINEAR`, + :py:data:`PIL.Image.Resampling.HAMMING`, + :py:data:`PIL.Image.Resampling.BICUBIC` or + :py:data:`PIL.Image.Resampling.LANCZOS`. + If omitted, it defaults to :py:data:`PIL.Image.Resampling.BICUBIC`. + (was :py:data:`PIL.Image.Resampling.NEAREST` prior to version 2.5.0). See: :ref:`concept-filters`. :param reducing_gap: Apply optimization by resizing the image in two steps. First, reducing the image by integer times @@ -2388,7 +2435,13 @@ def round_aspect(number, key): # FIXME: the different transform methods need further explanation # instead of bloating the method docs, add a separate chapter. def transform( - self, size, method, data=None, resample=NEAREST, fill=1, fillcolor=None + self, + size, + method, + data=None, + resample=Resampling.NEAREST, + fill=1, + fillcolor=None, ): """ Transforms this image. This method creates a new image with the @@ -2397,11 +2450,11 @@ def transform( :param size: The output size. :param method: The transformation method. This is one of - :py:data:`PIL.Image.EXTENT` (cut out a rectangular subregion), - :py:data:`PIL.Image.AFFINE` (affine transform), - :py:data:`PIL.Image.PERSPECTIVE` (perspective transform), - :py:data:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or - :py:data:`PIL.Image.MESH` (map a number of source quadrilaterals + :py:data:`PIL.Image.Transform.EXTENT` (cut out a rectangular subregion), + :py:data:`PIL.Image.Transform.AFFINE` (affine transform), + :py:data:`PIL.Image.Transform.PERSPECTIVE` (perspective transform), + :py:data:`PIL.Image.Transform.QUAD` (map a quadrilateral to a rectangle), or + :py:data:`PIL.Image.Transform.MESH` (map a number of source quadrilaterals in one operation). It may also be an :py:class:`~PIL.Image.ImageTransformHandler` @@ -2416,16 +2469,16 @@ def transform(self, size, data, resample, fill=1): class Example: def getdata(self): - method = Image.EXTENT + method = Image.Transform.EXTENT data = (0, 0, 100, 100) return method, data :param data: Extra data to the transformation method. :param resample: Optional resampling filter. It can be one of - :py:data:`PIL.Image.NEAREST` (use nearest neighbour), - :py:data:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + :py:data:`PIL.Image.Resampling.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.Resampling.BILINEAR` (linear interpolation in a 2x2 environment), or :py:data:`PIL.Image.BICUBIC` (cubic spline interpolation in a 4x4 environment). If omitted, or if the image - has mode "1" or "P", it is set to :py:data:`PIL.Image.NEAREST`. + has mode "1" or "P", it is set to :py:data:`PIL.Image.Resampling.NEAREST`. See: :ref:`concept-filters`. :param fill: If ``method`` is an :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of @@ -2435,7 +2488,7 @@ def getdata(self): :returns: An :py:class:`~PIL.Image.Image` object. """ - if self.mode in ("LA", "RGBA") and resample != NEAREST: + if self.mode in ("LA", "RGBA") and resample != Resampling.NEAREST: return ( self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) .transform(size, method, data, resample, fill, fillcolor) @@ -2456,10 +2509,12 @@ def getdata(self): if self.mode == "P" and self.palette: im.palette = self.palette.copy() im.info = self.info.copy() - if method == MESH: + if method == Transform.MESH: # list of quads for box, quad in data: - im.__transformer(box, self, QUAD, quad, resample, fillcolor is None) + im.__transformer( + box, self, Transform.QUAD, quad, resample, fillcolor is None + ) else: im.__transformer( (0, 0) + size, self, method, data, resample, fillcolor is None @@ -2467,25 +2522,27 @@ def getdata(self): return im - def __transformer(self, box, image, method, data, resample=NEAREST, fill=1): + def __transformer( + self, box, image, method, data, resample=Resampling.NEAREST, fill=1 + ): w = box[2] - box[0] h = box[3] - box[1] - if method == AFFINE: + if method == Transform.AFFINE: data = data[0:6] - elif method == EXTENT: + elif method == Transform.EXTENT: # convert extent to an affine transform x0, y0, x1, y1 = data xs = (x1 - x0) / w ys = (y1 - y0) / h - method = AFFINE + method = Transform.AFFINE data = (xs, 0, x0, 0, ys, y0) - elif method == PERSPECTIVE: + elif method == Transform.PERSPECTIVE: data = data[0:8] - elif method == QUAD: + elif method == Transform.QUAD: # quadrilateral warp. data specifies the four corners # given as NW, SW, SE, and NE. nw = data[0:2] @@ -2509,12 +2566,16 @@ def __transformer(self, box, image, method, data, resample=NEAREST, fill=1): else: raise ValueError("unknown transformation method") - if resample not in (NEAREST, BILINEAR, BICUBIC): - if resample in (BOX, HAMMING, LANCZOS): + if resample not in ( + Resampling.NEAREST, + Resampling.BILINEAR, + Resampling.BICUBIC, + ): + if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): message = { - BOX: "Image.BOX", - HAMMING: "Image.HAMMING", - LANCZOS: "Image.LANCZOS/Image.ANTIALIAS", + Resampling.BOX: "Image.Resampling.BOX", + Resampling.HAMMING: "Image.Resampling.HAMMING", + Resampling.LANCZOS: "Image.Resampling.LANCZOS", }[resample] + f" ({resample}) cannot be used." else: message = f"Unknown resampling filter ({resample})." @@ -2522,9 +2583,9 @@ def __transformer(self, box, image, method, data, resample=NEAREST, fill=1): filters = [ f"{filter[1]} ({filter[0]})" for filter in ( - (NEAREST, "Image.NEAREST"), - (BILINEAR, "Image.BILINEAR"), - (BICUBIC, "Image.BICUBIC"), + (Resampling.NEAREST, "Image.Resampling.NEAREST"), + (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), + (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), ) ] raise ValueError( @@ -2536,7 +2597,7 @@ def __transformer(self, box, image, method, data, resample=NEAREST, fill=1): self.load() if image.mode in ("1", "P"): - resample = NEAREST + resample = Resampling.NEAREST self.im.transform2(box, image.im, method, data, resample, fill) @@ -2544,10 +2605,13 @@ def transpose(self, method): """ Transpose image (flip or rotate in 90 degree steps) - :param method: One of :py:data:`PIL.Image.FLIP_LEFT_RIGHT`, - :py:data:`PIL.Image.FLIP_TOP_BOTTOM`, :py:data:`PIL.Image.ROTATE_90`, - :py:data:`PIL.Image.ROTATE_180`, :py:data:`PIL.Image.ROTATE_270`, - :py:data:`PIL.Image.TRANSPOSE` or :py:data:`PIL.Image.TRANSVERSE`. + :param method: One of :py:data:`PIL.Image.Transpose.FLIP_LEFT_RIGHT`, + :py:data:`PIL.Image.Transpose.FLIP_TOP_BOTTOM`, + :py:data:`PIL.Image.Transpose.ROTATE_90`, + :py:data:`PIL.Image.Transpose.ROTATE_180`, + :py:data:`PIL.Image.Transpose.ROTATE_270`, + :py:data:`PIL.Image.Transpose.TRANSPOSE` or + :py:data:`PIL.Image.Transpose.TRANSVERSE`. :returns: Returns a flipped or rotated copy of this image. """ diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 60e700f0905..1f104133d13 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -16,6 +16,7 @@ # below for the original description. import sys +from enum import IntEnum from PIL import Image @@ -100,14 +101,22 @@ # # intent/direction values -INTENT_PERCEPTUAL = 0 -INTENT_RELATIVE_COLORIMETRIC = 1 -INTENT_SATURATION = 2 -INTENT_ABSOLUTE_COLORIMETRIC = 3 -DIRECTION_INPUT = 0 -DIRECTION_OUTPUT = 1 -DIRECTION_PROOF = 2 +class Intent(IntEnum): + PERCEPTUAL = 0 + RELATIVE_COLORIMETRIC = 1 + SATURATION = 2 + ABSOLUTE_COLORIMETRIC = 3 + + +class Direction(IntEnum): + INPUT = 0 + OUTPUT = 1 + PROOF = 2 + + +globals().update({"INTENT_" + k: v for k, v in Intent.__members__.items()}) +globals().update({"DIRECTION_" + k: v for k, v in Direction.__members__.items()}) # # flags @@ -211,9 +220,9 @@ def __init__( output, input_mode, output_mode, - intent=INTENT_PERCEPTUAL, + intent=Intent.PERCEPTUAL, proof=None, - proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, + proof_intent=Intent.ABSOLUTE_COLORIMETRIC, flags=0, ): if proof is None: @@ -295,7 +304,7 @@ def profileToProfile( im, inputProfile, outputProfile, - renderingIntent=INTENT_PERCEPTUAL, + renderingIntent=Intent.PERCEPTUAL, outputMode=None, inPlace=False, flags=0, @@ -331,10 +340,10 @@ def profileToProfile( :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the transform - ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) - ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 - ImageCms.INTENT_SATURATION = 2 - ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + 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. @@ -412,7 +421,7 @@ def buildTransform( outputProfile, inMode, outMode, - renderingIntent=INTENT_PERCEPTUAL, + renderingIntent=Intent.PERCEPTUAL, flags=0, ): """ @@ -458,10 +467,10 @@ def buildTransform( :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the transform - ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) - ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 - ImageCms.INTENT_SATURATION = 2 - ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + 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. @@ -494,8 +503,8 @@ def buildProofTransform( proofProfile, inMode, outMode, - renderingIntent=INTENT_PERCEPTUAL, - proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, + renderingIntent=Intent.PERCEPTUAL, + proofRenderingIntent=Intent.ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"], ): """ @@ -550,20 +559,20 @@ def buildProofTransform( :param renderingIntent: Integer (0-3) specifying the rendering intent you wish to use for the input->proof (simulated) transform - ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) - ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 - ImageCms.INTENT_SATURATION = 2 - ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + 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 - ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) - ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 - ImageCms.INTENT_SATURATION = 2 - ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + 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. @@ -922,10 +931,10 @@ def getDefaultIntent(profile): :returns: Integer 0-3 specifying the default rendering intent for this profile. - ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) - ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 - ImageCms.INTENT_SATURATION = 2 - ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + 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. @@ -960,19 +969,19 @@ def isIntentSupported(profile, intent, direction): :param intent: Integer (0-3) specifying the rendering intent you wish to use with this profile - ImageCms.INTENT_PERCEPTUAL = 0 (DEFAULT) - ImageCms.INTENT_RELATIVE_COLORIMETRIC = 1 - ImageCms.INTENT_SATURATION = 2 - ImageCms.INTENT_ABSOLUTE_COLORIMETRIC = 3 + 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 - INPUT = 0 (or use ImageCms.DIRECTION_INPUT) - OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) - PROOF = 2 (or use ImageCms.DIRECTION_PROOF) + INPUT = 0 (or use ImageCms.Direction.INPUT) + OUTPUT = 1 (or use ImageCms.Direction.OUTPUT) + PROOF = 2 (or use ImageCms.Direction.PROOF) :returns: 1 if the intent/direction are supported, -1 if they are not. :exception PyCMSError: diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index d2ece37520e..1320af8f9b2 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -529,7 +529,7 @@ def filter(self, image): return image.color_lut_3d( self.mode or image.mode, - Image.LINEAR, + Image.Resampling.BILINEAR, self.channels, self.size[0], self.size[1], diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 805c8fff96b..9282d5279bf 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -28,13 +28,19 @@ import base64 import os import sys +from enum import IntEnum from io import BytesIO from . import Image from ._util import isDirectory, isPath -LAYOUT_BASIC = 0 -LAYOUT_RAQM = 1 + +class Layout(IntEnum): + BASIC = 0 + RAQM = 1 + + +globals().update({"LAYOUT_" + k: v for k, v in Layout.__members__.items()}) class _imagingft_not_installed: @@ -164,12 +170,12 @@ def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None) self.index = index self.encoding = encoding - if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM): - layout_engine = LAYOUT_BASIC + if layout_engine not in (Layout.BASIC, Layout.RAQM): + layout_engine = Layout.BASIC if core.HAVE_RAQM: - layout_engine = LAYOUT_RAQM - elif layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM: - layout_engine = LAYOUT_BASIC + layout_engine = Layout.RAQM + elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: + layout_engine = Layout.BASIC self.layout_engine = layout_engine @@ -751,15 +757,16 @@ def __init__(self, font, orientation=None): :param font: A font object. :param orientation: An optional orientation. If given, this should - be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM, - Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270. + be one of Image.Transpose.FLIP_LEFT_RIGHT, Image.Transpose.FLIP_TOP_BOTTOM, + Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_180, or + Image.Transpose.ROTATE_270. """ self.font = font self.orientation = orientation # any 'transpose' argument, or None def getsize(self, text, *args, **kwargs): w, h = self.font.getsize(text) - if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): + if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): return h, w return w, h @@ -827,7 +834,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): This specifies the character set to use. It does not alter the encoding of any text provided in subsequent operations. :param layout_engine: Which layout engine to use, if available: - :data:`.ImageFont.LAYOUT_BASIC` or :data:`.ImageFont.LAYOUT_RAQM`. + :data:`.ImageFont.Layout.BASIC` or :data:`.ImageFont.Layout.RAQM`. You can check support for Raqm layout using :py:func:`PIL.features.check_feature` with ``feature="raqm"``. diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index b170e9d8cc9..c6201d8ff08 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -237,7 +237,7 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi return _lut(image, red + green + blue) -def contain(image, size, method=Image.BICUBIC): +def contain(image, size, method=Image.Resampling.BICUBIC): """ Returns a resized version of the image, set to the maximum width and height within the requested size, while maintaining the original aspect ratio. @@ -265,7 +265,7 @@ def contain(image, size, method=Image.BICUBIC): return image.resize(size, resample=method) -def pad(image, size, method=Image.BICUBIC, color=None, centering=(0.5, 0.5)): +def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5, 0.5)): """ Returns a resized and padded version of the image, expanded to fill the requested aspect ratio and size. @@ -315,7 +315,7 @@ def crop(image, border=0): return image.crop((left, top, image.size[0] - right, image.size[1] - bottom)) -def scale(image, factor, resample=Image.BICUBIC): +def scale(image, factor, resample=Image.Resampling.BICUBIC): """ Returns a rescaled image by a specific factor given in parameter. A factor greater than 1 expands the image, between 0 and 1 contracts the @@ -336,7 +336,7 @@ def scale(image, factor, resample=Image.BICUBIC): return image.resize(size, resample) -def deform(image, deformer, resample=Image.BILINEAR): +def deform(image, deformer, resample=Image.Resampling.BILINEAR): """ Deform the image. @@ -347,7 +347,9 @@ def deform(image, deformer, resample=Image.BILINEAR): in the PIL.Image.transform function. :return: An image. """ - return image.transform(image.size, Image.MESH, deformer.getmesh(image), resample) + return image.transform( + image.size, Image.Transform.MESH, deformer.getmesh(image), resample + ) def equalize(image, mask=None): @@ -408,7 +410,7 @@ def expand(image, border=0, fill=0): return out -def fit(image, size, method=Image.BICUBIC, bleed=0.0, centering=(0.5, 0.5)): +def fit(image, size, method=Image.Resampling.BICUBIC, bleed=0.0, centering=(0.5, 0.5)): """ Returns a resized and cropped version of the image, cropped to the requested aspect ratio and size. @@ -500,7 +502,7 @@ def flip(image): :param image: The image to flip. :return: An image. """ - return image.transpose(Image.FLIP_TOP_BOTTOM) + return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) def grayscale(image): @@ -533,7 +535,7 @@ def mirror(image): :param image: The image to mirror. :return: An image. """ - return image.transpose(Image.FLIP_LEFT_RIGHT) + return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) def posterize(image, bits): @@ -579,13 +581,13 @@ def exif_transpose(image): exif = image.getexif() orientation = exif.get(0x0112) method = { - 2: Image.FLIP_LEFT_RIGHT, - 3: Image.ROTATE_180, - 4: Image.FLIP_TOP_BOTTOM, - 5: Image.TRANSPOSE, - 6: Image.ROTATE_270, - 7: Image.TRANSVERSE, - 8: Image.ROTATE_90, + 2: Image.Transpose.FLIP_LEFT_RIGHT, + 3: Image.Transpose.ROTATE_180, + 4: Image.Transpose.FLIP_TOP_BOTTOM, + 5: Image.Transpose.TRANSPOSE, + 6: Image.Transpose.ROTATE_270, + 7: Image.Transpose.TRANSVERSE, + 8: Image.Transpose.ROTATE_90, }.get(orientation) if method is not None: transposed_image = image.transpose(method) diff --git a/src/PIL/ImageTransform.py b/src/PIL/ImageTransform.py index 77791ab726f..7881f0d262b 100644 --- a/src/PIL/ImageTransform.py +++ b/src/PIL/ImageTransform.py @@ -47,7 +47,7 @@ class AffineTransform(Transform): from an affine transform matrix. """ - method = Image.AFFINE + method = Image.Transform.AFFINE class ExtentTransform(Transform): @@ -69,7 +69,7 @@ class ExtentTransform(Transform): input image's coordinate system. See :ref:`coordinate-system`. """ - method = Image.EXTENT + method = Image.Transform.EXTENT class QuadTransform(Transform): @@ -86,7 +86,7 @@ class QuadTransform(Transform): source quadrilateral. """ - method = Image.QUAD + method = Image.Transform.QUAD class MeshTransform(Transform): @@ -99,4 +99,4 @@ class MeshTransform(Transform): :param data: A list of (bbox, quad) tuples. """ - method = Image.MESH + method = Image.Transform.MESH diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0f596f1fdb3..94ca933b069 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -37,6 +37,7 @@ import struct import warnings import zlib +from enum import IntEnum from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence from ._binary import i16be as i16 @@ -94,36 +95,43 @@ # APNG frame disposal modes -APNG_DISPOSE_OP_NONE = 0 -""" -No disposal is done on this frame before rendering the next frame. -See :ref:`Saving APNG sequences`. -""" -APNG_DISPOSE_OP_BACKGROUND = 1 -""" -This frame’s modified region is cleared to fully transparent black before rendering -the next frame. -See :ref:`Saving APNG sequences`. -""" -APNG_DISPOSE_OP_PREVIOUS = 2 -""" -This frame’s modified region is reverted to the previous frame’s contents before -rendering the next frame. -See :ref:`Saving APNG sequences`. -""" +class Disposal(IntEnum): + OP_NONE = 0 + """ + No disposal is done on this frame before rendering the next frame. + See :ref:`Saving APNG sequences`. + """ + OP_BACKGROUND = 1 + """ + This frame’s modified region is cleared to fully transparent black before rendering + the next frame. + See :ref:`Saving APNG sequences`. + """ + OP_PREVIOUS = 2 + """ + This frame’s modified region is reverted to the previous frame’s contents before + rendering the next frame. + See :ref:`Saving APNG sequences`. + """ + # APNG frame blend modes -APNG_BLEND_OP_SOURCE = 0 -""" -All color components of this frame, including alpha, overwrite the previous output -image contents. -See :ref:`Saving APNG sequences`. -""" -APNG_BLEND_OP_OVER = 1 -""" -This frame should be alpha composited with the previous output image contents. -See :ref:`Saving APNG sequences`. -""" +class Blend(IntEnum): + OP_SOURCE = 0 + """ + All color components of this frame, including alpha, overwrite the previous output + image contents. + See :ref:`Saving APNG sequences`. + """ + OP_OVER = 1 + """ + This frame should be alpha composited with the previous output image contents. + See :ref:`Saving APNG sequences`. + """ + + +globals().update({"APNG_DISPOSE_" + k: v for k, v in Disposal.__members__.items()}) +globals().update(Blend.__members__) def _safe_zlib_decompress(s): @@ -861,13 +869,13 @@ def _seek(self, frame, rewind=False): raise EOFError # setup frame disposal (actual disposal done when needed in the next _seek()) - if self._prev_im is None and self.dispose_op == APNG_DISPOSE_OP_PREVIOUS: - self.dispose_op = APNG_DISPOSE_OP_BACKGROUND + if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: + self.dispose_op = Disposal.OP_BACKGROUND - if self.dispose_op == APNG_DISPOSE_OP_PREVIOUS: + if self.dispose_op == Disposal.OP_PREVIOUS: self.dispose = self._prev_im.copy() self.dispose = self._crop(self.dispose, self.dispose_extent) - elif self.dispose_op == APNG_DISPOSE_OP_BACKGROUND: + elif self.dispose_op == Disposal.OP_BACKGROUND: self.dispose = Image.core.fill(self.mode, self.size) self.dispose = self._crop(self.dispose, self.dispose_extent) else: @@ -956,7 +964,7 @@ def load_end(self): self.png.close() self.png = None else: - if self._prev_im and self.blend_op == APNG_BLEND_OP_OVER: + if self._prev_im and self.blend_op == Blend.OP_OVER: updated = self._crop(self.im, self.dispose_extent) self._prev_im.paste( updated, self.dispose_extent, updated.convert("RGBA") @@ -1061,10 +1069,8 @@ def _write_multiple_frames(im, fp, chunk, rawmode): default_image = im.encoderinfo.get("default_image", im.info.get("default_image")) duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) - disposal = im.encoderinfo.get( - "disposal", im.info.get("disposal", APNG_DISPOSE_OP_NONE) - ) - blend = im.encoderinfo.get("blend", im.info.get("blend", APNG_BLEND_OP_SOURCE)) + disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) + blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) if default_image: chain = itertools.chain(im.encoderinfo.get("append_images", [])) @@ -1094,10 +1100,10 @@ def _write_multiple_frames(im, fp, chunk, rawmode): previous = im_frames[-1] prev_disposal = previous["encoderinfo"].get("disposal") prev_blend = previous["encoderinfo"].get("blend") - if prev_disposal == APNG_DISPOSE_OP_PREVIOUS and len(im_frames) < 2: - prev_disposal = APNG_DISPOSE_OP_BACKGROUND + if prev_disposal == Disposal.OP_PREVIOUS and len(im_frames) < 2: + prev_disposal = Disposal.OP_BACKGROUND - if prev_disposal == APNG_DISPOSE_OP_BACKGROUND: + if prev_disposal == Disposal.OP_BACKGROUND: base_im = previous["im"] dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) bbox = previous["bbox"] @@ -1106,7 +1112,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode): else: bbox = (0, 0) + im.size base_im.paste(dispose, bbox) - elif prev_disposal == APNG_DISPOSE_OP_PREVIOUS: + elif prev_disposal == Disposal.OP_PREVIOUS: base_im = im_frames[-2]["im"] else: base_im = previous["im"] diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 062af9f983e..f21123c9b75 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -316,7 +316,7 @@ def _save_spider(im, fp, filename): outfile = sys.argv[2] # perform some image operation - im = im.transpose(Image.FLIP_LEFT_RIGHT) + im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) print( f"saving a flipped version of {os.path.basename(filename)} " f"as {outfile} " diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index ed63da95f22..59b89e9885f 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -152,7 +152,7 @@ def _open(self): def load_end(self): if self._flip_horizontally: - self.im = self.im.transpose(Image.FLIP_LEFT_RIGHT) + self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) # diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e54082feceb..0980fb40708 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1136,13 +1136,13 @@ def load(self): def load_end(self): if self._tile_orientation: method = { - 2: Image.FLIP_LEFT_RIGHT, - 3: Image.ROTATE_180, - 4: Image.FLIP_TOP_BOTTOM, - 5: Image.TRANSPOSE, - 6: Image.ROTATE_270, - 7: Image.TRANSVERSE, - 8: Image.ROTATE_90, + 2: Image.Transpose.FLIP_LEFT_RIGHT, + 3: Image.Transpose.ROTATE_180, + 4: Image.Transpose.FLIP_TOP_BOTTOM, + 5: Image.Transpose.TRANSPOSE, + 6: Image.Transpose.ROTATE_270, + 7: Image.Transpose.TRANSVERSE, + 8: Image.Transpose.ROTATE_90, }.get(self._tile_orientation) if method is not None: self.im = self.im.transpose(method) From ed8073e846dd585227ea5462381a62414f80629b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jan 2022 10:07:07 +1100 Subject: [PATCH 0064/1186] Deprecated constants in favour of enums --- Tests/test_file_apng.py | 10 ++++++++ Tests/test_file_blp.py | 13 +++++++++- Tests/test_file_ftex.py | 13 +++++++++- Tests/test_image.py | 25 +++++++++++++++++++ Tests/test_imagecms.py | 10 ++++++++ Tests/test_imagefont.py | 9 +++++++ src/PIL/BlpImagePlugin.py | 31 +++++++++++++++++++---- src/PIL/FtexImagePlugin.py | 23 +++++++++++++++++- src/PIL/Image.py | 50 +++++++++++++++++++++++++++++++++----- src/PIL/ImageCms.py | 25 +++++++++++++++++-- src/PIL/ImageFont.py | 23 +++++++++++++++++- src/PIL/PngImagePlugin.py | 23 ++++++++++++++++-- 12 files changed, 236 insertions(+), 19 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index f5c76708201..d1d5c85c1ae 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -635,3 +635,13 @@ def test_apng_save_blend(tmp_path): with Image.open(test_file) as im: im.seek(2) assert im.getpixel((0, 0)) == (0, 255, 0, 255) + + +def test_constants_deprecation(): + for enum, prefix in { + PngImagePlugin.Disposal: "APNG_DISPOSE_", + PngImagePlugin.Blend: "APNG_BLEND_", + }.items(): + for name in enum.__members__: + with pytest.warns(DeprecationWarning): + assert getattr(PngImagePlugin, prefix + name) == enum[name] diff --git a/Tests/test_file_blp.py b/Tests/test_file_blp.py index 15bd7e4f8ef..917f39530af 100644 --- a/Tests/test_file_blp.py +++ b/Tests/test_file_blp.py @@ -1,6 +1,6 @@ import pytest -from PIL import Image +from PIL import BlpImagePlugin, Image from .helper import assert_image_equal_tofile @@ -37,3 +37,14 @@ def test_crashes(test_file): with Image.open(f) as im: with pytest.raises(OSError): im.load() + + +def test_constants_deprecation(): + for enum, prefix in { + BlpImagePlugin.Format: "BLP_FORMAT_", + BlpImagePlugin.Encoding: "BLP_ENCODING_", + BlpImagePlugin.AlphaEncoding: "BLP_ALPHA_ENCODING_", + }.items(): + for name in enum.__members__: + with pytest.warns(DeprecationWarning): + assert getattr(BlpImagePlugin, prefix + name) == enum[name] diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py index f76fd895a58..5447dc7404f 100644 --- a/Tests/test_file_ftex.py +++ b/Tests/test_file_ftex.py @@ -1,4 +1,6 @@ -from PIL import Image +import pytest + +from PIL import FtexImagePlugin, Image from .helper import assert_image_equal_tofile, assert_image_similar @@ -12,3 +14,12 @@ def test_load_dxt1(): with Image.open("Tests/images/ftex_dxt1.ftc") as im: with Image.open("Tests/images/ftex_dxt1.png") as target: assert_image_similar(im, target.convert("RGBA"), 15) + + +def test_constants_deprecation(): + for enum, prefix in { + FtexImagePlugin.Format: "FORMAT_", + }.items(): + for name in enum.__members__: + with pytest.warns(DeprecationWarning): + assert getattr(FtexImagePlugin, prefix + name) == enum[name] diff --git a/Tests/test_image.py b/Tests/test_image.py index 2d46d760db4..1c30ca9fa09 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -802,6 +802,31 @@ def test_categories_deprecation(self): with pytest.warns(DeprecationWarning): assert Image.CONTAINER == 2 + def test_constants_deprecation(self): + with pytest.warns(DeprecationWarning): + assert Image.NEAREST == 0 + with pytest.warns(DeprecationWarning): + assert Image.NONE == 0 + + with pytest.warns(DeprecationWarning): + assert Image.LINEAR == Image.Resampling.BILINEAR + with pytest.warns(DeprecationWarning): + assert Image.CUBIC == Image.Resampling.BICUBIC + with pytest.warns(DeprecationWarning): + assert Image.ANTIALIAS == Image.Resampling.LANCZOS + + for enum in ( + Image.Transpose, + Image.Transform, + Image.Resampling, + Image.Dither, + Image.Palette, + Image.Quantize, + ): + for name in enum.__members__: + with pytest.warns(DeprecationWarning): + assert getattr(Image, name) == enum[name] + @pytest.mark.parametrize( "path", [ diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 09af1138989..e0093739cc5 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -593,3 +593,13 @@ def test_auxiliary_channels_isolated(): ) assert_image_equal(test_image.convert(dst_format[2]), reference_image) + + +def test_constants_deprecation(): + for enum, prefix in { + ImageCms.Intent: "INTENT_", + ImageCms.Direction: "DIRECTION_", + }.items(): + for name in enum.__members__: + with pytest.warns(DeprecationWarning): + assert getattr(ImageCms, prefix + name) == enum[name] diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 8e2c61848dd..23bfda81624 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1022,3 +1022,12 @@ def test_oom(test_file): font = ImageFont.truetype(BytesIO(f.read())) with pytest.raises(Image.DecompressionBombError): font.getmask("Test Text") + + +def test_constants_deprecation(): + for enum, prefix in { + ImageFont.Layout: "LAYOUT_", + }.items(): + for name in enum.__members__: + with pytest.warns(DeprecationWarning): + assert getattr(ImageFont, prefix + name) == enum[name] diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 111f03ae63f..dc68b95b52b 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -30,6 +30,7 @@ """ import struct +import warnings from enum import IntEnum from io import BytesIO @@ -52,11 +53,31 @@ class AlphaEncoding(IntEnum): DXT5 = 7 -globals().update({"BLP_FORMAT_" + k: v for k, v in Format.__members__.items()}) -globals().update({"BLP_ENCODING_" + k: v for k, v in Encoding.__members__.items()}) -globals().update( - {"BLP_ALPHA_ENCODING_" + k: v for k, v in AlphaEncoding.__members__.items()} -) +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in { + Format: "BLP_FORMAT_", + Encoding: "BLP_ENCODING_", + AlphaEncoding: "BLP_ALPHA_ENCODING_", + }.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") def unpack_565(i): diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index de5859ba53d..8629dcf64ad 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -52,6 +52,7 @@ """ import struct +import warnings from enum import IntEnum from io import BytesIO @@ -65,7 +66,27 @@ class Format(IntEnum): UNCOMPRESSED = 1 -globals().update({"FORMAT_" + k: v for k, v in Format.__members__.items()}) +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Format: "FORMAT_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") class FtexImageFile(ImageFile.ImageFile): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 30ee34c6bb8..c10e6d9cdee 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -54,15 +54,57 @@ def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2} if name in categories: warnings.warn( - "Image categories are deprecated and will be removed in Pillow 10 " - "(2023-07-01). Use is_animated instead.", + "Image categories are " + deprecated + "Use is_animated instead.", DeprecationWarning, stacklevel=2, ) return categories[name] + elif name in ("NEAREST", "NONE"): + warnings.warn( + name + + " is " + + deprecated + + "Use Resampling.NEAREST or Dither.NONE instead.", + DeprecationWarning, + stacklevel=2, + ) + return 0 + old_resampling = { + "LINEAR": "BILINEAR", + "CUBIC": "BICUBIC", + "ANTIALIAS": "LANCZOS", + } + if name in old_resampling: + warnings.warn( + name + + " is " + + deprecated + + "Use Resampling." + + old_resampling[name] + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return Resampling[old_resampling[name]] + for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): + if name in enum.__members__: + warnings.warn( + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") @@ -199,10 +241,6 @@ class Quantize(IntEnum): LIBIMAGEQUANT = 3 -for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): - globals().update(enum.__members__) -NEAREST = NONE = 0 - if hasattr(core, "DEFAULT_STRATEGY"): DEFAULT_STRATEGY = core.DEFAULT_STRATEGY FILTERED = core.FILTERED diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 1f104133d13..ea328e149fb 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -16,6 +16,7 @@ # below for the original description. import sys +import warnings from enum import IntEnum from PIL import Image @@ -115,8 +116,28 @@ class Direction(IntEnum): PROOF = 2 -globals().update({"INTENT_" + k: v for k, v in Intent.__members__.items()}) -globals().update({"DIRECTION_" + k: v for k, v in Direction.__members__.items()}) +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") + # # flags diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 9282d5279bf..84c9b430d86 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -28,6 +28,7 @@ import base64 import os import sys +import warnings from enum import IntEnum from io import BytesIO @@ -40,7 +41,27 @@ class Layout(IntEnum): RAQM = 1 -globals().update({"LAYOUT_" + k: v for k, v in Layout.__members__.items()}) +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Layout: "LAYOUT_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") class _imagingft_not_installed: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 94ca933b069..9d86afe3475 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -130,8 +130,27 @@ class Blend(IntEnum): """ -globals().update({"APNG_DISPOSE_" + k: v for k, v in Disposal.__members__.items()}) -globals().update(Blend.__members__) +def __getattr__(name): + deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " + for enum, prefix in {Disposal: "APNG_DISPOSE_", Blend: "APNG_BLEND_"}.items(): + if name.startswith(prefix): + name = name[len(prefix) :] + if name in enum.__members__: + warnings.warn( + prefix + + name + + " is " + + deprecated + + "Use " + + enum.__name__ + + "." + + name + + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return enum[name] + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") def _safe_zlib_decompress(s): From 86944abbabad62e53e644bd7375b9a56d66c1675 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 15 Jan 2022 16:08:37 +1100 Subject: [PATCH 0065/1186] Deprecated show_file "file" argument in favour of "path" --- Tests/test_imageshow.py | 15 +++++++++++ src/PIL/ImageShow.py | 59 +++++++++++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 5981e22c012..02edfdfa1f7 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -79,3 +79,18 @@ def test_ipythonviewer(): im = hopper() assert test_viewer.show(im) == 1 + + +@pytest.mark.skipif( + not on_ci() or is_win32(), + reason="Only run on CIs; hangs on Windows CIs", +) +def test_file_deprecated(): + for viewer in ImageShow._viewers: + with pytest.warns(DeprecationWarning): + try: + viewer.show_file(file="test.jpg") + except NotImplementedError: + pass + with pytest.raises(TypeError): + viewer.show_file() diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 2135293e5eb..068b7f7840b 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -16,6 +16,7 @@ import subprocess import sys import tempfile +import warnings from shlex import quote from PIL import Image @@ -105,9 +106,19 @@ def show_image(self, image, **options): """Display the given image.""" return self.show_file(self.save_image(image), **options) - def show_file(self, file, **options): - """Display the given file.""" - os.system(self.get_command(file, **options)) + def show_file(self, path=None, **options): + """Display given file.""" + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + os.system(self.get_command(path, **options)) return 1 @@ -145,18 +156,28 @@ def get_command(self, file, **options): command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" return command - def show_file(self, file, **options): + def show_file(self, path=None, **options): """Display given file""" - fd, path = tempfile.mkstemp() + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + fd, temp_path = tempfile.mkstemp() with os.fdopen(fd, "w") as f: - f.write(file) - with open(path) as f: + f.write(path) + with open(temp_path) as f: subprocess.Popen( ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"], shell=True, stdin=f, ) - os.remove(path) + os.remove(temp_path) return 1 @@ -172,17 +193,27 @@ def get_command(self, file, **options): command = self.get_command_ex(file, **options)[0] return f"({command} {quote(file)}; rm -f {quote(file)})&" - def show_file(self, file, **options): + def show_file(self, path=None, **options): """Display given file""" - fd, path = tempfile.mkstemp() + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + fd, temp_path = tempfile.mkstemp() with os.fdopen(fd, "w") as f: - f.write(file) - with open(path) as f: - command = self.get_command_ex(file, **options)[0] + f.write(path) + with open(temp_path) as f: + command = self.get_command_ex(path, **options)[0] subprocess.Popen( ["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f ) - os.remove(path) + os.remove(temp_path) return 1 From 26496a6c981bac4ab035369ee591616f31dcc67a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 16 Jan 2022 10:52:22 +1100 Subject: [PATCH 0066/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 49ab72962de..3e8c72dd8fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ +- Fixed Spider images for use with Bio-formats library #5956 + [radarhere] + - Ensure duplicated file pointer is closed #5946 [radarhere] From 5df83a57ff0f8362fc1d66180f777d5d79672342 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 16 Jan 2022 11:38:34 +1100 Subject: [PATCH 0067/1186] Documented deprecation --- docs/deprecations.rst | 12 ++++++++++++ src/PIL/ImageShow.py | 21 ++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index ce30fdf3b92..dbe65dda106 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -53,6 +53,18 @@ Before Pillow 8.3.0, ``ImagePalette`` required palette data of particular length default, and the size parameter could be used to override that. Pillow 8.3.0 removed the default required length, also removing the need for the size parameter. +ImageShow.Viewer.show_file file argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.1.0 + +The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been +deprecated, replaced by ``path``. + +In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. +``viewer.show_file(file="test.jpg")`` will raise a deprecation warning, and suggest +``viewer.show_file(path="test.jpg")`` instead. + Removed features ---------------- diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 068b7f7840b..e437bbadeab 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -107,7 +107,12 @@ def show_image(self, image, **options): return self.show_file(self.save_image(image), **options) def show_file(self, path=None, **options): - """Display given file.""" + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ if path is None: if "file" in options: warnings.warn( @@ -157,7 +162,12 @@ def get_command(self, file, **options): return command def show_file(self, path=None, **options): - """Display given file""" + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ if path is None: if "file" in options: warnings.warn( @@ -194,7 +204,12 @@ def get_command(self, file, **options): return f"({command} {quote(file)}; rm -f {quote(file)})&" def show_file(self, path=None, **options): - """Display given file""" + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ if path is None: if "file" in options: warnings.warn( From 1ac96dc923a5f2360348b4e00b070856fff8a78e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 16 Jan 2022 14:09:12 +1100 Subject: [PATCH 0068/1186] Invoke pip using Python --- .ci/after_success.sh | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/after_success.sh b/.ci/after_success.sh index ff91b481eca..53832c573dd 100755 --- a/.ci/after_success.sh +++ b/.ci/after_success.sh @@ -1,7 +1,7 @@ #!/bin/bash # gather the coverage data -pip3 install codecov +python3 -m pip install codecov if [[ $MATRIX_DOCKER ]]; then coverage xml --ignore-errors else diff --git a/Makefile b/Makefile index 44ab2ef1c50..74a6a5ab209 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,7 @@ test: .PHONY: valgrind valgrind: - python3 -c "import pytest_valgrind" || pip3 install pytest-valgrind + python3 -c "import pytest_valgrind" || python3 -m pip install pytest-valgrind PYTHONMALLOC=malloc valgrind --suppressions=Tests/oss-fuzz/python.supp --leak-check=no \ --log-file=/tmp/valgrind-output \ python3 -m pytest --no-memcheck -vv --valgrind --valgrind-log=/tmp/valgrind-output From 730e24e163ea6b891f24efe4dcb1869d85c2f63c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 17 Jan 2022 07:35:11 +1100 Subject: [PATCH 0069/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3e8c72dd8fb..b3379c1ee0d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,10 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ -- Fixed Spider images for use with Bio-formats library #5956 +- Deprecated show_file "file" argument in favour of "path" #5959 + [radarhere] + +- Fixed SPIDER images for use with Bio-formats library #5956 [radarhere] - Ensure duplicated file pointer is closed #5946 From b53cb3f675a2987986ff4f794df123a44fa6181c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 17 Jan 2022 09:47:27 +1100 Subject: [PATCH 0070/1186] Updated macOS tested Pillow versions [ci skip] --- docs/installation.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index df21a7cdc68..339ff966da8 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -493,9 +493,13 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 11.0 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | +| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.0.0 |arm | ++----------------------------------+---------------------------+------------------+--------------+ +| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | +| +---------------------------+------------------+--------------+ +| | 3.7, 3.8, 3.9, 3.10 | 9.0.0 |x86-64 | | +---------------------------+------------------+--------------+ -| | 3.6, 3.7, 3.8, 3.9, 3.10 | 8.4.0 |x86-64 | +| | 3.6 | 8.4.0 |x86-64 | +----------------------------------+---------------------------+------------------+--------------+ | macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9 | 8.3.2 |x86-64 | | +---------------------------+------------------+ | From c8d650f38303d17af0c4aefbb9ee9681e26fabea Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 17 Jan 2022 14:07:30 +1100 Subject: [PATCH 0071/1186] Added Debian 11 Bullseye --- .github/workflows/test-docker.yml | 1 + docs/installation.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 57396fddcce..df04d0a6cae 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -22,6 +22,7 @@ jobs: centos-8-amd64, centos-stream-8-amd64, debian-10-buster-x86, + debian-11-bullseye-x86, fedora-34-amd64, fedora-35-amd64, ubuntu-18.04-bionic-amd64, diff --git a/docs/installation.rst b/docs/installation.rst index df21a7cdc68..3b249b36873 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -458,6 +458,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ +| Debian 11 Bullseye | 3.9 | x86 | ++----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | Fedora 35 | 3.10 | x86-64 | From 1be53c8bb3b729ddbd445a150efbf17b852a9703 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jan 2022 09:08:50 +1100 Subject: [PATCH 0072/1186] Removed debugging --- src/PIL/BlpImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index f968ed85195..3026ec49fcc 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -361,7 +361,6 @@ def _decode_jpeg_stream(self): image.tile = [("jpeg", (0, 0) + self.size, 0, ("RGBA", ""))] b, g, r, a = image.split() - print(b, g, r, a) if not any( [a.getpixel((x, y)) for x in range(a.width) for y in range(a.height)] ): From acd33bf62acd2abb31fe5bc85c51f57406afc1ca Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jan 2022 09:10:36 +1100 Subject: [PATCH 0073/1186] Removed unused images --- Tests/images/blp/blp1_jpeg.jpg | Bin 1651 -> 0 bytes Tests/images/blp/blp1_jpeg.png | Bin 270 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Tests/images/blp/blp1_jpeg.jpg delete mode 100644 Tests/images/blp/blp1_jpeg.png diff --git a/Tests/images/blp/blp1_jpeg.jpg b/Tests/images/blp/blp1_jpeg.jpg deleted file mode 100644 index c045c5945c1071f3d4bd4e3f0d878cec6899bc17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1651 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<uA z0}~@NGZPClD=P~NP<1U(o`FS>RY=j$kxe)-kzJ`!#HexNLJno8jR!@8E`CrkPAY2R z|V^&07y2J$~}^+4C1KUw!=a`ODXD-+%o41@afjpD+ON7@EHXf&OA*VPR%r2lxYE#}NLy#lXYN2#h>tK?Zwfl gP32wyWDa4_AkC=Dz<6e<1v|(Sp00i_>zopr0LjD?Hvj+t From ddb0a6393f3fcb4aeb78f0567a58d9058ba9ca72 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jan 2022 11:46:29 +1100 Subject: [PATCH 0074/1186] Added test --- Tests/test_image.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index cf60f42af6e..db3e3063ee1 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -88,6 +88,17 @@ def test_sanity(self): # with pytest.raises(MemoryError): # Image.new("L", (1000000, 1000000)) + def test_repr_pretty(self): + class Pretty: + def text(self, text): + self.pretty_output = text + + im = Image.new("L", (100, 100)) + + p = Pretty() + im._repr_pretty_(p, None) + assert p.pretty_output == "" + def test_open_formats(self): PNGFILE = "Tests/images/hopper.png" JPGFILE = "Tests/images/hopper.jpg" From c692fb42f80fe7d37fc7b9b91a884b12842e990b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jan 2022 14:19:43 +1100 Subject: [PATCH 0075/1186] Fixed comparison warnings --- src/libImaging/TiffDecode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index 38deb53607e..f818f19d502 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -551,7 +551,7 @@ ImagingLibTiffDecode( uint16_t planarconfig = 0; int planes = 1; ImagingShuffler unpackers[4]; - UINT32 img_width, img_height; + INT32 img_width, img_height; memset(unpackers, 0, sizeof(ImagingShuffler) * 4); From a77babd6a459b9e1650bddc59ce6e3edf226f198 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jan 2022 14:26:52 +1100 Subject: [PATCH 0076/1186] Updated links --- README.md | 4 ++-- docs/about.rst | 2 +- docs/index.rst | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 782b81f3370..63671f4057d 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,10 @@ As of 2019, Pillow development is GitHub Actions wheels build status (Wheels) - Travis CI wheels build status (aarch64) - Code coverage Date: Tue, 18 Jan 2022 16:38:00 +1100 Subject: [PATCH 0077/1186] Raise an error when performing a negative crop --- Tests/test_decompression_bomb.py | 17 ++++------------- Tests/test_image_crop.py | 14 +++++--------- src/PIL/Image.py | 5 +++++ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index d918ef9410c..1778491ab64 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -86,21 +86,12 @@ def testEnlargeCrop(self): pytest.warns(Image.DecompressionBombWarning, src.crop, box) def test_crop_decompression_checks(self): - im = Image.new("RGB", (100, 100)) - good_values = ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990)) - - warning_values = ((-160, -160, 99, 99), (160, 160, -99, -99)) - - error_values = ((-99909, -99990, 99999, 99999), (99909, 99990, -99999, -99999)) - - for value in good_values: + for value in ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990)): assert im.crop(value).size == (9, 9) - for value in warning_values: - pytest.warns(Image.DecompressionBombWarning, im.crop, value) + pytest.warns(Image.DecompressionBombWarning, im.crop, (-160, -160, 99, 99)) - for value in error_values: - with pytest.raises(Image.DecompressionBombError): - im.crop(value) + with pytest.raises(Image.DecompressionBombError): + im.crop((-99909, -99990, 99999, 99999)) diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index e2228758c09..6574e6efd1a 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -47,16 +47,12 @@ def crop(*bbox): assert crop(-25, 75, 25, 125) == (1875, 625) -def test_negative_crop(): - # Check negative crop size (@PIL171) - - im = Image.new("L", (512, 512)) - im = im.crop((400, 400, 200, 200)) +@pytest.mark.parametrize("box", ((8, 2, 2, 8), (2, 8, 8, 2), (8, 8, 2, 2))) +def test_negative_crop(box): + im = Image.new("RGB", (10, 10)) - assert im.size == (0, 0) - assert len(im.getdata()) == 0 - with pytest.raises(IndexError): - im.getdata()[0] + with pytest.raises(ValueError): + im.crop(box) def test_crop_float(): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 69089a29063..439ffc65e8b 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1145,6 +1145,11 @@ def crop(self, box=None): if box is None: return self.copy() + if box[2] < box[0]: + raise ValueError("Region right less than region left") + elif box[3] < box[1]: + raise ValueError("Region lower less than region upper") + self.load() return self._new(self._crop(self.im, box)) From 67944cedc7293a2e1f568f47a1cce25ee7eaf148 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 18 Jan 2022 19:40:57 +1100 Subject: [PATCH 0078/1186] Always save with contiguous planar configuration --- Tests/test_file_tiff.py | 11 +++++++++++ src/PIL/TiffImagePlugin.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 5801e176636..68409ad1e8b 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -594,6 +594,17 @@ def test_tiled_planar_raw(self): with Image.open(infile) as im: assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") + def test_planar_configuration_save(self, tmp_path): + infile = "Tests/images/tiff_tiled_planar_raw.tif" + with Image.open(infile) as im: + assert im._planar_configuration == 2 + + outfile = str(tmp_path / "temp.tif") + im.save(outfile) + + with Image.open(outfile) as reloaded: + assert_image_equal_tofile(reloaded, infile) + def test_palette(self, tmp_path): def roundtrip(mode): outfile = str(tmp_path / "temp.tif") diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e54082feceb..39617c77d13 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1528,7 +1528,7 @@ def _save(im, fp, filename): libtiff = WRITE_LIBTIFF or compression != "raw" # required for color libtiff images - ifd[PLANAR_CONFIGURATION] = getattr(im, "_planar_configuration", 1) + ifd[PLANAR_CONFIGURATION] = 1 ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] From ef99a73473aed8860d85b2979f2928e0f1e8c4ed Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 20 Jan 2022 10:04:35 +1100 Subject: [PATCH 0079/1186] Clarified that version numbers refer to Tk, not Pillow --- src/Tk/tkImaging.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c index 9ae7edff108..5b3f18aced1 100644 --- a/src/Tk/tkImaging.c +++ b/src/Tk/tkImaging.c @@ -29,7 +29,7 @@ * 1995-09-12 fl Created * 1996-04-08 fl Ready for release * 1997-05-09 fl Use command instead of image type - * 2001-03-18 fl Initialize alpha layer pointer (struct changed in 8.3) + * 2001-03-18 fl Initialize alpha layer pointer (struct changed in Tk 8.3) * 2003-04-23 fl Fixed building for Tk 8.4.1 and later (Jack Jansen) * 2004-06-24 fl Fixed building for Tk 8.4.6 and later. * @@ -116,7 +116,7 @@ PyImagingPhotoPut( block.offset[1] = 1; block.offset[2] = 2; if (strcmp(im->mode, "RGBA") == 0) { - block.offset[3] = 3; /* alpha (or reserved, under 8.2) */ + block.offset[3] = 3; /* alpha (or reserved, under Tk 8.2) */ } else { block.offset[3] = 0; /* no alpha */ } From 591231bbb408985d2b8ca58809fd237d704ffd1d Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 21 Jan 2022 08:19:27 +1100 Subject: [PATCH 0080/1186] Changed error wording Co-authored-by: Hugo van Kemenade --- src/PIL/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 439ffc65e8b..302bd638efe 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1146,9 +1146,9 @@ def crop(self, box=None): return self.copy() if box[2] < box[0]: - raise ValueError("Region right less than region left") + raise ValueError("Coordinate 'right' is less than 'left'") elif box[3] < box[1]: - raise ValueError("Region lower less than region upper") + raise ValueError("Coordinate 'lower' is less than 'upper'") self.load() return self._new(self._crop(self.im, box)) From b894c8c73df04cb9ee2dc7b2e858278c59ece1be Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 21 Jan 2022 11:55:59 +1100 Subject: [PATCH 0081/1186] Connected discontiguous polygon corners --- .../discontiguous_corners_polygon.png | Bin 0 -> 486 bytes Tests/test_imagedraw.py | 12 +++++ src/libImaging/Draw.c | 47 +++++++++++++++--- 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 Tests/images/imagedraw/discontiguous_corners_polygon.png diff --git a/Tests/images/imagedraw/discontiguous_corners_polygon.png b/Tests/images/imagedraw/discontiguous_corners_polygon.png new file mode 100644 index 0000000000000000000000000000000000000000..509c42b26e0cbc5e01853915b083d367c1587579 GIT binary patch literal 486 zcmV@P)`!#sE^rA`<@Y%+RkqV?!_A|y&2pY*8)6=f4VlpVwn5}t^A6YuX03S#Y#g)7 zJQ^F#EHsbC_F-n5M`L?3Gt8s0{h7DSqp_oy7v|B}vCOaL^=6`p{x-yW@%sX@;oL`>6ss&}tQP{jy7s{L0??ngmnl7|5FWZZ5<^^5oX`ZzgZO!j@p}F~;z36X#*@aWg zPxj&<^KY|ooO$-7geQocksJTt*qn7e|89Ry{mlcOoKl@{@N4rK(#!8NvH9v#u!Q36 zd^nGC&Ywovw|zL9#`fS+c-x0_%4~m^#QgC7qU2=tJzr(chtir4_K?>8 z*}eohL7(jt&iOMT_E%jt+e0>wUv+tG52<#O=|9Yqe0QdtH`8R!&C>dOKMmcHy=s1@ cdrJ2_KmNq&!9TB{#sB~S07*qoM6N<$f>{CjhyVZp literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index b661494c733..3df4e7ca1db 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1440,3 +1440,15 @@ def test_continuous_horizontal_edges_polygon(): assert_image_equal_tofile( img, expected, "continuous horizontal edges polygon failed" ) + + +def test_discontiguous_corners_polygon(): + img, draw = create_base_image_draw((84, 68)) + draw.polygon(((1, 21), (34, 4), (71, 1), (38, 18)), BLACK) + draw.polygon(((71, 44), (38, 27), (1, 24)), BLACK) + draw.polygon( + ((38, 66), (5, 49), (77, 49), (47, 66), (82, 63), (82, 47), (1, 47), (1, 63)), + BLACK, + ) + expected = os.path.join(IMAGES_PATH, "discontiguous_corners_polygon.png") + assert_image_similar_tofile(img, expected, 1) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 0e4899b5b49..86cd6c3a058 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -450,7 +450,8 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h int edge_count = 0; int ymin = im->ysize - 1; int ymax = 0; - int i; + int i, j, k; + float adjacent_line_x, adjacent_line_x_other_edge; if (n <= 0) { return 0; @@ -493,16 +494,48 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h return -1; } for (; ymin <= ymax; ymin++) { - int j = 0; + j = 0; for (i = 0; i < edge_count; i++) { Edge *current = edge_table[i]; if (ymin >= current->ymin && ymin <= current->ymax) { xx[j++] = (ymin - current->y0) * current->dx + current->x0; - } - /* Needed to draw consistent polygons */ - if (ymin == current->ymax && ymin < ymax) { - xx[j] = xx[j - 1]; - j++; + + if (ymin == current->ymax && ymin < ymax) { + // Needed to draw consistent polygons + xx[j] = xx[j - 1]; + j++; + } else if (current->dx != 0 && roundf(xx[j-1]) == xx[j-1]) { + // Connect discontiguous corners + for (k = 0; k < i; k++) { + Edge *other_edge = edge_table[k]; + if ((current->dx > 0 && other_edge->dx <= 0) || + (current->dx < 0 && other_edge->dx >= 0)) { + continue; + } + // Check if the two edges join to make a corner + if (xx[j-1] == (ymin - other_edge->y0) * other_edge->dx + other_edge->x0) { + // Determine points from the edges on the next row + // Or if this is the last row, check the previous row + int offset = ymin == ymax ? -1 : 1; + adjacent_line_x = (ymin + offset - current->y0) * current->dx + current->x0; + adjacent_line_x_other_edge = (ymin + offset - other_edge->y0) * other_edge->dx + other_edge->x0; + if (ymin == current->ymax) { + if (current->dx > 0) { + xx[k] = fmax(adjacent_line_x, adjacent_line_x_other_edge) + 1; + } else { + xx[k] = fmin(adjacent_line_x, adjacent_line_x_other_edge) - 1; + } + } else { + if (current->dx > 0) { + xx[k] = fmin(adjacent_line_x, adjacent_line_x_other_edge); + } else { + xx[k] = fmax(adjacent_line_x, adjacent_line_x_other_edge) + 1; + } + } + break; + } + } + } } } qsort(xx, j, sizeof(float), x_cmp); From 4eb80cb6bfb2f83dc039fb5bbce56a4416bac30f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jan 2022 09:14:01 +1100 Subject: [PATCH 0082/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b3379c1ee0d..d6cd9d50f21 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ +- Raise an error when performing a negative crop #5972 + [radarhere, hugovk] + - Deprecated show_file "file" argument in favour of "path" #5959 [radarhere] From 54f85ddcade1c93edb84b47796cc178f37c7e8fd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 22 Jan 2022 09:30:12 +1100 Subject: [PATCH 0083/1186] Updated libwebp to 1.2.2 --- depends/install_webp.sh | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/depends/install_webp.sh b/depends/install_webp.sh index 8a9c968045c..a419a764609 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,7 +1,7 @@ #!/bin/bash # install webp -archive=libwebp-1.2.1 +archive=libwebp-1.2.2 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0589baf2136..3092c5a2ba8 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -154,9 +154,9 @@ def cmd_msbuild( # "bins": [r"libtiff\*.dll"], }, "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.1.tar.gz", - "filename": "libwebp-1.2.1.tar.gz", - "dir": "libwebp-1.2.1", + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.2.tar.gz", + "filename": "libwebp-1.2.2.tar.gz", + "dir": "libwebp-1.2.2", "build": [ cmd_rmdir(r"output\release-static"), # clean cmd_nmake( From e05b8d74819fa18a908ea201a86138ea3168aba9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 23 Jan 2022 08:56:14 +1100 Subject: [PATCH 0084/1186] libwebp 1.2.2 fixed endian bugs --- Tests/test_file_webp_animated.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index 25ebffe0248..cf14a4527d4 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -1,6 +1,7 @@ import pytest +from packaging.version import parse as parse_version -from PIL import Image +from PIL import Image, features from .helper import ( assert_image_equal, @@ -27,7 +28,6 @@ def test_n_frames(): assert im.is_animated -@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian") def test_write_animation_L(tmp_path): """ Convert an animated GIF to animated WebP, then compare the frame count, and first @@ -46,6 +46,11 @@ def test_write_animation_L(tmp_path): orig.load() im.load() assert_image_similar(im, orig.convert("RGBA"), 32.9) + + if is_big_endian(): + webp = parse_version(features.version_module("webp")) + if webp < parse_version("1.2.2"): + return orig.seek(orig.n_frames - 1) im.seek(im.n_frames - 1) orig.load() @@ -53,7 +58,6 @@ def test_write_animation_L(tmp_path): assert_image_similar(im, orig.convert("RGBA"), 32.9) -@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian") def test_write_animation_RGB(tmp_path): """ Write an animated WebP from RGB frames, and ensure the frames @@ -69,6 +73,10 @@ def check(temp_file): assert_image_equal(im, frame1.convert("RGBA")) # Compare second frame to original + if is_big_endian(): + webp = parse_version(features.version_module("webp")) + if webp < parse_version("1.2.2"): + return im.seek(1) im.load() assert_image_equal(im, frame2.convert("RGBA")) From eb1fc4ad9f0d6eba8b2b186360cd334740f3d52f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Jan 2022 07:47:44 +1100 Subject: [PATCH 0085/1186] Added pytest skip message --- Tests/test_file_webp_animated.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index cf14a4527d4..8606f6aafd4 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -50,7 +50,7 @@ def test_write_animation_L(tmp_path): if is_big_endian(): webp = parse_version(features.version_module("webp")) if webp < parse_version("1.2.2"): - return + pytest.skip("Fails with libwebp earlier than 1.2.2") orig.seek(orig.n_frames - 1) im.seek(im.n_frames - 1) orig.load() @@ -76,7 +76,7 @@ def check(temp_file): if is_big_endian(): webp = parse_version(features.version_module("webp")) if webp < parse_version("1.2.2"): - return + pytest.skip("Fails with libwebp earlier than 1.2.2") im.seek(1) im.load() assert_image_equal(im, frame2.convert("RGBA")) From 31aa2ad98cffb8fd8abd05a34e56ff7f1094b553 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 24 Jan 2022 11:06:41 +1100 Subject: [PATCH 0086/1186] Removed unused variables --- Tests/test_file_psd.py | 2 +- src/PIL/PsdImagePlugin.py | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index f50fe133ffc..a7f379e5570 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -123,7 +123,7 @@ def test_no_icc_profile(): def test_combined_larger_than_size(): - # The 'combined' sizes of the individual parts is larger than the + # The combined size of the individual parts is larger than the # declared 'size' of the extra data field, resulting in a backwards seek. # If we instead take the 'size' of the extra data field as the source of truth, diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 04b21e3debd..550a333dd5b 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -195,7 +195,6 @@ def read(size): x1 = i32(read(4)) # image info - info = [] mode = [] ct_types = i16(read(2)) types = list(range(ct_types)) @@ -211,8 +210,7 @@ def read(size): m = "RGBA"[type] mode.append(m) - size = i32(read(4)) - info.append((m, size)) + read(4) # size # figure out the image mode mode.sort() @@ -229,26 +227,22 @@ def read(size): read(12) # filler name = "" size = i32(read(4)) # length of the extra data field - combined = 0 if size: data_end = fp.tell() + size length = i32(read(4)) if length: fp.seek(length - 16, io.SEEK_CUR) - combined += length + 4 length = i32(read(4)) if length: fp.seek(length, io.SEEK_CUR) - combined += length + 4 length = i8(read(1)) if length: # Don't know the proper encoding, # Latin-1 should be a good guess name = read(length).decode("latin-1", "replace") - combined += length + 1 fp.seek(data_end) layers.append((name, mode, (x0, y0, x1, y1))) From 1d91f6dce5732c48710ad73f8489568471be449d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jan 2022 09:08:41 +1100 Subject: [PATCH 0087/1186] Document when file argument will be removed --- docs/deprecations.rst | 3 ++- src/PIL/ImageShow.py | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index dbe65dda106..a3abe81fa34 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -59,7 +59,8 @@ ImageShow.Viewer.show_file file argument .. deprecated:: 9.1.0 The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been -deprecated, replaced by ``path``. +deprecated and will be removed in Pillow 10.0.0 (2023-07-01). It has been replaced by +``path``. In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. ``viewer.show_file(file="test.jpg")`` will raise a deprecation warning, and suggest diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index e437bbadeab..2165da30732 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -111,7 +111,8 @@ def show_file(self, path=None, **options): Display given file. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. + and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used + instead. """ if path is None: if "file" in options: @@ -166,7 +167,8 @@ def show_file(self, path=None, **options): Display given file. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. + and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used + instead. """ if path is None: if "file" in options: @@ -208,7 +210,8 @@ def show_file(self, path=None, **options): Display given file. Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, - and ``path`` should be used instead. + and will be removed in Pillow 10.0.0 (2023-07-01). ``path`` should be used + instead. """ if path is None: if "file" in options: From 93008c20957a7ae94fbd7b03da1f2980d4abb50c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 26 Jan 2022 12:23:43 +1100 Subject: [PATCH 0088/1186] Upgraded raqm to 0.8.0 --- .ci/install.sh | 2 +- depends/install_raqm.sh | 4 ++-- docs/installation.rst | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index c48acf9eece..efc57a6414a 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -19,7 +19,7 @@ set -e sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ - cmake imagemagick libharfbuzz-dev libfribidi-dev + cmake meson imagemagick libharfbuzz-dev libfribidi-dev python3 -m pip install --upgrade pip python3 -m pip install --upgrade wheel diff --git a/depends/install_raqm.sh b/depends/install_raqm.sh index 3105465ec40..39b998d0545 100755 --- a/depends/install_raqm.sh +++ b/depends/install_raqm.sh @@ -2,13 +2,13 @@ # install raqm -archive=raqm-0.7.1 +archive=libraqm-0.8.0 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz pushd $archive -./configure --prefix=/usr && make -j4 && sudo make -j4 install +meson build --prefix=/usr && sudo ninja -C build install popd diff --git a/docs/installation.rst b/docs/installation.rst index 0699ef34188..ea0e7850010 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -394,7 +394,8 @@ Prerequisites for **Ubuntu 16.04 LTS - 20.04 LTS** are installed with:: libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ libharfbuzz-dev libfribidi-dev libxcb1-dev -Then see ``depends/install_raqm.sh`` to install libraqm. +To install libraqm, ``sudo apt-get install meson`` and then see +``depends/install_raqm.sh``. Prerequisites are installed on recent **Red Hat**, **CentOS** or **Fedora** with:: From 5848726cadd40ff5be20497d7e8168cd4acf1d07 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 28 Jan 2022 11:23:51 +1100 Subject: [PATCH 0089/1186] Link directly to workflow pages --- README.md | 8 ++++---- docs/index.rst | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 63671f4057d..a0cb25d326c 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,16 @@ As of 2019, Pillow development is tests - GitHub Actions build status (Lint) - GitHub Actions build status (Test Linux and macOS) - GitHub Actions build status (Test Windows) - GitHub Actions build status (Test Docker) Date: Fri, 28 Jan 2022 11:25:24 +1100 Subject: [PATCH 0090/1186] Added MinGW GitHub Actions badge --- README.md | 3 +++ docs/index.rst | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index a0cb25d326c..7bff737a289 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ As of 2019, Pillow development is GitHub Actions build status (Test Windows) + GitHub Actions build status (Test MinGW) GitHub Actions build status (Test Docker) diff --git a/docs/index.rst b/docs/index.rst index c57a779dfbe..f1a721c6ac1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,10 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more Date: Fri, 28 Jan 2022 22:04:57 +1100 Subject: [PATCH 0091/1186] Updated raqm to 0.8.0 --- src/thirdparty/raqm/COPYING | 2 +- src/thirdparty/raqm/NEWS | 16 +- src/thirdparty/raqm/README.md | 27 +- src/thirdparty/raqm/raqm-version.h | 6 +- src/thirdparty/raqm/raqm.c | 391 ++++++++++++++++++++++------- src/thirdparty/raqm/raqm.h | 9 +- 6 files changed, 343 insertions(+), 108 deletions(-) diff --git a/src/thirdparty/raqm/COPYING b/src/thirdparty/raqm/COPYING index 196511ef688..78db981bb54 100644 --- a/src/thirdparty/raqm/COPYING +++ b/src/thirdparty/raqm/COPYING @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright © 2015 Information Technology Authority (ITA) -Copyright © 2016 Khaled Hosny +Copyright © 2016-2021 Khaled Hosny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/thirdparty/raqm/NEWS b/src/thirdparty/raqm/NEWS index c49176a95bf..ae1128485f2 100644 --- a/src/thirdparty/raqm/NEWS +++ b/src/thirdparty/raqm/NEWS @@ -1,4 +1,18 @@ -Overview of changes leading to 0.7.1 +Overview of changes leading to 0.8.0 +Monday, December 13, 2021 +==================================== + +Remove autotools build. + +Support using SheenBiDi instead of FriBiDi for Unicode BiDi support. + +Fix running tests with Python <= 3.6. + +New API: + * raqm_get_par_resolved_direction + * raqm_get_direction_at_index + +Overview of changes leading to 0.7.2 Monday, September 27, 2021 ==================================== diff --git a/src/thirdparty/raqm/README.md b/src/thirdparty/raqm/README.md index 64937343a6f..17ce2efc968 100644 --- a/src/thirdparty/raqm/README.md +++ b/src/thirdparty/raqm/README.md @@ -6,26 +6,26 @@ Raqm Raqm is a small library that encapsulates the logic for complex text layout and provides a convenient API. -It currently provides bidirectional text support (using [FriBiDi][1]), shaping -(using [HarfBuzz][2]), and proper script itemization. As a result, -Raqm can support most writing systems covered by Unicode. +It currently provides bidirectional text support (using [FriBiDi][1] or +[SheenBidi][2]), shaping (using [HarfBuzz][3]), and proper script itemization. +As a result, Raqm can support most writing systems covered by Unicode. The documentation can be accessed on the web at: > http://host-oman.github.io/libraqm/ Raqm (Arabic: رَقْم) is writing, also number or digit and the Arabic word for -digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”. +digital (رَقَمِيّ) shares the same root, so it is a play on “digital writing”. Building -------- Raqm depends on the following libraries: -* [FreeType][3] -* [HarfBuzz][2] -* [FriBiDi][1] +* [FreeType][4] +* [HarfBuzz][3] +* [FriBiDi][1] or [SheenBidi][2] To build the documentation you will also need: -* [GTK-Doc][4] +* [GTK-Doc][5] To install dependencies on Fedora: @@ -48,11 +48,11 @@ directory: $ ninja -C build $ ninja -C build install -To build the documentation, pass `-Ddocs=enable` to the `meson`. +To build the documentation, pass `-Ddocs=true` to the `meson`. To run the tests: - $ ninja -C test + $ ninja -C build test Contributing ------------ @@ -78,6 +78,7 @@ The following projects have patches to support complex text layout using Raqm: [1]: http://fribidi.org -[2]: http://harfbuzz.org -[3]: https://www.freetype.org -[4]: https://www.gtk.org/gtk-doc +[2]: https://github.com/Tehreer/SheenBidi +[3]: http://harfbuzz.org +[4]: https://www.freetype.org +[5]: https://www.gtk.org/gtk-doc diff --git a/src/thirdparty/raqm/raqm-version.h b/src/thirdparty/raqm/raqm-version.h index 8b115f612c6..e96b1aea564 100644 --- a/src/thirdparty/raqm/raqm-version.h +++ b/src/thirdparty/raqm/raqm-version.h @@ -32,10 +32,10 @@ #define _RAQM_VERSION_H_ #define RAQM_VERSION_MAJOR 0 -#define RAQM_VERSION_MINOR 7 -#define RAQM_VERSION_MICRO 2 +#define RAQM_VERSION_MINOR 8 +#define RAQM_VERSION_MICRO 0 -#define RAQM_VERSION_STRING "0.7.2" +#define RAQM_VERSION_STRING "0.8.0" #define RAQM_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ diff --git a/src/thirdparty/raqm/raqm.c b/src/thirdparty/raqm/raqm.c index 31161c9d91d..46890b9a7dd 100644 --- a/src/thirdparty/raqm/raqm.c +++ b/src/thirdparty/raqm/raqm.c @@ -1,6 +1,6 @@ /* * Copyright © 2015 Information Technology Authority (ITA) - * Copyright © 2016 Khaled Hosny + * Copyright © 2016-2021 Khaled Hosny * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -30,11 +30,18 @@ #include #include +#ifdef RAQM_SHEENBIDI +#include +#else #ifdef HAVE_FRIBIDI_SYSTEM #include #else #include "../fribidi-shim/fribidi.h" #endif +#if FRIBIDI_MAJOR_VERSION >= 1 +#define USE_FRIBIDI_EX_API +#endif +#endif #include #include @@ -56,10 +63,6 @@ #include "raqm.h" -#if FRIBIDI_MAJOR_VERSION >= 1 -#define USE_FRIBIDI_EX_API -#endif - /** * SECTION:raqm * @title: Raqm @@ -178,6 +181,15 @@ # define RAQM_TEST(...) #endif +#define RAQM_BIDI_LEVEL_IS_RTL(level) \ + ((level) & 1) + +#ifdef RAQM_SHEENBIDI + typedef SBLevel _raqm_bidi_level_t; +#else + typedef FriBidiLevel _raqm_bidi_level_t; +#endif + typedef enum { RAQM_FLAG_NONE = 0, RAQM_FLAG_UTF8 = 1 << 0 @@ -438,6 +450,53 @@ raqm_set_text (raqm_t *rq, return true; } +static void * +_raqm_get_utf8_codepoint (const void *str, + uint32_t *out_codepoint) +{ + const char *s = (const char *)str; + + if (0xf0 == (0xf8 & s[0])) + { + *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | ((0x3f & s[2]) << 6) | (0x3f & s[3]); + s += 4; + } + else if (0xe0 == (0xf0 & s[0])) + { + *out_codepoint = ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]); + s += 3; + } + else if (0xc0 == (0xe0 & s[0])) + { + *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]); + s += 2; + } + else + { + *out_codepoint = s[0]; + s += 1; + } + + return (void *)s; +} + +static size_t +_raqm_u8_to_u32 (const char *text, size_t len, uint32_t *unicode) +{ + size_t in_len = 0; + uint32_t *out_utf32 = unicode; + const char *in_utf8 = text; + + while ((*in_utf8 != '\0') && (in_len < len)) + { + in_utf8 = _raqm_get_utf8_codepoint (in_utf8, out_utf32); + ++out_utf32; + ++in_len; + } + + return (out_utf32 - unicode); +} + /** * raqm_set_text_utf8: * @rq: a #raqm_t. @@ -482,9 +541,7 @@ raqm_set_text_utf8 (raqm_t *rq, memcpy (rq->text_utf8, text, sizeof (char) * len); - ulen = fribidi_charset_to_unicode (FRIBIDI_CHAR_SET_UTF8, - text, len, unicode); - + ulen = _raqm_u8_to_u32 (text, len, unicode); ok = raqm_set_text (rq, unicode, ulen); free (unicode); @@ -504,7 +561,7 @@ raqm_set_text_utf8 (raqm_t *rq, * * The default is #RAQM_DIRECTION_DEFAULT, which determines the paragraph * direction based on the first character with strong bidi type (see [rule - * P2](https://unicode.org/reports/tr9/#P2) in Unicode Bidirectional Algorithm), + * P2](http://unicode.org/reports/tr9/#P2) in Unicode Bidirectional Algorithm), * which can be good enough for many cases but has problems when a mainly * right-to-left paragraph starts with a left-to-right character and vice versa * as the detected paragraph direction will be the wrong one, or when text does @@ -971,17 +1028,78 @@ raqm_get_glyphs (raqm_t *rq, return rq->glyphs; } +/** + * raqm_get_par_resolved_direction: + * @rq: a #raqm_t. + * + * Gets the resolved direction of the paragraph; + * + * Return value: + * The #raqm_direction_t specifying the resolved direction of text, + * or #RAQM_DIRECTION_DEFAULT if raqm_layout() has not been called on @rq. + * + * Since: 0.8 + */ +RAQM_API raqm_direction_t +raqm_get_par_resolved_direction (raqm_t *rq) +{ + if (!rq) + return RAQM_DIRECTION_DEFAULT; + + return rq->resolved_dir; +} + +/** + * raqm_get_direction_at_index: + * @rq: a #raqm_t. + * @index: (in): character index. + * + * Gets the resolved direction of the character at specified index; + * + * Return value: + * The #raqm_direction_t specifying the resolved direction of text at the + * specified index, or #RAQM_DIRECTION_DEFAULT if raqm_layout() has not been + * called on @rq. + * + * Since: 0.8 + */ +RAQM_API raqm_direction_t +raqm_get_direction_at_index (raqm_t *rq, + size_t index) +{ + if (!rq) + return RAQM_DIRECTION_DEFAULT; + + for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) + { + if (run->pos <= index && index < run->pos + run->len) { + switch (run->direction) { + case HB_DIRECTION_LTR: + return RAQM_DIRECTION_LTR; + case HB_DIRECTION_RTL: + return RAQM_DIRECTION_RTL; + case HB_DIRECTION_TTB: + return RAQM_DIRECTION_TTB; + default: + return RAQM_DIRECTION_DEFAULT; + } + } + } + + return RAQM_DIRECTION_DEFAULT; +} + static bool _raqm_resolve_scripts (raqm_t *rq); static hb_direction_t -_raqm_hb_dir (raqm_t *rq, FriBidiLevel level) +_raqm_hb_dir (raqm_t *rq, _raqm_bidi_level_t level) { hb_direction_t dir = HB_DIRECTION_LTR; if (rq->base_dir == RAQM_DIRECTION_TTB) dir = HB_DIRECTION_TTB; - else if (FRIBIDI_LEVEL_IS_RTL (level)) + else if (RAQM_BIDI_LEVEL_IS_RTL(level)) dir = HB_DIRECTION_RTL; return dir; @@ -990,9 +1108,65 @@ _raqm_hb_dir (raqm_t *rq, FriBidiLevel level) typedef struct { size_t pos; size_t len; - FriBidiLevel level; + _raqm_bidi_level_t level; } _raqm_bidi_run; +#ifdef RAQM_SHEENBIDI +static _raqm_bidi_run * +_raqm_bidi_itemize (raqm_t *rq, size_t *run_count) +{ + _raqm_bidi_run *runs; + SBAlgorithmRef bidi; + SBParagraphRef par; + SBUInteger par_len; + SBLineRef line; + + SBLevel base_level = SBLevelDefaultLTR; + SBCodepointSequence input = { + SBStringEncodingUTF32, + (void *) rq->text, + rq->text_len + }; + + if (rq->base_dir == RAQM_DIRECTION_RTL) + base_level = 1; + else if (rq->base_dir == RAQM_DIRECTION_LTR) + base_level = 0; + + /* paragraph */ + bidi = SBAlgorithmCreate (&input); + par = SBAlgorithmCreateParagraph (bidi, 0, INT32_MAX, base_level); + par_len = SBParagraphGetLength (par); + + /* lines */ + line = SBParagraphCreateLine (par, 0, par_len); + *run_count = SBLineGetRunCount (line); + + if (SBParagraphGetBaseLevel (par) == 0) + rq->resolved_dir = RAQM_DIRECTION_LTR; + else + rq->resolved_dir = RAQM_DIRECTION_RTL; + + runs = malloc (sizeof (_raqm_bidi_run) * (*run_count)); + if (runs) + { + const SBRun *sheenbidi_runs = SBLineGetRunsPtr(line); + + for (size_t i = 0; i < (*run_count); ++i) + { + runs[i].pos = sheenbidi_runs[i].offset; + runs[i].len = sheenbidi_runs[i].length; + runs[i].level = sheenbidi_runs[i].level; + } + } + + SBLineRelease (line); + SBParagraphRelease (par); + SBAlgorithmRelease (bidi); + + return runs; +} +#else static void _raqm_reverse_run (_raqm_bidi_run *run, const size_t len) { @@ -1093,52 +1267,31 @@ _raqm_reorder_runs (const FriBidiCharType *types, return runs; } -static bool -_raqm_itemize (raqm_t *rq) +static _raqm_bidi_run * +_raqm_bidi_itemize (raqm_t *rq, size_t *run_count) { FriBidiParType par_type = FRIBIDI_PAR_ON; + _raqm_bidi_run *runs = NULL; + FriBidiCharType *types; + _raqm_bidi_level_t *levels; + int max_level = 0; #ifdef USE_FRIBIDI_EX_API FriBidiBracketType *btypes; #endif - FriBidiLevel *levels; - _raqm_bidi_run *runs = NULL; - raqm_run_t *last; - int max_level; - size_t run_count; - bool ok = true; - -#ifdef RAQM_TESTING - switch (rq->base_dir) - { - case RAQM_DIRECTION_RTL: - RAQM_TEST ("Direction is: RTL\n\n"); - break; - case RAQM_DIRECTION_LTR: - RAQM_TEST ("Direction is: LTR\n\n"); - break; - case RAQM_DIRECTION_TTB: - RAQM_TEST ("Direction is: TTB\n\n"); - break; - case RAQM_DIRECTION_DEFAULT: - default: - RAQM_TEST ("Direction is: DEFAULT\n\n"); - break; - } -#endif types = calloc (rq->text_len, sizeof (FriBidiCharType)); #ifdef USE_FRIBIDI_EX_API btypes = calloc (rq->text_len, sizeof (FriBidiBracketType)); #endif - levels = calloc (rq->text_len, sizeof (FriBidiLevel)); + levels = calloc (rq->text_len, sizeof (_raqm_bidi_level_t)); + if (!types || !levels #ifdef USE_FRIBIDI_EX_API || !btypes #endif ) { - ok = false; goto done; } @@ -1147,38 +1300,65 @@ _raqm_itemize (raqm_t *rq) else if (rq->base_dir == RAQM_DIRECTION_LTR) par_type = FRIBIDI_PAR_LTR; - if (rq->base_dir == RAQM_DIRECTION_TTB) - { - /* Treat every thing as LTR in vertical text */ - max_level = 1; - memset (types, FRIBIDI_TYPE_LTR, rq->text_len); - memset (levels, 0, rq->text_len); - rq->resolved_dir = RAQM_DIRECTION_LTR; - } - else - { - fribidi_get_bidi_types (rq->text, rq->text_len, types); + fribidi_get_bidi_types (rq->text, rq->text_len, types); #ifdef USE_FRIBIDI_EX_API - fribidi_get_bracket_types (rq->text, rq->text_len, types, btypes); - max_level = fribidi_get_par_embedding_levels_ex (types, btypes, - rq->text_len, &par_type, - levels); + fribidi_get_bracket_types (rq->text, rq->text_len, types, btypes); + max_level = fribidi_get_par_embedding_levels_ex (types, btypes, + rq->text_len, &par_type, + levels); #else - max_level = fribidi_get_par_embedding_levels (types, rq->text_len, - &par_type, levels); + max_level = fribidi_get_par_embedding_levels (types, rq->text_len, + &par_type, levels); #endif - if (par_type == FRIBIDI_PAR_LTR) - rq->resolved_dir = RAQM_DIRECTION_LTR; - else - rq->resolved_dir = RAQM_DIRECTION_RTL; - } + if (par_type == FRIBIDI_PAR_LTR) + rq->resolved_dir = RAQM_DIRECTION_LTR; + else + rq->resolved_dir = RAQM_DIRECTION_RTL; if (max_level == 0) - { - ok = false; goto done; + + /* Get the number of bidi runs */ + runs = _raqm_reorder_runs (types, rq->text_len, par_type, levels, run_count); + +done: + free (types); + free (levels); +#ifdef USE_FRIBIDI_EX_API + free (btypes); +#endif + + return runs; +} +#endif + +static bool +_raqm_itemize (raqm_t *rq) +{ + _raqm_bidi_run *runs = NULL; + raqm_run_t *last; + size_t run_count = 0; + bool ok = true; + +#ifdef RAQM_TESTING + switch (rq->base_dir) + { + case RAQM_DIRECTION_RTL: + RAQM_TEST ("Direction is: RTL\n\n"); + break; + case RAQM_DIRECTION_LTR: + RAQM_TEST ("Direction is: LTR\n\n"); + break; + case RAQM_DIRECTION_TTB: + RAQM_TEST ("Direction is: TTB\n\n"); + break; + case RAQM_DIRECTION_DEFAULT: + default: + RAQM_TEST ("Direction is: DEFAULT\n\n"); + break; } +#endif if (!_raqm_resolve_scripts (rq)) { @@ -1186,8 +1366,22 @@ _raqm_itemize (raqm_t *rq) goto done; } - /* Get the number of bidi runs */ - runs = _raqm_reorder_runs (types, rq->text_len, par_type, levels, &run_count); + if (rq->base_dir == RAQM_DIRECTION_TTB) + { + /* Treat every thing as LTR in vertical text */ + run_count = 1; + rq->resolved_dir = RAQM_DIRECTION_TTB; + runs = malloc (sizeof (_raqm_bidi_run)); + if (runs) + { + runs->pos = 0; + runs->len = rq->text_len; + runs->level = 0; + } + } else { + runs = _raqm_bidi_itemize (rq, &run_count); + } + if (!runs) { ok = false; @@ -1197,7 +1391,7 @@ _raqm_itemize (raqm_t *rq) #ifdef RAQM_TESTING RAQM_TEST ("Number of runs before script itemization: %zu\n\n", run_count); - RAQM_TEST ("Fribidi Runs:\n"); + RAQM_TEST ("BiDi Runs:\n"); for (size_t i = 0; i < run_count; i++) { RAQM_TEST ("run[%zu]:\t start: %zu\tlength: %zu\tlevel: %d\n", @@ -1309,11 +1503,6 @@ _raqm_itemize (raqm_t *rq) done: free (runs); - free (types); -#ifdef USE_FRIBIDI_EX_API - free (btypes); -#endif - free (levels); return ok; } @@ -1328,7 +1517,7 @@ typedef struct { /* Special paired characters for script detection */ static size_t paired_len = 34; -static const FriBidiChar paired_chars[] = +static const uint32_t paired_chars[] = { 0x0028, 0x0029, /* ascii paired punctuation */ 0x003c, 0x003e, @@ -1431,7 +1620,7 @@ _raqm_stack_push (_raqm_stack_t *stack, } static int -_get_pair_index (const FriBidiChar ch) +_get_pair_index (const uint32_t ch) { int lower = 0; int upper = paired_len - 1; @@ -1569,6 +1758,7 @@ _raqm_resolve_scripts (raqm_t *rq) return true; } +#ifdef HAVE_FT_GET_TRANSFORM static void _raqm_ft_transform (int *x, int *y, @@ -1583,6 +1773,7 @@ _raqm_ft_transform (int *x, *x = vector.x; *y = vector.y; } +#endif static bool _raqm_shape (raqm_t *rq) @@ -1634,20 +1825,30 @@ _raqm_shape (raqm_t *rq) return true; } +/* Count equivalent UTF-8 bytes in codepoint */ +static size_t +_raqm_count_codepoint_utf8_bytes (uint32_t chr) +{ + if (0 == ((uint32_t) 0xffffff80 & chr)) + return 1; + else if (0 == ((uint32_t) 0xfffff800 & chr)) + return 2; + else if (0 == ((uint32_t) 0xffff0000 & chr)) + return 3; + else + return 4; +} + /* Convert index from UTF-32 to UTF-8 */ static uint32_t _raqm_u32_to_u8_index (raqm_t *rq, uint32_t index) { - FriBidiStrIndex length; - char *output = malloc ((sizeof (char) * 4 * index) + 1); + size_t length = 0; - length = fribidi_unicode_to_charset (FRIBIDI_CHAR_SET_UTF8, - rq->text, - index, - output); + for (uint32_t i = 0; i < index; ++i) + length += _raqm_count_codepoint_utf8_bytes (rq->text[i]); - free (output); return length; } @@ -1656,15 +1857,27 @@ static uint32_t _raqm_u8_to_u32_index (raqm_t *rq, uint32_t index) { - FriBidiStrIndex length; - uint32_t *output = malloc (sizeof (uint32_t) * (index + 1)); + const unsigned char *s = (const unsigned char *) rq->text_utf8; + const unsigned char *t = s; + size_t length = 0; + + while (((size_t) (s - t) < index) && ('\0' != *s)) + { + if (0xf0 == (0xf8 & *s)) + s += 4; + else if (0xe0 == (0xf0 & *s)) + s += 3; + else if (0xc0 == (0xe0 & *s)) + s += 2; + else + s += 1; + + length++; + } - length = fribidi_charset_to_unicode (FRIBIDI_CHAR_SET_UTF8, - rq->text_utf8, - index, - output); + if ((size_t) (s-t) > index) + length--; - free (output); return length; } diff --git a/src/thirdparty/raqm/raqm.h b/src/thirdparty/raqm/raqm.h index 342afc8b29b..faab855915a 100644 --- a/src/thirdparty/raqm/raqm.h +++ b/src/thirdparty/raqm/raqm.h @@ -1,6 +1,6 @@ /* * Copyright © 2015 Information Technology Authority (ITA) - * Copyright © 2016 Khaled Hosny + * Copyright © 2016-2021 Khaled Hosny * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -156,6 +156,13 @@ RAQM_API raqm_glyph_t * raqm_get_glyphs (raqm_t *rq, size_t *length); +RAQM_API raqm_direction_t +raqm_get_par_resolved_direction (raqm_t *rq); + +RAQM_API raqm_direction_t +raqm_get_direction_at_index (raqm_t *rq, + size_t index); + RAQM_API bool raqm_index_to_position (raqm_t *rq, size_t *index, From 1b6a8c6122ad6b5e5794b04ca37660126675a8a4 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 10:00:14 -0500 Subject: [PATCH 0092/1186] TST: Parametrize numpy roundtrip to find failing case Segfaults are annoying to debug. --- Tests/test_numpy.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index def7adf3f02..936474fe8d2 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -189,8 +189,9 @@ def test_putdata(): assert len(im.getdata()) == len(arr) -def test_roundtrip_eye(): - for dtype in ( +@pytest.mark.parametrize( + "dtype", + ( bool, numpy.bool8, numpy.int8, @@ -202,9 +203,11 @@ def test_roundtrip_eye(): float, numpy.float32, numpy.float64, - ): - arr = numpy.eye(10, dtype=dtype) - numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) + ), +) +def test_roundtrip_eye(dtype): + arr = numpy.eye(10, dtype=dtype) + numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) def test_zero_size(): From 7ab973c4c95494cae655825cc36c5ba9abbfabd2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 30 Jan 2022 08:01:35 +1100 Subject: [PATCH 0093/1186] Updated lcms2 to 2.13 --- docs/installation.rst | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index ea0e7850010..88aaf58341d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -169,7 +169,7 @@ Many of Pillow's features require external libraries: * **littlecms** provides color management * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and - above uses liblcms2. Tested with **1.19** and **2.7-2.12**. + above uses liblcms2. Tested with **1.19** and **2.7-2.13**. * **libwebp** provides the WebP format. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 3092c5a2ba8..78916987c34 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -219,9 +219,9 @@ def cmd_msbuild( # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.12/lcms2-2.12.tar.gz", - "filename": "lcms2-2.12.tar.gz", - "dir": "lcms2-2.12", + "url": SF_MIRROR + "/project/lcms/lcms/2.12/lcms2-2.13.tar.gz", + "filename": "lcms2-2.13.tar.gz", + "dir": "lcms2-2.13", "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always From d8e94c206ef69989642e48aa2795a2a69f9d0577 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 30 Jan 2022 14:40:30 +1100 Subject: [PATCH 0094/1186] Switched from windows-2019 to windows-latest --- .github/workflows/test-mingw.yml | 2 +- .github/workflows/test-windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml index d94c7d53751..8a9c1725d9a 100644 --- a/.github/workflows/test-mingw.yml +++ b/.github/workflows/test-mingw.yml @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch] jobs: build: - runs-on: windows-2019 + runs-on: windows-latest strategy: fail-fast: false matrix: diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index c768838eb06..c78f9fd24ec 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch] jobs: build: - runs-on: windows-2019 + runs-on: windows-latest strategy: fail-fast: false matrix: From 36f293071e76c9e0206708cf01087055f1dc0912 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 31 Jan 2022 08:05:01 +1100 Subject: [PATCH 0095/1186] Updated raqm to 0.9.0 --- depends/install_raqm.sh | 2 +- src/thirdparty/raqm/COPYING | 2 +- src/thirdparty/raqm/README.md | 5 +- src/thirdparty/raqm/raqm-version.h | 4 +- src/thirdparty/raqm/raqm.c | 470 ++++++++++++++++++----------- src/thirdparty/raqm/raqm.h | 11 +- 6 files changed, 310 insertions(+), 184 deletions(-) diff --git a/depends/install_raqm.sh b/depends/install_raqm.sh index 39b998d0545..99250365065 100755 --- a/depends/install_raqm.sh +++ b/depends/install_raqm.sh @@ -2,7 +2,7 @@ # install raqm -archive=libraqm-0.8.0 +archive=libraqm-0.9.0 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/src/thirdparty/raqm/COPYING b/src/thirdparty/raqm/COPYING index 78db981bb54..c605a5dc67a 100644 --- a/src/thirdparty/raqm/COPYING +++ b/src/thirdparty/raqm/COPYING @@ -1,7 +1,7 @@ The MIT License (MIT) Copyright © 2015 Information Technology Authority (ITA) -Copyright © 2016-2021 Khaled Hosny +Copyright © 2016-2022 Khaled Hosny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/thirdparty/raqm/README.md b/src/thirdparty/raqm/README.md index 17ce2efc968..02e996e7a9c 100644 --- a/src/thirdparty/raqm/README.md +++ b/src/thirdparty/raqm/README.md @@ -68,6 +68,7 @@ Projects using Raqm 3. [FontView](https://github.com/googlei18n/fontview) 4. [Pillow](https://github.com/python-pillow) 5. [mplcairo](https://github.com/anntzer/mplcairo) +6. [CEGUI](https://github.com/cegui/cegui) The following projects have patches to support complex text layout using Raqm: @@ -77,8 +78,8 @@ The following projects have patches to support complex text layout using Raqm: -[1]: http://fribidi.org +[1]: https://github.com/fribidi/fribidi [2]: https://github.com/Tehreer/SheenBidi -[3]: http://harfbuzz.org +[3]: https://github.com/harfbuzz/harfbuzz [4]: https://www.freetype.org [5]: https://www.gtk.org/gtk-doc diff --git a/src/thirdparty/raqm/raqm-version.h b/src/thirdparty/raqm/raqm-version.h index e96b1aea564..78b70a5615e 100644 --- a/src/thirdparty/raqm/raqm-version.h +++ b/src/thirdparty/raqm/raqm-version.h @@ -32,10 +32,10 @@ #define _RAQM_VERSION_H_ #define RAQM_VERSION_MAJOR 0 -#define RAQM_VERSION_MINOR 8 +#define RAQM_VERSION_MINOR 9 #define RAQM_VERSION_MICRO 0 -#define RAQM_VERSION_STRING "0.8.0" +#define RAQM_VERSION_STRING "0.9.0" #define RAQM_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ diff --git a/src/thirdparty/raqm/raqm.c b/src/thirdparty/raqm/raqm.c index 46890b9a7dd..f852542b43e 100644 --- a/src/thirdparty/raqm/raqm.c +++ b/src/thirdparty/raqm/raqm.c @@ -1,6 +1,6 @@ /* * Copyright © 2015 Information Technology Authority (ITA) - * Copyright © 2016-2021 Khaled Hosny + * Copyright © 2016-2022 Khaled Hosny * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -24,7 +24,6 @@ #ifdef HAVE_CONFIG_H #include "config.h" -#undef HAVE_CONFIG_H // Workaround for Fribidi 1.0.5 and earlier #endif #include @@ -38,9 +37,6 @@ #else #include "../fribidi-shim/fribidi.h" #endif -#if FRIBIDI_MAJOR_VERSION >= 1 -#define USE_FRIBIDI_EX_API -#endif #endif #include @@ -190,13 +186,9 @@ typedef FriBidiLevel _raqm_bidi_level_t; #endif -typedef enum { - RAQM_FLAG_NONE = 0, - RAQM_FLAG_UTF8 = 1 << 0 -} _raqm_flags_t; - typedef struct { FT_Face ftface; + int ftloadflags; hb_language_t lang; hb_script_t script; } _raqm_text_info; @@ -209,6 +201,7 @@ struct _raqm { uint32_t *text; char *text_utf8; size_t text_len; + size_t text_capacity_bytes; _raqm_text_info *text_info; @@ -219,17 +212,17 @@ struct _raqm { size_t features_len; raqm_run_t *runs; - raqm_glyph_t *glyphs; + raqm_run_t *runs_pool; - _raqm_flags_t flags; + raqm_glyph_t *glyphs; + size_t glyphs_capacity; - int ft_loadflags; int invisible_glyph; }; struct _raqm_run { - int pos; - int len; + uint32_t pos; + uint32_t len; hb_direction_t direction; hb_script_t script; @@ -243,31 +236,21 @@ static uint32_t _raqm_u8_to_u32_index (raqm_t *rq, uint32_t index); -static bool +static void _raqm_init_text_info (raqm_t *rq) { - hb_language_t default_lang; - - if (rq->text_info) - return true; - - rq->text_info = malloc (sizeof (_raqm_text_info) * rq->text_len); - if (!rq->text_info) - return false; - - default_lang = hb_language_get_default (); + hb_language_t default_lang = hb_language_get_default (); for (size_t i = 0; i < rq->text_len; i++) { rq->text_info[i].ftface = NULL; + rq->text_info[i].ftloadflags = -1; rq->text_info[i].lang = default_lang; rq->text_info[i].script = HB_SCRIPT_INVALID; } - - return true; } static void -_raqm_free_text_info (raqm_t *rq) +_raqm_release_text_info (raqm_t *rq) { if (!rq->text_info) return; @@ -277,9 +260,6 @@ _raqm_free_text_info (raqm_t *rq) if (rq->text_info[i].ftface) FT_Done_Face (rq->text_info[i].ftface); } - - free (rq->text_info); - rq->text_info = NULL; } static bool @@ -289,6 +269,9 @@ _raqm_compare_text_info (_raqm_text_info a, if (a.ftface != b.ftface) return false; + if (a.ftloadflags != b.ftloadflags) + return false; + if (a.lang != b.lang) return false; @@ -298,6 +281,88 @@ _raqm_compare_text_info (_raqm_text_info a, return true; } +static void +_raqm_free_text(raqm_t* rq) +{ + free (rq->text); + rq->text = NULL; + rq->text_info = NULL; + rq->text_utf8 = NULL; + rq->text_len = 0; + rq->text_capacity_bytes = 0; +} + +static bool +_raqm_alloc_text(raqm_t *rq, + size_t len, + bool need_utf8) +{ + /* Allocate contiguous memory block for texts and text_info */ + size_t mem_size = (sizeof (uint32_t) + sizeof (_raqm_text_info)) * len; + if (need_utf8) + mem_size += sizeof (char) * len; + + if (mem_size > rq->text_capacity_bytes) + { + void* new_mem = realloc (rq->text, mem_size); + if (!new_mem) + { + _raqm_free_text (rq); + return false; + } + + rq->text_capacity_bytes = mem_size; + rq->text = new_mem; + } + + rq->text_info = (_raqm_text_info*)(rq->text + len); + rq->text_utf8 = need_utf8 ? (char*)(rq->text_info + len) : NULL; + + return true; +} + +static raqm_run_t* +_raqm_alloc_run (raqm_t *rq) +{ + raqm_run_t *run = rq->runs_pool; + if (run) + { + rq->runs_pool = run->next; + } + else + { + run = malloc (sizeof (raqm_run_t)); + run->font = NULL; + run->buffer = NULL; + } + + run->pos = 0; + run->len = 0; + run->direction = HB_DIRECTION_INVALID; + run->script = HB_SCRIPT_INVALID; + run->next = NULL; + + return run; +} + +static void +_raqm_free_runs (raqm_run_t *runs) +{ + while (runs) + { + raqm_run_t *run = runs; + runs = runs->next; + + if (run->buffer) + hb_buffer_destroy (run->buffer); + + if (run->font) + hb_font_destroy (run->font); + + free (run); + } +} + /** * raqm_create: * @@ -322,25 +387,25 @@ raqm_create (void) rq->ref_count = 1; - rq->text = NULL; - rq->text_utf8 = NULL; - rq->text_len = 0; - - rq->text_info = NULL; - rq->base_dir = RAQM_DIRECTION_DEFAULT; rq->resolved_dir = RAQM_DIRECTION_DEFAULT; rq->features = NULL; rq->features_len = 0; - rq->runs = NULL; - rq->glyphs = NULL; + rq->invisible_glyph = 0; + + rq->text = NULL; + rq->text_utf8 = NULL; + rq->text_info = NULL; + rq->text_capacity_bytes = 0; + rq->text_len = 0; - rq->flags = RAQM_FLAG_NONE; + rq->runs = NULL; + rq->runs_pool = NULL; - rq->ft_loadflags = -1; - rq->invisible_glyph = 0; + rq->glyphs = NULL; + rq->glyphs_capacity = 0; return rq; } @@ -366,28 +431,13 @@ raqm_reference (raqm_t *rq) return rq; } -static void -_raqm_free_runs (raqm_t *rq) -{ - raqm_run_t *runs = rq->runs; - while (runs) - { - raqm_run_t *run = runs; - runs = runs->next; - - hb_buffer_destroy (run->buffer); - hb_font_destroy (run->font); - free (run); - } -} - /** * raqm_destroy: * @rq: a #raqm_t. * * Decreases the reference count on @rq by one. If the result is zero, then @rq * and all associated resources are freed. - * See cairo_reference(). + * See raqm_reference(). * * Since: 0.1 */ @@ -397,14 +447,60 @@ raqm_destroy (raqm_t *rq) if (!rq || --rq->ref_count != 0) return; - free (rq->text); - free (rq->text_utf8); - _raqm_free_text_info (rq); - _raqm_free_runs (rq); + _raqm_release_text_info (rq); + _raqm_free_text (rq); + _raqm_free_runs (rq->runs); + _raqm_free_runs (rq->runs_pool); free (rq->glyphs); + free (rq->features); free (rq); } +/** + * raqm_clear_contents: + * @rq: a #raqm_t. + * + * Clears internal state of previously used raqm_t object, making it ready + * for reuse and keeping some of allocated memory to increase performance. + * + * Since: 0.9 + */ +void +raqm_clear_contents (raqm_t *rq) +{ + if (!rq) + return; + + _raqm_release_text_info (rq); + + /* Return allocated runs to the pool, keep hb buffers for reuse */ + raqm_run_t *run = rq->runs; + while (run) + { + if (run->buffer) + hb_buffer_reset (run->buffer); + + if (run->font) + { + hb_font_destroy (run->font); + run->font = NULL; + } + + if (!run->next) + { + run->next = rq->runs_pool; + rq->runs_pool = rq->runs; + rq->runs = NULL; + break; + } + + run = run->next; + } + + rq->text_len = 0; + rq->resolved_dir = RAQM_DIRECTION_DEFAULT; +} + /** * raqm_set_text: * @rq: a #raqm_t. @@ -429,23 +525,20 @@ raqm_set_text (raqm_t *rq, if (!rq || !text) return false; - rq->text_len = len; + /* Call raqm_clear_contents to reuse this raqm_t */ + if (rq->text_len) + return false; /* Empty string, don’t fail but do nothing */ if (!len) return true; - free (rq->text); - - rq->text = malloc (sizeof (uint32_t) * rq->text_len); - if (!rq->text) - return false; - - _raqm_free_text_info (rq); - if (!_raqm_init_text_info (rq)) - return false; + if (!_raqm_alloc_text(rq, len, false)) + return false; - memcpy (rq->text, text, sizeof (uint32_t) * rq->text_len); + rq->text_len = len; + memcpy (rq->text, text, sizeof (uint32_t) * len); + _raqm_init_text_info (rq); return true; } @@ -511,41 +604,29 @@ _raqm_u8_to_u32 (const char *text, size_t len, uint32_t *unicode) * Since: 0.1 */ bool -raqm_set_text_utf8 (raqm_t *rq, - const char *text, - size_t len) +raqm_set_text_utf8 (raqm_t *rq, + const char *text, + size_t len) { - uint32_t *unicode; - size_t ulen; - bool ok; - if (!rq || !text) return false; + /* Call raqm_clear_contents to reuse this raqm_t */ + if (rq->text_len) + return false; + /* Empty string, don’t fail but do nothing */ if (!len) - { - rq->text_len = len; return true; - } - rq->flags |= RAQM_FLAG_UTF8; - - rq->text_utf8 = malloc (sizeof (char) * len); - if (!rq->text_utf8) - return false; - - unicode = malloc (sizeof (uint32_t) * len); - if (!unicode) - return false; + if (!_raqm_alloc_text(rq, len, true)) + return false; + rq->text_len = _raqm_u8_to_u32 (text, len, rq->text); memcpy (rq->text_utf8, text, sizeof (char) * len); + _raqm_init_text_info (rq); - ulen = _raqm_u8_to_u32 (text, len, unicode); - ok = raqm_set_text (rq, unicode, ulen); - - free (unicode); - return ok; + return true; } /** @@ -561,7 +642,7 @@ raqm_set_text_utf8 (raqm_t *rq, * * The default is #RAQM_DIRECTION_DEFAULT, which determines the paragraph * direction based on the first character with strong bidi type (see [rule - * P2](http://unicode.org/reports/tr9/#P2) in Unicode Bidirectional Algorithm), + * P2](https://unicode.org/reports/tr9/#P2) in Unicode Bidirectional Algorithm), * which can be good enough for many cases but has problems when a mainly * right-to-left paragraph starts with a left-to-right character and vice versa * as the detected paragraph direction will be the wrong one, or when text does @@ -629,7 +710,7 @@ raqm_set_language (raqm_t *rq, if (!rq->text_len) return true; - if (rq->flags & RAQM_FLAG_UTF8) + if (rq->text_utf8) { start = _raqm_u8_to_u32_index (rq, start); end = _raqm_u8_to_u32_index (rq, end); @@ -686,13 +767,14 @@ raqm_add_font_feature (raqm_t *rq, ok = hb_feature_from_string (feature, len, &fea); if (ok) { - rq->features_len++; - rq->features = realloc (rq->features, - sizeof (hb_feature_t) * (rq->features_len)); - if (!rq->features) + void* new_features = realloc (rq->features, + sizeof (hb_feature_t) * (rq->features_len + 1)); + if (!new_features) return false; - rq->features[rq->features_len - 1] = fea; + rq->features = new_features; + rq->features[rq->features_len] = fea; + rq->features_len++; } return ok; @@ -700,12 +782,13 @@ raqm_add_font_feature (raqm_t *rq, static hb_font_t * _raqm_create_hb_font (raqm_t *rq, - FT_Face face) + FT_Face face, + int loadflags) { hb_font_t *font = hb_ft_font_create_referenced (face); - if (rq->ft_loadflags >= 0) - hb_ft_font_set_load_flags (font, rq->ft_loadflags); + if (loadflags >= 0) + hb_ft_font_set_load_flags (font, loadflags); return font; } @@ -796,7 +879,7 @@ raqm_set_freetype_face_range (raqm_t *rq, if (!rq->text_len) return true; - if (rq->flags & RAQM_FLAG_UTF8) + if (rq->text_utf8) { start = _raqm_u8_to_u32_index (rq, start); end = _raqm_u8_to_u32_index (rq, end); @@ -805,6 +888,30 @@ raqm_set_freetype_face_range (raqm_t *rq, return _raqm_set_freetype_face (rq, face, start, end); } +static bool +_raqm_set_freetype_load_flags (raqm_t *rq, + int flags, + size_t start, + size_t end) +{ + if (!rq) + return false; + + if (!rq->text_len) + return true; + + if (start >= rq->text_len || end > rq->text_len) + return false; + + if (!rq->text_info) + return false; + + for (size_t i = start; i < end; i++) + rq->text_info[i].ftloadflags = flags; + + return true; +} + /** * raqm_set_freetype_load_flags: * @rq: a #raqm_t. @@ -823,14 +930,59 @@ raqm_set_freetype_face_range (raqm_t *rq, */ bool raqm_set_freetype_load_flags (raqm_t *rq, - int flags) + int flags) { + return _raqm_set_freetype_load_flags(rq, flags, 0, rq->text_len); +} + +/** + * raqm_set_freetype_load_flags_range: + * @rq: a #raqm_t. + * @flags: FreeType load flags. + * @start: index of first character that should use @flags. + * @len: number of characters using @flags. + * + * Sets the load flags passed to FreeType when loading glyphs for @len-number + * of characters staring at @start. Flags should be the same as used by the + * client when rendering corresponding FreeType glyphs. The @start and @len + * are input string array indices (i.e. counting bytes in UTF-8 and scaler + * values in UTF-32). + * + * This method can be used repeatedly to set different flags for different + * parts of the text. It is the responsibility of the client to make sure that + * flag ranges cover the whole text. + * + * This requires version of HarfBuzz that has hb_ft_font_set_load_flags(), for + * older version the flags will be ignored. + * + * See also raqm_set_freetype_load_flags(). + * + * Return value: + * %true if no errors happened, %false otherwise. + * + * Since: 0.9 + */ +bool +raqm_set_freetype_load_flags_range (raqm_t *rq, + int flags, + size_t start, + size_t len) +{ + size_t end = start + len; + if (!rq) return false; - rq->ft_loadflags = flags; + if (!rq->text_len) + return true; - return true; + if (rq->text_utf8) + { + start = _raqm_u8_to_u32_index (rq, start); + end = _raqm_u8_to_u32_index (rq, end); + } + + return _raqm_set_freetype_load_flags (rq, flags, start, end); } /** @@ -841,17 +993,10 @@ raqm_set_freetype_load_flags (raqm_t *rq, * Sets the glyph id to be used for invisible glyhphs. * * If @gid is negative, invisible glyphs will be suppressed from the output. - * This requires HarfBuzz 1.8.0 or later. If raqm is used with an earlier - * HarfBuzz version, the return value will be %false and the shaping behavior - * does not change. * * If @gid is zero, invisible glyphs will be rendered as space. - * This works on all versions of HarfBuzz. * * If @gid is a positive number, it will be used for invisible glyphs. - * This requires a version of HarfBuzz that has - * hb_buffer_set_invisible_glyph(). For older versions, the return value - * will be %false and the shaping behavior does not change. * * Return value: * %true if no errors happened, %false otherwise. @@ -865,17 +1010,6 @@ raqm_set_invisible_glyph (raqm_t *rq, if (!rq) return false; -#ifndef HAVE_HB_BUFFER_SET_INVISIBLE_GLYPH - if (gid > 0) - return false; -#endif - -#if !defined(HAVE_DECL_HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) || \ - !HAVE_DECL_HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES - if (gid < 0) - return false; -#endif - rq->invisible_glyph = gid; return true; } @@ -961,18 +1095,21 @@ raqm_get_glyphs (raqm_t *rq, for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) count += hb_buffer_get_length (run->buffer); - *length = count; - - if (rq->glyphs) - free (rq->glyphs); - - rq->glyphs = malloc (sizeof (raqm_glyph_t) * count); - if (!rq->glyphs) + if (count > rq->glyphs_capacity) { - *length = 0; - return NULL; + void* new_mem = realloc (rq->glyphs, sizeof (raqm_glyph_t) * count); + if (!new_mem) + { + *length = 0; + return NULL; + } + + rq->glyphs = new_mem; + rq->glyphs_capacity = count; } + *length = count; + RAQM_TEST ("Glyph information:\n"); count = 0; @@ -1005,7 +1142,7 @@ raqm_get_glyphs (raqm_t *rq, count += len; } - if (rq->flags & RAQM_FLAG_UTF8) + if (rq->text_utf8) { #ifdef RAQM_TESTING RAQM_TEST ("\nUTF-32 clusters:"); @@ -1276,24 +1413,14 @@ _raqm_bidi_itemize (raqm_t *rq, size_t *run_count) FriBidiCharType *types; _raqm_bidi_level_t *levels; int max_level = 0; -#ifdef USE_FRIBIDI_EX_API FriBidiBracketType *btypes; -#endif types = calloc (rq->text_len, sizeof (FriBidiCharType)); -#ifdef USE_FRIBIDI_EX_API btypes = calloc (rq->text_len, sizeof (FriBidiBracketType)); -#endif levels = calloc (rq->text_len, sizeof (_raqm_bidi_level_t)); - if (!types || !levels -#ifdef USE_FRIBIDI_EX_API - || !btypes -#endif - ) - { + if (!types || !levels || !btypes) goto done; - } if (rq->base_dir == RAQM_DIRECTION_RTL) par_type = FRIBIDI_PAR_RTL; @@ -1301,15 +1428,10 @@ _raqm_bidi_itemize (raqm_t *rq, size_t *run_count) par_type = FRIBIDI_PAR_LTR; fribidi_get_bidi_types (rq->text, rq->text_len, types); -#ifdef USE_FRIBIDI_EX_API fribidi_get_bracket_types (rq->text, rq->text_len, types, btypes); max_level = fribidi_get_par_embedding_levels_ex (types, btypes, rq->text_len, &par_type, levels); -#else - max_level = fribidi_get_par_embedding_levels (types, rq->text_len, - &par_type, levels); -#endif if (par_type == FRIBIDI_PAR_LTR) rq->resolved_dir = RAQM_DIRECTION_LTR; @@ -1325,9 +1447,7 @@ _raqm_bidi_itemize (raqm_t *rq, size_t *run_count) done: free (types); free (levels); -#ifdef USE_FRIBIDI_EX_API free (btypes); -#endif return runs; } @@ -1403,7 +1523,7 @@ _raqm_itemize (raqm_t *rq) last = NULL; for (size_t i = 0; i < run_count; i++) { - raqm_run_t *run = calloc (1, sizeof (raqm_run_t)); + raqm_run_t *run = _raqm_alloc_run (rq); if (!run) { ok = false; @@ -1422,13 +1542,14 @@ _raqm_itemize (raqm_t *rq) { run->pos = runs[i].pos + runs[i].len - 1; run->script = rq->text_info[run->pos].script; - run->font = _raqm_create_hb_font (rq, rq->text_info[run->pos].ftface); + run->font = _raqm_create_hb_font (rq, rq->text_info[run->pos].ftface, + rq->text_info[run->pos].ftloadflags); for (int j = runs[i].len - 1; j >= 0; j--) { _raqm_text_info info = rq->text_info[runs[i].pos + j]; if (!_raqm_compare_text_info (rq->text_info[run->pos], info)) { - raqm_run_t *newrun = calloc (1, sizeof (raqm_run_t)); + raqm_run_t *newrun = _raqm_alloc_run (rq); if (!newrun) { ok = false; @@ -1438,7 +1559,8 @@ _raqm_itemize (raqm_t *rq) newrun->len = 1; newrun->direction = _raqm_hb_dir (rq, runs[i].level); newrun->script = info.script; - newrun->font = _raqm_create_hb_font (rq, info.ftface); + newrun->font = _raqm_create_hb_font (rq, info.ftface, + info.ftloadflags); run->next = newrun; run = newrun; } @@ -1453,13 +1575,14 @@ _raqm_itemize (raqm_t *rq) { run->pos = runs[i].pos; run->script = rq->text_info[run->pos].script; - run->font = _raqm_create_hb_font (rq, rq->text_info[run->pos].ftface); + run->font = _raqm_create_hb_font (rq, rq->text_info[run->pos].ftface, + rq->text_info[run->pos].ftloadflags); for (size_t j = 0; j < runs[i].len; j++) { _raqm_text_info info = rq->text_info[runs[i].pos + j]; if (!_raqm_compare_text_info (rq->text_info[run->pos], info)) { - raqm_run_t *newrun = calloc (1, sizeof (raqm_run_t)); + raqm_run_t *newrun = _raqm_alloc_run (rq); if (!newrun) { ok = false; @@ -1469,7 +1592,8 @@ _raqm_itemize (raqm_t *rq) newrun->len = 1; newrun->direction = _raqm_hb_dir (rq, runs[i].level); newrun->script = info.script; - newrun->font = _raqm_create_hb_font (rq, info.ftface); + newrun->font = _raqm_create_hb_font (rq, info.ftface, + info.ftloadflags); run->next = newrun; run = newrun; } @@ -1758,7 +1882,6 @@ _raqm_resolve_scripts (raqm_t *rq) return true; } -#ifdef HAVE_FT_GET_TRANSFORM static void _raqm_ft_transform (int *x, int *y, @@ -1773,22 +1896,19 @@ _raqm_ft_transform (int *x, *x = vector.x; *y = vector.y; } -#endif static bool _raqm_shape (raqm_t *rq) { hb_buffer_flags_t hb_buffer_flags = HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT; -#if defined(HAVE_DECL_HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && \ - HAVE_DECL_HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES if (rq->invisible_glyph < 0) hb_buffer_flags |= HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES; -#endif for (raqm_run_t *run = rq->runs; run != NULL; run = run->next) { - run->buffer = hb_buffer_create (); + if (!run->buffer) + run->buffer = hb_buffer_create (); hb_buffer_add_utf32 (run->buffer, rq->text, rq->text_len, run->pos, run->len); @@ -1797,15 +1917,12 @@ _raqm_shape (raqm_t *rq) hb_buffer_set_direction (run->buffer, run->direction); hb_buffer_set_flags (run->buffer, hb_buffer_flags); -#ifdef HAVE_HB_BUFFER_SET_INVISIBLE_GLYPH if (rq->invisible_glyph > 0) hb_buffer_set_invisible_glyph (run->buffer, rq->invisible_glyph); -#endif hb_shape_full (run->font, run->buffer, rq->features, rq->features_len, NULL); -#ifdef HAVE_FT_GET_TRANSFORM { FT_Matrix matrix; hb_glyph_position_t *pos; @@ -1819,7 +1936,6 @@ _raqm_shape (raqm_t *rq) _raqm_ft_transform (&pos[i].x_offset, &pos[i].y_offset, matrix); } } -#endif } return true; @@ -1917,7 +2033,7 @@ raqm_index_to_position (raqm_t *rq, if (rq == NULL) return false; - if (rq->flags & RAQM_FLAG_UTF8) + if (rq->text_utf8) *index = _raqm_u8_to_u32_index (rq, *index); if (*index >= rq->text_len) @@ -1974,7 +2090,7 @@ raqm_index_to_position (raqm_t *rq, } found: - if (rq->flags & RAQM_FLAG_UTF8) + if (rq->text_utf8) *index = _raqm_u32_to_u8_index (rq, *index); RAQM_TEST ("The position is %d at index %zu\n",*x ,*index); return true; diff --git a/src/thirdparty/raqm/raqm.h b/src/thirdparty/raqm/raqm.h index faab855915a..bdb5a50d884 100644 --- a/src/thirdparty/raqm/raqm.h +++ b/src/thirdparty/raqm/raqm.h @@ -1,6 +1,6 @@ /* * Copyright © 2015 Information Technology Authority (ITA) - * Copyright © 2016-2021 Khaled Hosny + * Copyright © 2016-2022 Khaled Hosny * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -106,6 +106,9 @@ raqm_reference (raqm_t *rq); RAQM_API void raqm_destroy (raqm_t *rq); +RAQM_API void +raqm_clear_contents (raqm_t *rq); + RAQM_API bool raqm_set_text (raqm_t *rq, const uint32_t *text, @@ -145,6 +148,12 @@ RAQM_API bool raqm_set_freetype_load_flags (raqm_t *rq, int flags); +RAQM_API bool +raqm_set_freetype_load_flags_range (raqm_t *rq, + int flags, + size_t start, + size_t len); + RAQM_API bool raqm_set_invisible_glyph (raqm_t *rq, int gid); From 999ef3e4eaf793612d9f3525fcbff2bac629e135 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 31 Jan 2022 00:11:47 +0000 Subject: [PATCH 0096/1186] Revert "detect FreeType / HarfBuzz features" This reverts commit 6565d13275cead21dc6f369204f0dc3d0b43bc18. --- src/thirdparty/raqm/raqm.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/thirdparty/raqm/raqm.c b/src/thirdparty/raqm/raqm.c index f852542b43e..13f6e1f023c 100644 --- a/src/thirdparty/raqm/raqm.c +++ b/src/thirdparty/raqm/raqm.c @@ -42,21 +42,6 @@ #include #include -#if FREETYPE_MAJOR > 2 || \ - FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 11 -#define HAVE_FT_GET_TRANSFORM -#endif - -#if HB_VERSION_ATLEAST(2, 0, 0) -#define HAVE_HB_BUFFER_SET_INVISIBLE_GLYPH -#endif - -#if HB_VERSION_ATLEAST(1, 8, 0) -#define HAVE_DECL_HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES 1 -#else -#define HAVE_DECL_HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES 0 -#endif - #include "raqm.h" /** From 3ac2da533a1e74f4a1bb38e8e812190c49fbdc53 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 31 Jan 2022 18:56:52 +0200 Subject: [PATCH 0097/1186] Fix lcms2 URL --- winbuild/build_prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 78916987c34..182146fbc57 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -219,7 +219,7 @@ def cmd_msbuild( # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.12/lcms2-2.13.tar.gz", + "url": SF_MIRROR + "/project/lcms/lcms/2.13/lcms2-2.13.tar.gz", "filename": "lcms2-2.13.tar.gz", "dir": "lcms2-2.13", "patch": { From cc4f9a2a7dcb3e69dd4ca4ab752ebcd0fba0bbb2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 31 Jan 2022 20:38:47 +0200 Subject: [PATCH 0098/1186] Update harfbuzz to 3.3.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 3092c5a2ba8..e6bcbc6fc07 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -278,9 +278,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/3.2.0.zip", - "filename": "harfbuzz-3.2.0.zip", - "dir": "harfbuzz-3.2.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.0.zip", + "filename": "harfbuzz-3.3.0.zip", + "dir": "harfbuzz-3.3.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 87ab18f804ed860f4315038980bcf20044ae352c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 31 Jan 2022 23:27:35 +0200 Subject: [PATCH 0099/1186] Update harfbuzz to 3.3.1 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index e6bcbc6fc07..220956adc2e 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -278,9 +278,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.0.zip", - "filename": "harfbuzz-3.3.0.zip", - "dir": "harfbuzz-3.3.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.1.zip", + "filename": "harfbuzz-3.3.1.zip", + "dir": "harfbuzz-3.3.1", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 54e9decde30915e1f89ed7cda0bc49870707a02e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 31 Jan 2022 23:41:16 +0200 Subject: [PATCH 0100/1186] Remove EOL CentOS 8 --- .github/workflows/test-docker.yml | 1 - docs/installation.rst | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index df04d0a6cae..656df5e913c 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -19,7 +19,6 @@ jobs: amazon-2-amd64, arch, centos-7-amd64, - centos-8-amd64, centos-stream-8-amd64, debian-10-buster-x86, debian-11-bullseye-x86, diff --git a/docs/installation.rst b/docs/installation.rst index 88aaf58341d..984a689c2fb 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -453,8 +453,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS 7 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| CentOS 8 | 3.9 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | @@ -530,6 +528,8 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+---------------------------+------------------+--------------+ | CentOS 6.3 | 2.7, 3.3 | |x86 | +----------------------------------+---------------------------+------------------+--------------+ +| CentOS 8 | 3.9 | 9.0.0 |x86-64 | ++----------------------------------+---------------------------+------------------+--------------+ | Fedora 23 | 2.7, 3.4 | 3.1.0 |x86-64 | +----------------------------------+---------------------------+------------------+--------------+ | Ubuntu Linux 12.04 LTS (Precise) | | 2.6, 3.2, 3.3, 3.4, 3.5 | 3.4.1 |x86,x86-64 | From f22d3561fd764e9600fe4640856c50ad74a90eaf Mon Sep 17 00:00:00 2001 From: Gabor Kertesz Date: Fri, 29 Oct 2021 12:31:40 +0200 Subject: [PATCH 0101/1186] Windows: Enable ARM64 for MSVC This patch enables ARM64 as a new platform for Windows. Platform query and documentation is updated accordingly. --- winbuild/build.rst | 6 +++--- winbuild/build_prepare.py | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/winbuild/build.rst b/winbuild/build.rst index b30a94226d7..661c5a5ecea 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -24,7 +24,7 @@ Download and install: * `CMake 3.12 or newer `_ (also available as Visual Studio component C++ CMake tools for Windows) -* `NASM `_ +* x86/x64: `NASM `_ Any version of Visual Studio 2017 or newer should be supported, including Visual Studio 2017 Community, or Build Tools for Visual Studio 2019. @@ -42,8 +42,8 @@ behaviour of ``build_prepare.py``: If ``PYTHON`` is unset, the version of Python used to run ``build_prepare.py`` will be used. If only ``PYTHON`` is set, ``EXECUTABLE`` defaults to ``python.exe``. -* ``ARCHITECTURE`` is used to select a ``x86`` or ``x64`` build. By default, - uses same architecture as the version of Python used to run ``build_prepare.py``. +* ``ARCHITECTURE`` is used to select a ``x86``, ``x64`` or ``ARM64``build. + By default, uses same architecture as the version of Python used to run ``build_prepare.py``. is used. * ``PILLOW_BUILD`` can be used to override the ``winbuild\build`` directory path, used to store generated build scripts and compiled libraries. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 4d9f7921131..bebafff9f88 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -1,4 +1,5 @@ import os +import platform import shutil import struct import subprocess @@ -93,6 +94,7 @@ def cmd_msbuild( architectures = { "x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"}, "x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"}, + "ARM64": {"vcvars_arch": "x86_arm64", "msbuild_arch": "ARM64"}, } header = [ @@ -490,7 +492,10 @@ def build_pillow(): python_dir = os.environ.get("PYTHON") python_exe = os.environ.get("EXECUTABLE", "python.exe") architecture = os.environ.get( - "ARCHITECTURE", "x86" if struct.calcsize("P") == 4 else "x64" + "ARCHITECTURE", + "ARM64" + if platform.machine() == "ARM64" + else ("x86" if struct.calcsize("P") == 4 else "x64"), ) build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build")) sources_dir = "" From 22a1f6db5e097783885be99970ae45496fc296e8 Mon Sep 17 00:00:00 2001 From: Gabor Kertesz Date: Fri, 29 Oct 2021 12:20:56 +0200 Subject: [PATCH 0102/1186] lcms2: Update to VS2019 In order to enable win-arm64, VS2019 should be used, while other platforms should work with newer version as well. Tested on x64-win10. --- winbuild/build_prepare.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index bebafff9f88..01f1bac298b 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -225,21 +225,21 @@ def cmd_msbuild( "filename": "lcms2-2.13.tar.gz", "dir": "lcms2-2.13", "patch": { - r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { + r"Projects\VC2019\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always "MultiThreaded": "MultiThreadedDLL", # noqa: E501 # retarget to default toolset (selected by vcvarsall.bat) - "v141": "$(DefaultPlatformToolset)", # noqa: E501 + "v142": "$(DefaultPlatformToolset)", # noqa: E501 # retarget to latest (selected by vcvarsall.bat) - "10.0.17134.0": "$(WindowsSDKVersion)", # noqa: E501 + "10.0": "$(WindowsSDKVersion)", # noqa: E501 } }, "build": [ cmd_rmdir("Lib"), - cmd_rmdir(r"Projects\VC2017\Release"), - cmd_msbuild(r"Projects\VC2017\lcms2.sln", "Release", "Clean"), + cmd_rmdir(r"Projects\VC2019\Release"), + cmd_msbuild(r"Projects\VC2019\lcms2.sln", "Release", "Clean"), cmd_msbuild( - r"Projects\VC2017\lcms2.sln", "Release", "lcms2_static:Rebuild" + r"Projects\VC2019\lcms2.sln", "Release", "lcms2_static:Rebuild" ), cmd_xcopy("include", "{inc_dir}"), ], From a13ba2ee3b4b8719379bc86d3008512499381a97 Mon Sep 17 00:00:00 2001 From: Luke Granger-Brown Date: Tue, 1 Feb 2022 19:49:45 +0000 Subject: [PATCH 0103/1186] Support Python distributions where _tkinter is compiled in In some cases, the Tk library may have been directly compiled in or bundled into the main executable by the time Pillow runs, in which case __file__ isn't available (nor would it make sense to use, anyway). If __file__ is missing, then set the library path to None and rely on our Tk loader being able to find the function pointers within the main binary - we know we probably have it because we've managed to import it already. --- src/PIL/_tkinter_finder.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/PIL/_tkinter_finder.py b/src/PIL/_tkinter_finder.py index ba4d045e681..5253f075956 100644 --- a/src/PIL/_tkinter_finder.py +++ b/src/PIL/_tkinter_finder.py @@ -5,10 +5,15 @@ import warnings from tkinter import _tkinter as tk -if hasattr(sys, "pypy_find_executable"): - TKINTER_LIB = tk.tklib_cffi.__file__ -else: - TKINTER_LIB = tk.__file__ +try: + if hasattr(sys, "pypy_find_executable"): + TKINTER_LIB = tk.tklib_cffi.__file__ + else: + TKINTER_LIB = tk.__file__ +except AttributeError: + # _tkinter may be compiled directly into Python, in which case __file__ is + # not available. load_tkinter_funcs will check the binary first in any case. + TKINTER_LIB = None tk_version = str(tkinter.TkVersion) if tk_version == "8.4": From fb7edfda68a8972ec487eab845506fc08f25cae8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 11:49:31 +1100 Subject: [PATCH 0104/1186] Improved consistency of returning an image access object from load() --- Tests/test_file_eps.py | 9 +++++++++ Tests/test_file_gbr.py | 22 +++++++++++++++------- Tests/test_file_icns.py | 8 ++++++++ Tests/test_file_ico.py | 5 +++++ Tests/test_file_wal.py | 16 ++++++++++------ Tests/test_file_wmf.py | 6 ++++++ src/PIL/EpsImagePlugin.py | 12 ++++++------ src/PIL/GbrImagePlugin.py | 10 ++++------ src/PIL/IcnsImagePlugin.py | 9 +++++---- src/PIL/IcoImagePlugin.py | 2 +- src/PIL/ImageFile.py | 1 + src/PIL/WalImageFile.py | 13 +++++-------- src/PIL/WmfImagePlugin.py | 2 +- 13 files changed, 76 insertions(+), 39 deletions(-) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 4c0b96f7376..1790f4f7701 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -58,6 +58,15 @@ def test_sanity(): assert image2_scale2.format == "EPS" +@pytest.mark.skipif(not HAS_GHOSTSCRIPT, reason="Ghostscript not available") +def test_load(): + with Image.open(FILE1) as im: + assert im.load()[0, 0] == (255, 255, 255) + + # Test again now that it has already been loaded once + assert im.load()[0, 0] == (255, 255, 255) + + def test_invalid_file(): invalid_file = "Tests/images/flower.jpg" diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 8d7fcf14779..1ea8af8ee34 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -5,20 +5,28 @@ from .helper import assert_image_equal_tofile -def test_invalid_file(): - invalid_file = "Tests/images/flower.jpg" - - with pytest.raises(SyntaxError): - GbrImagePlugin.GbrImageFile(invalid_file) - - def test_gbr_file(): with Image.open("Tests/images/gbr.gbr") as im: assert_image_equal_tofile(im, "Tests/images/gbr.png") +def test_load(): + with Image.open("Tests/images/gbr.gbr") as im: + assert im.load()[0, 0] == (0, 0, 0, 0) + + # Test again now that it has already been loaded once + assert im.load()[0, 0] == (0, 0, 0, 0) + + def test_multiple_load_operations(): with Image.open("Tests/images/gbr.gbr") as im: im.load() im.load() assert_image_equal_tofile(im, "Tests/images/gbr.png") + + +def test_invalid_file(): + invalid_file = "Tests/images/flower.jpg" + + with pytest.raises(SyntaxError): + GbrImagePlugin.GbrImageFile(invalid_file) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 3afbbeaac05..b492f6cb227 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -28,6 +28,14 @@ def test_sanity(): assert im.format == "ICNS" +def test_load(): + with Image.open(TEST_FILE) as im: + assert im.load()[0, 0] == (0, 0, 0, 0) + + # Test again now that it has already been loaded once + assert im.load()[0, 0] == (0, 0, 0, 0) + + def test_save(tmp_path): temp_file = str(tmp_path / "temp.icns") diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index 317264db646..73ac6f74286 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -18,6 +18,11 @@ def test_sanity(): assert im.get_format_mimetype() == "image/x-icon" +def test_load(): + with Image.open(TEST_ICO_FILE) as im: + assert im.load()[0, 0] == (1, 1, 9, 255) + + def test_mask(): with Image.open("Tests/images/hopper_mask.ico") as im: assert_image_equal_tofile(im, "Tests/images/hopper_mask.png") diff --git a/Tests/test_file_wal.py b/Tests/test_file_wal.py index f25b42fe0c4..4be46e9d673 100644 --- a/Tests/test_file_wal.py +++ b/Tests/test_file_wal.py @@ -2,15 +2,11 @@ from .helper import assert_image_equal_tofile +TEST_FILE = "Tests/images/hopper.wal" -def test_open(): - # Arrange - TEST_FILE = "Tests/images/hopper.wal" - # Act +def test_open(): with WalImageFile.open(TEST_FILE) as im: - - # Assert assert im.format == "WAL" assert im.format_description == "Quake2 Texture" assert im.mode == "P" @@ -19,3 +15,11 @@ def test_open(): assert isinstance(im, WalImageFile.WalImageFile) assert_image_equal_tofile(im, "Tests/images/hopper_wal.png") + + +def test_load(): + with WalImageFile.open(TEST_FILE) as im: + assert im.load()[0, 0] == 122 + + # Test again now that it has already been loaded once + assert im.load()[0, 0] == 122 diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index 3f8bc96ccdd..d6769a24b0b 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -24,6 +24,12 @@ def test_load_raw(): assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref.png", 2.0) +def test_load(): + with Image.open("Tests/images/drawing.emf") as im: + if hasattr(Image.core, "drawwmf"): + assert im.load()[0, 0] == (255, 255, 255) + + def test_register_handler(tmp_path): class TestHandler: methodCalled = False diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index 5d99202807e..b67363beb53 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -329,12 +329,12 @@ def _find_offset(self, fp): def load(self, scale=1, transparency=False): # Load EPS via Ghostscript - if not self.tile: - return - self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) - self.mode = self.im.mode - self._size = self.im.size - self.tile = [] + if self.tile: + self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) + self.mode = self.im.mode + self._size = self.im.size + self.tile = [] + return Image.Image.load(self) def load_seek(self, *args, **kwargs): # we can't incrementally load, so force ImageFile.parser to diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index 0f230602db8..3d8fc47b224 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -84,12 +84,10 @@ def _open(self): self._data_size = width * height * color_depth def load(self): - if self.im: - # Already loaded - return - - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self._data_size)) + if not self.im: + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self._data_size)) + return Image.Image.load(self) # diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 6412d1cfb4b..069aff96b63 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -286,21 +286,22 @@ def load(self): self.best_size[1] * self.best_size[2], ) - Image.Image.load(self) + px = Image.Image.load(self) if self.im and self.im.size == self.size: # Already loaded - return + return px self.load_prepare() # This is likely NOT the best way to do it, but whatever. im = self.icns.getimage(self.best_size) # If this is a PNG or JPEG 2000, it won't be loaded yet - im.load() + px = im.load() self.im = im.im self.mode = im.mode self.size = im.size - self.load_end() + + return px def _save(im, fp, filename): diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index d9ff9b5e731..82837f3073f 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -306,7 +306,7 @@ def size(self, value): def load(self): if self.im and self.im.size == self.size: # Already loaded - return + return Image.Image.load(self) im = self.ico.getimage(self.size) # if tile is PNG, it won't really be loaded yet im.load() diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 3374a5b1dae..331410f0e1a 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -328,6 +328,7 @@ def load(self): # become the other object (!) self.__class__ = image.__class__ self.__dict__ = image.__dict__ + return image.load() def _load(self): """(Hook) Find actual image loader.""" diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index 1354ad32b52..0dc695a88d4 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -51,14 +51,11 @@ def _open(self): self.info["next_name"] = next_name def load(self): - if self.im: - # Already loaded - return - - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self.size[0] * self.size[1])) - self.putpalette(quake2palette) - Image.Image.load(self) + if not self.im: + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self.size[0] * self.size[1])) + self.putpalette(quake2palette) + return Image.Image.load(self) def open(filename): diff --git a/src/PIL/WmfImagePlugin.py b/src/PIL/WmfImagePlugin.py index 27f5d2f870c..c32cc52f8d0 100644 --- a/src/PIL/WmfImagePlugin.py +++ b/src/PIL/WmfImagePlugin.py @@ -158,7 +158,7 @@ def load(self, dpi=None): (x1 - x0) * self.info["dpi"] // self._inch, (y1 - y0) * self.info["dpi"] // self._inch, ) - super().load() + return super().load() def _save(im, fp, filename): From 8e3878dec2b300f5b495dd81989a77089747abee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 14:33:19 +1100 Subject: [PATCH 0105/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d6cd9d50f21..66d41739330 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ +- Enable arm64 for MSVC on Windows #5811 + [gaborkertesz-linaro, gaborkertesz] + +- Keep IPython/Jupyter text/plain output stable #5891 + [shamrin, radarhere] + - Raise an error when performing a negative crop #5972 [radarhere, hugovk] From e1c27358ba72a2a9f797e2eec81f90d9676c484c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 14:44:16 +1100 Subject: [PATCH 0106/1186] Switched to windows-latest --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 162522c0aa3..ca18309be98 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch] jobs: build: - runs-on: windows-2019 + runs-on: windows-latest name: Cygwin Python 3.8 steps: From 629340654c72f67baa8bedd61d9e38141c0222df Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:31:20 +1100 Subject: [PATCH 0107/1186] Switched to cygwin/cygwin-install-action --- .ci/after_success.sh | 4 +- .github/workflows/test-cygwin.yml | 63 +++++++++++-------------------- 2 files changed, 23 insertions(+), 44 deletions(-) diff --git a/.ci/after_success.sh b/.ci/after_success.sh index 53832c573dd..23a6fcd4d45 100755 --- a/.ci/after_success.sh +++ b/.ci/after_success.sh @@ -3,7 +3,7 @@ # gather the coverage data python3 -m pip install codecov if [[ $MATRIX_DOCKER ]]; then - coverage xml --ignore-errors + python3 -m coverage xml --ignore-errors else - coverage xml + python3 -m coverage xml fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index ca18309be98..022ef480848 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -8,73 +8,52 @@ jobs: name: Cygwin Python 3.8 steps: + - name: Fix line endings + run: | + git config --global core.autocrlf input + - name: Checkout Pillow uses: actions/checkout@v2 - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 + - name: Install Cygwin + uses: cygwin/cygwin-install-action@v1 with: - platform: x64 + platform: x86_64 packages: > - libfreetype-devel libimagequant-devel libjpeg-devel - liblcms2-devel libopenjp2-devel libraqm-devel - libtiff-devel libwebp-devel libxcb-devel libpng-devel - ImageMagick jpeg netpbm perl xorg-server-extra - xorg-server-common xinit subversion python38-setuptools - python38-devel python38-tkinter python38-wheel - python38-pip python38-olefile python38-numpy - python38-pytest python38-sphinx python38-packaging - python38-cffi python38-requests - install-dir: 'C:\tools\cygwin' - - - name: Clean up path - uses: egor-tensin/cleanup-path@v2 - with: - dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + ImageMagick python38-cffi python38-numpy python38-sip + python38-devel python38-tkinter ghostscript libfreetype-devel + libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel + libraqm-devel libtiff-devel libwebp-devel libxcb-devel + libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information - run: C:\tools\cygwin\bin\python3.8 .github/workflows/system-info.py + run: | + bash.exe -c "python3 .github/workflows/system-info.py" - name: Ensure Python dependencies are installed run: | - C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools - C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt - C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked + bash.exe -c "python3 -m pip install pip wheel setuptools" + bash.exe -c "python3 -m pip install -r requirements.txt" - name: Download extra test images run: | - C:\tools\cygwin\bin\dash -c "tr -d '\r' install_extra_test_images.sh" - C:\tools\cygwin\bin\dash -c "mv install_extra_test_images.sh depends/install_extra_test_images.sh" - C:\tools\cygwin\bin\dash -c "cd depends; dash install_extra_test_images.sh" + bash.exe -c "cd depends; dash install_extra_test_images.sh" - name: Build Pillow run: | - C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel + bash.exe -c "python3 setup.py bdist_wheel" - name: Install Pillow run: | - C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - - - name: Check Pillow configuration - run: | - C:\tools\cygwin\bin\python3.8 selftest.py + bash.exe -c "python3 -m pip install dist/*.whl" - name: Test Pillow run: | - C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos - C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" - C:\tools\cygwin\bin\chmod u+x .ci/test.sh - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 --ignore=Tests/test_numpy.py - - - name: Test the possibly-segfaulting NumPy tests separately - run: | - C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip + bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh - name: After success run: | - C:\tools\cygwin\bin\mv .ci/after_success.sh .ci/after_success.sh.dos - C:\tools\cygwin\bin\dash -c "tr -d '\r' <.ci/after_success.sh.dos >.ci/after_success.sh" - C:\tools\cygwin\bin\bash .ci/after_success.sh + bash.exe .ci/after_success.sh - name: Upload coverage uses: codecov/codecov-action@v2 From 728bc7d7789aeba50a6720282bf9b90da5389693 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:05:54 +1100 Subject: [PATCH 0108/1186] Added matrix to test Python 3.7 and 32-bit --- .github/workflows/test-cygwin.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 022ef480848..77d55682ffe 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -5,7 +5,15 @@ on: [push, pull_request, workflow_dispatch] jobs: build: runs-on: windows-latest - name: Cygwin Python 3.8 + strategy: + fail-fast: false + matrix: + python-minor-version: [7, 8] + architecture: ["x86", "x86_64"] + + timeout-minutes: 30 + + name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} steps: - name: Fix line endings @@ -18,12 +26,15 @@ jobs: - name: Install Cygwin uses: cygwin/cygwin-install-action@v1 with: - platform: x86_64 + platform: ${{ matrix.architecture }} packages: > - ImageMagick python38-cffi python38-numpy python38-sip - python38-devel python38-tkinter ghostscript libfreetype-devel - libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel - libraqm-devel libtiff-devel libwebp-devel libxcb-devel + ImageMagick python3${{ matrix.python-minor-version }}-cffi + python3${{ matrix.python-minor-version }}-devel + python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-sip + python3${{ matrix.python-minor-version }}-tkinter ghostscript + libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel + libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information @@ -60,7 +71,7 @@ jobs: with: file: ./coverage.xml flags: GHA_Cygwin - name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} + name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} - name: After failure if: failure() From 4be3b760f2de24770823f1f9d592618b197c116e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:26:38 +1100 Subject: [PATCH 0109/1186] Connected Cygwin jobs to mergify --- .github/mergify.yml | 1 + .github/workflows/test-cygwin.yml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.github/mergify.yml b/.github/mergify.yml index 8b289bda671..8dfa07f4ec5 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -8,6 +8,7 @@ pull_request_rules: - status-success=Docker Test Successful - status-success=Windows Test Successful - status-success=MinGW Test Successful + - status-success=Cygwin Test Successful - status-success=continuous-integration/appveyor/pr actions: merge: diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 77d55682ffe..b78e1b231f0 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -79,3 +79,11 @@ jobs: C:\tools\cygwin\bin\uname -a C:\tools\cygwin\bin\python3.8 -m pip list C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow + + success: + needs: build + runs-on: ubuntu-latest + name: Cygwin Test Successful + steps: + - name: Success + run: echo Cygwin Test Successful From 8d2c56ec1f85bec979bad2178309c8c9491bc1c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:09:49 +1100 Subject: [PATCH 0110/1186] Upload errors on failure --- .github/workflows/test-cygwin.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b78e1b231f0..f4fbfd1f869 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -62,6 +62,18 @@ jobs: run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + - name: Prepare to upload errors + if: failure() + run: | + mkdir -p Tests/errors + + - name: Upload errors + uses: actions/upload-artifact@v2 + if: failure() + with: + name: errors + path: Tests/errors + - name: After success run: | bash.exe .ci/after_success.sh @@ -73,13 +85,6 @@ jobs: flags: GHA_Cygwin name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} - - name: After failure - if: failure() - run: | - C:\tools\cygwin\bin\uname -a - C:\tools\cygwin\bin\python3.8 -m pip list - C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow - success: needs: build runs-on: ubuntu-latest From 05637393a62a79ce2e58e7458667cb8e8845a45a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:14:31 +1100 Subject: [PATCH 0111/1186] Install Pillow directly --- .ci/build_cygwin.sh | 8 +++++ .ci/install.sh | 55 ++++++++++++++++++------------- .github/workflows/test-cygwin.yml | 19 +++-------- 3 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 .ci/build_cygwin.sh diff --git a/.ci/build_cygwin.sh b/.ci/build_cygwin.sh new file mode 100644 index 00000000000..d621a53364f --- /dev/null +++ b/.ci/build_cygwin.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +python3 -m coverage erase +make clean +CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . +python3 selftest.py diff --git a/.ci/install.sh b/.ci/install.sh index efc57a6414a..ec427ff27d4 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -13,17 +13,20 @@ aptget_update() return 1 fi } -aptget_update || aptget_update retry || aptget_update retry +if [[ $(uname) != CYGWIN* ]]; then + aptget_update || aptget_update retry || aptget_update retry +fi set -e -sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ - ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ - cmake meson imagemagick libharfbuzz-dev libfribidi-dev +if [[ $(uname) != CYGWIN* ]]; then + sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ + ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ + cmake meson imagemagick libharfbuzz-dev libfribidi-dev + python3 -m pip install --upgrade pip +fi -python3 -m pip install --upgrade pip python3 -m pip install --upgrade wheel -PYTHONOPTIMIZE=0 python3 -m pip install cffi python3 -m pip install coverage python3 -m pip install defusedxml python3 -m pip install olefile @@ -32,24 +35,30 @@ python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma python3 -m pip install test-image-results -python3 -m pip install numpy - -# PyQt5 doesn't support PyPy3 -if [[ $GHA_PYTHON_VERSION == 3.* ]]; then - # arm64, ppc64le, s390x CPUs: - # "ERROR: Could not find a version that satisfies the requirement pyqt5" - sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools - python3 -m pip install pyqt5 -fi -# webp -pushd depends && ./install_webp.sh && popd +if [[ $(uname) != CYGWIN* ]]; then + PYTHONOPTIMIZE=0 python3 -m pip install cffi + python3 -m pip install numpy -# libimagequant -pushd depends && ./install_imagequant.sh && popd + # PyQt5 doesn't support PyPy3 + if [[ $GHA_PYTHON_VERSION == 3.* ]]; then + # arm64, ppc64le, s390x CPUs: + # "ERROR: Could not find a version that satisfies the requirement pyqt5" + sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools + python3 -m pip install pyqt5 + fi -# raqm -pushd depends && ./install_raqm.sh && popd + # webp + pushd depends && ./install_webp.sh && popd -# extra test images -pushd depends && ./install_extra_test_images.sh && popd + # libimagequant + pushd depends && ./install_imagequant.sh && popd + + # raqm + pushd depends && ./install_raqm.sh && popd + + # extra test images + pushd depends && ./install_extra_test_images.sh && popd +else + cd depends && ./install_extra_test_images.sh && cd .. +fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f4fbfd1f869..6e2dc5b596d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -41,24 +41,15 @@ jobs: run: | bash.exe -c "python3 .github/workflows/system-info.py" - - name: Ensure Python dependencies are installed + - name: Install dependencies run: | - bash.exe -c "python3 -m pip install pip wheel setuptools" - bash.exe -c "python3 -m pip install -r requirements.txt" + bash.exe .ci/install.sh - - name: Download extra test images + - name: Build run: | - bash.exe -c "cd depends; dash install_extra_test_images.sh" + bash.exe .ci/build_cygwin.sh - - name: Build Pillow - run: | - bash.exe -c "python3 setup.py bdist_wheel" - - - name: Install Pillow - run: | - bash.exe -c "python3 -m pip install dist/*.whl" - - - name: Test Pillow + - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From dd46100bdc7fbb6c2fb71008a49c40f081eb0c7c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Jan 2022 21:49:55 +1100 Subject: [PATCH 0112/1186] Restrict builtins within lambdas for ImageMath.eval --- Tests/test_imagemath.py | 12 ++++++++++-- src/PIL/ImageMath.py | 15 +++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 25811aa89d7..39d91eadea6 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -52,9 +52,17 @@ def test_ops(): assert pixel(ImageMath.eval("float(B)**33", images)) == "F 8589934592.0" -def test_prevent_exec(): +@pytest.mark.parametrize( + "expression", + ( + "exec('pass')", + "(lambda: exec('pass'))()", + "(lambda: (lambda: exec('pass'))())()", + ), +) +def test_prevent_exec(expression): with pytest.raises(ValueError): - ImageMath.eval("exec('pass')") + ImageMath.eval(expression) def test_logical(): diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index 4b6e4ccda3a..09d9898d750 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -240,11 +240,18 @@ def eval(expression, _dict={}, **kw): if hasattr(v, "im"): args[k] = _Operand(v) - code = compile(expression, "", "eval") - for name in code.co_names: - if name not in args and name != "abs": - raise ValueError(f"'{name}' not allowed") + compiled_code = compile(expression, "", "eval") + def scan(code): + for const in code.co_consts: + if type(const) == type(compiled_code): + scan(const) + + for name in code.co_names: + if name not in args and name != "abs": + raise ValueError(f"'{name}' not allowed") + + scan(compiled_code) out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args) try: return out.im From 8da80130dbc747f3954b4904247d26289fe722f9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 17 Jan 2022 08:59:17 +1100 Subject: [PATCH 0113/1186] In show_file, use os.remove to remove temporary images --- Tests/test_imageshow.py | 6 +- src/PIL/ImageShow.py | 145 +++++++++++++++++++++++++++++++--------- 2 files changed, 119 insertions(+), 32 deletions(-) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 02edfdfa1f7..bf19a603362 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -85,11 +85,13 @@ def test_ipythonviewer(): not on_ci() or is_win32(), reason="Only run on CIs; hangs on Windows CIs", ) -def test_file_deprecated(): +def test_file_deprecated(tmp_path): + f = str(tmp_path / "temp.jpg") for viewer in ImageShow._viewers: + hopper().save(f) with pytest.warns(DeprecationWarning): try: - viewer.show_file(file="test.jpg") + viewer.show_file(file=f) except NotImplementedError: pass with pytest.raises(TypeError): diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 2165da30732..7212baa1cb1 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -15,7 +15,6 @@ import shutil import subprocess import sys -import tempfile import warnings from shlex import quote @@ -180,16 +179,15 @@ def show_file(self, path=None, **options): path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") - fd, temp_path = tempfile.mkstemp() - with os.fdopen(fd, "w") as f: - f.write(path) - with open(temp_path) as f: - subprocess.Popen( - ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"], - shell=True, - stdin=f, - ) - os.remove(temp_path) + subprocess.call(["open", "-a", "Preview.app", path]) + subprocess.Popen( + [ + sys.executable, + "-c", + "import os, sys, time;time.sleep(20);os.remove(sys.argv[1])", + path, + ] + ) return 1 @@ -205,6 +203,16 @@ def get_command(self, file, **options): command = self.get_command_ex(file, **options)[0] return f"({command} {quote(file)}; rm -f {quote(file)})&" + +class XDGViewer(UnixViewer): + """ + The freedesktop.org ``xdg-open`` command. + """ + + def get_command_ex(self, file, **options): + command = executable = "xdg-open" + return command, executable + def show_file(self, path=None, **options): """ Display given file. @@ -223,28 +231,11 @@ def show_file(self, path=None, **options): path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") - fd, temp_path = tempfile.mkstemp() - with os.fdopen(fd, "w") as f: - f.write(path) - with open(temp_path) as f: - command = self.get_command_ex(path, **options)[0] - subprocess.Popen( - ["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f - ) - os.remove(temp_path) + subprocess.Popen(["xdg-open", path]) + os.remove(path) return 1 -class XDGViewer(UnixViewer): - """ - The freedesktop.org ``xdg-open`` command. - """ - - def get_command_ex(self, file, **options): - command = executable = "xdg-open" - return command, executable - - class DisplayViewer(UnixViewer): """ The ImageMagick ``display`` command. @@ -257,6 +248,32 @@ def get_command_ex(self, file, title=None, **options): command += f" -name {quote(title)}" return command, executable + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + args = ["display"] + if "title" in options: + args += ["-name", options["title"]] + args.append(path) + + subprocess.Popen(args) + os.remove(path) + return 1 + class GmDisplayViewer(UnixViewer): """The GraphicsMagick ``gm display`` command.""" @@ -266,6 +283,27 @@ def get_command_ex(self, file, **options): command = "gm display" return command, executable + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + subprocess.Popen(["gm", "display", path]) + os.remove(path) + return 1 + class EogViewer(UnixViewer): """The GNOME Image Viewer ``eog`` command.""" @@ -275,6 +313,27 @@ def get_command_ex(self, file, **options): command = "eog -n" return command, executable + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + subprocess.Popen(["eog", "-n", path]) + os.remove(path) + return 1 + class XVViewer(UnixViewer): """ @@ -290,6 +349,32 @@ def get_command_ex(self, file, title=None, **options): command += f" -name {quote(title)}" return command, executable + def show_file(self, path=None, **options): + """ + Display given file. + + Before Pillow 9.1.0, the first argument was ``file``. This is now deprecated, + and ``path`` should be used instead. + """ + if path is None: + if "file" in options: + warnings.warn( + "The 'file' argument is deprecated and will be removed in Pillow " + "10 (2023-07-01). Use 'path' instead.", + DeprecationWarning, + ) + path = options.pop("file") + else: + raise TypeError("Missing required argument: 'path'") + args = ["xv"] + if "title" in options: + args += ["-name", options["title"]] + args.append(path) + + subprocess.Popen(args) + os.remove(path) + return 1 + if sys.platform not in ("win32", "darwin"): # unixoids if shutil.which("xdg-open"): From 143032103c9f2d55a0a7960bd3e630cb72549e8a Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 18 Jan 2022 11:24:01 +1100 Subject: [PATCH 0114/1186] Updated formatting Co-authored-by: Hugo van Kemenade --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 7212baa1cb1..ccdb0b2a0e9 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -184,7 +184,7 @@ def show_file(self, path=None, **options): [ sys.executable, "-c", - "import os, sys, time;time.sleep(20);os.remove(sys.argv[1])", + "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", path, ] ) From 10c4f75aaa383bd9671e923e3b91d391ea12d781 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 08:58:12 +1100 Subject: [PATCH 0115/1186] Added delay after opening image with xdg-open --- src/PIL/ImageShow.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index ccdb0b2a0e9..f8829fc21e0 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -126,6 +126,16 @@ def show_file(self, path=None, **options): os.system(self.get_command(path, **options)) return 1 + def _remove_path_after_delay(self, path): + subprocess.Popen( + [ + sys.executable, + "-c", + "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", + path, + ] + ) + # -------------------------------------------------------------------- @@ -180,14 +190,7 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.call(["open", "-a", "Preview.app", path]) - subprocess.Popen( - [ - sys.executable, - "-c", - "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", - path, - ] - ) + self._remove_path_after_delay(path) return 1 @@ -232,7 +235,7 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.Popen(["xdg-open", path]) - os.remove(path) + self._remove_path_after_delay(path) return 1 From 596eaf35cc983fe73e2408e3ca7f3e1431fd06e3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 09:46:57 +1100 Subject: [PATCH 0116/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 66d41739330..fc94556528a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -32,6 +32,15 @@ Changelog (Pillow) - Remove readonly from Image.__eq__ #5930 [hugovk] +9.0.1 (2022-02-03) +------------------ + +- In show_file, use os.remove to remove temporary images. CVE-2022-24303 #6010 + [radarhere, hugovk] + +- Restrict builtins within lambdas for ImageMath.eval. CVE-2022-22817 #6009 + [radarhere] + 9.0.0 (2022-01-02) ------------------ From 8ef2d987ab652346126cf7e567eac7d2c0754940 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 09:48:56 +1100 Subject: [PATCH 0117/1186] Added release notes for 9.0.1 --- docs/releasenotes/9.0.1.rst | 23 +++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 24 insertions(+) create mode 100644 docs/releasenotes/9.0.1.rst diff --git a/docs/releasenotes/9.0.1.rst b/docs/releasenotes/9.0.1.rst new file mode 100644 index 00000000000..5d1b246bce9 --- /dev/null +++ b/docs/releasenotes/9.0.1.rst @@ -0,0 +1,23 @@ +9.0.1 +----- + +Security +======== + +This release addresses several security problems. + +:cve:`CVE-2022-24303`: If the path to the temporary directory on Linux or macOS +contained a space, this would break removal of the temporary image file after +``im.show()`` (and related actions), and potentially remove an unrelated file. This +been present since PIL. + +:cve:`CVE-2022-22817`: While Pillow 9.0 restricted top-level builtins available to +:py:meth:`PIL.ImageMath.eval`, it did not prevent builtins available to lambda +expressions. These are now also restricted. + +Other Changes +============= + +Pillow 9.0 added support for ``xdg-open`` as an image viewer, but there have been +reports that the temporary image file was removed too quickly to be loaded into the +final application. A delay has been added. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 8d1ad78379a..e9b11c220e8 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 9.0.1 9.0.0 8.4.0 8.3.2 From 6fded1ac97a5d2a588a7b60e49869525d04eb6f3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 14:07:51 +1100 Subject: [PATCH 0118/1186] Install cjpeg and djpeg --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6e2dc5b596d..2dcdecd3009 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -28,7 +28,7 @@ jobs: with: platform: ${{ matrix.architecture }} packages: > - ImageMagick python3${{ matrix.python-minor-version }}-cffi + ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-sip From 131212368d4f57f23b6c3a4520b3ca9545a57282 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 15:55:44 +1100 Subject: [PATCH 0119/1186] Install netpbm --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 2dcdecd3009..e169a31419e 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -35,7 +35,7 @@ jobs: python3${{ matrix.python-minor-version }}-tkinter ghostscript libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel - libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel + libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information run: | From 9d8f173e39458bb73107f90813c745717d87ea78 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 18:05:50 +1100 Subject: [PATCH 0120/1186] Updated libimagequant to 4.0.0 --- depends/install_imagequant.sh | 11 ++++++----- docs/installation.rst | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 774f2676750..31fc2adaabe 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,14 +1,15 @@ #!/bin/bash # install libimagequant -archive=libimagequant-2.17.0 +archive=libimagequant-4.0.0 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz -pushd $archive +pushd $archive/imagequant-sys -make shared -sudo cp libimagequant.so* /usr/lib/ -sudo cp libimagequant.h /usr/include/ +cargo install cargo-c +cargo cinstall --prefix=/usr --destdir=. +sudo cp usr/lib/libimagequant.so* /usr/lib/ +sudo cp usr/include/libimagequant.h /usr/include/ popd diff --git a/docs/installation.rst b/docs/installation.rst index 984a689c2fb..a6ed8f68198 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -187,7 +187,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-2.17.0** + * Pillow has been tested with libimagequant **2.6-4.0** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. From caf6fc60cac8ed59b8b2c361df1e9705be3d0504 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Feb 2022 03:19:49 +1100 Subject: [PATCH 0121/1186] Corrected sentence [ci skip] --- docs/releasenotes/9.0.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/9.0.1.rst b/docs/releasenotes/9.0.1.rst index 5d1b246bce9..c1feee088b6 100644 --- a/docs/releasenotes/9.0.1.rst +++ b/docs/releasenotes/9.0.1.rst @@ -9,7 +9,7 @@ This release addresses several security problems. :cve:`CVE-2022-24303`: If the path to the temporary directory on Linux or macOS contained a space, this would break removal of the temporary image file after ``im.show()`` (and related actions), and potentially remove an unrelated file. This -been present since PIL. +has been present since PIL. :cve:`CVE-2022-22817`: While Pillow 9.0 restricted top-level builtins available to :py:meth:`PIL.ImageMath.eval`, it did not prevent builtins available to lambda From 943479941578b78ed3f8ea6bd1e3031b1244f006 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Feb 2022 03:29:26 +1100 Subject: [PATCH 0122/1186] Updated lcms2 to 2.13.1 --- docs/installation.rst | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index a6ed8f68198..023d27ddde0 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -169,7 +169,7 @@ Many of Pillow's features require external libraries: * **littlecms** provides color management * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and - above uses liblcms2. Tested with **1.19** and **2.7-2.13**. + above uses liblcms2. Tested with **1.19** and **2.7-2.13.1**. * **libwebp** provides the WebP format. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 01f1bac298b..6a37ff8bb5d 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -221,9 +221,9 @@ def cmd_msbuild( # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.13/lcms2-2.13.tar.gz", - "filename": "lcms2-2.13.tar.gz", - "dir": "lcms2-2.13", + "url": SF_MIRROR + "/project/lcms/lcms/2.13/lcms2-2.13.1.tar.gz", + "filename": "lcms2-2.13.1.tar.gz", + "dir": "lcms2-2.13.1", "patch": { r"Projects\VC2019\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always From f3762905e08a96902f20391289d487b028e68f98 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 6 Feb 2022 08:32:37 +1100 Subject: [PATCH 0123/1186] Upgraded Python 3.10 image to VS 2022 --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index d525e4cfc07..f86500b4848 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,7 +12,7 @@ environment: matrix: - PYTHON: C:/Python310 ARCHITECTURE: x86 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - PYTHON: C:/Python37-x64 ARCHITECTURE: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 From 2d6dee1dae7c6f4ce639e1042c05152ef5d14c9e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 6 Feb 2022 07:34:15 -0500 Subject: [PATCH 0124/1186] CI: Try to get Cygwin workflow working. --- .github/workflows/test-cygwin.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index e169a31419e..3b133254206 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -11,7 +11,7 @@ jobs: python-minor-version: [7, 8] architecture: ["x86", "x86_64"] - timeout-minutes: 30 + timeout-minutes: 40 name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} @@ -31,11 +31,18 @@ jobs: ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-cython liblapack-devel gcc-g++ python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-tkinter ghostscript libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel + subversion make + + - name: Add Lapack to PATH + uses: egor-tensin/cleanup-path@v1 + with: + dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' - name: Build system information run: | @@ -45,10 +52,22 @@ jobs: run: | bash.exe .ci/install.sh + - name: Install a different NumPy + run: | + bash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*'" + + - name: Check imports + run: | + bash.exe -c "python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)'" + - name: Build run: | bash.exe .ci/build_cygwin.sh + - name: Rebase dlls + run: | + bash.exe -c '/usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe)' + - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh @@ -56,7 +75,7 @@ jobs: - name: Prepare to upload errors if: failure() run: | - mkdir -p Tests/errors + dash.exe -c "mkdir -p Tests/errors" - name: Upload errors uses: actions/upload-artifact@v2 From 5588f572de4cd386f576fc761e50d373cf1ca239 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 6 Feb 2022 12:03:36 -0500 Subject: [PATCH 0125/1186] BUG: Only set title in ImageShow.DisplayViewer when title provided --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f8829fc21e0..d76c2609016 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -269,7 +269,7 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") args = ["display"] - if "title" in options: + if "title" in options and options["title"] is not None: args += ["-name", options["title"]] args.append(path) From 718b72c0fd949ba6cf11bc5ffd936e772132231a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Feb 2022 08:13:20 +1100 Subject: [PATCH 0126/1186] Updated harfbuzz to 3.3.2 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 01f1bac298b..b00905b0936 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -280,9 +280,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.1.zip", - "filename": "harfbuzz-3.3.1.zip", - "dir": "harfbuzz-3.3.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.2.zip", + "filename": "harfbuzz-3.3.2.zip", + "dir": "harfbuzz-3.3.2", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 85b872deb650847f9b087db53b6d4bb963679653 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Feb 2022 10:18:14 +1100 Subject: [PATCH 0127/1186] Added unpacker from RGBA;15 to RGB --- src/libImaging/Unpack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 5dac95c1d01..4f9838fa8f6 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1529,6 +1529,7 @@ static struct { {"RGB", "RGBX", 32, copy4}, {"RGB", "RGBX;L", 32, unpackRGBAL}, {"RGB", "RGBA;L", 32, unpackRGBAL}, + {"RGB", "RGBA;15", 16, ImagingUnpackRGBA15}, {"RGB", "BGRX", 32, ImagingUnpackBGRX}, {"RGB", "XRGB", 32, ImagingUnpackXRGB}, {"RGB", "XBGR", 32, ImagingUnpackXBGR}, From c6b81d5989e3c0c01ccf683ed22df4c13e093165 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Feb 2022 19:15:25 +1100 Subject: [PATCH 0128/1186] Ensure Tkinter hook is activated for getimage() --- Tests/test_imagetk.py | 5 ++-- src/PIL/ImageTk.py | 59 +++++++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 928b8cbd188..5996183b3c3 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -75,8 +75,9 @@ def test_photoimage_blank(): assert im_tk.width() == 100 assert im_tk.height() == 100 - # reloaded = ImageTk.getimage(im_tk) - # assert_image_equal(reloaded, im) + im = Image.new(mode, (100, 100)) + reloaded = ImageTk.getimage(im_tk) + assert_image_equal(reloaded.convert(mode), im) def test_bitmapimage(): diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 62db7a717c6..8e540b05e96 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -58,6 +58,35 @@ def _get_image_from_kw(kw): return Image.open(source) +def _pyimagingtkcall(command, photo, id): + tk = photo.tk + try: + tk.call(command, photo, id) + except tkinter.TclError: + # activate Tkinter hook + try: + from . import _imagingtk + + try: + if hasattr(tk, "interp"): + # Required for PyPy, which always has CFFI installed + from cffi import FFI + + ffi = FFI() + + # PyPy is using an FFI CDATA element + # (Pdb) self.tk.interp + # + _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) + else: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call(command, photo, id) + except (ImportError, AttributeError, tkinter.TclError): + raise # configuration problem; cannot attach to Tkinter + + # -------------------------------------------------------------------- # PhotoImage @@ -170,33 +199,7 @@ def paste(self, im, box=None): block = image.new_block(self.__mode, im.size) image.convert2(block, image) # convert directly between buffers - tk = self.__photo.tk - - try: - tk.call("PyImagingPhoto", self.__photo, block.id) - except tkinter.TclError: - # activate Tkinter hook - try: - from . import _imagingtk - - try: - if hasattr(tk, "interp"): - # Required for PyPy, which always has CFFI installed - from cffi import FFI - - ffi = FFI() - - # PyPy is using an FFI CDATA element - # (Pdb) self.tk.interp - # - _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) - else: - _imagingtk.tkinit(tk.interpaddr(), 1) - except AttributeError: - _imagingtk.tkinit(id(tk), 0) - tk.call("PyImagingPhoto", self.__photo, block.id) - except (ImportError, AttributeError, tkinter.TclError): - raise # configuration problem; cannot attach to Tkinter + _pyimagingtkcall("PyImagingPhoto", self.__photo, block.id) # -------------------------------------------------------------------- @@ -276,7 +279,7 @@ def getimage(photo): im = Image.new("RGBA", (photo.width(), photo.height())) block = im.im - photo.tk.call("PyImagingPhotoGet", photo, block.id) + _pyimagingtkcall("PyImagingPhotoGet", photo, block.id) return im From 3114064b1690c62467ff7cb401c683aecade8f93 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 7 Feb 2022 21:06:36 +1100 Subject: [PATCH 0129/1186] Removed redundant try catch --- src/PIL/ImageTk.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 8e540b05e96..d151b9b0e2e 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -64,27 +64,25 @@ def _pyimagingtkcall(command, photo, id): tk.call(command, photo, id) except tkinter.TclError: # activate Tkinter hook + # may raise an error if it cannot attach to Tkinter + from . import _imagingtk + try: - from . import _imagingtk - - try: - if hasattr(tk, "interp"): - # Required for PyPy, which always has CFFI installed - from cffi import FFI - - ffi = FFI() - - # PyPy is using an FFI CDATA element - # (Pdb) self.tk.interp - # - _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) - else: - _imagingtk.tkinit(tk.interpaddr(), 1) - except AttributeError: - _imagingtk.tkinit(id(tk), 0) - tk.call(command, photo, id) - except (ImportError, AttributeError, tkinter.TclError): - raise # configuration problem; cannot attach to Tkinter + if hasattr(tk, "interp"): + # Required for PyPy, which always has CFFI installed + from cffi import FFI + + ffi = FFI() + + # PyPy is using an FFI CDATA element + # (Pdb) self.tk.interp + # + _imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1) + else: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call(command, photo, id) # -------------------------------------------------------------------- From 4d0e294eb0b5ede4a4c92293aa4ef2794c3b6025 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:00:37 -0500 Subject: [PATCH 0130/1186] CI: Revert changes to test.sh Some early runs needed to explicitly specify the minor version to avoid calling python3.9 which had none of the dependencies installed. That problem should be fixed. --- .ci/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index b0f019d6607..8ff7c5f6483 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,6 +2,6 @@ set -e -python3$1 -c "from PIL import Image" +python3 -c "from PIL import Image" -python3$1 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE +python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE From ecb64fe2103b9393fd5cf77d3965504e1749ce6d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Feb 2022 09:12:01 +1100 Subject: [PATCH 0131/1186] Allow 1 mode images to be inverted --- Tests/test_imageops.py | 1 + src/PIL/ImageOps.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 6aa1cf35edf..87fffa7b724 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -63,6 +63,7 @@ def test_sanity(): ImageOps.grayscale(hopper("L")) ImageOps.grayscale(hopper("RGB")) + ImageOps.invert(hopper("1")) ImageOps.invert(hopper("L")) ImageOps.invert(hopper("RGB")) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index b170e9d8cc9..86db777d98c 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -523,7 +523,7 @@ def invert(image): lut = [] for i in range(256): lut.append(255 - i) - return _lut(image, lut) + return image.point(lut) if image.mode == "1" else _lut(image, lut) def mirror(image): From a278e0aa656745122e76c21d1589e9c9762ca743 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 7 Feb 2022 23:57:35 +0000 Subject: [PATCH 0132/1186] issue warning if Raqm layout is requested, but Raqm is not available --- Tests/test_imagefont.py | 13 +++++++++++++ src/PIL/ImageFont.py | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0d423aab7be..3dcbf18d2dd 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1022,3 +1022,16 @@ def test_oom(test_file): font = ImageFont.truetype(BytesIO(f.read())) with pytest.raises(Image.DecompressionBombError): font.getmask("Test Text") + + +def test_raqm_missing_warning(monkeypatch): + monkeypatch.setattr(ImageFont.core, "HAVE_RAQM", False) + with pytest.warns(UserWarning) as record: + font = ImageFont.truetype( + FONT_PATH, FONT_SIZE, layout_engine=ImageFont.LAYOUT_RAQM + ) + assert font.layout_engine == ImageFont.LAYOUT_BASIC + assert str(record[-1].message) == ( + "Raqm layout was requested, but Raqm is not available. " + "Falling back to basic layout." + ) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 805c8fff96b..58bf46a32a5 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -169,6 +169,12 @@ def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None) if core.HAVE_RAQM: layout_engine = LAYOUT_RAQM elif layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM: + import warnings + + warnings.warn( + "Raqm layout was requested, but Raqm is not available. " + "Falling back to basic layout." + ) layout_engine = LAYOUT_BASIC self.layout_engine = layout_engine From 92371504316194883518dd5f0182ea01cbbab0f7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 8 Feb 2022 23:35:01 +1100 Subject: [PATCH 0133/1186] Added CentOS Stream 9 --- .github/workflows/test-docker.yml | 1 + docs/installation.rst | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 656df5e913c..db866774c27 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -20,6 +20,7 @@ jobs: arch, centos-7-amd64, centos-stream-8-amd64, + centos-stream-9-amd64, debian-10-buster-x86, debian-11-bullseye-x86, fedora-34-amd64, diff --git a/docs/installation.rst b/docs/installation.rst index 023d27ddde0..cf94f0c7252 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -455,6 +455,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| CentOS Stream 9 | 3.9 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Debian 11 Bullseye | 3.9 | x86 | From d7922d1e85186951dbd1e1ac856a0cb45c4a7e06 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Feb 2022 14:27:21 +1100 Subject: [PATCH 0134/1186] Updated macOS tested Pillow 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 cf94f0c7252..14164910739 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -496,11 +496,11 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.0.0 |arm | +| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.0.1 |arm | +----------------------------------+---------------------------+------------------+--------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | | +---------------------------+------------------+--------------+ -| | 3.7, 3.8, 3.9, 3.10 | 9.0.0 |x86-64 | +| | 3.7, 3.8, 3.9, 3.10 | 9.0.1 |x86-64 | | +---------------------------+------------------+--------------+ | | 3.6 | 8.4.0 |x86-64 | +----------------------------------+---------------------------+------------------+--------------+ From 601c9d8515dba996af3f0b96d1a671619de37f10 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 9 Feb 2022 14:28:43 +0200 Subject: [PATCH 0135/1186] Fix return in docs --- src/PIL/JpegImagePlugin.py | 1 + src/PIL/PngImagePlugin.py | 1 + src/PIL/TiffImagePlugin.py | 1 + 3 files changed, 3 insertions(+) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index ccdcc20a896..b9999bdaf7d 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -482,6 +482,7 @@ def getxmp(self): """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. + :returns: XMP tags in a dictionary. """ diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0f596f1fdb3..ae38b0ed38f 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -982,6 +982,7 @@ def getxmp(self): """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. + :returns: XMP tags in a dictionary. """ return ( diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e54082feceb..a3922d6994d 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1124,6 +1124,7 @@ def getxmp(self): """ Returns a dictionary containing the XMP tags. Requires defusedxml to be installed. + :returns: XMP tags in a dictionary. """ return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {} From bb5a090f60ed08941e5bdd7077f9a53caec99b57 Mon Sep 17 00:00:00 2001 From: Mikhail Iurkov Date: Wed, 9 Feb 2022 16:16:33 +0300 Subject: [PATCH 0136/1186] Drop excess values in BITSPERSAMPLE --- Tests/images/tiff_wrong_bits_per_sample_2.tiff | Bin 0 -> 1041 bytes Tests/test_file_tiff.py | 14 +++++++++----- src/PIL/TiffImagePlugin.py | 8 ++++++-- 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 Tests/images/tiff_wrong_bits_per_sample_2.tiff diff --git a/Tests/images/tiff_wrong_bits_per_sample_2.tiff b/Tests/images/tiff_wrong_bits_per_sample_2.tiff new file mode 100644 index 0000000000000000000000000000000000000000..d44176ce76ce43f738cf215db0b7974fe3e3ff2a GIT binary patch literal 1041 zcmebD)MDUZW*BkcKPmwFA1DAJAu^BopA1 len(bps_tuple) and len(bps_tuple) == 1: + # while should have more. Or have more values + # than expected. Fix it + bps_actual_count = len(bps_tuple) + if bps_count < bps_actual_count: + bps_tuple = bps_tuple[:bps_count] + elif bps_count > bps_actual_count and bps_actual_count == 1: bps_tuple = bps_tuple * bps_count samplesPerPixel = self.tag_v2.get( From 2bbf5f0981bbaa9a0ba8a5411b537389d3d6904f Mon Sep 17 00:00:00 2001 From: Mikhail Iurkov Date: Wed, 9 Feb 2022 16:53:27 +0300 Subject: [PATCH 0137/1186] Lint --- Tests/test_file_tiff.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index fd526089a78..55e2c8be3df 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -90,10 +90,13 @@ def test_mac_tiff(self): assert_image_similar_tofile(im, "Tests/images/pil136.png", 1) - @pytest.mark.parametrize("file_name,mode,w,h,offset", [ - ("tiff_wrong_bits_per_sample.tiff","RGBA",52,53,160), - ("tiff_wrong_bits_per_sample_2.tiff","RGB",16,16,8), - ]) + @pytest.mark.parametrize( + "file_name,mode,w,h,offset", + [ + ("tiff_wrong_bits_per_sample.tiff", "RGBA", 52, 53, 160), + ("tiff_wrong_bits_per_sample_2.tiff", "RGB", 16, 16, 8), + ], + ) def test_wrong_bits_per_sample(self, file_name, mode, w, h, offset): with Image.open("Tests/images/" + file_name) as im: assert im.mode == mode From 4e65e2c29de489ca098399c5c179168bd73ef17c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 09:47:43 +1100 Subject: [PATCH 0138/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fc94556528a..900346f74f5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ +- Added unpacker from RGBA;15 to RGB #6031 + [radarhere] + - Enable arm64 for MSVC on Windows #5811 [gaborkertesz-linaro, gaborkertesz] From e098481279477c0091d7947d589276afd587983d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 11:02:13 +1100 Subject: [PATCH 0139/1186] Combined width and height into size --- Tests/test_file_tiff.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 55e2c8be3df..d4ffe88bb88 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -91,17 +91,17 @@ def test_mac_tiff(self): assert_image_similar_tofile(im, "Tests/images/pil136.png", 1) @pytest.mark.parametrize( - "file_name,mode,w,h,offset", + "file_name,mode,size,offset", [ - ("tiff_wrong_bits_per_sample.tiff", "RGBA", 52, 53, 160), - ("tiff_wrong_bits_per_sample_2.tiff", "RGB", 16, 16, 8), + ("tiff_wrong_bits_per_sample.tiff", "RGBA", (52, 53), 160), + ("tiff_wrong_bits_per_sample_2.tiff", "RGB", (16, 16), 8), ], ) - def test_wrong_bits_per_sample(self, file_name, mode, w, h, offset): + def test_wrong_bits_per_sample(self, file_name, mode, size, offset): with Image.open("Tests/images/" + file_name) as im: assert im.mode == mode - assert im.size == (w, h) - assert im.tile == [("raw", (0, 0, w, h), offset, (mode, 0, 1))] + assert im.size == size + assert im.tile == [("raw", (0, 0) + size, offset, (mode, 0, 1))] im.load() def test_set_legacy_api(self): From 1f82202998db41f135878e96c2dfed1321323361 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 11:08:25 +1100 Subject: [PATCH 0140/1186] Adjusted comments --- src/PIL/TiffImagePlugin.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 307a08cea55..d6f23f11b8e 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1307,13 +1307,14 @@ def _setup(self): else: bps_count = 1 bps_count += len(extra_tuple) - # Some files have only one value in bps_tuple, - # while should have more. Or have more values - # than expected. Fix it bps_actual_count = len(bps_tuple) if bps_count < bps_actual_count: + # If a file has more values in bps_tuple than expected, + # remove the excess. bps_tuple = bps_tuple[:bps_count] elif bps_count > bps_actual_count and bps_actual_count == 1: + # If a file has only one value in bps_tuple, when it should have more, + # presume it is the same number of bits for all of the samples. bps_tuple = bps_tuple * bps_count samplesPerPixel = self.tag_v2.get( From 2ae70f144f144a9e24d0618e3d30375c6b46ed02 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 12:00:23 +1100 Subject: [PATCH 0141/1186] Added get_photoshop_blocks() to parse Photoshop tag --- Tests/test_file_tiff.py | 26 ++++++++++++++++++++++++++ src/PIL/TiffImagePlugin.py | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 5801e176636..21033cc5dd2 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -685,6 +685,32 @@ def test_getxmp(self): assert description[0]["format"] == "image/tiff" assert description[3]["BitsPerSample"]["Seq"]["li"] == ["8", "8", "8"] + def test_get_photoshop_blocks(self): + with Image.open("Tests/images/lab.tif") as im: + assert list(im.get_photoshop_blocks().keys()) == [ + 1061, + 1002, + 1005, + 1062, + 1037, + 1049, + 1011, + 1034, + 10000, + 1013, + 1016, + 1032, + 1054, + 1050, + 1064, + 1041, + 1044, + 1036, + 1057, + 4000, + 4001, + ] + def test_close_on_load_exclusive(self, tmp_path): # similar to test_fd_leak, but runs on unixlike os tmpfile = str(tmp_path / "temp.tif") diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index a3922d6994d..764739e90b5 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -41,6 +41,7 @@ import io import itertools import logging +import math import os import struct import warnings @@ -49,6 +50,8 @@ from numbers import Number, Rational from . import Image, ImageFile, ImageOps, ImagePalette, TiffTags +from ._binary import i16be as i16 +from ._binary import i32be as i32 from ._binary import o8 from .TiffTags import TYPES @@ -1129,6 +1132,27 @@ def getxmp(self): """ return self._getxmp(self.tag_v2[700]) if 700 in self.tag_v2 else {} + def get_photoshop_blocks(self): + """ + Returns a dictionary of Photoshop "Image Resource Blocks". + The keys are the image resource ID. For more information, see + https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037727 + + :returns: Photoshop "Image Resource Blocks" in a dictionary. + """ + blocks = {} + val = self.tag_v2.get(0x8649) + if val: + while val[:4] == b"8BIM": + id = i16(val[4:6]) + n = math.ceil((val[6] + 1) / 2) * 2 + size = i32(val[6 + n : 10 + n]) + data = val[10 + n : 10 + n + size] + blocks[id] = {"data": data} + + val = val[math.ceil((10 + n + size) / 2) * 2 :] + return blocks + def load(self): if self.tile and self.use_load_libtiff: return self._load_libtiff() From ef1373838dd9e8c35e490136c42a45d0ab6c534b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 12:15:25 +1100 Subject: [PATCH 0142/1186] Added release notes --- docs/releasenotes/9.1.0.rst | 16 ++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 17 insertions(+) create mode 100644 docs/releasenotes/9.1.0.rst diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst new file mode 100644 index 00000000000..9e4429b24a9 --- /dev/null +++ b/docs/releasenotes/9.1.0.rst @@ -0,0 +1,16 @@ +9.1.0 +----- + +API Additions +============= + +Added get_photoshop_blocks() to parse Photoshop TIFF tag +-------------------------------------------------------- + +:py:meth:`~PIL.TiffImagePlugin.TiffImageFile.get_photoshop_blocks` has been added, to +allow users to determine what Photoshop "Image Resource Blocks" are contained within an +image. The keys of the returned dictionary are the image resource IDs. + +At present, the information within each block is merely returned as a dictionary with a +"data" entry. This will allow more useful information to be added in the future without +breaking backwards compatibility. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index e9b11c220e8..656acef95d2 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 9.1.0 9.0.1 9.0.0 8.4.0 From 657ec4aa3dd253400ac72fdf1d8b44960d36df84 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 13:53:30 +1100 Subject: [PATCH 0143/1186] Added release notes for #5891 --- docs/releasenotes/9.1.0.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index 9e4429b24a9..734eb1d99ef 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -14,3 +14,13 @@ image. The keys of the returned dictionary are the image resource IDs. At present, the information within each block is merely returned as a dictionary with a "data" entry. This will allow more useful information to be added in the future without breaking backwards compatibility. + +Other Changes +============= + +Image._repr_pretty_ +------------------- + +`im._repr_pretty_` has been added to provide a representation of an image without the +identity of the object. This allows Jupyter to describe an image and have that +description stay the same on subsequent executions of the same code. From f797137ab56eb13da1381ce9154637f5beebcbb7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 12:33:42 +1100 Subject: [PATCH 0144/1186] Added release notes for #5972 --- docs/releasenotes/9.1.0.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index 734eb1d99ef..a7254c6f76f 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -1,6 +1,16 @@ 9.1.0 ----- +API Changes +=========== + +Raise an error when performing a negative crop +---------------------------------------------- + +Performing a negative crop on an image previously just returned a `(0, 0)` image. Now +it will raise a `ValueError`, to help reduce confusion if a user has unintentionally +provided the wrong arguments. + API Additions ============= From 912296200c3017d51cee52a19b6c906081fe704e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 12:41:36 +1100 Subject: [PATCH 0145/1186] Added release notes for #5942 --- CHANGES.rst | 2 +- docs/releasenotes/9.1.0.rst | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 900346f74f5..6785011227c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -26,7 +26,7 @@ Changelog (Pillow) - Ensure duplicated file pointer is closed #5946 [radarhere] -- Added specific error if ImagePath coordinate type is incorrect #5942 +- Added specific error if path coordinate type is incorrect #5942 [radarhere] - Return an empty bytestring from tobytes() for an empty image #5938 diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index a7254c6f76f..d0a8758bb74 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -11,6 +11,13 @@ Performing a negative crop on an image previously just returned a `(0, 0)` image it will raise a `ValueError`, to help reduce confusion if a user has unintentionally provided the wrong arguments. +Added specific error if path coordinate type is incorrect +--------------------------------------------------------- + +Rather than returning a `SystemError`, passing the incorrect types of coordinates into +a path will now raise a more specific `ValueError`, with the message "incorrect +coordinate type". + API Additions ============= From 8b6ee688d806e0ec7f3342582c41d8635ccf0a15 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 10 Feb 2022 13:56:27 +1100 Subject: [PATCH 0146/1186] Added release notes for #5959 --- docs/releasenotes/9.1.0.rst | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index d0a8758bb74..cbf9fe6e1bd 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -5,24 +5,37 @@ API Changes =========== Raise an error when performing a negative crop ----------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Performing a negative crop on an image previously just returned a `(0, 0)` image. Now -it will raise a `ValueError`, to help reduce confusion if a user has unintentionally +Performing a negative crop on an image previously just returned a ``(0, 0)`` image. Now +it will raise a ``ValueError``, to help reduce confusion if a user has unintentionally provided the wrong arguments. Added specific error if path coordinate type is incorrect ---------------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Rather than returning a `SystemError`, passing the incorrect types of coordinates into -a path will now raise a more specific `ValueError`, with the message "incorrect +Rather than returning a ``SystemError``, passing the incorrect types of coordinates into +a path will now raise a more specific ``ValueError``, with the message "incorrect coordinate type". +Deprecations +^^^^^^^^^^^^ + +ImageShow.Viewer.show_file file argument +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``file`` argument in :py:meth:`~PIL.ImageShow.Viewer.show_file()` has been +deprecated, replaced by ``path``. + +In effect, ``viewer.show_file("test.jpg")`` will continue to work unchanged. +``viewer.show_file(file="test.jpg")`` will raise a deprecation warning, and suggest +``viewer.show_file(path="test.jpg")`` instead. + API Additions ============= Added get_photoshop_blocks() to parse Photoshop TIFF tag --------------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :py:meth:`~PIL.TiffImagePlugin.TiffImageFile.get_photoshop_blocks` has been added, to allow users to determine what Photoshop "Image Resource Blocks" are contained within an @@ -36,8 +49,8 @@ Other Changes ============= Image._repr_pretty_ -------------------- +^^^^^^^^^^^^^^^^^^^ -`im._repr_pretty_` has been added to provide a representation of an image without the +``im._repr_pretty_`` has been added to provide a representation of an image without the identity of the object. This allows Jupyter to describe an image and have that description stay the same on subsequent executions of the same code. From 164632650696429818fe9ff087b1189d38875b7b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 11 Feb 2022 08:23:04 +1100 Subject: [PATCH 0147/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6785011227c..12a30361362 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ +- Added get_photoshop_blocks() to parse Photoshop TIFF tag #6030 + [radarhere] + +- Drop excess values in BITSPERSAMPLE #6041 + [mikhail-iurkov] + - Added unpacker from RGBA;15 to RGB #6031 [radarhere] From 70a17080e49d5accad8ec5e665f07ea76f21074b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Feb 2022 07:49:20 +1100 Subject: [PATCH 0148/1186] Updated return values to match docstring --- src/PIL/ImageShow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f8829fc21e0..c6829ad4b32 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -54,8 +54,8 @@ def show(image, title=None, **options): """ for viewer in _viewers: if viewer.show(image, title=title, **options): - return 1 - return 0 + return True + return False class Viewer: From 7f8df9d7125d4307421d066b1f44d5cc9ea6ab01 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 9 Feb 2022 16:21:47 +1100 Subject: [PATCH 0149/1186] Use "title" argument for display --- src/PIL/ImageShow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index c6829ad4b32..eafbff875f0 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -248,7 +248,7 @@ class DisplayViewer(UnixViewer): def get_command_ex(self, file, title=None, **options): command = executable = "display" if title: - command += f" -name {quote(title)}" + command += f" -title {quote(title)}" return command, executable def show_file(self, path=None, **options): @@ -270,7 +270,7 @@ def show_file(self, path=None, **options): raise TypeError("Missing required argument: 'path'") args = ["display"] if "title" in options: - args += ["-name", options["title"]] + args += ["-title", options["title"]] args.append(path) subprocess.Popen(args) From e19447cbbbe8fe7832e2826f399c4aa77815e94c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Feb 2022 08:04:40 +1100 Subject: [PATCH 0150/1186] Do not manually remove temporary files on Unix --- src/PIL/ImageShow.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index eafbff875f0..964ef57ea65 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -126,16 +126,6 @@ def show_file(self, path=None, **options): os.system(self.get_command(path, **options)) return 1 - def _remove_path_after_delay(self, path): - subprocess.Popen( - [ - sys.executable, - "-c", - "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", - path, - ] - ) - # -------------------------------------------------------------------- @@ -190,7 +180,14 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.call(["open", "-a", "Preview.app", path]) - self._remove_path_after_delay(path) + subprocess.Popen( + [ + sys.executable, + "-c", + "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", + path, + ] + ) return 1 @@ -235,7 +232,6 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.Popen(["xdg-open", path]) - self._remove_path_after_delay(path) return 1 @@ -274,7 +270,6 @@ def show_file(self, path=None, **options): args.append(path) subprocess.Popen(args) - os.remove(path) return 1 @@ -304,7 +299,6 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.Popen(["gm", "display", path]) - os.remove(path) return 1 @@ -334,7 +328,6 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.Popen(["eog", "-n", path]) - os.remove(path) return 1 @@ -375,7 +368,6 @@ def show_file(self, path=None, **options): args.append(path) subprocess.Popen(args) - os.remove(path) return 1 From 3ba9587675a80dcff958fa1c60de84f6b274b815 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Feb 2022 09:07:17 +1100 Subject: [PATCH 0151/1186] Added test --- Tests/test_imageshow.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index bf19a603362..55d7c94798f 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -51,6 +51,16 @@ def test_show(): assert ImageShow.show(im) +def test_show_without_viewers(): + viewers = ImageShow._viewers + ImageShow._viewers = [] + + im = hopper() + assert not ImageShow.show(im) + + ImageShow._viewers = viewers + + def test_viewer(): viewer = ImageShow.Viewer() From b818ad6103bed6d5231bf48a0f265c47bf99d2d1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 13 Feb 2022 21:58:46 +1100 Subject: [PATCH 0152/1186] Updated harfbuzz to 3.4.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 71aed705ae4..ade6203474e 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -280,9 +280,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/3.3.2.zip", - "filename": "harfbuzz-3.3.2.zip", - "dir": "harfbuzz-3.3.2", + "url": "https://github.com/harfbuzz/harfbuzz/archive/3.4.0.zip", + "filename": "harfbuzz-3.4.0.zip", + "dir": "harfbuzz-3.4.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 993bb23ce0e9cd1ee4a2e6f05a1225ff0df20e4f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Feb 2022 09:33:58 +1100 Subject: [PATCH 0153/1186] Do not manually remove temporary files on Unix in get_command() --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 964ef57ea65..f7920692159 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -201,7 +201,7 @@ class UnixViewer(Viewer): def get_command(self, file, **options): command = self.get_command_ex(file, **options)[0] - return f"({command} {quote(file)}; rm -f {quote(file)})&" + return f"({command} {quote(file)}" class XDGViewer(UnixViewer): From 45534d130b2727d59681ae0dbb6ee60d484043d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Feb 2022 12:12:33 +1100 Subject: [PATCH 0154/1186] Only skip test if libimagequant is earlier than 4 on ppc64le --- Tests/test_image_quantize.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 53b6c900793..16cb8b41a13 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -1,8 +1,9 @@ import pytest +from packaging.version import parse as parse_version -from PIL import Image +from PIL import Image, features -from .helper import assert_image_similar, hopper, is_ppc64le +from .helper import assert_image_similar, hopper, is_ppc64le, skip_unless_feature def test_sanity(): @@ -17,16 +18,14 @@ def test_sanity(): assert_image_similar(converted.convert("RGB"), image, 60) -@pytest.mark.xfail(is_ppc64le(), reason="failing on ppc64le on GHA") +@skip_unless_feature("libimagequant") def test_libimagequant_quantize(): image = hopper() - try: - converted = image.quantize(100, Image.LIBIMAGEQUANT) - except ValueError as ex: # pragma: no cover - if "dependency" in str(ex).lower(): - pytest.skip("libimagequant support not available") - else: - raise + if is_ppc64le(): + libimagequant = parse_version(features.version_feature("libimagequant")) + if libimagequant < parse_version("4"): + pytest.skip("Fails with libimagequant earlier than 4.0.0 on ppc64le") + converted = image.quantize(100, Image.LIBIMAGEQUANT) assert converted.mode == "P" assert_image_similar(converted.convert("RGB"), image, 15) assert len(converted.getcolors()) == 100 From 83d4f451fa0f38aea058084b38d0f32b74f93da3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Feb 2022 16:10:59 +1100 Subject: [PATCH 0155/1186] Ensure image is opaque after converting P to PA with RGB palette --- Tests/test_image_convert.py | 7 ++++ src/libImaging/Convert.c | 82 ++++++++++++++++++------------------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index a5a95e96255..4afb80a22a1 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -76,6 +76,13 @@ def test_16bit_workaround(): _test_float_conversion(im.convert("I")) +def test_opaque(): + alpha = hopper("P").convert("PA").getchannel("A") + + solid = Image.new("L", (128, 128), 255) + assert_image_equal(alpha, solid) + + def test_rgba_p(): im = hopper("RGBA") im.putalpha(hopper("L")) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 517a4dbe363..c899a996c29 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -991,115 +991,115 @@ static struct { /* ------------------- */ static void -p2bit(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++) { - *out++ = (L(&palette[in[x] * 4]) >= 128000) ? 255 : 0; + *out++ = (L(&palette->palette[in[x] * 4]) >= 128000) ? 255 : 0; } } static void -pa2bit(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2bit(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++, in += 4) { - *out++ = (L(&palette[in[0] * 4]) >= 128000) ? 255 : 0; + *out++ = (L(&palette->palette[in[0] * 4]) >= 128000) ? 255 : 0; } } static void -p2l(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++) { - *out++ = L24(&palette[in[x] * 4]) >> 16; + *out++ = L24(&palette->palette[in[x] * 4]) >> 16; } } static void -pa2l(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++, in += 4) { - *out++ = L24(&palette[in[0] * 4]) >> 16; + *out++ = L24(&palette->palette[in[0] * 4]) >> 16; } } static void -p2pa(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, in++) { - const UINT8 *rgba = &palette[in[0]]; + const UINT8 *rgba = &palette->palette[in[0]]; *out++ = in[0]; *out++ = in[0]; *out++ = in[0]; - *out++ = rgba[3]; + *out++ = strcmp(palette->mode, "RGB") == 0 ? 255 : rgba[3]; } } static void -p2la(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++, out += 4) { - const UINT8 *rgba = &palette[*in++ * 4]; + const UINT8 *rgba = &palette->palette[*in++ * 4]; out[0] = out[1] = out[2] = L24(rgba) >> 16; out[3] = rgba[3]; } } static void -pa2la(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2la(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++, in += 4, out += 4) { - out[0] = out[1] = out[2] = L24(&palette[in[0] * 4]) >> 16; + out[0] = out[1] = out[2] = L24(&palette->palette[in[0] * 4]) >> 16; out[3] = in[3]; } } static void -p2i(UINT8 *out_, const UINT8 *in, int xsize, const UINT8 *palette) { +p2i(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, out_ += 4) { - INT32 v = L24(&palette[in[x] * 4]) >> 16; + INT32 v = L24(&palette->palette[in[x] * 4]) >> 16; memcpy(out_, &v, sizeof(v)); } } static void -pa2i(UINT8 *out_, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2i(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) { int x; INT32 *out = (INT32 *)out_; for (x = 0; x < xsize; x++, in += 4) { - *out++ = L24(&palette[in[0] * 4]) >> 16; + *out++ = L24(&palette->palette[in[0] * 4]) >> 16; } } static void -p2f(UINT8 *out_, const UINT8 *in, int xsize, const UINT8 *palette) { +p2f(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, out_ += 4) { - FLOAT32 v = L(&palette[in[x] * 4]) / 1000.0F; + FLOAT32 v = L(&palette->palette[in[x] * 4]) / 1000.0F; memcpy(out_, &v, sizeof(v)); } } static void -pa2f(UINT8 *out_, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2f(UINT8 *out_, const UINT8 *in, int xsize, ImagingPalette palette) { int x; FLOAT32 *out = (FLOAT32 *)out_; for (x = 0; x < xsize; x++, in += 4) { - *out++ = (float)L(&palette[in[0] * 4]) / 1000.0F; + *out++ = (float)L(&palette->palette[in[0] * 4]) / 1000.0F; } } static void -p2rgb(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2rgb(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++) { - const UINT8 *rgb = &palette[*in++ * 4]; + const UINT8 *rgb = &palette->palette[*in++ * 4]; *out++ = rgb[0]; *out++ = rgb[1]; *out++ = rgb[2]; @@ -1108,10 +1108,10 @@ p2rgb(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { } static void -pa2rgb(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2rgb(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, in += 4) { - const UINT8 *rgb = &palette[in[0] * 4]; + const UINT8 *rgb = &palette->palette[in[0] * 4]; *out++ = rgb[0]; *out++ = rgb[1]; *out++ = rgb[2]; @@ -1120,30 +1120,30 @@ pa2rgb(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { } static void -p2hsv(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2hsv(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, out += 4) { - const UINT8 *rgb = &palette[*in++ * 4]; + const UINT8 *rgb = &palette->palette[*in++ * 4]; rgb2hsv_row(out, rgb); out[3] = 255; } } static void -pa2hsv(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2hsv(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, in += 4, out += 4) { - const UINT8 *rgb = &palette[in[0] * 4]; + const UINT8 *rgb = &palette->palette[in[0] * 4]; rgb2hsv_row(out, rgb); out[3] = 255; } } static void -p2rgba(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2rgba(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++) { - const UINT8 *rgba = &palette[*in++ * 4]; + const UINT8 *rgba = &palette->palette[*in++ * 4]; *out++ = rgba[0]; *out++ = rgba[1]; *out++ = rgba[2]; @@ -1152,10 +1152,10 @@ p2rgba(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { } static void -pa2rgba(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2rgba(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; for (x = 0; x < xsize; x++, in += 4) { - const UINT8 *rgb = &palette[in[0] * 4]; + const UINT8 *rgb = &palette->palette[in[0] * 4]; *out++ = rgb[0]; *out++ = rgb[1]; *out++ = rgb[2]; @@ -1164,25 +1164,25 @@ pa2rgba(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { } static void -p2cmyk(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2cmyk(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { p2rgb(out, in, xsize, palette); rgb2cmyk(out, out, xsize); } static void -pa2cmyk(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2cmyk(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { pa2rgb(out, in, xsize, palette); rgb2cmyk(out, out, xsize); } static void -p2ycbcr(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +p2ycbcr(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { p2rgb(out, in, xsize, palette); ImagingConvertRGB2YCbCr(out, out, xsize); } static void -pa2ycbcr(UINT8 *out, const UINT8 *in, int xsize, const UINT8 *palette) { +pa2ycbcr(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { pa2rgb(out, in, xsize, palette); ImagingConvertRGB2YCbCr(out, out, xsize); } @@ -1192,7 +1192,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) { ImagingSectionCookie cookie; int alpha; int y; - void (*convert)(UINT8 *, const UINT8 *, int, const UINT8 *); + void (*convert)(UINT8 *, const UINT8 *, int, ImagingPalette); /* Map palette image to L, RGB, RGBA, or CMYK */ @@ -1241,7 +1241,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) { (UINT8 *)imOut->image[y], (UINT8 *)imIn->image[y], imIn->xsize, - imIn->palette->palette); + imIn->palette); } ImagingSectionLeave(&cookie); From 5411263d9276cb8af4a5ea6ab731f7c8cc1f037f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Feb 2022 19:24:47 +1100 Subject: [PATCH 0156/1186] Simplified code --- src/libImaging/Convert.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 517a4dbe363..2cec2a57881 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1216,9 +1216,7 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) { convert = alpha ? pa2f : p2f; } else if (strcmp(mode, "RGB") == 0) { convert = alpha ? pa2rgb : p2rgb; - } else if (strcmp(mode, "RGBA") == 0) { - convert = alpha ? pa2rgba : p2rgba; - } else if (strcmp(mode, "RGBX") == 0) { + } else if (strcmp(mode, "RGBA") == 0 || strcmp(mode, "RGBX") == 0) { convert = alpha ? pa2rgba : p2rgba; } else if (strcmp(mode, "CMYK") == 0) { convert = alpha ? pa2cmyk : p2cmyk; From 9cdb0508b6cbd3a3061017760a5eab4d13c3924a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Feb 2022 20:28:47 +1100 Subject: [PATCH 0157/1186] Attach RGBA palettes from putpalette() when suitable --- Tests/test_image_putpalette.py | 13 +++++++++++++ src/PIL/Image.py | 17 +++++++---------- src/_imaging.c | 7 ++++--- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 012a57a0999..725ecaade01 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -62,3 +62,16 @@ def test_putpalette_with_alpha_values(): im.putpalette(palette_with_alpha_values, "RGBA") assert_image_equal(im.convert("RGBA"), expected) + + +@pytest.mark.parametrize( + "mode, palette", + ( + ("RGBA", (1, 2, 3, 4)), + ("RGBAX", (1, 2, 3, 4, 0)), + ), +) +def test_rgba_palette(mode, palette): + im = Image.new("P", (1, 1)) + im.putpalette(palette, mode) + assert im.palette.colors == {(1, 2, 3, 4): 0} diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 02b71e612a0..8d36e8871b0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -821,12 +821,6 @@ def load(self): if self.im and self.palette and self.palette.dirty: # realize palette mode, arr = self.palette.getdata() - if mode == "RGBA": - mode = "RGB" - self.info["transparency"] = arr[3::4] - arr = bytes( - value for (index, value) in enumerate(arr) if index % 4 != 3 - ) palette_length = self.im.putpalette(mode, arr) self.palette.dirty = 0 self.palette.rawmode = None @@ -837,8 +831,11 @@ def load(self): self.im.putpalettealphas(self.info["transparency"]) self.palette.mode = "RGBA" else: - self.palette.mode = "RGB" - self.palette.palette = self.im.getpalette()[: palette_length * 3] + palette_mode = "RGBA" if mode.startswith("RGBA") else "RGB" + self.palette.mode = palette_mode + self.palette.palette = self.im.getpalette(palette_mode, palette_mode)[ + : palette_length * len(palette_mode) + ] if self.im: if cffi and USE_CFFI_ACCESS: @@ -1761,8 +1758,8 @@ def putpalette(self, data, rawmode="RGB"): Alternatively, an 8-bit string may be used instead of an integer sequence. :param data: A palette sequence (either a list or a string). - :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a - mode that can be transformed to "RGB" (e.g. "R", "BGR;15", "RGBA;L"). + :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a mode + that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "RGBA;L"). """ from . import ImagePalette diff --git a/src/_imaging.c b/src/_imaging.c index 2a42c046109..2ea517816a0 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1641,7 +1641,7 @@ _putpalette(ImagingObject *self, PyObject *args) { ImagingShuffler unpack; int bits; - char *rawmode; + char *rawmode, *palette_mode; UINT8 *palette; Py_ssize_t palettesize; if (!PyArg_ParseTuple(args, "sy#", &rawmode, &palette, &palettesize)) { @@ -1654,7 +1654,8 @@ _putpalette(ImagingObject *self, PyObject *args) { return NULL; } - unpack = ImagingFindUnpacker("RGB", rawmode, &bits); + palette_mode = strncmp("RGBA", rawmode, 4) == 0 ? "RGBA" : "RGB"; + unpack = ImagingFindUnpacker(palette_mode, rawmode, &bits); if (!unpack) { PyErr_SetString(PyExc_ValueError, wrong_raw_mode); return NULL; @@ -1669,7 +1670,7 @@ _putpalette(ImagingObject *self, PyObject *args) { strcpy(self->image->mode, strlen(self->image->mode) == 2 ? "PA" : "P"); - self->image->palette = ImagingPaletteNew("RGB"); + self->image->palette = ImagingPaletteNew(palette_mode); unpack(self->image->palette->palette, palette, palettesize * 8 / bits); From 41a997537791c76463058da1ac6632fcf5a9f8bc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 14 Feb 2022 21:50:19 +1100 Subject: [PATCH 0158/1186] Moved strcmp outside of loop --- src/libImaging/Convert.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index c899a996c29..57c66d2cc28 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1029,12 +1029,13 @@ pa2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { static void p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; + int rgb = strcmp(palette->mode, "RGB"); for (x = 0; x < xsize; x++, in++) { const UINT8 *rgba = &palette->palette[in[0]]; *out++ = in[0]; *out++ = in[0]; *out++ = in[0]; - *out++ = strcmp(palette->mode, "RGB") == 0 ? 255 : rgba[3]; + *out++ = rgb == 0 ? 255 : rgba[3]; } } From dfdb17671d5579f5ca475e02bbb08e6f6f274695 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Feb 2022 11:22:46 +1100 Subject: [PATCH 0159/1186] Added FITS reading --- Tests/images/hopper.fits | Bin 54720 -> 20160 bytes ...est_file_fitsstub.py => test_file_fits.py} | 32 +++---------- docs/handbook/image-file-formats.rst | 15 ++---- docs/reference/plugins.rst | 4 +- ...sStubImagePlugin.py => FitsImagePlugin.py} | 45 ++++-------------- src/PIL/__init__.py | 2 +- 6 files changed, 23 insertions(+), 75 deletions(-) rename Tests/{test_file_fitsstub.py => test_file_fits.py} (51%) rename src/PIL/{FitsStubImagePlugin.py => FitsImagePlugin.py} (63%) diff --git a/Tests/images/hopper.fits b/Tests/images/hopper.fits index 85afa4ac1671b9ce2b33fe50d148df9dc4604a46..7f28f75e5a892f1200baf2af6da5053f8d764457 100644 GIT binary patch literal 20160 zcmeHuXLwZSweGzqi9u8M-qZH<8I2kU0YY>PrW%9ky%+V~d+*X{G}1_;-g_5FAbKx0 z7=z;yJF%UFxZybN9SrE+_uC_!+~@Hm#(*b>xyOnr$4->&*Ws4=ggV^#<*YjdikDJTQ=hJf9f~$zjeKp%eF0B zziijPTyNI&Idh1D(C@$C5AJ$vHvG%={(Jg8w`S|Mjazmbg5%?(;}*`C5fX7YC;&ep zeqqz2VuOBxq5u4&RU20R=fCjlx4$y*D+9kW@GAqqGVm({|KBmtU$vN`1R{|@C=?2W zA~F7p#S{cjL>MP#5+jre#6kg|k6(a@DVmYU`9hf!C6UNvG6gG_P+~?VlgboIR>rc7 zmStrMg-Ioqs^l7lNT!g`5{XpeL{n1yEki>j0;xzUT-VZ5yGl%ngd$v=PXHt$r%61a zSWJLKz~uc#LQ5y{84=Wj5Cxzf&4{HGBch;}n3l*Dz>`WCsZ1_cD>MeRLdvR*YANfi zr{prZP9~A6Xqk$V2!sNHP5z#tq#_CB+t}H(RVb$T4*MOKrzH%<83|1(2_<)O5<@Xg z1i<5jBBt;Ze9%Nuj8vhNNx+FhCY38BDpsM=s1-_`mQ|>g3f4f&W$j60+JSRT!1La~170w=jxBqq+3E7hz_A~!8qZ#_9s zw`Jqz_>L=n)lIMOjq0_A?>V@2{nlOE_vg9Ollfv$Pc*=zf|fMb^>$fgKo>uWKcI5Z zfD!U}0ud#a8eF{QiXGZfoVg@CCF(7UmN*MITO?pzA(zV}?39HWM@O4BY}jn>yzspJ z#78Uk4%Nl&+PP!P=4~6c6d7otR7^1v4xeHajJBa+ptDIYppgJDm4p^gLN_9j(j(2DP4jIMX@@E$v6iN|xG$WNF{A8?*mMDF82Knwz+_^uo z!IGR&6Bbh(vD1IYq3zFa+Povh&52LLeg{4+Wu((<8izW1+{Fw$jq4Ns={W~tqM|a& z%Bvb$J9~#mdk4C@I$IizgnZ%=4jsOu#*W5rD4!~pGO!47uaQgStW3r#W^4#P`}IhO z+5c2Vd17DqndFesvaEyqcP3W1Z)Zd_aXoop7@0`+w7sFXw`(OW7JzPq9|^#9IY-`m zr#iK$p{c!VcyR3a(V@Pf?s{k9doJ%7kxD^nlxwb^Z!EDi(Q*+|S}B#XDy2e>D5RC1 zTY7#N3AY3e$L0I?_??LizgnEL>+t5Hp5`4C584tRk||qX%rx09Kn|_ z8v4WUuimXG9lY?;&6lp+c=7W26PN0?Oh%G&_!6j&bfXJzymPv!>FP$%E@kC%xkBl{ zS4tSo{^pP<->u2MfxF^2XIuAW1@GIiYOQ}n$Wls35*rF2{%8d)JzP`Y*U=RxrT|RP zA(K4&&%O1*%N@3!k<(+Rj-NhtWZ-DSMhRDSkn6NWC}lvl=kcq5d$&Kg)GU`NP+5oq ztO7wOW8`{9Dwoyo*tu)ho;`c_`;JVNG6FerOvFl^#9~76gehWKQdRXpdxwQfb$mf| z;2Fwac<}J`G<#ibd+*6pXOAE6A3V%Fg};P{$e#)sC99D5pLqBC&)#eZOZAqr3MKGm zqzD3EFA+<16l2}Cd+XLcyLbBS9@QZ@!I^|lQ#=7FWaKhpFd<;gmG8 ztXw7&YoL!@wQk|El`B`SSibVP7=TN7QV`700x``6I?(_%)>Knfd!(zaMai3pJ^)M> z<&~7CM(x}7+?**=M#z&;67gi26Hyt_FBA!sd|IkofBNlj9z1^hn!3T>&Q=dTg}Ocwe@+4wpO2D2 zQF0y}#h)w?$rZn45q$Us6bYEa8ndteb^P(8$K&7ru_e|dRj3gDETSL7p@LCpRSLCA zE~P}1#TaoQKUAwYcgH7DXfwS60_n_w{wR`Up^fC-C_^$?sT-2VDh{$s!SN zvK+1vG01z)|CCgqVwLjxD<3|3Fg`y1@WC4wmZ&utb2LhYLWZPfDWy!Rlu8iYe1!;w zLL?PSp%?}Vm_X{OV`xRmV>VaS*^hR0H!X#3Tu^Wf6RL@sgl_?#xSR_Ne2m|r7@kb6 zre;*$`Q-8V{l|~SzqxZ%rqa?X1*;_Pm#b7t7J>&Eu0S!QETck7xT1$9f?SK#axhE# zRh3tb_H{R{bGQVRpM1+HBa+Za6|O8!;Nx@dJ1HuTl$u)g%Kb+m;HU9VUwd9AM+HFs z5qu;eCx}TYY^4wkDEAYC4#pJD7J>&>fg1-`jR8~Q=8d=~i5&)0JAARxOQU+B~A_X+T zhadsCbVOSQe9(X)ic7Kro}6}F1e!H$MZ=;^Rez~}G?7JP)y{)x{8C;|Xb z=%s|T=ICF>$M28-^x(6<#4E+5uRt#d24oW0iIj&^ScP=L@o3FB1t>A0fcRA;aGGCU zS!N#_=x)f7a$!aS?q__5I|%;Il#ss?3mG{>u}VaF@a?Y<0goPi{nx!J466hm=)fWU z6`_Mg41fyggNm#mVF+-}2e>4k&)a3GD62Zz*WZ|};&Hy6;2M}<3BLnu5(5(@i)e&^ zrj;~b=p;e?%pSP$$2)Jlbh=qfOVLh|E`;DqFt(y0KwnH^z=4l$82BUzNc%)^gaUbL zX?aEEiSB{MOuYaj5TKy=gt?po4t%cjArV5{4{kML3N?_G$Y@p|_t2T90aA+o7u`4T z9Z|@Q1>D0EeDsHKii9C?LWy?FFk!Ay~a{TFF~WH z^e1Wop@pJVgI|36{rH3N2gMuL_?UDU=_vO*4^W2_6qpAxARNMJ;7CP>^D!Pz5P%Mn ztH|4mD~qkAX4|zAN1_#VHCFqYCMkkc%%F4>DmJ)MG5Q?Wr#1wflloS2{ zeJ&9$1UP)eA1MMNWG){qG?V(lSK|o4@5=KVtL=p)xdqi9-T#sF0S^Ym0KiCy+{N=m z8jzVG*PIf^pgR|9C@ty4pM8Qqm1bmUh3fW;o>J00hz}mI&-2mA*&$xdFTB3SmLIV*ZQpit z+O`iLJ^%sVWg&DNwUeYW%?*cOo>U{~f^sO6Vm(5SD3Co}Wwly4e5o84rs(c78-?s- z1lm9C=X~%pKKUH<^uG7thw-0Y^sDHwq{kjy?!SNAo}7g3?|<{?@%_(lNO&Yj;V(iK z=Gnmh8D2%Q5bdD`sUVw`S;66#l$0T`@whr`1n1Am@J`@bcF#l7kek3#*(s*{Mu@)8C{K5%(Z2Ww=~J@0<^zfAWvN|6>Jk0ng(>0hyeUC|Me#lSHAyxTsdKQ?_K*Rod(R16% z%dF){KcWBzWr~vOx&|%`^q;;kxOVO=Pm@|I;$iKH7>?8w^PNPL6D~Z-Rwco-k?-V$d4iZ>_y{bD5e$5K zc4k(iB`q~8CHl~T9ou$Cga-!3Bu2+)m#5$P{O^C0FtD7%$5ccul=&6zi6rl-4c<~%Pog`r6#7EA^K z6sB%8@5*P-tZi(_%}Y#*3EaMW!L=9P4Ra`(dp=mtke6)?L*Q9Dv zPLxI>UYuTCVXLmI0rMchUQt;o5c_4Uibvz(+Q@;@xlb?f z_L;k2!R#4RJ*Ihk&6q#m65V#OJ8+o?hG-Fzkb3Q}D@q$m@}q-8!h^O0eu~M}bKa_e z=mUomZMh96K1jsxk>OJ;=6M__YpN)#wcE-n;e9+f;2qva>raXg@G(x(LaabbHH+3R z@L4!_hR^&3bAazL+iRisg6fE#D@OtjZqHmVp#(g7(yiC#wbz;pV)ujv1+87+tXAq& zrs+#|hKB45wHDi}UrWa-4;I-3>ebnn>Kc2cy$bQhNfpEnrz7+^A+{625W!#~fq9k6 z;Osqr+6ifKRQC%*L5KI}?@ykEtpU-+v1ht#iZTNC@87Y}+e3$1 zAyzt1e=Z>G(1FAPdqt0BvJ5Q!ZgyU!)m~c-JnIvDQg+~T4xiLM2Rgw=`^*(fDJA!q zw_ujfoVl~Tr+ZGDIooqiU~_ugwa%DOzoNM8^gxx6*Liezb9q_v>Xpwty}+Qv&IhZ+ zg3q+)cJ6#WtkTljmcU>kN3yNL-eiZl2)ME*_>Qu};S+Gq_e2LYYFH*f+lQr7MrN8n zd-mM9b7oDSHqB?2&#cTkTm8kF^n}2?+>-dxbQv$7RK4@I-)_6bYFEx7ZqmZSJsqSN;85s zE}ye9t2={UkZQHsDr%A2<;XFT_6{LMf5Q75zGXrOFVT`>jm@^@#U{6de)4R{~##yYLO+AAnJ$>DE zqg_eqS=pI6IVp!0>@er%*lL^F`+8d&2d`c`f9cYhi|0=l@JZ66-ZRSyl=} z#D962ZNl*t=r>?GiF-u9*<5VK4_J_@8I}?A*obLLnFij6213T&d&;zJJ;z6m9v|rK zsvYgi%t*H6+NKa6+oNqqnW8w7t2ttRibyKx%1auA|Hl z!YAU53pm2}6MXod^Z!QS-guROtv-Zp1u-keL=pSF&R(8N?W4y=jvOE9?`b~W6%n5k zzszWy>b@brym73*rKqF2qQ1Va`^53?zP65r($d=go|4|v&CLzv2Y01Zbir;%kt0Jq znk)!H4PjTp=|L>Agdu{jVVBfK0-9yKQ+G}dtONPddw)E8}Z3!zo(pmmru8ePYIMmm4 z@k(pxX21COr`hcV&8@@j84+c+=IY|C!fbQTk*4w-tIbl;R@o3%)!W}!U$kyR_*iFI zVM%c@aWjGD@JZSc1supQe**vIt{@Gq;!~dMOe!Ov?BQaimhy~lX+LthzwhkHk)FnD zmsv%)QFqgufi7(@z{0uI1-CiYAOlymJ-RNv9Tq2BsS zr@FI>PF*{DW2CgYt-Ic4IkdF0wX1iyyZ^}1u93Ee#?Hph+U;IDLaj9wb=5tWjtmdl zi?OsrLf=Y!&e`u!!0f;$k6bk6oRorMtSeF?*+fb60C$OJ93MR$-I9vD(s{;=Lj;Hm|a^v#D+DY}<(; z5@!T>0-q@1z~l_)UJ`GVTD5YCt6a|fR)%p<&8^pvHEWqnD3c%V>>3#z9f+OCU(qiv(robO5S zahYFA>XAbMhu=x)gBq)q(y>wxM!=}}II*IXsFZpo2*8mDl)^PNM~)pCs=9K{Ts>G* z<(pMhP|<9wYHRPS4cO3>vE=FH>-X&0w&CE~UDbYGftH%`)|#fC#zt#ac42q@ll*&v zkI|PKc8C)22hfT2Wo1~XR;r|AqfV(Jdv`J=*{Z>k9F7=Ji`$M4kJ>KZD6Z;0Gtga< zm2a~ZH?;PToo+t3A$ardgCa%_>QCfCr)*>G?&@U4FdxiO|>OOW=pY!%<_np zcqqZ}&(#12GEsxX0QgsF6=E@muU2a`I&7TcbPq*gJzFg_<@Yq7c(He|vF+?&c2Yq> zWoN#*{p^LN!VtfXu?x3eyY>5*U%YiPJ31jCsGxV`ShpDqOC2XGlN(CST|fJA)YpIf!r5MXNl|6bnbr(zg~eQg3S7wH6Hmif zG6(~bWIoC>se@)SHtE!QRxDJI^HNHM5~sIVDNbFHW?0RaDxxe4n=a=!H5^Q_Wfs@g z76qi8e)F|Iy#3}Mu4kQj@y?w$U%!3lboR)#>$hJz**DbRTvuOc=@^O0D62xHFDxm0 zQg%xwe9zTEf?dRo0L8_?*BF#ypc3A}qnpR;2F}aNIogCOzdH7IX zL2Z3qYFb6#`J1o3_4^yI*K}OI_R5>D-+HZW_}a@iZ{E6e{KSdA=C=CuwxZ2ZZT9jq zOMZTF5lK42I+u^caP*! zpgkkIxTK%}{mukF>MCunI&RrbETDodd`DX=KN$)1-~^1_ATl$PtSymI|^`QX^?3k`Mo*6f1P;l7%> z+RDB+so@Jk6iBe9lY_{t-gVi^`%GqPF^|H zW$Es3Y-w)EORQVvq-tm}2KGrMtF=Mnr{Xhi%=o_St1?)&&FvoZJ%3SvU|!s1f-5d;k+6-{5UtQnbowP>IA-Z{B<5 z-mU3kg;A%}Jd+gS#*?!=ie+M~&I=lflj*Yh0 z6qFX{hn5vZF5Ea%<5&+W6eY)lYj&-I7`}+>&K3%*`|B=H})Q z$s=%id3c*k9&)mCa_}lY|M+LGyz^lH-v8Sl54y?K8nuC1=i^=8saNo^yP1|^ zkeUJu!*?v#&0g%IPrNu>mY!KtduYqj`0C47&y01Kg{O>~6MWn~7q3rAX?khN%ss1D z?Ta{AkXf9UQ;?OF4atH4jzfnkoJtO@a&tfa?%NOEoNr+K-}&m(Kc4r*CW%&~lu<(C zLN9b-QYtG;DNrqniw)SfdCzk*XKB@Xw<)fs=}+&?>brjZ%I)93c%mlpxo1OaQVXm# zsnvfE*UX$cd(DnzOX4dE^YXG%^RlxtGqbWXGr%~}BolUX0>!xJ_y6(U$9r6b)TR$U z4?X%pfljT_sufrflbAGEmXgW}5~R!|=fLR0A&FJZ71h-h*%AK!VaWxlRsEw!PQG;O z+GurQa==p8MOy>+@7v@#EoQ&h>Rkt>P2F3Vm6Do~k`CxhNCw9O!FLb>oxlyK`0}Io z|9VL8!awwWldB=1VWwKEQ_HY>B~fDiUL=(#XMqIWnvjr~3|nh+eN$6OVq|1=Tte;8 ziP7ORH?N#O)@jcV*tvi6l2uC=EPH0{`U403W4BGwuPw;Knn_MZMn*bOAR{v)gJ1%m zu#ujQk1{f|bMyZ8)t4Wfn<9~IcxR5Qn)WoQGw zs;<7izGj=*Tvp%Nd*;gJ^Ve=)xj5R@)RcJ8_dr;7%}DpLs>+u7_~iA|lnZlml2WoV zQvn49h#CYlGXv;=PfO!I2LqqHA8_mA!y57GBvyw8NXx4AI?|4)^;(@$rjle9iKzde zr!3vDE4{qDskgVOuCk<{sG{-YjdNkuxXTnu>CZQi|I;PIWXiHPxDnl2^-x zGt<)&lao_Y)6>(^ITG*;NyBUM78*b=$UkcJ-sVwfzQ&~0s+=(bY4m!uBNEo2#gSrG zl2#~?2*u8;w{8e1Y3e;Tdg1c5>zB`+I(g;#jXN)1ymsZxrSr!|+bc^dD_UA>n_D{? z?G?Fu4Sea;wAAGEq_nK`v{V9|hUe6@)RdG|0t~@B@^RWlXOpXH`BW*Z6r=EI^g7)H zzDdI>ad0KsDila2{nmBQzU^^MgU3croIHE+`b)R(+_-V`m6xu*c5B4@STT2cu!YMg@QbKHUe6j;GH8mwQ1#F}wCy~bqfeLA9&%0~gbna@J(JED} zi;K?L0DSN-$9jNDEN0Vlv7dwEA)cF`-C5ExdUE9W$qzwpwXm#)2d{e|D(x%|p2 zH!r>Z#;seY&JLXz9BOFDI=Dn6poQv`q}Y_C)a0LWlM<64+yi-$3{~Kr43$x1FuGz( z8s{dRmF{|j(Wp~k;KzYN4TgVdezF348aOS*stjxMx&{WjI*(qs_QH$T&R)B9>HL)o zmo8kmdgJExiz8#l1_y_`YO+_eIHQVoHYq+KAu%aA6}BfkOiv_&*F=rP1iVg2NJ?Q1 z8Y97%XymL>?qV=G8?|!8KP%D7RdT*MJxzk`3GC-!2VCN|+tvaV28TwDj|~hTX{~Rn zYi()k#yt5*e|LLFUt7*$S9%h5K(P-W8wVqkk_gwFwG)_}5_o`i@$vDA$qK#3=C?GIA zA)BoDT5aWdhi8d#vjD{>Tcy!)AUgpX;6GT0vETzX0~Oc_agf-!_(b44o807Ljfz$} ztBo!u;1l~bMwu2?E8|Ns7shFX--&5#!ttC$a)aA6pLz3VO`YPdL%6z6nK5_inoZkw zZ(1?Sz+fR8);kGlp#c|PP6;*e)17dN>`rIM($O0BEW z=&Wb4<47AdI8ZKDC0J=jNKQkDum#2wkR!P`qle9SY{--IFxWXDXYH_wf_*POPav2y znHD7hjE{_uiRBOp zBp8Rq(NR%Rusb$7lEYUJ{3&XgMk&#IEp*kmn4I+_|Lna=vDQZ;%S%?^Kno5ZbfIszxbq)FJpWS-&)Cb3T9-{Fuy%@-|=3I$SlL}XM<3_xM@&k`9P4%6{AIwme2 zb>HCas*tPn0=>trK$G({HxCu&pMGClxpdt}WVA5F5uEhavCuTCm`=9WNkoxUpDYYaV_~8*zFgBW)3^AI8glNwcP?71(bffEO$IxD>1z)CN(6vn6H&eqXooK;ik%Wg$lxG5iLPLY01CRk5L9oNa!@>v)Xdq&s0SS#b zo%7RG27?wSI8{>j>tDv_y?A?}YUYlbTYq`?m=~*ZYdV0TSb}X;p;#di(!Y~aA~mIA zaTkJ0?!xL>y^htoDV1)%xqo?Mle3%>(qi0d@D#H)ATTgEFeE%2*Z?LHMugJ@_Cqhk zWK4|A_{{H@8FV7KyVUjh7w`Y@&Bx(7^~?jMuYG>~)B!o`(zaKwa#rIEK7${zT%r;Q zRP*(Eqrs$;>Qqt}9Xs7L-Fxxs;js(LRP3RW`OZqLWwGM@p&@}`Asl2_7}iTdiG+oP zhj9u;Mv=$>1u>NC#!s^_^VUr{H2RnO-+uMpfR{5RVYN|T{N=jGO5kW$NbAftR^8N4cT-mF0QLXF!{L@c9 z|IEWJ ze}8z1+N5xs{?f3lDxG2*Y3i13EY;FdzV4z}w(Z zqE#61hwgp-=-Yeow6lqkPWi)|J2qc@b+a7zFL+zraVMefnf}$R+C{BXAO6!fpM}m9 z<37Lx(FPUck@@A{U+?(frw5n+@agBfrg|7mAf5i_( zDB_EdP^iP@XBtc@XTO1We;9xC`0MQ00RIShn-iE!m=AW? z9~cN%ka!^QAz|T>k!szwho9fOwpOo}t6fhWG_od~l+#UC$doEHK&quf%T+oTtjt9= z|M7>%U$>fLpEfGx29?{HKR3Mf;L+npKYrQxcBqH5$D)i&?>v0;&1*PQV8NArPcPgw*54S21=XV32}RyMypfESi^IJ zi`7O~gI1YTz3BUI9)AD+@bIgPuvq6F@%GC<{`AwsA09qN}tO`S?4l-Dl5ic#Qjj#vea; zBO@#(@^FG5M?QH7Kp_l78glVZpot(a@ksC$uDAEPP;$mqEtGjEbb1#P?%hE*tVR2; zS8LV|%uyLl&KlOg(;(jX!Tm=+j6c|+b~YxTd*|+V55D{9;e!u9d~@drbq|mB8unVAI zXeVWYVd8JM<={*R>xW|1=(osaf&GZ$Byjx6ymz-NaCctXnn07! zXODlpWs3+xfkmw+4koNaLb(D+V$a{#*AKoRERYxoMG?fY__u>oXUsbke)v#$ zRG@D#YzKZY7j^_4V5Ia0fPeshBAjAe=y3ypTp_{4my$V~lyWRR81$}29OTAPRRiuG zc9rkwovCGAT=kM&1I}u#(qx?e%B>k{#$fQMv#c?H8EZ`I-#ByX+(S_TKt^>Tbr<|Q zY$kRSKR}+OV*qhMU~o9s7m*eO#}k1smKvQ|8Ssq;R|CPt~^1-(pd4qDSqPwz6*8%6|B}pr_h>`{GJ(Dq0=jkUh?OTEM&AM zrTd(hKKts6_bfWssczcY?o($^U$!kMAl5e=&h_*44GeqYUxXOPct8NYAZ{nz<0U=~ z3<{6oCM1}RB;aONY&Ssk=s*WT9N+ae7R1G}5|L|+Ov5v#s@cF;!>-2wgJ%@ho{apKv{Cm++5eWJq*ZMPjj7B9%%d#=-Cy z4kJ(!PvI<={lg{4kmYNHn@)TTBX zxm*V-)mn*=FOkY*QZb(|5-LRE=7SgI9h;?7GCU*yED4}cCIN>s29!#bj7A`kfpHK( zB*-KZkw^e!M#Tu#d?AM;5Hl*ZMkW?B25Vaz3p0alo3?G+SZNhnbG=HZHMQ-m(`&V@ zY+5SSe6c_bc!`+c6cUk%*Zka5b2M^>D*{VWphET%A&<)!3WUVI5M-#uVm-qnbQ)4A zeJm8orOI}u3Nb_9#eB(SQbud@hl9%`pV4DSO&H?r+)r&`GpKFH9$mWi>SOm;Pp7_p z?9?2QR0Qt8Kk(&pVeW2D&#RoU0uRC>o-@)VivX68X znec$mFcKb~WlDaF<@3(vZM2p#e5elr5-_fYS5he^0r*O#&ZbT0KV?J!0fB$gPex-o z*0FyZMj^&rKvSg>nNn}rssHq&&mT1$IO9TtTeKaA}EzFJ;6$q1viVm)?WM%nY6vo4$H&*7^hsQVr4p z)-a@u(sbJDp-x^Zol4HgWQbE(@H!VP{GC zrHZ_5yZSLA3HS%TfG3j3r9$%sQDwPjdT5joy9iv>9WxV2qXS|uY_D3uD0Orq53&07C4X4#gPO%F47L=>*v zuquDv(TM1VvYN`Pm#$V9`;#}0vnbvEo504{?$C{zl85V44i!~aR9>hoD?WR&XzfToeorn*3QMQ z(x^3R*nmVNW6au4-yRjWBz%2LM*QxDn>KmHEnb4MxN2aaqQt^zpB7MjNh8L2|A>TtaD z>4U>DY4MDhk!z(OKuZF^6e(J2)Oz!rw4DbJ?muum=fM4*a*ei2J4*|VPV%cr3}mw@4&YcVQGtKR01_~jGipn@dRy9o{d@QBIecdK<90j|qtls7cSsq0=SZh@Q}-er}i3Pkw5Xxg)#X}O<&V8H*7y~CjY?Zn8hALWi9le+30t~ za=uc^m}Q=O-PH8y)@Dz4rBV&ou2gBXI!HhvGdD9eXidjW2@VYj4G9j7NYYK=q=D%2`Dq8y_()hP7_t=^y$DGX*URi+${iI|bX zmn)PKa#vCopDz~vxoB>1*@c254oamE+C}aKlrhcK5}s5d5X!k?F`uiVkj#+Z!5735 zxj>^*D>CyRHhl;Fx9>Mg`&$^45=?>;Y5_TjH3$tbMX64ze(W%!mrzz0dJ-y_ypzt5Fxb&^i070)4l zQ1IdMA+^pRQOQ+M0);|Jrl>+ZRIAmDSO`;9DHzxeMLkGZ2n}E(39N|68!^AjJ>c#SZr5Xu!y`;4k@Gy&h6o>jSE)`6@d@1vx-r=iiP zpj2XsLQZZ0_(T9aEP^+#DUry8ycW~v1_fTZkbfjt3tHh@p(qkEK^lOkjK=r|@971- zhxy2KI;FDvHfR7y`1a=EZY84?!VAL<8wnvwG#a&%fu>OoAts*(zeRIsT9R zyxIPNwHFJIq**EDPyizBpZF~QEIxgkr2-_ue@$nX>Htjy zQ9$_6VOXbti-;+X-gW+JSkU#laSRZc0ObQRIkZ6m9aJlg_zE%u(m~2zFhn9= z3xRidNKnAlqLQP@oiu7h00>1YW0mX&0R&0#40;4AISt5}5@GZO~at7WD6eHTn& zN~qy?X$ttwO?u7^@ec_oFDlr%%ti<5*zgLA0ewUY4NyU{X9a*cgA^nJgTu=(Mj(*z z6%6HSO0`yQ##3nY5_1*JvQYpK0UQvxhZ>SwQmJWT0D?RXA`}aGykBQVga&$tRGvM* z$IC{p0zNxf@jw8qmjogvo`hgD2E36NqTV!3RjA|?kr_sXh$rV;iX7A;y<92RP#j2i|>T30N`ArMs=Q zsoub}Df@y*5URo=h=KlN7>O_{0_zmzjL|c|{IUK}NCh0H;Baq0AJ2e_4ORw?($Gsu z?uk4EzJVV$N?}>fBZo^G$l9Morf66H^3yjq^nHEv;n{@){j41B!3}=Ach=a zWw1F`4_H!21L22pZTgYGP#H0&8B8zG%g@92jGYRRUOrbRkxK+3l~TZ$fC&n|U{}E9 z(sB`Ga9i+rSc6K`qhCC3`uY)WcW=RsXYbxWZ#atApOFcC8pp`QS~-J16C(n!?uMlT zQv!itfrw}%QK$`Sg{cW&JSQy3&&SKtE252D$`@%hNaK7No;oM^fbG}m^0*%;-)C@RK6-ZFZg3^O=7RV7J28qR1 zS}Q4E(&CSZ(4YXq@2XKK1XAs04<%0{;Uc*ZvI|6#KWq8e888|@{DzWIn-$!DLut>k zZKbv6j%~}`zI)fQ?});UU%x-=tWwbYk*ZQOqSX*`MOF{hIxUotO%N59NQN?2;_!sQ1x%!UGf z|M;R*L$W7pfeJu;Vt5-|f^spP&fL^guLBu6WLO*v&)GqN0lq%ozP@6)M5R-h9VpAO zF(6K32Z^#45+beC6b$AbI}^r?LL|4k^YTqoyhma=TYo}DXJeErn) z>it7|F(MKSm*$RK8G^M;i7Wss1D&~6qc=sGB^1cJghqvj1qS;2`FQ*JQEsA^+3Y!a zX}675YodY3;3R-YYRAKca6P<(L|~SpuJ`YMXnGXA@6_27r?TSLM6QgF&0K$KV^GGY zFK<6T$-wRc#Y;YNYfL2K4Wm%&bUKO!I=CV=ER!!Ey>L!gn17(ZzaRbu$b=H)h@JPG zJ6CvePh2|#B_ibO0mkQzr)*QBIGWpCP&wI7lQ zIrQ$^i%0i#3hZpbXJU1Q9V%A!)X2wSfuKbp6L6&Zkx}!)!)N=4__HG*0DDn9sqFV7 z7tfwMU3MdHkSW}OfZO6XUUR-k%9nHTdp@?!Km}jOXGYb%ZORNhdG6Su)7#d>W~FRB zwEy6~V|&-FUU~V`>&H)>7&$htNMprPwML=T>*N|lR=E;uU99=F{d^Wj&Y2St7VPgE z;P2;45XRsh24sc$HcPqY&^#V#~iK zghVZf42uX03keJ$Y+oM|fr=4uc&fHJxs}IHRNSgx9y!Nv{D5v&Dh69>VsVS+kflj; zp0EY>B(Z#3{-9~yvZ8{cN3#!QuStkc$U3ld%a$VtHtagFG5_4;4p@0XjpD*wO0s;(D9`IWqE4^@H&z16BQA=0FE{m8psMp`! zyR@@57q#GG=?eqkzym6^$|K({yC0}2KD_t9o-HW}%a$hW*tun6_P*?WIeD8Oe0zCM zqreImd#P|@LVO=tz2p~(+k$0LbHhWxdZ1rmP!KE+g9r!+2n^K9q=FVg+k?4hk7r*w zn4Az9wIXgsl;6xr0g?X0dbTx%3}qCBU|KS@{9@y{z)P3UoY}K$>*^J;QL`hGGt*Ny z?%BQj=&@tPP0yeHEh6bdW?1?rYSrR-CwFd2pWeZs!9pBb zEtOO~o3x?wVC z;unPYdW6qOOh~-Cwc*Rdb!p3X1$MB)_Pwz3?)a+nCy#Ggm6;glH{Py$$3ME+4VkfI z?YhME`KOEWUai7*FT$8OxbdK=iR0!TeC8AleUhqw$v!kpZBOee|&G+s^w9k?#_R9 zYirx4Tfd>fX<4ZWJ5HU?FWn$f^F+d5T6%~2g+xY1g!p)}_#h!9Bm}(sv-o89Kk?xu z;cfUrMq_64=a`_Vg)8F|VwNq8oE;fGcg2zVoI6c7vp20jcj(N4*luQuUHJ>j^YeB_ zg!p<+AJC^q&mKMg`mxbk7G6@|Ja0}^NN7ZOc!r)#>s2$(%6L9r3;tF#VlG{R&}B5d)cYOJMxQa^D4@+ zx_A9@qM{(lbAor|>b(bdu8oOLS-&nLEi*HIk>{vcy9$bu7^Pw2yrAHKQ1D5b7W_lq z0?Fq5jO-IUBENCS6qJu5WGfMEbcVLWJR(;n#4V0q5|=uE!@7n&+gAK6!uTTxZ@ zASdSUva1*N#x6-s+g@CLvE*XznPaC)E}T7EbY$&+I^gFg|v{+~`&5Nre@ahsv9(kZG0WUn{7r zFUbv!x_)+Z_VL0a#n-P@Uc7PrPW|PZcW&Nls5rG_N%G>vQ=9uG!pjE+dHef%d3yW$ zht2(oPt*QS@q>V$1KYt2;meTu@;O|Yy6d>GWy|Kyj#`la(LKsw<9`6<(^Y zx_aT#f|h6;w7e<85TfXYXm9nyun!D9^ zYK|5ZT&S(Re7Pub)ultZg*VEt)K}fPeeVf)8_w;n!hxc<1Y>EqYNPoF=$dHeF& z<>Km+^5c2okt?I;`T6_6zj=B228KrvJ{cWi{*M9G@HP^F^xH?wlZtsT!uB?LnMNX& z3Zyz)yXj%UQHu(%Up!xVp{6Ro{_>84XRg=WxK@@OmUy`G+{M%Nw<}67S6;Y$|M8vM zwO1~jExK5H?ON4?;&Z3N7RAnv4xb(5>kkw24-5$fJ}Y_%#0RXC-b4Gmu=2nUq##vG zRX7jP2X`{44UABQ?L}3G5y7Fe)34T?FRH4jt_S1OsX<(68tLcvksBui>N&@VLX$kmI5g%#B|>+&lK)*sHharuzHlY__Z zl1ue3?&jCuy>h9vI4|eGuB|%{AHQ(#;mz9fcW>P(&D|QYATB*AJR-;+b4vpMp>M3h zk-Uvmc+>Q=^FLlaBho^m&zvUKD#QvtvM!<2($PC)(fP8o`2}UQcNz+>HbKj|>TQ}^v_~=&6?YgqO%4^t) zE-T&_xpMc(U6Db)-mo}dFOQ#&J`nmxB>cel^dJlL_VuedGt5?NUvuHG$551*Hq6X)V~wEFt}uN5U_*Ge`ljZaKY-EnZ;>Wri% zF)KD7ExYxk>0U+o*`SrjkESm62>}H#GjA`*9jMR+JkvxF0W|d(c<)zovaeN3xvX#0 zXdO=_!vQf@uC(qlZ1t{^yxe0smFH_~a_evB$Ardi+Tz(K@<>(fqlVM_FJHNJrTBDF zLD`E>kFJ$eRFsrARMqXRef;P_WlrRR)inne_=W~~dw6(y5(tZL1ZF3Jpow`F|Js2d z8;!YG+IEno%G3m_5jX>bh5f3dM@|+TK3ZOLvnKy;L*CN)nHR3_a9NRCf3x=T_Ep8D zkMF&>`~KN`a)94Hynk}P{`$GZlA4CwWhE&)&+j>s7K+gCg?R?6EHV*6i0l&{{)vBJ zYhp_j03ch(9;rZr6+8}dRn|^PJB}VcmRC~UP;>U)^|Oho)z2P3&(5hTy>qd8@1l*> zw;#NE^ztd4fqnjX>)wNVb=B!HJMzn_>q`sYJuW)3Iy%(X3;qUfz$kK>4A$mJ{zMT= z3}$6u`z6Co)qK7gUydC_u@GxDoUcffHmmpT&N*_iy#89@-P)pU2OqwB`sv=;vTHZ1 z%PwqQa`oQBhtKXleD>nrtA?xB8ftIXqz7c|yiipNhyCu(^-Ehjeug6R~7s_QCVj^$q#=YLDb!uP&{= zbNOuPmFuOq>uZbSbD!U@zfrw^@uaYw=PS$W3g(6HFR7?1zj6KI)i(|ID{t(KoHYx( z3;tMXlI8nirR!@XfW4Gyr!hQuHj+r_Jq1qbHe8=jEh^bBz;L*^|qj071t_nR@dIFD9=4~ z>~!ssD34j5P=EkHv-pUB}#4%NdB4{aRMZX-?yvE9Kq?z?dH z_VGJ+x98VBy>akgWDGC^`bcu7C384x}w@WuL^k&8@FnZh1lV3$OUy*EZAm*b#E zHKp?Q%_9xBPL@2Zt-E z)Lbb&T~c@H#)H$bfnFXnJv?W!_=H5(4zd5l|49Y*H&H}_Rkl=ttztSxmSV3PtA8Bk z$^_ccvyMmZuNuPMY(5B6hFFCyk+B_J$vJ78{c9dvau1HpRXPryZ`3t`ww^P zZj_XsF0H$N>PA6au$Kp%FUvIG$>R_{Yxh6g080Y4wiuNZI~KslRwRz`Q5?lV-H{xn z5?V=@U6)T+KB&A?bLGXY>bwBg>JYt>hBPuIRV zdOGhwa)b|pF9HzDs29sSRG;7pd)6#^^zZ<@+MpB(uv3gvV2Q$Dp_Fi&VGm7?lX#)j zYSS4Tm(5uZu4Jt(D!qQS;^2wOd+%t!^=nhvbsPlH&hwQA_~3iv>xWOC--UnNUt5~K zulne^Xo^5HjHxCI?CnjAdwBwT=FC|%2wyH&8&o0@c1cZj*o@HPC`8agz(th;VzJ1O zdGwFoJC` zfiCG#pHZ2SB~>}eXR1nyE}YtX>D-O-Gl$MMHN84nhkf4dyB7m^;^gT+Bu(N5?sOyHGk-|H!=trEWol~R3)@wWG`BVX z1B*em!Qe1;{<_gPNs)(xX3HibFwD{%??5b{Is8vpFYD#*f6^`a)p^p zYF@s&i_hU;xm>GPs`}4g=0}BMVww5s;|@VbSFBvWCNp_^%7P_}qGm6M&dgkUs`Of2 z!xq$Fd^q{x)xEOQmvVCs?mK?5s;H#&`iTR3i!X((j}8d+_5h#8K|}z4>U03JuK`aV z%nw8PrdBNLRPn0j*`*Fx{^+!p&e6dgq*@(cqBdW9rl0%igIm@H&z>-K$o$ZhaYHBc z?&iK^b!5`I^8EXaU+!LMxUnyHXWEMBsO9Nddrndc=%78Io&u| zDu6$A>NE^`nvQo_eD~2m2PSM! z4;$@1+O6A?HJhW{2Rin5o;nvUxa!K;^Mz-3R+jD!T{N-Fu+ft}7B5fO78e=0XlMS~ z?2}R9Gp2h_2NK4VDbolKfW~)ICQq8ozMPtMdvM?BYbN)pdHmo(aVHC^W3kW<30rk$ zq+9@Nu*lxh-F`~)rsdva+y7zXxOMO0*qLKTyRW=daHizay$5&8F65j#xqtnbu>O-K zP6%2)$3Jk^q=8QU8&4E$3JjPvY4Xg;lP6E2F@@l!Ou^g9^e}1Sq)B)+b!z^{np-b- zo7+{qe)+UvMr)k$sLh!nJ-a7wGgo10YPoIOANtOb*~xRJ_H-HU)_?Qk+MI1W@{5yJ z%-&b=_3P_rH;cFIy?b=+n871_=WW|`=KiPwv!{&-SQ!x;9x`*n#4%$gOq@7z0tVn% zbdbW5WTa~H-&a+b~q@+$t{jxEdOjLMLmKcduJ<&rE5~!iurz_ zQ`{yD?9<0-khAkB&zM6E-@d{7e|dai=j@Q=+${%B7jDUETG-ji$#vAUk#4~YeWy(r zIefx|@#DsgA3ts^I0hBt$ANElfG+m=^~a|#Cbp8uM?L)*boAj;9pVt`nBe@()jSSg zV!n61B_r$8>5~`-iF}U8eEl{vIn!#=(iJHi4;K`cRFs|Cv0=let=mg(KD^uTqDg`{ZB!#hK@p9bwiIb;1 z|MKDCsa7(@(3^j?)=OF$Br=s!Dxi~ClyLDR`Zb5mgkrNEljg;4J9x6}YF&MGabDip zvf67;UcbWD&&!8(*RIwbU9&oA?SXT*s&3>Q&dW=S8TXfYC!d*P$4;6!cHEdTAYd%W z7&GRl9LC`deKZadc=yEn!h?w#k#mI3Os`cba0aVpkd7$S3Z+aWH*DIYlQviP80j@< z>xtv%uGE$m<>eeXmS1x3`I~pIUVnLW^}*8{H;PW4+L=>%`B7Ezxq{P&_9Tq7lD3>V zX%yH7?&vY}NY5Cf@tWR_8I8B}>fL&)&P#XcO>~w9y-JTNL77@3$6<#IN+=b}4OzRj zN{OQ57$3j5W5t#Ab&sCDc>Ut>{ktz-zJbes`}*FqS5F>aEzHS1Q(jq8cIi^VsZ-e@ z9Z`ioZQRI7W5-VzJ9-oWj~+96)Tq&;Mvfdsz!-Q(ACErQ%Ensxmlf7MBAo)Y9H`*a zARQ9R^e8YFh)mZW*5gn|^hdzlsEx%}u;X{{$;;^lxYOg00TM=!c57p3skOk~ zn?|XW>9q{XHjoaXI#LVn`GS_4cbUQ%)Y`v2J?8B_ccs4m?!(7VUw`=g^~0;Tuit+E zPGy+SzI}Q1_RiCr4{y}uoyuA`&Qd9F!JFtl7`P+;iR(Vx9fN(K7b8H*$Wdd)YRz>9 zQ%gB&iX>9K%uI!C04;;63mg|Japr8bZ)+>#S*}&PZd2D6R@78oZg~Fg!^gKz-hO)i z;@gqk8iXoY4we$mHf(d(A|IkIE3#G_|q|PS|nq$kF3D zM|Ng!PKis1O-Rh#wD)9w;qenEbFvq^w^oYL?I031Yd&_=DDXKF*sRpS>@b#RpaOf? z5Dd2=Lx%(3)Xa+EtCf5Pr=@!Gz7pUowPKYCS75#Fh(XH1d0KOsN}gF~WW5^Jh7VQ(3GM*A5P|6gjY*f>7L?qOyQKHPXTyt121|34GDG&+eW@cKY zMsF~+K*+W*7|hH~%`B~2+O%%d(%hg^qta7of)aI^xVe7fD0kN(gGUSn(}c@D0v!L{ zXbg6Bb#Zka>?Q;MrWV-wz+tIODl<^oARYg9S6FhJN#Xx?=><4c6Dd%2ClRA6g3G7+ zbt;v@@jluRXrQtKwPw^Xz~{F>*cR}bTaFtsV(>7RVUY1KVwuDaWN-}V!GrPOG6)2? zN>Qv!_)-OfA^|0eYqe?_&N`RaDK$cY&}!o*GaS5$C0v=5+Z=0hA)jl~f-8h;5DHKw zK}Y?l0Egd_Gh!}>$NlXeCO9e-^4g3YKFnq4prM1^SVV$^#KEzvi;D~Rc5@xX;!6nM z5`{aAP-FXtnZf}5$8HdlzRMudcU0=PueXFZlrmV1@wqKHyyjdUhbt7IM+T>|LdXPl z5+n&c;2pmO4y#c(A^Nq;*dc?R-3AV0pLX8X>A`qf@TqNeol+Bbl=0ho+47=6vm3+Pm z)h;}tn4{tF|AFmvsY$brqg-7E4j3?Skjvn~00q;=0ZIn;?~jjJlH7(U4JvJ0Gdiv3 z>TOGASX#DgX{!}0qaStxC8B5K}l_#l@7{Rg^$Syy5i=mb5Go{ev58iowv>00$rnJL9+ z)Uni^Khwdg&BT!kjoN0_lNaa0Eex&BtTNZC;eA9Z317(*=|u`if|1$U7}RQb47r-G zG*enySazD)%~EX;$^{&_ZD})38)QjN4$gxJ8Gr)`d%%GH{Xqm#;fjxG7Tk1ZJrfKD zG)keTp;htQh!q#h|I&6Ev$pEdquj2hhTjVonrcn4N1;T~EhAKLwQ{+I(%cLUP3GEG zdMmw^rKxoX3#(2(F%Rpy$qXa>rP&|-9qjDvoG=3z1K81@27N%YLO)P{SGjrb zi+#-uLS-vOE4R|d?_XZ{n`+xnjKBJ^vcN;H>s&bBTxp8xQod5da3y-3wMNy_v0b|k zZ9B9!ZDpzN(AK6`mp*oGWAe{ej_BAfB-6gNx!S;!{Ngyssjs7Be{hTeQ8?28yfY{u zwdhX-fC5QNwm14$p$UukQPL2R} zbZ~IQkCT(5qZ7dKpH)kufh3?ejC`NluV*_$_l(=$KYaW0F38%TYG%p|d2&7T;u5Q_ zr9tLWy-uxBnIJ%*C@;;oJ}z^ShUHzSyi&B^2N7@k5`3u>(saF@7?-N&AV#n~c20c0;pol&%AZFj-jN@FTln+G?2N@%HVy&&}O_Uo*jNBtgB zSyTORkKgPwl{L4+`V8(gGOcxRbf>?j9scyX&UeV*fk1R3nL`U5@MCWWDcIZf>*t7% z`u4T6@7K@H-g$tF>%k}Qn?61cmswfKm{ymohYvnkK18XOirX%+z>b)i>qSpXm7Y;q zL^n2l2z9n;W2yCwur}3niD~-w;Lxk4rmDwHO`biww(k_yse8L=d#jp0JScQ>cLu6c z|Nh|H{(r@{bA~`*81{XC;I~@!@ZIZI+iVSHdbL^MMzy*5gy|}gTBP}1q(n1=>!lgy zDxF+qx%3e#?N`ikwrOo@tyZ_PN;q`&^^>OeU+>&4tM%;AyJOdOT|V%eT(F!B$Jbfk~Ky`8E_{okR4G23Pgm>-K-8H@f6n_2o{>$Of zE>2D)UKYuY7zIA7gdoDn!LFZOzrKB-4E9dW0|u&EpMSk@*zR#!9m5#rSSm~nrb;d% z2r75c+G#QT{1{7}L9MdLJU;LS;agI$;HP^Uzl!~$ zS?s40pAjPm_a54Z#P49=uP-SYWbFuc@l2*bmT2G49wZUI%KE}+D|DS&sJLQFDYl+y z4Z`Om{gue{Isr1XU}>AmJ*{W_E)I`TH?q>s4gTZx?5KC&C9Plz_ztdY?Xhp@$A+6yy_G`S(Y>FlZD!krExO$h_2Q zGcB?&p#)7Jg`w6e^W+ zRV%}c!a-ItgQ?Ci=TdvMiZQqBa;2(gOMRO*T{a~TP5d~gRn*R5om+PrG11LsptFO+ z0Qg;~HTWlQPXkI%gIo?fV2yABE^bIjk&Yt&mI{E+6=`)+5%9HYgNE>B1{8A}26dTQ zHQHRMR#=#itsbCcbY?AW&o`}EdCcCjV@r!s%NCF5)2(A$v)=s&_lAeD>p!^vUywI~ zoTEMYd=?)%0H*Ew5bt;i>DxOE9O8}z0lN~C%h03-{X-)ui?7kEsTq^Os$Xk8*L!H` zWE-7K+uGEt(ow20*R<_*`2%)L;z5X&_IKO1f3)rCG;F|-za61lKyesAj+UYh>AsQk zJ~Zt{;o~JFl**pF*QE1-=OVOd7eyeC0|1im^RM_S8tM z$6gp=5bNF3tG_mWeqCm3fb4qx@%I2X7uWtys08c%caJ~&+V|<*mjWFteb(IU?fU@} zyhHYV@kyV)xqofMDNeh8(noukW8to=v252uyThL~<>E)ijkPrVjGSRNHllLIggDM#8u zD!~2(T1Kf$9xFj=Cu0;mfr5hu7}U5^TZI5Urs%Rj^*Cyt73SzPP|D3vQ=~_4CYM1` zGhMPdnxt`YtW|IUG}SXg*A469dsA;a))2TpiZ_k@{~{4SLm?*`4}L|3X%S! zu$0Be--{U7h(v)t73wIa_H(N6XBdq_t=B3Z7Rg_(z5c*S zw)yY94Xv&0%q-iRi#RQ~oMtA!G}mykbOW`-KSTmjnDormYXi}q%0t~cD_*KSXD1(g z0w0B}&3WSHX#Oz%mQ@W>46HSEe|K$;>UHWekfQ}uBNqvkt^bPLv0+IYi}vF)a;K(b zUK}z#D|+-_zgtCmy4pE-6iF#R-8_K6dBX8oFVYZggNe##&EM{Sn`Ef!;?&4EJj zB?j|0zhefds+gAiM%@&v26yXe;6RmV_Hk^A7Cyc})5>#A)n-G1^Q=YWA7JDQ;>yZa3FuUzWHrA{-5AOAXLtlyZq-ja`gbQ;8=`1f&s-rloUu1ZK- zleKBbo=safY{*)jVG2nQh17LMiYXQB%l7HN!kQ<+X*F#?=&|xc+l+*7`p2LRcasK( zjoK5k*lYEaqhXWR2aIZGYvnR8#Tk|M)D%G!qbE-&@0GT0?YgzSsk;PPNhAn`1Lp6l zuSpDBl$Nn>!;WqH_U_!VWyi)eGxT9W@US~6`u|a~=a`EspCxGm8Hg96Y7CoWFn=_E z%1!N8KV35_a@-b=Ah)F92fSt;4V=`ri>>GCbeG?LrN%mz0f`uUer~{Dvt}r&FWEUK z@4|H7QE}nS^~7b{athBDp3Kibm2>c9s#^=VC>EdE%{>kN#c2g54RoRG{DH&_mWvun# zL54=|1%hU_TaQ)VJhyJu#$AU{WqFL%VjJI=1WBBN>~UzjA;1h09mVInZiW%h*{I$-`4KveGk` zaH(g5WDg=ZhE@3=K9_~ZrzEf4bl~8Tefzg%Pn0w>A-6-WS1h3Zf7#RnwXYslq|FTP zjQby`us=aMh(;fU0k*6aiKGAMU}M|4Q|qqnlhK8U0$-_=E5$J@KmY_@gt!NVYDG%g zri}C?9uoea_4mKzY^A9kY;4=MZPU6{n+_Av{U!Em4->{o<5HBTZpbvai13i$A=!wT#mYt%CQWi$9shO6UCCGd9SJlM8U)~%8g7uls|W~^JcHlsa9$oXl1 z|LXteE6cjwE;@87?D`EcoI50zHM=nPta{675pRe*k}y$;A^P|Jf6^J& zD+6DO{(r5weZqx@U%!9+_O#%kn7oz1!rluxkY+t`N#gRhE`c+cYh&|HyRAw=kGm3Z;Pm&V4% z&y7zi2BL-?$k21N`2B3Uq<~0s4o61;4+ck9H3k=C71Y%x3Q9@M*s*a<%4iep*c#Of zSOCPJoW@8E;e!JDffo?K)U|4P=Fk~IF&>>If!Ovr`JOOSs-`;qDL%spXL&>z&v zAsXO@C=B0#vm4Z}Vvn5S06VDn8Yw9-VSt!^;+_&}x<0Nh;>!jkCB&vCZO_VD=_BQn zgNL4w!c&e(N`OIP!VjDGQ*b~K=IiZWgvxM0%~LR3ECCSz#Rd)%P_da3Sr|3RVi+9( zWCEqcBW8x?g>p55rx}(?W+x;k#-#6Bzde4iv>C~q0Fj%swo2<2f~U`M(!wU6Mm^L< zzHFR~T3J56j$Jy;rz_pCsyfL+XFP{`+<*N&;-!hi>63sS^Vr{<5VHeYj_fcSW62nnP~X^P6`=R z3S@!V>8yR7s zEWTvO&ClOHf1D-5iXAtd5~4_qXXtqM^K!KUH-e%9QX{YI{Np9)AvGjQh$VPp;!Yw_ z#;iz+k4ulq-Zj59M~D;mf8x_LP^QDqKJ*W7$qGn4G2|f)ufM-<{N$%>p+^phWtge7 zP3`x0w{6u_H2GtY#AzO22tV-26;Mba3!*7*la#zFDK05-|AxNJQERJ%Yh_uc`Jusr zk;*|~*e4cWELLrQ^6KsPPv1W+b9L@yj!jFIsO7~^jb9r>l*p=aLP6ulBhg@-eB?w% z0RWoS0@3hg$*U3*<5CVS!&-_b?Mwx?WSt}=vPITMu;xd^(A<-TNM)8)_uqc`*7)_~ zt0%YbUOSN4PX6ajxWTUVkU6DBAL#V5i8??2`ov|xos&Pk$hy?qDwe`x&jAUbVdLD9)W`;#Ai`;I)I z@xxg3{wuK_!W07*g><+UBtc36D(JRCEL9w}yyJqFwnRBNF%%0Hj1$7wT8lQGV#sewi%a}^ida^&l zrw2G`)&>N8y%MXhxp4{caj`Kmakv*5o!hR-7U+I6QIqR~0EW`MOinh3rL7gX^1 zE>~|iegFJ%&4d+8qeCY+d$~By@f%S0`Th4VO>gJ1?u(@!stnn)+Q(WX%>%Rm>7f$4 zn2fSlDl2~AOJyeH*f$?@;mN7&6$1=Vc8olMM++wUkjI4~3QR<@tv5en+x5h-c?olT zrn+`^cXb#TIBUSQ=byiQdUj4id5qCcu*xIXYcxo*2zrGr2NgDBRLb8MBt!g|{@7Tt z3?ZK*+gh+o2TE{PA}qi`0C54sz=E1=z&HzHuGr${lV?pYE{_ey?HeWy9oE@#xP8xQ z(_F^feEj9*+q-|ZFk#JxSb!oBDxwm*F{B{xphoH-Rhac%mX?Ix*2Dy;UmV?$N7!7o zVQ*fRRxJ4icb1{KCrp|MqYt78$VF?8MBMYit!Hm9x%qhedk2s1KeUT|fBQkMzN5SQ z-F*K1{rx0i3o;#KG4L9sgqVAZ#56RxISXW6kUQ|Ps~Sbucy@%_1H z*SAKg2XYyy2zU6QwJwMH|8Wxv(gu-0KKA;(=e|9{=6THwoG^0Wn1LfFjvPID`egTU zWA@*F`0!Cbx)~()ZMev*QzzA$hFzGSbplC#R&P69J@z35jvB2uggUpEiqkuM6#?lJc4(7|^uy z7!k)r>KhQ~+eg?8MKu%(8EI9+-`*)>HKtXdxnZc|fIGuN+PotcrI0`6e|DJdj>uut=^P&7lGy+G%&E&^vD zC^drjf$cMLE=QsWnv`;6-Gm(qMNKZZ>h zG_z*RfzLxGRrg&;3zGdW>>O5&RIl%%wbjFec>)#o_Z4%;uRgYEw3~xpUoOX_h2YoXi*1vZ&k3D6aQv9Dz1k_T(JWS3 zbaWa$a=@rLp$nH4hvQ};*!>op4O$$RmIje$lDX5!z(~iWlkz2DkpCeKL^guEQ9|pG zPooDIjsu=kh`7U`Cam@137Mxij2=87z{R^&GocCR#NOXimxlX|8tm5J&RWk%xW6?M zDlK}rk9KvL9uytDdU11X6-i9GFH28M%fK|#JR3)PI!${Li~ke*-|&%~^MCzSf_pXs zAGgP9!4)w)ne$?=(i6qN8GuuJWJZ9uP2lMpO|0x1c=Fkz}Bd3O9t{ zbDDFI!`RGSv24Xr6mpO951c?rqPgPs1i zRatv3Ne$yDti9t_#3!UDr$Pc?nRJdu8X5c#_9^oJ8$M1=;Drsi9WO|cQwK2i?Knuw zoL8pLUwLYg&(!gu9w7k%%e@9mH?Lpe)4z|4%lO&R(eoFChWG@`UlbWJ+k0f6E-n8E z$P5*9kBEs)NXkq|Oi4{oO-f4rNx(m)4>AZp?#Do`ik(mxjYf@gZ&VhNvqcvwDo{1P zaqH&CpI8zeFfC%n?1?_}0{z?nl^!*5O2BN7*(;VWm>(OH9JeqIj3q4#pXD&Px7*@* zZC2q9E-8qw@NjUtaC1iX@&2FUPekDR=RlSKd^l^`*hbA~#(g|Cq^sB09W)*HD7B4)E@DBncXmx=>p)@@z8W@A!T zg0KI~nZD@Yo7A>#q-Stg%)G@b=f%b)Y&)?3@ZPMgd-rB%ZCth>WMXLO!XUpT@o~v% zDNwzP3{ts7GI&sdy{4pu1cY76dtz3tib3v;|M5C1d*%GXZze%`nUKK-F(QZzQBl$@ zIyGzCx~w%ZThctdyuD_5drx&X{1xo)H*eAMSZMgtw7q+G?A*C!*N!b)H*b!MS`s?f zcj2I!iv|Eic6mI#?fjC)v{C_HU(FGCR@ zw}?XVB%|Q+yC-hlzGFw$+BNanS;2lY!+oQor&^o12F{O6Sukhm@}yOZ!zSVUdia20 zlY5=8Tw-xTv`c7cPihwB>c+VGC1}!;?0zSQWE)gj?XsteE%&@^W}gG@@h95$t0BAF93rJPIodH>E(_Ml(K{ ziMzYIySuv)4{>)#0|~*4g#cuc-5h*!yW8qI+UiQ0+m205j*L`!L^e;K zfI=|XAsljE!H*DIAYFKc-`_`O;NbAct&>h_vMO?Zr3fxpX2|1mov}@kN^>mOed6rt z<1^={rgqI;*_-F>m(-Ce5;cqr?|GxPc*o8{FC!}}D+_mj3v+8LBWtUW)Ebacjdk{S z+xWGP_3s@R>L0-IVq{D*WPCwKppTIeGIlb2(ufkSo-)GwzN|xiEL#z_C{ID$lZuKc ztM<_GGbc_g9y>O@d*RCNG@saVZr~J~ARj#_(U^KRBFDlc#N0*7FyFsTuF` zVAfZX6=aOcRG@&c0N$pr(Pb=>ta&9G^dZ zdUCRFWcNtdPG|Gb;`*-Ly&dOHj*gFX_w@~oV4;)9oD7`IHp!fW@QM9Px`E%gYS#LJ zG)RG`K*0?njt}bU*yqYA@^{UgK6m)!^x=bp^D_r4cAlC)b7iWoeRx-QL$$wNT^HV` zhsXEr8`#&|(K*yL*y2ogiL7mF?(W)q7Hk;<9Z)Xtfh(0fF@DkkIDLXIwF1n5VUTHC zF>v>!Kx1*hMJ9QyKqx)lv4`gN!L=Wr9=I?&(KUAB(7E$d)jJ2r+uA$xTmstryGOdm z`s)jJbT@UiRCT3m7=$F2G!KpqjvP5PcIt3f-{2rI_gDC&6HnA2iq%l0Ap{^tV z?cnPq+zuW9K9D?CKovAb`t;Pv6GxBq&R-ZFJh;1m|KYachTf6JvEJ4atE};n-hu9H zJGwz~eQRS^fhH%aq^7ZXaHM6*_obk<@^IEM*$aXY}|R$By(Y%(wMTc6Y^;m6tRPHn$B9jo znL7IWdpHJKdo%@5gUg$0ds};V_4hZHmR9W=8i4F!{G))D;A8cb`dy41hWN_+jgI0j z3eSVM+aT99xGBIT8o8%Y#PgK0vi-!#V<$T<-E8gNbNb-^`uyUimg?^QU6Y49eQm;> zyt9j&+dG?*LN)DKPBo)ry~F*TqumXi*`tS>_jdOUjgE|s4@;&!xAjpeReF4uU;7LT*8xHJhYM?+sc#v}VrSm5{7M4f)kBmXhsj^YOXz?#{rR`rPui&YH-) zn2O7rQcy8v+;} zlK4Wbhyek$#;CQ{CEl!LsJN-zL!KKArO{E!C7v9*D(K~nj`uuX^zQfCBdOFK$3QB7a?rrbvX{}Bj z8VPJ{8|;J&l8T?Ky=cVw6VU^|zrR<~2^AWH%cLtR8;&S3ILb6t9yr@^8srFq1we( zj0L0S2B=U=DwRSKonw>J6FT~`Q*6z&O+p*?4DM{(x##Ry$iB&Ut3 zf%3tzZfxr+GFtmb(cID*I(75bsr-rImXh4& z%94!ul+voQhOWI;IjL2R`UUkZz3tt7MBt>6UJ&@69;xcd*WRA)u8Km;JG_ACu6(}s z#XSQBbq+(F;aHU`-mXsBhPcn{W~S+IeP*J)kBN0G~=S3DUKp-Q+--uMqYq62Snrg4%sP*O|3iX zs&>TImc?i}@?~(v&9-t1D2(;=4BpY0SaYbQuep5)VkWJpM>^vOa8FNncXwB3SGVNz z_;XLIr13bL`|rQ^?y?SY9D#fq}8Vd+R+Vd z4Ain`CYy>%y1GJLjnms_=H58At1&TudS{-F2FJi5uW;;^wN9|3b5vSXb7N0e&maNn z=x9d+oCF;!5q{zBIwB<%kTkLwLZg6^J#%yCuidIT9`ry0oK84sRfp3}e zsR}v*M6v>33Aq?3S%a{el5T`qPzBCWsTGZVxKh~D-&9@Q&^@;I^u-G^^UD`593AcI zNbwH|N+|6-wEI|3_pYIWaz7oKeOr57LtATOD;fcB!*@wP@VT|6xtaWFL6^SzIOF~o zDQqRL3Uy?^qkbF7f5r7ZlZ|zds-arbqNwtkirCgGxU{unbl<+A{*L;pn%4f47cO7D zIJY=|`1H~Kjo+V(=)e$&H_`0dMzNdjOo2E`zsc4cz$rGNdzeh~{$id^sW-l%*%$_-U z^1{N^8<%G;%$}J&JvBMhTHnz=G~7LefQD}D5!iB6gT|)Drn;tfAQ2pN1BlH{O-%$C z4Il8exp|St;ZdFSu=L3xcmpYKsH6mZJQFf;3!tJ>+99t<-6q2i3-!tzo<4c(jW^EB zT)eWheC5jG&Bcog7nhf=TwR!-yEwmm1hISvMn{?(A}tiu6ofnL>l$z(Aeha~%}vcs zjg3uBjSUSAjsIG^P+p7BZxs~sRMi@87&}`pm%-lSf8|D#2H?LMG_Xm70MLV-_*)Qx*Y0efvhgO(QAas2WYek&Fn%zGV>?bmFcDpS8?~&A zT#q9jTw&XG6=4_-pc4lU9GpIS?7-Bak-ouR9I$pz9zHxZv3Gpef$^$Xp~@CnY@Vo; z${LKXzMcph{Q#&GQ_=~7T?c;Nn!0)lqF6L|$OT}i(YWe79v^A!=ndX2Im+8`7A~ko zD=I3hNd7-hld6n&Qbes%h2R$8l7J8^b%DGblR+tM!22g+I_L;4 zfIZ+zl-v%=lVlM9OJDoQ+Z4%^z;sn5J79)K=D&*H+g^HBH10;Xub2*w!!1LIom}RpAE{5@NTv>ro2t2~egU1~t5I7(p;vV)z-dQm% zA#FMG3Z#HAVaNhsw#$M}Ta990x}&PRc1I0#Ob`iVHIaE023|pa1TiqdSI78;S{mS1 zSLGo8pDEyTk^ir#GIW@#z_DPf?JUK85P5}0?JUGYDNjJ3QRpp``E<-mP~UxA-h#B`QOi=%`}J`}w}Tp?<@pi-Km z%Fg!#MMDi5l+F^8S3!}70F)RsRRkk%S5=o$#USOj$t%ez;;l$=+cp&xD%gtmBMk+0 zSv628E2+Ad6$7-ith@q41v)_{jeID>ZzW==s;Q;(G`L2{7oxF10dqHv$I<3%(ov2w z^~KWSO*46RZw@4mfJ-%G8lZ^P7R9Y9Dl+OyTja1rp=2X&Y~iOla(FYwc1b}7Ars1~ zTjl8#*An1ksARC@J79P00P{T&!Yg4wG_|#|EL}Uqk0S|>EnwWatjiKaha#kr-}wHA zmx~b7J(L7$HYM1$x`He+0~vCvO52c(#OEQPM;Y->46s(K@mOsAL{pBsh8&(Ql_?6i zQ+LlRC@C#2kr-Jya0zy4X-Nsj5B$n1VgoRZ8Yb6n6#3-{r=+WiZ@egNxV~aZGm6^1 z`tCb3`cx`^ELsKO)$($9*kzz%sSFiw1S%j4p{Q%1k_Z=P7CKwZV2ION-hUD-0CNT& z4^>rUX+d~1DJhgt`~$j_v=Y)F3eu-a@}~+-h3mNC%wfr^2-HNrw}1ZUx1Z!_(2bJ& zH@>+wpP;VJ-4mhC6llQvBI8M34Q~_*a!hM3m(Lf1Erp>f(4gq?^o;C0-Z+2RkxGg0 zv=%Z{HBiVTrMS2dq|YT1*y7@%V$w?RBP2o8QARp|9w-QdzG*pE78OhICFUS83E+kbxlw_FZ#{RQfNpKJ`=YT$?u`P1kk0d7gQq8tW`qTjj8=KSaLv!DFq#ZT8u z0@&s`w}1WSyU*``_fK-IZ?p}@)@tth`ZA0)&Dpuv`q{*gWupa*39um)m` z5*08^s(=0I;q@2>m*#)$+ZSK|{kaW~!P!Rf{A`eEQ(Zh5j{qMYc&LY{yVEP~LPv(~v73Y-|>?kZO1n(hW zLHr2fKlB8Qzk~uDjM(JMj*80JkAC>|x8Ke>(%5Q3!+U@G^oK8|I2?*R)$*Aa?ge=! zQ?3*Zkp_nm_WqY$EIFn0Bty|&o@;m8`|Z1Lo~73iwrheTaOk4TAMAhr3liRc{eJ5G zTrpc{U3cy6zmSpf)1TK1!MP0TNogy{gVG5sMlDeSVS+@jr1P<~OjYa7_l=>a<7o)p zYCBW-=h-;oVN7y&-0DlDqVD*`%rA7OEM8pTP##4oiv4p`x-Z$NQ)m6-A4f>Uk6FxIbS0`pa)WeBi7_=U6^_`1GT< z-r9WktM@Lx>!E34v>%!Ipricvv!D6ddTtqESww*(AYe_1~p+qL%=k=`3s0g3s79N z`})s+8~AGF`TfD4-hLL&G!kFI_+LJ2DJ(0^tPRNAFc*X5&EW4*en!^6>+6CiV-=9OcTH3wrU(r+_>cI(V$^#Sn$rDFT0s;wJ|%> z1F}zi_SuPysFqsqbb;C5|N8aeXerjbQsQ%q3ZZ(8wy>x~>VN>6my?qVHd2^{#2Vm- z$XbLKH`Gv82Gs=!n1Bys2k|hKCIBZTu0unQSkM__k%n^c5s?~2%+)r&a&(+U+xhE9 zlVAVzpWlCZ`Sa%5=gY=Aoi`_{a1#TCmXr|7g4vTnO3j{(72O~LfEX|#paK+UC?kHD zLRDjHFhF$2Lp??C53qz>3JINM@Q}z$RS!9BBH##^>YRv^A~q;uRgIp9>wpR@>Z^bM z@u=soQ-^;2^Pi`*_01CV)6&Z-3bOKnj{g9LSw_%_6+i*`MCxQlk}jYF#PkS!1;Qk8 z)K{VMP~in9ZZ=!UL$or3i3E6cHGx{#v_6Y26tWaUra7oB%VX<5Skb2|vDu=rF0YOk zCA^xGA*Q+(Nky3;8-~q+^A|=3Fp0mxxG`4J04iyD*}3>$SW*t(k5*ZQ7ayFsl-0Rh z%r_Qt*HFtH_!^q1=E!u{3OsFv8Oh))8=tVlmIFn0ul{qO{-h-hOql_7m0qTX#wG?~ z=|!oT*;!dd`mIuCDB5#S<3EL_8NWSD1m1AE_8uouQWjwp6fO!px(l`sT z_-tsO&f+uG7~Jv{yJ-g&m(JByaXxCTs=-qiny!5P?TZh)SORSk+gz+|X5bo?ms6Qt z1~Su}98gXZ{SpBa8zd%|hd((GI)NsXvqTH|B^BfhhVuaMDN3l0rbc12smT9F%_u&& zuCN=Wv8i-c)7jW#7ACPyTvfWsv@cJU6VSi%J$M&4^tBUGvZ4bclQYu6uv(CnmY0X& zm*i$-=H`OfHIIlKY`ZyGSp*YfCz>Z8bBX;Im7y3@Z5_@9Rn?%AVuE{tO1O7fEINya z@#8Iw!vzJ6TK=b#XC~*XXj(#LgX2DQ?W+4<{p*MC|MBgrsbg+ZT4q6RQC@apUIx(f zB>y;MkeizY%G*3*{~&+O%KRVEFa8H&d-z{4e96|6oWUxG8V$F>-IGHNj+>;$8N!Yc z6osb{Nu!|#7(<<8_DO$>r!zLmm@ERwhH6J)Q0(zEnDX&@K|^hocaNipXt*PGeT$ z{*6TRt7G(}42gf-LK3?^4 zdQo^CJPLT_MlU%$0UI|hT4v|gyL%#KaJWGbq!Lc8B$*!nGbtj4I5Z*zyh@|yC=H1P z1fL`kkgO8qIjPe0_)v_bFQDYZKg(bX^mL5v3=B0yx)!d!Vj}}zvA&}T6ZtM%Wwyw? z&P1X%>M}!k#L7T*BR0FU*3+B)+BO`n5b`OfL=u6JWP&lm2GJEf3X_}&B}vvfBrka) zCJcQfs$1aZsEoR;xH-eM86OIuYl!WgQyXfEEgZbdj;$6<9DXyg;?kZ>zi>B0BTF-f z0&T*=06DS1vWOzxLM_XUN4>qZ^4k#jCC&OEd16G?lvh;{N#ZO+q-XAcCt-q55_(8I zL2x1RA{^bcut!7DS11(K4Z#4Qp>Gge(NJb$@6&zqZq&r$H=b!rM|0i1olz!Nq?6C* zB14G$1EeS)ogvhAe)y!XyIXx5*luvD#(AEKvaVSERajbvmaahwxs~-jW4rfFPK}6& z0g%)lVsBh1nv*>2<(Q5s3-1NaOvoQsS5&$h-NHXGa7SWJ+Z%h^I}Q|AAE@>9_wqH+ z5Nhhg>yaA;lBKPIYiYtiE{>I$wmCJK8r)N)1>JWHn8n-k|v52M355Bv!zpsCa&SMHVTrGh>3?e#k7t-WZ zM8PNix?Hor`bOv8>MPZc>r&1&S3A3y=eBkwgBT9CoX{c!NaM0~D;~Vn(b?}UCpp_m z@im-h-gH){R@YGF^YWd8F-n+r5`4^MjzB14^xb=Jq5I(7NUl&U&=R3( zX_3nF8XPKxn|c`c(Mj#aWBY&@qD7V$w43%X>*8!#UJ1si&1l)qSg}_;^zn6%k+#zwlW8o6*TqjsvOkT zKwb=~cnKCyCf90y^R14?j;^h8nCGwXd9oc>v6USeM{v{CrL%{6+H%6EvOFA0AUH^Y z%A)dlJe|qa?_U1#(+7tOl8EF*B7vqrDBy80u?@6DLJl{`!5tJ!zP^FswRm9XsSo22ZhdvLxF3T)I$|V-+$}lmX^*wo-EQt@YW7|3PV;wLtdVTTC-{llwL$Ayb8FX z1 zXHlYmI!hgr=1s4D{U3}UeR%tphd>k5b(%a(Y5dD<3cLUxSIQb3H9nqpS-4;WIRG2} z2^nz+G9cM_nEPxxTete&d++tMc6VCH!tqNajA;NXBr%xq@Z{DJGJ)SEn*s9o5_}jt z-D>ZXzk_V;uRmY@^5h~*AkrYF2l{9x$Xt0`oNdP8d)Zw+*Ice;?bZis~}a0HL6dq`%LFNx9 z9XlT~atxB}enCe!ZZCojQ&{+~a7dU2kkA;N|Y2oN*r z0w04GiO8OWjvW#%>NN<38)+c5#`xiW7)o*)1{c?#KI`l4=*ZXt4=lmQ^NGkeF4#XJIpdD}A9RIsiTt@W~Qtj!wm(OkX&TRyVaBZ4OzF46%OG*9ZmGO?aBqi$?Id1tV* zyR}qqJJMeveZWfC1As3XzXV@0El3yeTR1=#W)_b}<_IH%TjWXcVUWOw z5kMTUK{Asxj3;kwy!HM_+dx|pZd~xdg3ST8l~SXVbU=!atABI=+9yjea5QOjzAE^B za70vL=n1vWBx?z=ZVvE?mU(U8PiI&kX&_qDi(%nDc zah)%n5JUse0gbLM^8dMBLk)r2OgeH0)!<^NB4sun4YY7T)&ONE#?KRw!9xT%&cYSJ z8;J1()(8lJ%frPMg{ftH`1-x|NAK_I-_sGG24*sJK!Q(H4FQlpL=}=bKxV-!|3lMk z|K(*iwhtsr60uuYk*F%#RDT{-2m%r|olJCsFIkX%Um3NSSZp$1@jGzo*Msm z8_*y#5J`#QL;if6!z3OEd!<(mgO?7U;8S@zk-m!^%1-v~#*uclyAVDW7 zhr|^T`2!p>k<1R@Gh)BQr2Dgo334?KxaxSY5Rui_{~6@5c_5~N?MwZSME?RV*%^t^ z7orRTPbMY`?a@U}*Ke(@tUP;oaI#8E7Uc61d<3In9hT@_Dt;_L#4d?SV4GyxaO%JM z4R>IFRd5tF8FaF-5z$RQc=_+Ij6f0$e=EVq_+bHPd>+Ig!PgRNVkIIDh)2roh@&1p zzIXr5`o{5H_OHn+P=ydO(8)eYYJ8HxOPwI`OXvZ?r!)BH|M}~`vGo5n5E*Q%D}uBs z`cJSEcv**g6A-Aw4`{&oktQIPFI7I)1E~P;y3gk_C=9KZ+fVP`TUpsW(}GY(6}mNu zRnRa8h~c3T?=N}G!gm}^lt74vjWdg7@ZNV=bx?E;rG7qseqq19s{JQqBk;$2Fl`3P zMvy34%t12o$($2PI)L*wKBGf8_F(a&v-M|IH=cn$f9e*^IE#*NmU>* z1SkM+o7=Z;!xF7Zr?Z98U%z+>n&uzgDyrXgYHs$8sg_S9w5%`xNX7mKJ_%Mx_DUcC zVzO{GprxfvCZG^5iFhO-{)(Z^GIGE9@X6E7^#>2Lc?@hW)!K9rX2WFQCtSBx&BQr5OIi>vZkJ6pc47mTmzQra1#MsAS5#Yk@ zYH4drtWOAo(89)}SE*5|J)1UtMb?* zq`|V`xYfcx{PM5ge*AXOza%du!dXAe(kv(5?$J-A5aDmFTeQ1Ai57P&%^ME2MR z5Z5ap?nkJlWvHobV3~P$b?xz!P2g+k@Ucl&nmFg9h+8hCB9kT=Kgl|mM{Rgg5rg=2 zIDHE3TT4=?~KK?U&f z*xImIYIM=ZKYahoM>o9^lVc*>EUanT)<$AKZ&Q!QpZ)psFJCw-qG%ae$Wf}Dtob6b zmIz)S14oGk0bdKf(K4}d%{+JW*6M@JCyzJR*B%%f8|mr6O7JSt1} zJ+M7xWJ01g9XT|qF`-(gufO>Fi$$Bz;NXA=CnFoWSkJ)9G{}|}_vCNi{qoHoux}_U zkvW8YI$N>}hSS4lP$(4Zh_&<$_4F+qgLd7%dF%el`r{{$AHn|24U9w_B}U(&BWF(^ zZsTFlxQ3970n0JWP)T;X9MqtI#2{qnMSlM2j~iO9;UP}Gfp%7w8VI^Hck;5Z5Y;{T z``6!m$p!T!)@!U4D2RX#!00e^tUg*g21aIPCWf|w<9AnX-`iMuwE6fk#=rj1$jCsG zt3V$(wExt}#XE-#>I*LDW3y%F*tk64fGNXi2R87uwczrD!NijHz|2zIhJ zb2GJbcXDy^ax!;tJoxrk-~Pjo93IHjg)hZAiz7Az(idnOLFu**4k0zi@7{g5zOlKv zx&HLYqxJQ*wTA{qMw%K5jNQAAOirDHMnow^N^jd zKwHNcQcoVbczfmH{ngdAN9&tUo}yvyJ$&$RV|~*^M_UBK(p`t9#@nX)!stwnP>6bP zxDX_D9%N;~4M6Ty$;B*%rgZkJlaz$wC|4(ts9W2b8(Vn50zADO9sQG|7e4*!Gtj)j zcMyEgO=#+Ai*+q^otqc#-n?^X<>BMU8=FryHl93v_Waq?jfeN|uWdYfWT0iBtwH1N zpPCw}8&A^afUJRRktr&CZJvS*96t%QLA3>t%i(ao{E3!sSwV1slZ%U;k(Hs2uA{Am zrHi+lgKt<$=kqr|K?nfWFtU}=;EBXK#+J5G!`GIs-d%g}^wHy|kJr~=a!=lT{?@Zc z8xQW^d$95Nv5weC4>z;9(=+3BwdPPU4W~O*MOkEGvNT238Zv0FV{3*EP$;r1&lPP8 ziXvStTpg`-O?5>AGcz3ndrxa)_rRo*BOiVg2R2$Vesv0qF0wSY$U2FFhz}nn!@&NBn?_UGtS46 zPI`ds5M)Lx-QN@jR>k`HxSG4z8|jO&>lN|EW^Q%{_Tk|zf-mX1@3Q*|Vpc4yO`j_IMLE2Y37M;o8IX$4{R=-h8lncje*6kxO+k$%w(@kdbk9o4ExK5uMB|`-EYqrJ_U8H{i2Kb)8ym1PROx}gA^vV{P5Sd1 z8vf$3rIop%CgMJM0s}*lm<_IAgpwi91R)0sN}A>7ZZZBefmW!Vr>qRT!PP^w^33Q! zJ98^ba|Y5R6=)Z%gAh-IA~Lz zl^Kj<44boU3nC5WWVeFalV@n8<&~3M(U_sAz~tyv-&p_P`J0c|*ViAd6CBcjxV{GY z<0C8rSDR==E~ciYdO{o@xjOn#|YUvaZ;O$~#qRm&9k@e4MD%j4m9lE{o?)x7G8%gk~G`^{&p|L)nfz1+!3Kk$W4UVEqU1x+zl%=tWp1r0fUDZS+ zc+Q&+hv5gYmQlRX;@7jZRKmPp9wR@}g?%#g^ zedm2GHcVxXsq%Og!b12aGoVFy`FJrTBXu3ny2t|B8j zH648?rpe9(TYKM7r@(~x@Q~CL1c6r6RTSl=g!$?ib4`mY&5oa1T6^ci&9%E^i?Q*D z7=ff=$fT`pV9_D#&j0Yu%ne0|{S|5(o0}Vo1$4Him`zwm)mg5H56^Im^-WH3^3WA> z=>oB%vP^WMn}1l8cXVQEQd~ZAJgVy22RbXWvm<@&0)iqc8upyOwfXGnqt#Wc^4MWM z`7gc%9bX7O`SU+~8#7ZbUcd!HGgAvAT>)Q>DiAVIa*N8W>KYoY2o8>`D)MsF6>73M zx+Zj9s#j=Oc65ANR8CG|>%idXKx1oHOKn+IMsk>AOmcN))3K{J9&D@-xBK`R9NmTl zX$?F5HLN%fB)Z48^FFZ!SmLIQuDPuNOM$MYX<}ezs4v2a3KyB`a>^9P_TjNzwQ(WQ zwWTh$raCy^3RsG!vHpHBiD@V+o1IZPh#btGmd@7d%JS;m_>{2t$SS|Qp(6`R_inFZ zz1w{HjBN2CdHlgvPtu4UV2RQWtgUaD7+4r$2h6rGGcmF<(ACw^5`g=iE~~;S8V7?% zeQH=}^$u9LIiJPT7g24zVuM0lQ_{nNV&h^msvD|`(=%ecy+YFR^I~%X;|uK~%A1Z| zdVrx~tk`I5ZoX1H*4ujz9^7Ar3LXIe!F}|Gh(O0g*I0|E&a1RDF|jog8|&+d5&8@k zTD#%tsfm3}X;D!%72b{p=9*>>1~M`(0l|TpVF9rTIT@+pu9lY8wuTmtKG6}O+35)} z(cyW1ZZSDyGdDNZHpqA%!qZ^9WZ-L9@6c8ypC!KnI>e$Y78!{JjGBG}LlaXok+HF% zHX^c_imEAxj!hrj*P0Ta+*0G?U}|q=gmqBeKO!(8KiM%ZDLfGLM?UTj=GGpbff>0O z84;-o$x%6R22SZc$4{-0{T1Md@X28B-zVTJE31$J`hwpPbfW)AN=}OolQ)s8VPtA( zsb^$n0(Z<|t7-2%eC)`7BLC4lWqGjEcNCG&H6(E8ZhDGCVmoy}Z4>srd@yp-y)FjN1k<1=&1o6nwXO00kk9u41!LzLp9510TF{71{( zIF+h|I2jw7+8P*Im|L0(nL-^-cISzcZ|pnJUy_~N)}8EY#O0yPld5-oZh2F^zqc_* zP2bif(A&z$Ho7uDAtN=)KR&j)v?PB=vWaWa@X?buS2vzMS-(#VUD|NefPgIqk9hyW~OHJ?Q0A%&~q*dq$tN^ z<`%a^_=P5f8q#pZCor?&bNOr)Cf(FMKC8H`C?zMGZC6&?d};aC-Hpd7HM6xeM`*iQdH0#KXAe!BZp%o>-hHsaSI^qR zhbm(hmY%sI)73mGHZL5vPFuSYJNXv-HwNJ4zx6`q8b}%>7=28sHrjO3dPwhKCm=l*Z zF;VYiON*!Hbr4*nN^eFGFh=6YhjhRDRi%F@i%$==3X%%ia#dyiZ^ zd+g}Jp6uA{{kt1HTq}FJh9i=)qD$kGZRB*5i>o_Zd)oRYhDSTvODk(D3)4gl^T7DD z;`Fq=!v(ebPF}vfh6zBlEX9}3Ky(4~ko-o{8SF+pjmPpV3|MSyeT>~1RV?hyw6O}Y z45t^)otc6OZ((Kq5hgqV0~(Yd?Eq$>#0534CZoGeC?}_Q2%;fC-<8@gjE!7N)bj4F61@|2vKQNYWMgjaYpTbi zm^iz7`MA0`Iy;ydXlv{03iWgfj!sSwWTj_K?k>!$DoQJ^%L)pKO%Kn{N(*E9*H#pl z6#DUH)I8I{Mi-&5#W6BIB`G&Qt#qKawytUprImk1uck)Urr zdSvG2YU`=RXsxVw4T8|fJs2y}Jy#35rxM{?704(&{jC6$NT!UOo8LRlrgo4z};;fv60N>D>?(+}V*H-Qj`AhM!_azco zB}11kiDdMTAKN>-yI5;6xrX+>0p6Z&s9<7aZj5?P`X<0h1 zZ3*^E$}Vs1Dm3O;^L2beGxGB?JZ$8}+jLTEYYKPdrxj<$r@Iw3ruLnL{o#apPcrS% z1+w)AwsbW_2c#R*%}r-_oMg2aY-0!CKtC^(Z1J|YGS)ZNH#IXf_<$u2JnHmNE|uWlI~>1=F6m38yLrta>Y`KhUyDRJ3VsRb3aCvQF6 zxVv(1^_AOsxCVcVeFs^4qzfMwAJQCMz1(eeHP}XWUVc6v?(ROmj#fsxM*1ce7A9H@ zUT$G_+0m1&;M;4gj`H?)56MaKH!^Z?vS%cBHFS<0J8*Dv*I0Y4S9xPiTU&8Jca|3PC-DEeGh?`?1Cj5v&~IA6mM}f;_-}ZJ$z9Z4K>l6Ee*8| z^i0g`O?3?!>OSeU2ana4r{;H-`#AgiMr8Vg74;35#Mqb=Hup`QK74p`-=Ur`H&08e zu(F!k!myb5*usu9_o~seHzDyy*m%9NcPN95UxE+IhYHC0yJ3av12=P#rn$YRzqgw! zDi*p}8H)9FElgks+QQc~aw-lTZ>SFmDlhhM3X6;lb1&Y1{M6J~tG7pO@8prmW5-X9 z`&s4G?5wJ6=x8a9j|__|?k@Gq88~uzbrmj9f=^}}>4p>@g22TS8WDk&JxY?|kxp!p_|+bC9ZXmJ@_lS-PcXxEd<&GJj%G9>8b1>A= zRnW zepZrqak*XViQ7v==0x}ZZ2$%?hL5|KSASva>=}ULg~0x}RbZ^AgDJ{_c^HY*`Dx9T zHd+R{N`ZkgrdEPTZzt#2d=GD9ZEc4b0t59t{r%nZ3f=6vY&E8BMtx~gdR%!|q0WxMeZxg%jTzB9 zE7}LziX#e3QxO3gdNiAkBIq^27G|7!XZn0gq^TND(MLks0L=h;jivC z-Ephe#wR?)UhI*#BTH4NtFLXK?iwQNR$#2}>Jt=@;-RaJ7X&SB7bpAB=;XvSy{f%? z>K&>|b7I2slB4}y+`}UM!{UpATx>#<=p~1*-Mxk7_EjT&0Q_CD2bFw={-KA}nmW4x zegC=}UM%Bh0 zQF&2*CB5w#A@K>e;eM`|v|6|f_fCyW4R*BiVsZP<-n_iLwoG}|9ewC+l9alE*uFjhxYAMjyQ!g;H=5oB2$UsL+)!Rt$`{ZIqvdI7mF3nVMTgk7j{3^XATy8lv`D&=n#epprs|}TR)D=j zczWEx-fOpR+*!VH8|@a_ojZV*3WLDkC6++6^89b#e*Te{AtUkrhwneT<80>W;pb_k z&16``gxVPD>&m*j;xbM@I@rn9+TDmk!BwatQb3iMX5rPNqXP#f2kKKj*&MIRq}<<>e(Qfhcnqofeu` z*V@prf8R($W=w!RQ%1ww%h}07nVKHTwhd3xGc7-Vb!qwL(hWf0M1ve41cFc6ZHOO@ zn7~)>z58CSp#>xDlW8+sus%3?1o+q)>FC>ed%Icb>MGhfZ^f;cW0;R$Ol}3@^-B`` zP|eZTH@B{>p|)f9@IXgZb_@>TdQ4o~YigQUcsToH1d6n>=WZUSd;{AbR z>E<1ioKfD`P@R_?pO{uy1(vApZ#8Ksu|m7;~Nqd?C%Z-Z+B-Qhedyq!iM`;NoocJ8tt`o}El9D`S5%?<%$%QHm|s|22IexpEH7QVwzPB& zKVL^9CW(H)Im9^mCCA1)8=1JGc4SmkSaftusDqBCjuoocdRmzC0{msY*7dV- zN-yv1YiaH38``yh^6-K2J$v>ZJv=rwwQuy;(TR!9?z)!Bih_n5SJ4(Zb&kjE+{~qm zSFQk4f_rW8+TzmU5wUK#a@ z9UO(eMEThoXqos1M>-o@nyI_^Zy_`gNdAwQV%((byRJiu=^$Cbb z%*e@413_x8A4g`3th$=4EQM=-^xUQS%ZoRF4HW==3GJ0IhzX!8=+E_=E`EMd32}CY zF3whNQDKpwQxErZHq|i+it+R^)z_!HMs5W&r>d%~I#o$_+iNm1uPLe{p+d|9j*5&3bhk1vbC2@1v9i=*xJ7N};7ygpL8*WhSXCBX*z)>TStMf0 z%Ypz|4$1d;Glp_rC!Fa@h~h?l9J(Oljk$%nE7xulp-TXzgC;)_;A__w7q2cZUR(0Q zOL|PCtr03{diV$WM~8(+1i4umnq_(h7;BrFP(4Dna}ZF0dN)W;Q32(^77P*bf(mGO zriNwUu|RJ7HWDg;NHrv7Q&BOCB8)pZcje;JwOg=zj2}oCE07n7Gzc)hUcvW8pAi4h z=tz4L2RBFW(D>lc;IOD*2V+Cy!>KklTH40^0B?CV2?~-2K_Dd0QbRO~tb&R(|6f@S zFY^kL@@=G0f;?F3kf5(BuRzyMJ8}N<#cPYV2^f&1kc;?!^~#kiSD}W-K;(0^TK^YL!$z%b#=6lUNzA&^t5G%xhvsKkwzgY7!UyPLPypv}y&Ro5Ei(p?~xP19CzF!6Q<;x2TmtWySG)sQ* z-o8;0?iS8o4tBAR1A}~m!+gzjdAzpIlPo3%9R$hF>O7RtR8f|tf{qMSc~oVTCubmB zozxmYJx6p!7Mb^oYFoD}Y-7l(ZbJ%%x|(MGiPINn=NGPCzOuLoP%>yq!*}qOVCYxj zmzHnb3<>x0P6)Ec?T?Xjz;`{~Uf|ZV=5y52U);EL-`&U{-A-A|rZT_?2VN0XrZQ8N z#oEq7ngO2X5iCXO{(&a%4oIZX2+?hFu9;lGN zRPvWDNwom`Bk*uV{{9K)UEG{)TwN`7?BDvUW6#Ggk{rC_hF9NOJ7%b5XIf;aYGy9x zBI6&t0^6ywNR#6z(bcyrE7B=iY%v?bz$}593OlOu(TW#C5d;~k;NGX1G#ov1?#!u~ zim#nu?wzRfeL7SSuYiRa-+S&@uFWIcy#ilouy5UA>YQ`$xKI!hJto-@)Q{*tuW@6u@l2Jp^mlzmH{r_Edxu-Sq8x& zGFyF)kM-_0um+2VMfA~&SLRT_arVO8+}s6xxriPBo8S|3gc^trmX~eJ)Bf`)D$Lh4 zV*33bKmY2pkBe%H6k5 zXLzzr6J|f%SYMvJc{yoda`)BaYTLAzm#e(Id(T{4n!hwNGjm~Xb{1Wjg$U4vOHc)w zfsntX2TRK)ZjHbH`OZd}yN^-$@vlC7|BDZTUF@86O`^Wr&9iUvacR>NI|uqWS|?rY zr(s)hbnd`#-rj}VXHEs$ZIJ{0KZS16KY4qjt3UbdT)&zVhkNknuP=1&o}2~#!p!-( zd5rzS#S3$Qzi{EgMJNJ&xFj)0NCCd_%OC#v<4>o2orCOMoi;!J@RxsHG`BR;wg~+$ z6#+v|_RS)ZTZEsNY1Ffi8suf+k;c;{8H~7lIl* zzIZP&{mJK9hGxd5;phBqZLDnFoA?6fFkesopvBMMixkLg-6G=|i-L!mN!RC&bUpd* zn=@GC9JpE>wJ>FdqI>t5ufO>I#Q-)Q^B2HO1T={M|HUVIAT~*+gaqGs=*u6!{pOIV zYp}1A(+3yy9o*{MEDi0gJW}m(e{s2ro*f6 zA`o)r!r*>ywiRDZ=5@c_4_^F?oc!tI$7ilxx;Q&CGkftOh79e`UXYG|b{49@Z;3U+ z7r_VV8?3+l`S17QyaL_byq{J(*!eg+IJg-)xu8$Zf zs`fS+Wg%BEdAat+uZa5l?pg6ntXN0SFzdvf?|%6E&p*F>cYONXmCKjrX3ox>htjbR zCHN3OkYNf?0BJdONMe*69VpTGV2<*x@O z&n{kGx^(e8tnbn#qJL<79!)XfELC_d!sF(O$faZ)D^bF9Dp`9f2o5UxZJZ#{^h3b9+(mWn^d= z=5OT`=^Yl87#AGie9lEg?RwaFI-u;0M`S2;)DKAd)L<6MiEB; zK|un98W1ED=SE1AIG6RVz23FG>%Dk=+uDf}(pJ3CB9O`hR4Of@kw_FNfYm}x+9}q?50Z{>kG?hTDsCY;uggN6#h+n{y=gpF(^Uay_oo{CL^BMtV z33dvDM3KvvO1-9!RhDY+5dG{xAm9%AM>QvJ=On++gWk3-*JQ&$FsRGTy+@kZPVN?d znzenX>8Z!w_~gcoW4BkYQ0f04MlBUtWeC zr!n(dGX9HCOAd3(UO}}miccR3gghL}<6n(*P3ogMrLoXuf7q^0 zH;QkH2L*a`;m7aJji|335?*bk?&IYL<%K9wcMi@9_(X!}p9BEgLDnH35K$IYIh`S% zgjc5Gi$;oZistq%Nn_25&o|`PRhG8crO==vT6^2IDNOt}=%`f~CixOgz%T%MR|vy(c@YR-4dmzWq`! zTB!#&wyL+n)eDiotDCc(*1*)vxkxmzl)98h#Vr=|ndLaauStzqFK~$!?IZ&jKqit( zv|6T2lmqx(nr^d5F1PN}6+DN-=|AKbX>*hM&aUSv&8O@!j`u6GZG-seXTSq3w z+_Z_YisSRyIFuDzOs>Sp(xTwQ{sDve*Xj;dp_YY^0+1n*(kj4Feb=w>mVTaNTDrVJ ziI)|pFEFTa7S^tf47y}~@|EsMLnFm`Lapsn{RYm(+00*V{`ULES)*OC^~b91Q@S!9yyWY0lNFN1&}HhkD&0o))P$mXw7UIV_J?c+G+1*1NqIdJ56 zhxYsIR;$D78}>-dskAz^yZ!J1r>XUc6SSet^7{N&ckgZ9y~)bcADwyc=vX*1cM*N7 zvBgw65l>uLNM_TJJz=1R54Wu`Pr^U|B$7ftvyv~60}xKb<+aOpwx8wEN*qKBktPe; z=s{iNZBGBlkfIuo{rcet@5WCW74hj^CybAqPHk-cdFQ8}f4MHaJR3QG{_~mma(*c~ zyO0EEJpRSZe5Qmr;!ycc008~31V|o&h)fap0LVRZBTymrtUl@t)ETHVP-mddK%Id) N19b-K4E$dV`~%WG*USI_ diff --git a/Tests/test_file_fitsstub.py b/Tests/test_file_fits.py similarity index 51% rename from Tests/test_file_fitsstub.py rename to Tests/test_file_fits.py index c77457947ef..68d708fb173 100644 --- a/Tests/test_file_fitsstub.py +++ b/Tests/test_file_fits.py @@ -2,7 +2,9 @@ import pytest -from PIL import FitsStubImagePlugin, Image +from PIL import FitsImagePlugin, Image + +from .helper import assert_image_equal, hopper TEST_FILE = "Tests/images/hopper.fits" @@ -16,6 +18,8 @@ def test_open(): assert im.size == (128, 128) assert im.mode == "L" + assert_image_equal(im, hopper("L")) + def test_invalid_file(): # Arrange @@ -23,23 +27,14 @@ def test_invalid_file(): # Act / Assert with pytest.raises(SyntaxError): - FitsStubImagePlugin.FITSStubImageFile(invalid_file) - - -def test_load(): - # Arrange - with Image.open(TEST_FILE) as im: - - # Act / Assert: stub cannot load without an implemented handler - with pytest.raises(OSError): - im.load() + FitsImagePlugin.FitsImageFile(invalid_file) def test_truncated_fits(): # No END to headers image_data = b"SIMPLE = T" + b" " * 50 + b"TRUNCATE" with pytest.raises(OSError): - FitsStubImagePlugin.FITSStubImageFile(BytesIO(image_data)) + FitsImagePlugin.FitsImageFile(BytesIO(image_data)) def test_naxis_zero(): @@ -48,16 +43,3 @@ def test_naxis_zero(): with pytest.raises(ValueError): with Image.open("Tests/images/hopper_naxis_zero.fits"): pass - - -def test_save(): - # Arrange - with Image.open(TEST_FILE) as im: - dummy_fp = None - dummy_filename = "dummy.filename" - - # Act / Assert: stub cannot save without an implemented handler - with pytest.raises(OSError): - im.save(dummy_filename) - with pytest.raises(OSError): - FitsStubImagePlugin._save(im, dummy_fp, dummy_filename) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index bd44f63a3bb..0a40a0b0b87 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1064,6 +1064,11 @@ is commonly used in fax applications. The DCX decoder can read files containing When the file is opened, only the first image is read. You can use :py:meth:`~PIL.Image.Image.seek` or :py:mod:`~PIL.ImageSequence` to read other images. +FITS +^^^^ + +Pillow identifies and reads FITS files, commonly used for astronomy. + FLI, FLC ^^^^^^^^ @@ -1354,16 +1359,6 @@ Pillow provides a stub driver for BUFR files. To add read or write support to your application, use :py:func:`PIL.BufrStubImagePlugin.register_handler`. -FITS -^^^^ - -.. versionadded:: 1.1.5 - -Pillow provides a stub driver for FITS files. - -To add read or write support to your application, use -:py:func:`PIL.FitsStubImagePlugin.register_handler`. - GRIB ^^^^ diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index 7094f87846c..1ef5d7230fe 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -41,10 +41,10 @@ Plugin reference :undoc-members: :show-inheritance: -:mod:`~PIL.FitsStubImagePlugin` Module +:mod:`~PIL.FitsImagePlugin` Module -------------------------------------- -.. automodule:: PIL.FitsStubImagePlugin +.. automodule:: PIL.FitsImagePlugin :members: :undoc-members: :show-inheritance: diff --git a/src/PIL/FitsStubImagePlugin.py b/src/PIL/FitsImagePlugin.py similarity index 63% rename from src/PIL/FitsStubImagePlugin.py rename to src/PIL/FitsImagePlugin.py index a3a94cf4b21..c16300efa89 100644 --- a/src/PIL/FitsStubImagePlugin.py +++ b/src/PIL/FitsImagePlugin.py @@ -2,44 +2,28 @@ # The Python Imaging Library # $Id$ # -# FITS stub adapter +# FITS file handling # # Copyright (c) 1998-2003 by Fredrik Lundh # # See the README file for information on usage and redistribution. # -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler): - """ - Install application-specific FITS image handler. +import math - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter +from . import Image, ImageFile def _accept(prefix): return prefix[:6] == b"SIMPLE" -class FITSStubImageFile(ImageFile.StubImageFile): +class FitsImageFile(ImageFile.ImageFile): format = "FITS" format_description = "FITS" def _open(self): - offset = self.fp.tell() - headers = {} while True: header = self.fp.read(80) @@ -75,26 +59,13 @@ def _open(self): self.mode = "F" # rawmode = "F" if number_of_bits == -32 else "F;64F" - self.fp.seek(offset) - - loader = self._load() - if loader: - loader.open(self) - - def _load(self): - return _handler - - -def _save(im, fp, filename): - if _handler is None or not hasattr("_handler", "save"): - raise OSError("FITS save handler not installed") - _handler.save(im, fp, filename) + offset = math.ceil(self.fp.tell() / 2880) * 2880 + self.tile = [("raw", (0, 0) + self.size, offset, (self.mode, 0, -1))] # -------------------------------------------------------------------- # Registry -Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept) -Image.register_save(FITSStubImageFile.format, _save) +Image.register_open(FitsImageFile.format, FitsImageFile, _accept) -Image.register_extensions(FITSStubImageFile.format, [".fit", ".fits"]) +Image.register_extensions(FitsImageFile.format, [".fit", ".fits"]) diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index 45fef241ee0..6352e088fe6 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -30,7 +30,7 @@ "DcxImagePlugin", "DdsImagePlugin", "EpsImagePlugin", - "FitsStubImagePlugin", + "FitsImagePlugin", "FliImagePlugin", "FpxImagePlugin", "FtexImagePlugin", From 95c17a8334f91f39669cb4333b3e850f552caa50 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Feb 2022 17:39:56 +1100 Subject: [PATCH 0160/1186] Replaced _MODE_CONV extra with bands length --- src/PIL/Image.py | 55 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 02b71e612a0..ef27d614538 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -210,38 +210,39 @@ def isImageType(t): _MODE_CONV = { # official modes - "1": ("|b1", None), # Bits need to be extended to bytes - "L": ("|u1", None), - "LA": ("|u1", 2), - "I": (_ENDIAN + "i4", None), - "F": (_ENDIAN + "f4", None), - "P": ("|u1", None), - "RGB": ("|u1", 3), - "RGBX": ("|u1", 4), - "RGBA": ("|u1", 4), - "CMYK": ("|u1", 4), - "YCbCr": ("|u1", 3), - "LAB": ("|u1", 3), # UNDONE - unsigned |u1i1i1 - "HSV": ("|u1", 3), + "1": "|b1", # Bits need to be extended to bytes + "L": "|u1", + "LA": "|u1", + "I": _ENDIAN + "i4", + "F": _ENDIAN + "f4", + "P": "|u1", + "RGB": "|u1", + "RGBX": "|u1", + "RGBA": "|u1", + "CMYK": "|u1", + "YCbCr": "|u1", + "LAB": "|u1", # UNDONE - unsigned |u1i1i1 + "HSV": "|u1", # I;16 == I;16L, and I;32 == I;32L - "I;16": ("u2", None), - "I;16L": ("i2", None), - "I;16LS": ("u4", None), - "I;32L": ("i4", None), - "I;32LS": ("u2", + "I;16L": "i2", + "I;16LS": "u4", + "I;32L": "i4", + "I;32LS": " Date: Tue, 15 Feb 2022 18:01:02 +1100 Subject: [PATCH 0161/1186] Merged _MODE_CONV typ into ImageMode as typestr --- Tests/test_image_mode.py | 2 + src/PIL/Image.py | 42 +++------------------ src/PIL/ImageMode.py | 80 +++++++++++++++++++++++----------------- 3 files changed, 54 insertions(+), 70 deletions(-) diff --git a/Tests/test_image_mode.py b/Tests/test_image_mode.py index 0232a5536d0..670b2f4ebde 100644 --- a/Tests/test_image_mode.py +++ b/Tests/test_image_mode.py @@ -21,6 +21,7 @@ def test_sanity(): assert m.bands == ("1",) assert m.basemode == "L" assert m.basetype == "L" + assert m.typestr == "|b1" for mode in ( "I;16", @@ -45,6 +46,7 @@ def test_sanity(): assert m.bands == ("R", "G", "B") assert m.basemode == "RGB" assert m.basetype == "L" + assert m.typestr == "|u1" def test_properties(): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ef27d614538..2977cfd3828 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -208,44 +208,14 @@ def isImageType(t): else: _ENDIAN = ">" -_MODE_CONV = { - # official modes - "1": "|b1", # Bits need to be extended to bytes - "L": "|u1", - "LA": "|u1", - "I": _ENDIAN + "i4", - "F": _ENDIAN + "f4", - "P": "|u1", - "RGB": "|u1", - "RGBX": "|u1", - "RGBA": "|u1", - "CMYK": "|u1", - "YCbCr": "|u1", - "LAB": "|u1", # UNDONE - unsigned |u1i1i1 - "HSV": "|u1", - # I;16 == I;16L, and I;32 == I;32L - "I;16": "u2", - "I;16L": "i2", - "I;16LS": "u4", - "I;32L": "i4", - "I;32LS": "u2", + "I;16BS": ">i2", + "I;16N": Image._ENDIAN + "u2", + "I;16NS": Image._ENDIAN + "i2", + "I;32": "u4", + "I;32L": "i4", + "I;32LS": " Date: Tue, 15 Feb 2022 21:50:20 +1100 Subject: [PATCH 0162/1186] Replaced absolute PIL import with relative import --- src/PIL/BlpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 7b78597b443..367c4f47936 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -347,7 +347,7 @@ def _load(self): ) def _decode_jpeg_stream(self): - from PIL.JpegImagePlugin import JpegImageFile + from .JpegImagePlugin import JpegImageFile (jpeg_header_size,) = struct.unpack(" Date: Tue, 15 Feb 2022 22:30:12 +1100 Subject: [PATCH 0163/1186] Avoid circular dependency --- src/PIL/ImageMode.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index c76ad3993dc..318f1372810 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -from PIL import Image +import sys # mode descriptor cache _modes = None @@ -39,13 +39,14 @@ def getmode(mode): if not _modes: # initialize mode cache modes = {} + endian = "<" if sys.byteorder == "little" else ">" for m, (basemode, basetype, bands, typestr) in { # core modes # Bits need to be extended to bytes "1": ("L", "L", ("1",), "|b1"), "L": ("L", "L", ("L",), "|u1"), - "I": ("L", "I", ("I",), Image._ENDIAN + "i4"), - "F": ("L", "F", ("F",), Image._ENDIAN + "f4"), + "I": ("L", "I", ("I",), endian + "i4"), + "F": ("L", "F", ("F",), endian + "f4"), "P": ("P", "L", ("P",), "|u1"), "RGB": ("RGB", "L", ("R", "G", "B"), "|u1"), "RGBX": ("RGB", "L", ("R", "G", "B", "X"), "|u1"), @@ -75,8 +76,8 @@ def getmode(mode): "I;16LS": "u2", "I;16BS": ">i2", - "I;16N": Image._ENDIAN + "u2", - "I;16NS": Image._ENDIAN + "i2", + "I;16N": endian + "u2", + "I;16NS": endian + "i2", "I;32": "u4", "I;32L": " Date: Tue, 15 Feb 2022 21:45:35 +1100 Subject: [PATCH 0164/1186] Use ternary operator --- src/PIL/Image.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2977cfd3828..78149e091d6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -203,10 +203,7 @@ def isImageType(t): # -------------------------------------------------------------------- # Modes -if sys.byteorder == "little": - _ENDIAN = "<" -else: - _ENDIAN = ">" +_ENDIAN = "<" if sys.byteorder == "little" else ">" def _conv_type_shape(im): From fbd23bbf2839d70e3f82c2c35a7426fbdbf5db13 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Feb 2022 21:47:12 +1100 Subject: [PATCH 0165/1186] Clarified code --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 78149e091d6..c13448dce21 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -208,7 +208,7 @@ def isImageType(t): def _conv_type_shape(im): m = ImageMode.getmode(im.mode) - shape = (im.size[1], im.size[0]) + shape = (im.height, im.width) extra = len(m.bands) if extra != 1: shape += (extra,) From 9d3c8d27142d44d1fca0d5096c6e19386cbe4602 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 15 Feb 2022 22:19:34 +1100 Subject: [PATCH 0166/1186] Added further typestr entries --- src/PIL/ImageMode.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index 318f1372810..0973536c934 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -57,13 +57,13 @@ def getmode(mode): "LAB": ("RGB", "L", ("L", "A", "B"), "|u1"), "HSV": ("RGB", "L", ("H", "S", "V"), "|u1"), # extra experimental modes - "RGBa": ("RGB", "L", ("R", "G", "B", "a"), None), - "BGR;15": ("RGB", "L", ("B", "G", "R"), None), - "BGR;16": ("RGB", "L", ("B", "G", "R"), None), - "BGR;24": ("RGB", "L", ("B", "G", "R"), None), - "BGR;32": ("RGB", "L", ("B", "G", "R"), None), + "RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"), + "BGR;15": ("RGB", "L", ("B", "G", "R"), endian + "u2"), + "BGR;16": ("RGB", "L", ("B", "G", "R"), endian + "u2"), + "BGR;24": ("RGB", "L", ("B", "G", "R"), endian + "u3"), + "BGR;32": ("RGB", "L", ("B", "G", "R"), endian + "u4"), "LA": ("L", "L", ("L", "A"), "|u1"), - "La": ("L", "L", ("L", "a"), None), + "La": ("L", "L", ("L", "a"), "|u1"), "PA": ("RGB", "L", ("P", "A"), "|u1"), }.items(): modes[m] = ModeDescriptor(m, bands, basemode, basetype, typestr) From 10e1731149ebd6dde5f9d4e9ca502ea8a17f9955 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Feb 2022 07:39:35 +1100 Subject: [PATCH 0167/1186] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 12a30361362..826caa78333 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.1.0 (unreleased) ------------------ +- Attach RGBA palettes from putpalette() when suitable #6054 + [radarhere] + - Added get_photoshop_blocks() to parse Photoshop TIFF tag #6030 [radarhere] From 948c064b282f80492f5b88d3f06a599b76516edf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Feb 2022 09:56:13 +1100 Subject: [PATCH 0168/1186] Allow getpalette() to return less than 256 colors --- Tests/test_image_putpalette.py | 1 + src/PIL/Image.py | 6 ++---- src/_imaging.c | 9 ++++++--- src/libImaging/Imaging.h | 1 + src/libImaging/Palette.c | 1 + 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 725ecaade01..3b29769a7a4 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -74,4 +74,5 @@ def test_putpalette_with_alpha_values(): def test_rgba_palette(mode, palette): im = Image.new("P", (1, 1)) im.putpalette(palette, mode) + assert im.getpalette() == [1, 2, 3] assert im.palette.colors == {(1, 2, 3, 4): 0} diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8d36e8871b0..449d29c4437 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -821,7 +821,7 @@ def load(self): if self.im and self.palette and self.palette.dirty: # realize palette mode, arr = self.palette.getdata() - palette_length = self.im.putpalette(mode, arr) + self.im.putpalette(mode, arr) self.palette.dirty = 0 self.palette.rawmode = None if "transparency" in self.info and mode in ("LA", "PA"): @@ -833,9 +833,7 @@ def load(self): else: palette_mode = "RGBA" if mode.startswith("RGBA") else "RGB" self.palette.mode = palette_mode - self.palette.palette = self.im.getpalette(palette_mode, palette_mode)[ - : palette_length * len(palette_mode) - ] + self.palette.palette = self.im.getpalette(palette_mode, palette_mode) if self.im: if cffi and USE_CFFI_ACCESS: diff --git a/src/_imaging.c b/src/_imaging.c index 2ea517816a0..0888188fb20 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1063,7 +1063,7 @@ _gaussian_blur(ImagingObject *self, PyObject *args) { static PyObject * _getpalette(ImagingObject *self, PyObject *args) { PyObject *palette; - int palettesize = 256; + int palettesize; int bits; ImagingShuffler pack; @@ -1084,6 +1084,7 @@ _getpalette(ImagingObject *self, PyObject *args) { return NULL; } + palettesize = self->image->palette->size; palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); if (!palette) { return NULL; @@ -1672,9 +1673,11 @@ _putpalette(ImagingObject *self, PyObject *args) { self->image->palette = ImagingPaletteNew(palette_mode); - unpack(self->image->palette->palette, palette, palettesize * 8 / bits); + self->image->palette->size = palettesize * 8 / bits; + unpack(self->image->palette->palette, palette, self->image->palette->size); - return PyLong_FromLong(palettesize * 8 / bits); + Py_INCREF(Py_None); + return Py_None; } static PyObject * diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 9b1c1024dc4..b65f8eadd51 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -143,6 +143,7 @@ struct ImagingPaletteInstance { char mode[IMAGING_MODE_LENGTH]; /* Band names */ /* Data */ + int size; UINT8 palette[1024]; /* Palette data (same format as image data) */ INT16 *cache; /* Palette cache (used for predefined palettes) */ diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c index 43bea61e327..174e58d34ea 100644 --- a/src/libImaging/Palette.c +++ b/src/libImaging/Palette.c @@ -40,6 +40,7 @@ ImagingPaletteNew(const char *mode) { palette->mode[IMAGING_MODE_LENGTH - 1] = 0; /* Initialize to ramp */ + palette->size = 256; for (i = 0; i < 256; i++) { palette->palette[i * 4 + 0] = palette->palette[i * 4 + 1] = palette->palette[i * 4 + 2] = (UINT8)i; From 54cb09d8b48cd6781a261621d596b614687c6246 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Feb 2022 11:01:00 +1100 Subject: [PATCH 0169/1186] When converting to P, restrict colors to palette size --- Tests/test_image_quantize.py | 15 +++++++++++++++ src/libImaging/Palette.c | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 16cb8b41a13..a36f789fffe 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -108,3 +108,18 @@ def test_palette(method, color): converted = im.quantize(method=method) converted_px = converted.load() assert converted_px[0, 0] == converted.palette.colors[color] + + +def test_small_palette(): + # Arrange + im = hopper() + + colors = (255, 0, 0, 0, 0, 255) + p = Image.new("P", (1, 1)) + p.putpalette(colors) + + # Act + im = im.quantize(palette=p) + + # Assert + assert len(im.getcolors()) == 2 diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c index 174e58d34ea..20c6bc84b1a 100644 --- a/src/libImaging/Palette.c +++ b/src/libImaging/Palette.c @@ -194,7 +194,7 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) { dmax = (unsigned int)~0; - for (i = 0; i < 256; i++) { + for (i = 0; i < palette->size; i++) { int r, g, b; unsigned int tmin, tmax; @@ -227,7 +227,7 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) { d[i] = (unsigned int)~0; } - for (i = 0; i < 256; i++) { + for (i = 0; i < palette->size; i++) { if (dmin[i] <= dmax) { int rd, gd, bd; int ri, gi, bi; From 852859476b07d4ca8244e5701377c1cc85df44a5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 17 Feb 2022 10:04:43 +1100 Subject: [PATCH 0170/1186] Added rawmode argument to getpalette() --- Tests/test_image_getpalette.py | 27 +++++++++++++++++++++++++++ src/PIL/Image.py | 6 ++++-- src/libImaging/Pack.c | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_getpalette.py b/Tests/test_image_getpalette.py index 1818adca234..1690e411ce8 100644 --- a/Tests/test_image_getpalette.py +++ b/Tests/test_image_getpalette.py @@ -1,3 +1,5 @@ +from PIL import Image + from .helper import hopper @@ -17,3 +19,28 @@ def palette(mode): assert palette("RGBA") is None assert palette("CMYK") is None assert palette("YCbCr") is None + + +def test_palette_rawmode(): + im = Image.new("P", (1, 1)) + im.putpalette((1, 2, 3)) + + rgb = im.getpalette("RGB") + assert len(rgb) == 256 * 3 + assert rgb[:3] == [1, 2, 3] + + # Convert the RGB palette to RGBA + rgba = im.getpalette("RGBA") + assert len(rgba) == 256 * 4 + assert rgba[:4] == [1, 2, 3, 255] + + im.putpalette((1, 2, 3, 4), "RGBA") + + # Convert the RGBA palette to RGB + rgb = im.getpalette("RGB") + assert len(rgb) == 256 * 3 + assert rgb[:3] == [1, 2, 3] + + rgba = im.getpalette("RGBA") + assert len(rgba) == 256 * 4 + assert rgba[:4] == [1, 2, 3, 4] diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8d36e8871b0..cabcf435a93 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1401,19 +1401,21 @@ def getim(self): self.load() return self.im.ptr - def getpalette(self): + def getpalette(self, rawmode="RGB"): """ Returns the image palette as a list. + :param rawmode: The mode in which to return the palette. :returns: A list of color values [r, g, b, ...], or None if the image has no palette. """ self.load() try: - return list(self.im.getpalette()) + mode = self.im.getpalettemode() except ValueError: return None # no palette + return list(self.im.getpalette(mode, rawmode)) def getpixel(self, xy): """ diff --git a/src/libImaging/Pack.c b/src/libImaging/Pack.c index 0c7c0497efe..01760e742be 100644 --- a/src/libImaging/Pack.c +++ b/src/libImaging/Pack.c @@ -574,6 +574,7 @@ static struct { /* true colour */ {"RGB", "RGB", 24, ImagingPackRGB}, {"RGB", "RGBX", 32, copy4}, + {"RGB", "RGBA", 32, copy4}, {"RGB", "XRGB", 32, ImagingPackXRGB}, {"RGB", "BGR", 24, ImagingPackBGR}, {"RGB", "BGRX", 32, ImagingPackBGRX}, From 6be87277f71948bc7e4b945c46660cac3e5ce919 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 17 Feb 2022 10:35:13 +1100 Subject: [PATCH 0171/1186] Allow rawmode None to return the palette in the current mode --- Tests/test_image_getpalette.py | 14 ++++++++------ src/PIL/Image.py | 5 ++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Tests/test_image_getpalette.py b/Tests/test_image_getpalette.py index 1690e411ce8..1a84b69280c 100644 --- a/Tests/test_image_getpalette.py +++ b/Tests/test_image_getpalette.py @@ -25,9 +25,10 @@ def test_palette_rawmode(): im = Image.new("P", (1, 1)) im.putpalette((1, 2, 3)) - rgb = im.getpalette("RGB") - assert len(rgb) == 256 * 3 - assert rgb[:3] == [1, 2, 3] + for rawmode in ("RGB", None): + rgb = im.getpalette(rawmode) + assert len(rgb) == 256 * 3 + assert rgb[:3] == [1, 2, 3] # Convert the RGB palette to RGBA rgba = im.getpalette("RGBA") @@ -41,6 +42,7 @@ def test_palette_rawmode(): assert len(rgb) == 256 * 3 assert rgb[:3] == [1, 2, 3] - rgba = im.getpalette("RGBA") - assert len(rgba) == 256 * 4 - assert rgba[:4] == [1, 2, 3, 4] + for rawmode in ("RGBA", None): + rgba = im.getpalette(rawmode) + assert len(rgba) == 256 * 4 + assert rgba[:4] == [1, 2, 3, 4] diff --git a/src/PIL/Image.py b/src/PIL/Image.py index cabcf435a93..305ef445b84 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1405,7 +1405,8 @@ def getpalette(self, rawmode="RGB"): """ Returns the image palette as a list. - :param rawmode: The mode in which to return the palette. + :param rawmode: The mode in which to return the palette. ``None`` will + return the palette in its current mode. :returns: A list of color values [r, g, b, ...], or None if the image has no palette. """ @@ -1415,6 +1416,8 @@ def getpalette(self, rawmode="RGB"): mode = self.im.getpalettemode() except ValueError: return None # no palette + if rawmode is None: + rawmode = mode return list(self.im.getpalette(mode, rawmode)) def getpixel(self, xy): From d4ee19199ca05839297749cc3f046fa5a4bc5192 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 17 Feb 2022 14:39:18 +1100 Subject: [PATCH 0172/1186] Replaced test image to avoid copyrighted color space --- Tests/images/pillow3.icns | Bin 1224971 -> 1333395 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/images/pillow3.icns b/Tests/images/pillow3.icns index ef9b8917872e3790b9ae56456449645ebea6c420..49b691d90f29b8b8532f49feec08b083bc0e9d1f 100644 GIT binary patch literal 1333395 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≧!k5<=?c~%mTNF_#pPm^$sB!Er$2(d~Sfl;L*;! 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ȋ=zT*`a)+0bsG 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%0ACl3#rOi4rGuUlXDv;J%i@RNP@#KVCmdP&{R5 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

j6C^=K5>+UQPKTfrg(`5VH4YG|eBUCir@W~QmF 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