From a4721d374fa743a2763627d807c75b5bd5eaf8cc Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 23 Dec 2019 21:59:32 +0100 Subject: [PATCH 001/262] Fixes djpeg load test * Test fails with `libjpeg-turbo` and `libjpeg-progs` on Ubuntu 16.04 * Epsilon reported is 4.18... --- Tests/test_file_jpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 59eb7c64193..23d931cd6a9 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -494,7 +494,7 @@ def test_qtables(self): def test_load_djpeg(self): with Image.open(TEST_FILE) as img: img.load_djpeg() - assert_image_similar(img, Image.open(TEST_FILE), 0) + assert_image_similar(img, Image.open(TEST_FILE), 5) @unittest.skipUnless(cjpeg_available(), "cjpeg not available") def test_save_cjpeg(self): From 0a77f73ef3b4d4086dae0cdd291ddb854d626538 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 1 Apr 2020 18:55:23 +0300 Subject: [PATCH 002/262] 7.2.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 1e1f1af9343..eeee6a1f50a 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "7.1.0" +__version__ = "7.2.0.dev0" From 0a757b7085b399e71a22c4deb46332c9a1c9d0e2 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 1 Apr 2020 22:17:39 +0300 Subject: [PATCH 003/262] Initialise __frame = 0 --- src/PIL/PngImagePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 81a9e36af5e..2c51caf47ce 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -630,6 +630,7 @@ class PngImageFile(ImageFile.ImageFile): format = "PNG" format_description = "Portable network graphics" + __frame = 0 def _open(self): From 1c2b2b085a7bb50564e5d7ae64939bffe781574f Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 1 Apr 2020 22:32:14 +0300 Subject: [PATCH 004/262] Add test case --- Tests/test_file_png.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index b6da365309c..d095c26a16d 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -629,6 +629,10 @@ def test_exif_argument(self, tmp_path): with Image.open(test_file) as reloaded: assert reloaded.info["exif"] == b"Exif\x00\x00exifstring" + def test_seek(self, tmp_path): + with Image.open(TEST_PNG_FILE) as im: + im.seek(0) + @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS") @skip_unless_feature("zlib") From 2e9030ddca4a0c7f7cc7078f2a81c51a578bd1ae Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 2 Apr 2020 08:49:26 +0300 Subject: [PATCH 005/262] Initialise __frame = 0 in open, and test tell --- Tests/test_file_png.py | 4 ++++ src/PIL/PngImagePlugin.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index d095c26a16d..f4b0de81bc1 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -629,6 +629,10 @@ def test_exif_argument(self, tmp_path): with Image.open(test_file) as reloaded: assert reloaded.info["exif"] == b"Exif\x00\x00exifstring" + def test_tell(self, tmp_path): + with Image.open(TEST_PNG_FILE) as im: + assert im.tell() == 0 + def test_seek(self, tmp_path): with Image.open(TEST_PNG_FILE) as im: im.seek(0) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 2c51caf47ce..0291df4b097 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -630,13 +630,13 @@ class PngImageFile(ImageFile.ImageFile): format = "PNG" format_description = "Portable network graphics" - __frame = 0 def _open(self): if self.fp.read(8) != _MAGIC: raise SyntaxError("not a PNG file") self.__fp = self.fp + self.__frame = 0 # # Parse headers up to the first IDAT or fDAT chunk From 6574552821e0d85c2269e307e7396727e59f566e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Apr 2020 19:17:54 +1100 Subject: [PATCH 006/262] Removed redundant arguments --- Tests/test_file_png.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index f4b0de81bc1..469d186e816 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -629,11 +629,11 @@ def test_exif_argument(self, tmp_path): with Image.open(test_file) as reloaded: assert reloaded.info["exif"] == b"Exif\x00\x00exifstring" - def test_tell(self, tmp_path): + def test_tell(self): with Image.open(TEST_PNG_FILE) as im: assert im.tell() == 0 - def test_seek(self, tmp_path): + def test_seek(self): with Image.open(TEST_PNG_FILE) as im: im.seek(0) From c19bed8f27908003d67826625ba05e750fd18c92 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 2 Apr 2020 12:51:17 +0300 Subject: [PATCH 007/262] Release notes for 7.1.1 [CI skip] --- CHANGES.rst | 6 ++++++ docs/releasenotes/7.1.1.rst | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 docs/releasenotes/7.1.1.rst diff --git a/CHANGES.rst b/CHANGES.rst index eec3c2fcf71..4ece866f2d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ Changelog (Pillow) ================== +7.1.1 (2020-04-02) +------------------ + +- Fix regression seeking and telling PNGs #4512 #4514 + [hugovk, radarhere] + 7.1.0 (2020-04-01) ------------------ diff --git a/docs/releasenotes/7.1.1.rst b/docs/releasenotes/7.1.1.rst new file mode 100644 index 00000000000..9bfa28399ed --- /dev/null +++ b/docs/releasenotes/7.1.1.rst @@ -0,0 +1,25 @@ +7.1.1 +----- + +Fix regression seeking PNG files +================================ + +This fixes a regression introduced 7.1.0 when adding support for APNG files when calling +``seek`` and ``tell``: + +.. code-block:: python + + >>> from PIL import Image + >>> with Image.open("Tests/images/hopper.png") as im: + ... im.seek(0) + ... + Traceback (most recent call last): + File "", line 2, in + File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/PngImagePlugin.py", line 739, in seek + if not self._seek_check(frame): + File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/ImageFile.py", line 306, in _seek_check + return self.tell() != frame + File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/PngImagePlugin.py", line 827, in tell + return self.__frame + AttributeError: 'PngImageFile' object has no attribute '_PngImageFile__frame' + >>> From c2a0005dc4fa0ae7d4ee009788a8c1fe59675fe3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 2 Apr 2020 12:55:05 +0300 Subject: [PATCH 008/262] Fix typo [CI skip] Co-Authored-By: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/releasenotes/7.1.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/7.1.1.rst b/docs/releasenotes/7.1.1.rst index 9bfa28399ed..4c33adf8e9a 100644 --- a/docs/releasenotes/7.1.1.rst +++ b/docs/releasenotes/7.1.1.rst @@ -4,7 +4,7 @@ Fix regression seeking PNG files ================================ -This fixes a regression introduced 7.1.0 when adding support for APNG files when calling +This fixes a regression introduced in 7.1.0 when adding support for APNG files when calling ``seek`` and ``tell``: .. code-block:: python From 8c9100e267f40a63081b344220abaa57325a3d6d Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 2 Apr 2020 15:41:33 +0300 Subject: [PATCH 009/262] Add 7.1.1 release notes to index --- docs/releasenotes/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 1838803ded3..7cdb5849448 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 7.1.1 7.1.0 7.0.0 6.2.2 From bcfe61495b803a79a198699f2acd9d4c0d309d3e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Apr 2020 13:14:08 +1000 Subject: [PATCH 010/262] Update pip to fix pyqt5 install --- .ci/install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 8e819631aa5..180fa6582ce 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -7,6 +7,7 @@ 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 +pip install --upgrade pip PYTHONOPTIMIZE=0 pip install cffi pip install coverage pip install olefile @@ -20,7 +21,7 @@ if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then # "ERROR: Could not find a version that satisfies the requirement pyqt5" if [[ $TRAVIS_CPU_ARCH == "amd64" ]]; then sudo apt-get -qq install pyqt5-dev-tools - pip install pyqt5!=5.14.1 + pip install pyqt5 fi fi From c414810d12a7204eeaee764520f645e559be94a4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Apr 2020 15:23:18 +1000 Subject: [PATCH 011/262] Replaced property methods for n_frames and is_animated with normal properties --- src/PIL/PngImagePlugin.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 0291df4b097..ee9d52b4cc6 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -673,7 +673,7 @@ def _open(self): self._text = None self.tile = self.png.im_tile self.custom_mimetype = self.png.im_custom_mimetype - self._n_frames = self.png.im_n_frames + self.n_frames = self.png.im_n_frames or 1 self.default_image = self.info.get("default_image", False) if self.png.im_palette: @@ -685,15 +685,16 @@ def _open(self): else: self.__prepare_idat = length # used by load_prepare() - if self._n_frames is not None: + if self.png.im_n_frames is not None: self._close_exclusive_fp_after_loading = False self.png.save_rewind() self.__rewind_idat = self.__prepare_idat self.__rewind = self.__fp.tell() if self.default_image: # IDAT chunk contains default image and not first animation frame - self._n_frames += 1 + self.n_frames += 1 self._seek(0) + self.is_animated = self.n_frames > 1 @property def text(self): @@ -710,16 +711,6 @@ def text(self): self.seek(frame) return self._text - @property - def n_frames(self): - if self._n_frames is None: - return 1 - return self._n_frames - - @property - def is_animated(self): - return self._n_frames is not None and self._n_frames > 1 - def verify(self): """Verify PNG file""" From 7475c06b1c1b5145eff202d9bd3e05668825a9cd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 5 Apr 2020 15:29:13 +1000 Subject: [PATCH 012/262] Assert that seeking too far raises an EOFError --- Tests/test_file_png.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 469d186e816..476995dd7a6 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -637,6 +637,9 @@ def test_seek(self): with Image.open(TEST_PNG_FILE) as im: im.seek(0) + with pytest.raises(EOFError): + im.seek(1) + @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS") @skip_unless_feature("zlib") From f1f177ce800c5be7d06b0119b979c661c1bfbbb9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Apr 2020 19:41:38 +1000 Subject: [PATCH 013/262] Fixed error making HTML from docs --- docs/reference/plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index 46f657fce57..cc0742fde44 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -229,7 +229,7 @@ Plugin reference ---------------------------- .. automodule:: PIL.PngImagePlugin - :members: ChunkStream, PngImageFile, PngStream, getchunks, is_cid, putchunk + :members: ChunkStream, PngStream, getchunks, is_cid, putchunk :show-inheritance: .. autoclass:: PIL.PngImagePlugin.ChunkStream :members: From c82ffc7d7f3355e1c0540d35aa351126f77eb79f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Apr 2020 19:19:35 +1000 Subject: [PATCH 014/262] Updated deprecated method --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index d3431810020..a0a3315c61a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -289,4 +289,4 @@ def setup(app): - app.add_javascript("js/script.js") + app.add_js_file("js/script.js") From 7f5aa05f225c41d2eebfb19bdd162eb25ffd1b7c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Apr 2020 22:23:41 +1000 Subject: [PATCH 015/262] Retry on apt-get update problems --- .ci/install.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 180fa6582ce..3a82216596d 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -1,8 +1,22 @@ #!/bin/bash +aptget_update() +{ + if [ ! -z $1 ]; then + echo "" + echo "Retrying apt-get update..." + echo "" + fi + output=`sudo apt-get update 2>&1` + echo "$output" + if [[ $output == *[WE]:\ * ]]; then + return 1 + fi +} +aptget_update || aptget_update retry || aptget_update retry + set -e -sudo apt-get update 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 From f3d9377238b22289d6e2596743ad7d6e234ef728 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 3 Apr 2020 18:07:09 +0300 Subject: [PATCH 016/262] Revert "Only check docs on Travis" This reverts commit fbb14f67a30b612454bc7cf9a5f73d6b35e7eb14. --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1643edd615..51bb39918ff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -95,6 +95,12 @@ jobs: name: errors path: Tests/errors + - name: Docs + if: matrix.python-version == 3.8 + run: | + pip install sphinx-rtd-theme + make doccheck + - name: After success if: success() run: | From c87c1f33276c575b7365af5bd2d61c8231952335 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 3 Apr 2020 18:14:48 +0300 Subject: [PATCH 017/262] Only doccheck on Ubuntu/3.8 --- .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 51bb39918ff..f56af719c45 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -96,7 +96,7 @@ jobs: path: Tests/errors - name: Docs - if: matrix.python-version == 3.8 + if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.8 run: | pip install sphinx-rtd-theme make doccheck From 54f1ed6710a2e17dc6882528dc42358ed44fc6cd Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 3 Apr 2020 17:26:38 +0300 Subject: [PATCH 018/262] Ensure Python 3's pip to install requirements --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6e55e4c7a98..06208ad981e 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ debug: CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null install-req: - pip install -r requirements.txt + python3 -m pip install -r requirements.txt install-venv: virtualenv . From 86ef29efd69c36eac32fde2f3ce2c31f3c6126da Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 3 Apr 2020 17:27:16 +0300 Subject: [PATCH 019/262] Check GHA and AppVeyor as well as Travis CI --- RELEASING.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 3a285662cb3..5c08f9dcd48 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -6,7 +6,10 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th. * [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154 * [ ] Develop and prepare release in `master` branch. -* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in `master` branch. +* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions), + [Travis CI](https://travis-ci.org/github/python-pillow/Pillow) and + [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm + passing tests in `master` branch. * [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI. * [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py` * [ ] Update `CHANGES.rst`. @@ -38,7 +41,13 @@ Released as needed for security, installation or critical bug fixes. git checkout -t remotes/origin/5.2.x ``` * [ ] Cherry pick individual commits from `master` branch to release branch e.g. `5.2.x`. -* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. `5.2.x`. + + + +* [ ] Check [GitHub Actions](https://github.com/python-pillow/Pillow/actions), + [Travis CI](https://travis-ci.org/github/python-pillow/Pillow) and + [AppVeyor](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm + passing tests in release branch e.g. `5.2.x`. * [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py` * [ ] Run pre-release check via `make release-test`. * [ ] Create tag for release e.g.: From 595caf31fc414132278b37853e0ff1d43771b9c0 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 3 Apr 2020 17:27:34 +0300 Subject: [PATCH 020/262] Also push commits --- RELEASING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASING.md b/RELEASING.md index 5c08f9dcd48..a33a1684a7b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -53,6 +53,7 @@ Released as needed for security, installation or critical bug fixes. * [ ] Create tag for release e.g.: ```bash git tag 5.2.1 + git push git push --tags ``` * [ ] Create source distributions e.g.: From 3f2205d4ebbafdea70955261bb1277d8c4de009c Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 7 Apr 2020 10:31:41 +0300 Subject: [PATCH 021/262] Update release notes with CVEs [CI skip] --- docs/releasenotes/7.1.0.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/releasenotes/7.1.0.rst b/docs/releasenotes/7.1.0.rst index 346b9b49099..55a970c1e31 100644 --- a/docs/releasenotes/7.1.0.rst +++ b/docs/releasenotes/7.1.0.rst @@ -69,6 +69,16 @@ Passing a different value on Windows or macOS will force taking a snapshot using the selected X server; pass an empty string to use the default X server. XCB support is not included in pre-compiled wheels for Windows and macOS. +Security +======== + +This release includes security fixes. + +* CVE-2020-10177 Fix multiple OOB reads in FLI decoding +* CVE-2020-10378 Fix bounds overflow in PCX decoding +* CVE-2020-10379 Fix two buffer overflows in TIFF decoding +* CVE-2020-10994 Fix bounds overflow in JPEG 2000 decoding +* CVE-2020-11538 Fix buffer overflow in SGI-RLE decoding Other Changes ============= From 41799985e119e56de08d1356bea0048eb0e7b8d0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 10 Apr 2020 14:18:42 +1000 Subject: [PATCH 022/262] Removed success conditions --- .github/workflows/test-docker.yml | 2 -- .github/workflows/test-windows.yml | 2 -- .github/workflows/test.yml | 2 -- 3 files changed, 6 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index c10f9af3e74..b6e703fd9a3 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -46,7 +46,6 @@ jobs: sudo chown -R runner $GITHUB_WORKSPACE - name: After success - if: success() run: | PATH="$PATH:~/.local/bin" docker start pillow_container @@ -59,7 +58,6 @@ jobs: MATRIX_DOCKER: ${{ matrix.docker }} - name: Upload coverage - if: success() uses: codecov/codecov-action@v1 with: flags: GHA_Docker diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 705c61e50a0..adbe979686e 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -352,13 +352,11 @@ jobs: path: Tests/errors - name: After success - if: success() run: | .ci/after_success.sh shell: pwsh - name: Upload coverage - if: success() uses: codecov/codecov-action@v1 with: file: ./coverage.xml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f56af719c45..b8d2c6374c5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -102,12 +102,10 @@ jobs: make doccheck - name: After success - if: success() run: | .ci/after_success.sh - name: Upload coverage - if: success() run: bash <(curl -s https://codecov.io/bash) -F ${{ matrix.codecov-flag }} env: CODECOV_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} From dda6145fce8fbbe82db4d92cde41a64136ec02df Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 7 Apr 2020 09:58:21 +0300 Subject: [PATCH 023/262] Since Python 3.3 IOError and WindowsError have been merged into OSError --- Tests/check_j2k_overflow.py | 2 +- Tests/check_libtiff_segfault.py | 2 +- Tests/test_file_bufrstub.py | 4 ++-- Tests/test_file_dds.py | 4 ++-- Tests/test_file_fitsstub.py | 6 +++--- Tests/test_file_fpx.py | 2 +- Tests/test_file_gribstub.py | 4 ++-- Tests/test_file_hdf5stub.py | 6 +++--- Tests/test_file_jpeg.py | 12 ++++++------ Tests/test_file_jpeg2k.py | 2 +- Tests/test_file_libtiff.py | 8 ++++---- Tests/test_file_msp.py | 2 +- Tests/test_file_palm.py | 8 ++++---- Tests/test_file_pdf.py | 2 +- Tests/test_file_png.py | 12 ++++++------ Tests/test_file_ppm.py | 2 +- Tests/test_file_psd.py | 2 +- Tests/test_file_spider.py | 2 +- Tests/test_file_tiff.py | 6 +++--- Tests/test_file_webp.py | 2 +- Tests/test_file_wmf.py | 2 +- Tests/test_image.py | 4 ++-- Tests/test_imagefile.py | 16 ++++++++-------- Tests/test_imagefont.py | 14 +++++++------- Tests/test_imagegrab.py | 8 ++++---- Tests/test_imagepalette.py | 2 +- Tests/test_map.py | 2 +- Tests/test_sgi_crash.py | 2 +- docs/handbook/image-file-formats.rst | 2 +- docs/handbook/tutorial.rst | 10 +++++----- docs/releasenotes/7.0.0.rst | 2 +- selftest.py | 2 +- setup.py | 2 +- src/PIL/FpxImagePlugin.py | 2 +- src/PIL/GdImageFile.py | 2 +- src/PIL/GifImagePlugin.py | 2 +- src/PIL/Image.py | 2 +- src/PIL/ImageFile.py | 20 ++++++++++---------- src/PIL/ImageFont.py | 14 +++++++------- src/PIL/ImageGrab.py | 2 +- src/PIL/TiffImagePlugin.py | 4 ++-- src/PIL/__init__.py | 2 +- src/_imaging.c | 4 ++-- src/_imagingcms.c | 12 ++++++------ src/_imagingft.c | 6 +++--- src/_webp.c | 4 ++-- src/display.c | 22 +++++++++++----------- src/encode.c | 2 +- src/libImaging/Except.c | 2 +- src/libImaging/File.c | 2 +- src/libImaging/Imaging.h | 2 +- src/map.c | 6 +++--- 52 files changed, 135 insertions(+), 135 deletions(-) diff --git a/Tests/check_j2k_overflow.py b/Tests/check_j2k_overflow.py index f20ad674819..7a0a5f948d9 100644 --- a/Tests/check_j2k_overflow.py +++ b/Tests/check_j2k_overflow.py @@ -5,5 +5,5 @@ def test_j2k_overflow(tmp_path): im = Image.new("RGBA", (1024, 131584)) target = str(tmp_path / "temp.jpc") - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(target) diff --git a/Tests/check_libtiff_segfault.py b/Tests/check_libtiff_segfault.py index 5187385d621..6663ac09775 100644 --- a/Tests/check_libtiff_segfault.py +++ b/Tests/check_libtiff_segfault.py @@ -9,6 +9,6 @@ def test_libtiff_segfault(): libtiff >= 4.0.0 """ - with pytest.raises(IOError): + with pytest.raises(OSError): with Image.open(TEST_FILE) as im: im.load() diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py index ee6f3f2a4a2..6803a12307a 100644 --- a/Tests/test_file_bufrstub.py +++ b/Tests/test_file_bufrstub.py @@ -32,7 +32,7 @@ def test_load(): with Image.open(TEST_FILE) as im: # Act / Assert: stub cannot load without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() @@ -42,5 +42,5 @@ def test_save(tmp_path): tmpfile = str(tmp_path / "temp.bufr") # Act / Assert: stub cannot save without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(tmpfile) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index d157e15fd71..8960edea3dd 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -138,7 +138,7 @@ def test_short_header(): def short_header(): Image.open(BytesIO(img_file[:119])) - with pytest.raises(IOError): + with pytest.raises(OSError): short_header() @@ -152,7 +152,7 @@ def short_file(): with Image.open(BytesIO(img_file[:-100])) as im: im.load() - with pytest.raises(IOError): + with pytest.raises(OSError): short_file() diff --git a/Tests/test_file_fitsstub.py b/Tests/test_file_fitsstub.py index f9f6c4ba9b4..01bc2deeeb1 100644 --- a/Tests/test_file_fitsstub.py +++ b/Tests/test_file_fitsstub.py @@ -30,7 +30,7 @@ def test_load(): with Image.open(TEST_FILE) as im: # Act / Assert: stub cannot load without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() @@ -41,7 +41,7 @@ def test_save(): dummy_filename = "dummy.filename" # Act / Assert: stub cannot save without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(dummy_filename) - with pytest.raises(IOError): + with pytest.raises(OSError): FitsStubImagePlugin._save(im, dummy_fp, dummy_filename) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index ef8cdb5770b..a247de79c30 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -19,5 +19,5 @@ def test_invalid_file(): def test_fpx_invalid_number_of_bands(): - with pytest.raises(IOError, match="Invalid number of bands"): + with pytest.raises(OSError, match="Invalid number of bands"): Image.open("Tests/images/input_bw_five_bands.fpx") diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index 1cc1f47ac9e..9d9def96b10 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -32,7 +32,7 @@ def test_load(): with Image.open(TEST_FILE) as im: # Act / Assert: stub cannot load without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() @@ -42,5 +42,5 @@ def test_save(tmp_path): tmpfile = str(tmp_path / "temp.grib") # Act / Assert: stub cannot save without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(tmpfile) diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index 526fd7c99ce..862cafa912c 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -30,7 +30,7 @@ def test_load(): with Image.open(TEST_FILE) as im: # Act / Assert: stub cannot load without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() @@ -41,7 +41,7 @@ def test_save(): dummy_filename = "dummy.filename" # Act / Assert: stub cannot save without an implemented handler - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(dummy_filename) - with pytest.raises(IOError): + with pytest.raises(OSError): Hdf5StubImagePlugin._save(im, dummy_fp, dummy_filename) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 33045122891..9f963d80f92 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -147,7 +147,7 @@ def test_large_icc_meta(self, tmp_path): with Image.open("Tests/images/icc_profile_big.jpg") as im: f = str(tmp_path / "temp.jpg") icc_profile = im.info["icc_profile"] - # Should not raise IOError for image with icc larger than image size. + # Should not raise OSError for image with icc larger than image size. im.save( f, format="JPEG", @@ -379,14 +379,14 @@ def test_truncated_jpeg_should_read_all_the_data(self): ImageFile.LOAD_TRUNCATED_IMAGES = False assert im.getbbox() is not None - def test_truncated_jpeg_throws_IOError(self): + def test_truncated_jpeg_throws_oserror(self): filename = "Tests/images/truncated_jpeg.jpg" with Image.open(filename) as im: - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() # Test that the error is raised if loaded a second time - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() def test_qtables(self, tmp_path): @@ -552,7 +552,7 @@ def test_save_wrong_modes(self): out = BytesIO() for mode in ["LA", "La", "RGBA", "RGBa", "P"]: img = Image.new(mode, (20, 20)) - with pytest.raises(IOError): + with pytest.raises(OSError): img.save(out, "JPEG") def test_save_tiff_with_dpi(self, tmp_path): @@ -702,7 +702,7 @@ def test_fd_leak(self, tmp_path): im = Image.open(tmpfile) fp = im.fp assert not fp.closed - with pytest.raises(WindowsError): + with pytest.raises(OSError): os.remove(tmpfile) im.load() assert fp.closed diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 72bc7df672f..7b8b7a04a45 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -218,7 +218,7 @@ def test_16bit_jp2_roundtrips(): def test_unbound_local(): # prepatch, a malformed jp2 file could cause an UnboundLocalError exception. - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open("Tests/images/unbound_variable.jp2") diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 923bd610714..9523e5901b5 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -479,11 +479,11 @@ def xtest_bw_compression_w_rgb(self, tmp_path): im = hopper("RGB") out = str(tmp_path / "temp.tif") - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(out, compression="tiff_ccitt") - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(out, compression="group3") - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(out, compression="group4") def test_fp_leak(self): @@ -831,7 +831,7 @@ def test_sampleformat_not_corrupted(self): def test_realloc_overflow(self): TiffImagePlugin.READ_LIBTIFF = True with Image.open("Tests/images/tiff_overflow_rows_per_strip.tif") as im: - with pytest.raises(IOError) as e: + with pytest.raises(OSError) as e: im.load() # Assert that the error code is IMAGING_CODEC_MEMORY diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index 8f261388ee7..9b508a4e4ba 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -86,5 +86,5 @@ def test_cannot_save_wrong_mode(tmp_path): filename = str(tmp_path / "temp.msp") # Act/Assert - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(filename) diff --git a/Tests/test_file_palm.py b/Tests/test_file_palm.py index 886332dea82..38f6dccd967 100644 --- a/Tests/test_file_palm.py +++ b/Tests/test_file_palm.py @@ -72,19 +72,19 @@ def test_p_mode(tmp_path): roundtrip(tmp_path, mode) -def test_l_ioerror(tmp_path): +def test_l_oserror(tmp_path): # Arrange mode = "L" # Act / Assert - with pytest.raises(IOError): + with pytest.raises(OSError): helper_save_as_palm(tmp_path, mode) -def test_rgb_ioerror(tmp_path): +def test_rgb_oserror(tmp_path): # Arrange mode = "RGB" # Act / Assert - with pytest.raises(IOError): + with pytest.raises(OSError): helper_save_as_palm(tmp_path, mode) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index ea3b6c1d9f3..14a7a654f99 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -172,7 +172,7 @@ def test_pdf_open(tmp_path): def test_pdf_append_fails_on_nonexistent_file(): im = hopper("RGB") with tempfile.TemporaryDirectory() as temp_dir: - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(os.path.join(temp_dir, "nonexistent.pdf"), append=True) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 476995dd7a6..a2535b97fd0 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -105,7 +105,7 @@ def test_broken(self): # file was checked into Subversion as a text file. test_file = "Tests/images/broken.png" - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open(test_file) def test_bad_text(self): @@ -334,7 +334,7 @@ def test_load_verify(self): def test_verify_struct_error(self): # Check open/load/verify exception (#1755) - # offsets to test, -10: breaks in i32() in read. (IOError) + # offsets to test, -10: breaks in i32() in read. (OSError) # -13: breaks in crc, txt chunk. # -14: malformed chunk @@ -344,7 +344,7 @@ def test_verify_struct_error(self): with Image.open(BytesIO(test_file)) as im: assert im.fp is not None - with pytest.raises((IOError, SyntaxError)): + with pytest.raises((OSError, SyntaxError)): im.verify() def test_verify_ignores_crc_error(self): @@ -463,7 +463,7 @@ def test_scary(self): data = b"\x89" + fd.read() pngfile = BytesIO(data) - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open(pngfile) def test_trns_rgb(self): @@ -575,13 +575,13 @@ def test_textual_chunks_after_idat(self): # Raises a SyntaxError in load_end with Image.open("Tests/images/broken_data_stream.png") as im: - with pytest.raises(IOError): + with pytest.raises(OSError): assert isinstance(im.text, dict) # Raises a UnicodeDecodeError in load_end with Image.open("Tests/images/truncated_image.png") as im: # The file is truncated - with pytest.raises(IOError): + with pytest.raises(OSError): im.text() ImageFile.LOAD_TRUNCATED_IMAGES = True assert isinstance(im.text, dict) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 6b91ba28adb..15c08e43857 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -64,7 +64,7 @@ def test_neg_ppm(): # has been removed. The default opener doesn't accept negative # sizes. - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open("Tests/images/negative_size.ppm") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 4d8c6eba466..011efc9773e 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -125,5 +125,5 @@ def test_combined_larger_than_size(): # If we instead take the 'size' of the extra data field as the source of truth, # then the seek can't be negative - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open("Tests/images/combined_larger_than_size.psd") diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index c7446e161c9..8c69491e692 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -134,7 +134,7 @@ def test_is_int_not_a_number(): def test_invalid_file(): invalid_file = "Tests/images/invalid.spider" - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open(invalid_file) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index a996f0b0e7f..7aa55dad016 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -196,7 +196,7 @@ def test_save_rgba(self, tmp_path): def test_save_unsupported_mode(self, tmp_path): im = hopper("HSV") outfile = str(tmp_path / "temp.tif") - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(outfile) def test_little_endian(self): @@ -249,7 +249,7 @@ def test_32bit_float(self): assert im.getextrema() == (-3.140936851501465, 3.140684127807617) def test_unknown_pixel_mode(self): - with pytest.raises(IOError): + with pytest.raises(OSError): Image.open("Tests/images/hopper_unknown_pixel_mode.tif") def test_n_frames(self): @@ -614,7 +614,7 @@ def test_fd_leak(self, tmp_path): im = Image.open(tmpfile) fp = im.fp assert not fp.closed - with pytest.raises(WindowsError): + with pytest.raises(OSError): os.remove(tmpfile) im.load() assert fp.closed diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 22957f06d9b..1b8aa9f8a6a 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -22,7 +22,7 @@ def test_unsupported(self): WebPImagePlugin.SUPPORTED = False file_path = "Tests/images/hopper.webp" - pytest.warns(UserWarning, lambda: pytest.raises(IOError, Image.open, file_path)) + pytest.warns(UserWarning, lambda: pytest.raises(OSError, Image.open, file_path)) if HAVE_WEBP: WebPImagePlugin.SUPPORTED = True diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index 03444eb9d3f..9db4f63583f 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -74,5 +74,5 @@ def test_save(tmp_path): for ext in [".wmf", ".emf"]: tmpfile = str(tmp_path / ("temp" + ext)) - with pytest.raises(IOError): + with pytest.raises(OSError): im.save(tmpfile) diff --git a/Tests/test_image.py b/Tests/test_image.py index 3a0b7bd62d9..4d1b66dff61 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -57,7 +57,7 @@ def test_image_modes_fail(self): assert str(e.value) == "unrecognized image mode" def test_exception_inheritance(self): - assert issubclass(UnidentifiedImageError, IOError) + assert issubclass(UnidentifiedImageError, OSError) def test_sanity(self): @@ -687,5 +687,5 @@ def test_encode_registry(self): assert enc.args == ("RGB", "args", "extra") def test_encode_registry_fail(self): - with pytest.raises(IOError): + with pytest.raises(OSError): Image._getencoder("RGB", "DoesNotExist", ("args",), extra=("extra",)) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 883e3f5668e..e2b52857d57 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -71,7 +71,7 @@ def roundtrip(format): im1, im2 = roundtrip("JPEG") # lossy compression assert_image(im1, im2.mode, im2.size) - with pytest.raises(IOError): + with pytest.raises(OSError): roundtrip("PDF") def test_ico(self): @@ -93,9 +93,9 @@ def test_safeblock(self): assert_image_equal(im1, im2) - def test_raise_ioerror(self): - with pytest.raises(IOError): - ImageFile.raise_ioerror(1) + def test_raise_oserror(self): + with pytest.raises(OSError): + ImageFile.raise_oserror(1) def test_raise_typeerror(self): with pytest.raises(TypeError): @@ -107,17 +107,17 @@ def test_negative_stride(self): input = f.read() p = ImageFile.Parser() p.feed(input) - with pytest.raises(IOError): + with pytest.raises(OSError): p.close() @skip_unless_feature("zlib") def test_truncated_with_errors(self): with Image.open("Tests/images/truncated_image.png") as im: - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() # Test that the error is raised if loaded a second time - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() @skip_unless_feature("zlib") @@ -132,7 +132,7 @@ def test_truncated_without_errors(self): @skip_unless_feature("zlib") def test_broken_datastream_with_errors(self): with Image.open("Tests/images/broken_data_stream.png") as im: - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() @skip_unless_feature("zlib") diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index e93aff4b22d..0e642cde28e 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -393,14 +393,14 @@ def test_load_path_not_found(self): filename = "somefilenamethatdoesntexist.ttf" # Act/Assert - with pytest.raises(IOError): + with pytest.raises(OSError): ImageFont.load_path(filename) - with pytest.raises(IOError): + with pytest.raises(OSError): ImageFont.truetype(filename) def test_load_non_font_bytes(self): with open("Tests/images/hopper.jpg", "rb") as f: - with pytest.raises(IOError): + with pytest.raises(OSError): ImageFont.truetype(f) def test_default_font(self): @@ -615,9 +615,9 @@ def test_variation_get(self): font.get_variation_axes() return - with pytest.raises(IOError): + with pytest.raises(OSError): font.get_variation_names() - with pytest.raises(IOError): + with pytest.raises(OSError): font.get_variation_axes() font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf") @@ -669,7 +669,7 @@ def test_variation_set_by_name(self): font.set_variation_by_name("Bold") return - with pytest.raises(IOError): + with pytest.raises(OSError): font.set_variation_by_name("Bold") def _check_text(font, path, epsilon): @@ -701,7 +701,7 @@ def test_variation_set_by_axes(self): font.set_variation_by_axes([100]) return - with pytest.raises(IOError): + with pytest.raises(OSError): font.set_variation_by_axes([500, 50]) def _check_text(font, path, epsilon): diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 7908477344c..82e746fda97 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -31,23 +31,23 @@ def test_grab_x11(self): im2 = ImageGrab.grab(xdisplay="") assert_image(im2, im2.mode, im2.size) - except IOError as e: + except OSError as e: pytest.skip(str(e)) @pytest.mark.skipif(Image.core.HAVE_XCB, reason="tests missing XCB") def test_grab_no_xcb(self): if sys.platform not in ("win32", "darwin"): - with pytest.raises(IOError) as e: + with pytest.raises(OSError) as e: ImageGrab.grab() assert str(e.value).startswith("Pillow was built without XCB support") - with pytest.raises(IOError) as e: + with pytest.raises(OSError) as e: ImageGrab.grab(xdisplay="") assert str(e.value).startswith("Pillow was built without XCB support") @pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB") def test_grab_invalid_xdisplay(self): - with pytest.raises(IOError) as e: + with pytest.raises(OSError) as e: ImageGrab.grab(xdisplay="error.test:0.0") assert str(e.value).startswith("X connection failed") diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index 29771cf0301..4ef2d3ffdb5 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -145,5 +145,5 @@ def test_2bit_palette(tmp_path): def test_invalid_palette(): - with pytest.raises(IOError): + with pytest.raises(OSError): ImagePalette.load("Tests/images/hopper.jpg") diff --git a/Tests/test_map.py b/Tests/test_map.py index b2f3ff2271b..bdb59bfe07e 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -20,7 +20,7 @@ def test_overflow(): # This image hits the offset test. with Image.open("Tests/images/l2rgb_read.bmp") as im: - with pytest.raises((ValueError, MemoryError, IOError)): + with pytest.raises((ValueError, MemoryError, OSError)): im.load() Image.MAX_IMAGE_PIXELS = max_pixels diff --git a/Tests/test_sgi_crash.py b/Tests/test_sgi_crash.py index 6f3fc6f5d13..b1a3e1515a9 100644 --- a/Tests/test_sgi_crash.py +++ b/Tests/test_sgi_crash.py @@ -10,5 +10,5 @@ def test_crashes(test_file): with open(test_file, "rb") as f: im = Image.open(f) - with pytest.raises(IOError): + with pytest.raises(OSError): im.load() diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index a12de82e2c0..40db9fe2b5d 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1226,7 +1226,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum **append** Set to True to append pages to an existing PDF file. If the file doesn't - exist, an :py:exc:`IOError` will be raised. + exist, an :py:exc:`OSError` will be raised. .. versionadded:: 5.1.0 diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst index 86840261578..94a8e3aa1b0 100644 --- a/docs/handbook/tutorial.rst +++ b/docs/handbook/tutorial.rst @@ -29,7 +29,7 @@ bands in the image, and also the pixel type and depth. Common modes are “L” (luminance) for greyscale images, “RGB” for true color images, and “CMYK” for pre-press images. -If the file cannot be opened, an :py:exc:`IOError` exception is raised. +If the file cannot be opened, an :py:exc:`OSError` exception is raised. Once you have an instance of the :py:class:`~PIL.Image.Image` class, you can use the methods defined by this class to process and manipulate the image. For @@ -76,7 +76,7 @@ Convert files to JPEG try: with Image.open(infile) as im: im.save(outfile) - except IOError: + except OSError: print("cannot convert", infile) A second argument can be supplied to the :py:meth:`~PIL.Image.Image.save` @@ -100,7 +100,7 @@ Create JPEG thumbnails with Image.open(infile) as im: im.thumbnail(size) im.save(outfile, "JPEG") - except IOError: + except OSError: print("cannot create thumbnail for", infile) It is important to note that the library doesn’t decode or load the raster data @@ -125,7 +125,7 @@ Identify Image Files try: with Image.open(infile) as im: print(infile, im.format, "%dx%d" % im.size, im.mode) - except IOError: + except OSError: pass Cutting, pasting, and merging images @@ -450,7 +450,7 @@ context manager:: ... If everything goes well, the result is an :py:class:`PIL.Image.Image` object. -Otherwise, an :exc:`IOError` exception is raised. +Otherwise, an :exc:`OSError` exception is raised. You can use a file-like object instead of the filename. The object must implement :py:meth:`~file.read`, :py:meth:`~file.seek` and diff --git a/docs/releasenotes/7.0.0.rst b/docs/releasenotes/7.0.0.rst index e0e76434249..73c44275ca9 100644 --- a/docs/releasenotes/7.0.0.rst +++ b/docs/releasenotes/7.0.0.rst @@ -85,7 +85,7 @@ Custom unidentified image error ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Pillow will now throw a custom ``UnidentifiedImageError`` when an image cannot be -identified. For backwards compatibility, this will inherit from ``IOError``. +identified. For backwards compatibility, this will inherit from ``OSError``. New argument ``reducing_gap`` for Image.resize() and Image.thumbnail() methods ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/selftest.py b/selftest.py index ea52256f79a..a9a02ef710c 100755 --- a/selftest.py +++ b/selftest.py @@ -47,7 +47,7 @@ def testimage(): ('PPM', 'RGB', (128, 128)) >>> try: ... _info(Image.open("Tests/images/hopper.jpg")) - ... except IOError as v: + ... except OSError as v: ... print(v) ('JPEG', 'RGB', (128, 128)) diff --git a/setup.py b/setup.py index 3e1a812b6a3..1236b5df5a8 100755 --- a/setup.py +++ b/setup.py @@ -579,7 +579,7 @@ def build_extensions(self): try: listdir = os.listdir(directory) except Exception: - # WindowsError, FileNotFoundError + # OSError, FileNotFoundError continue for name in listdir: if name.startswith("openjpeg-") and os.path.isfile( diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 8d252c79cf5..81501e2442e 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -99,7 +99,7 @@ def _open_index(self, index=1): colors = [] bands = i32(s, 4) if bands > 4: - raise IOError("Invalid number of bands") + raise OSError("Invalid number of bands") for i in range(bands): # note: for now, we ignore the "uncalibrated" flag colors.append(i32(s, 8 + i * 4) & 0x7FFFFFFF) diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index 54c88712c84..b3ab01a4ebd 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -74,7 +74,7 @@ def open(fp, mode="r"): :param mode: Optional mode. In this version, if the mode argument is given, it must be "r". :returns: An image instance. - :raises IOError: If the image could not be read. + :raises OSError: If the image could not be read. """ if mode != "r": raise ValueError("bad mode") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 1d94fc7c7c7..9d360beaec2 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -255,7 +255,7 @@ def _seek(self, frame): else: pass - # raise IOError, "illegal GIF tag `%x`" % i8(s) + # raise OSError, "illegal GIF tag `%x`" % i8(s) try: if self.disposal_method < 2: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3ced965e870..19ddcbba506 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2080,7 +2080,7 @@ def save(self, fp, format=None, **params): :returns: None :exception ValueError: If the output format could not be determined from the file name. Use the format option to solve this. - :exception IOError: If the file could not be written. The file + :exception OSError: If the file could not be written. The file may have been created, and may contain partial data. """ diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 6287968652e..2177dbf5255 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -49,7 +49,12 @@ } -def raise_ioerror(error): +# +# -------------------------------------------------------------------- +# Helpers + + +def raise_oserror(error): try: message = Image.core.getcodecstatus(error) except AttributeError: @@ -59,11 +64,6 @@ def raise_ioerror(error): raise OSError(message + " when reading image file") -# -# -------------------------------------------------------------------- -# Helpers - - def _tilesort(t): # sort on offset return t[2] @@ -267,7 +267,7 @@ def load(self): if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: # still raised if decoder fails to return anything - raise_ioerror(err_code) + raise_oserror(err_code) return Image.Image.load(self) @@ -358,7 +358,7 @@ def feed(self, data): (Consumer) Feed data to the parser. :param data: A string buffer. - :exception IOError: If the parser failed to parse the image file. + :exception OSError: If the parser failed to parse the image file. """ # collect data @@ -390,7 +390,7 @@ def feed(self, data): if e < 0: # decoding error self.image = None - raise_ioerror(e) + raise_oserror(e) else: # end of image return @@ -444,7 +444,7 @@ def close(self): (Consumer) Close the stream. :returns: An image object. - :exception IOError: If the parser failed to parse the image file either + :exception OSError: If the parser failed to parse the image file either because it cannot be identified or cannot be decoded. """ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 027e4c42e6c..25ceaa16aeb 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -499,7 +499,7 @@ def font_variant( def get_variation_names(self): """ :returns: A list of the named styles in a variation font. - :exception IOError: If the font is not a variation font. + :exception OSError: If the font is not a variation font. """ try: names = self.font.getvarnames() @@ -510,7 +510,7 @@ def get_variation_names(self): def set_variation_by_name(self, name): """ :param name: The name of the style. - :exception IOError: If the font is not a variation font. + :exception OSError: If the font is not a variation font. """ names = self.get_variation_names() if not isinstance(name, bytes): @@ -529,7 +529,7 @@ def set_variation_by_name(self, name): def get_variation_axes(self): """ :returns: A list of the axes in a variation font. - :exception IOError: If the font is not a variation font. + :exception OSError: If the font is not a variation font. """ try: axes = self.font.getvaraxes() @@ -542,7 +542,7 @@ def get_variation_axes(self): def set_variation_by_axes(self, axes): """ :param axes: A list of values for each axis. - :exception IOError: If the font is not a variation font. + :exception OSError: If the font is not a variation font. """ try: self.font.setvaraxes(axes) @@ -586,7 +586,7 @@ def load(filename): :param filename: Name of font file. :return: A font object. - :exception IOError: If the file could not be read. + :exception OSError: If the file could not be read. """ f = ImageFont() f._load_pilfont(filename) @@ -638,7 +638,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): :param layout_engine: Which layout engine to use, if available: `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`. :return: A font object. - :exception IOError: If the file could not be read. + :exception OSError: If the file could not be read. """ def freetype(font): @@ -698,7 +698,7 @@ def load_path(filename): :param filename: Name of font file. :return: A font object. - :exception IOError: If the file could not be read. + :exception OSError: If the file could not be read. """ for directory in sys.path: if isDirectory(directory): diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 66e2e856009..39d5e23c72d 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -60,7 +60,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N return im # use xdisplay=None for default display on non-win32/macOS systems if not Image.core.HAVE_XCB: - raise IOError("Pillow was built without XCB support") + raise OSError("Pillow was built without XCB support") size, data = Image.core.grabscreen_x11(xdisplay) im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) if bbox: diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 74fb695162f..4b04752edae 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1123,7 +1123,7 @@ def _load_libtiff(self): if hasattr(self.fp, "flush"): self.fp.flush() except OSError: - # io.BytesIO have a fileno, but returns an IOError if + # io.BytesIO have a fileno, but returns an OSError if # it doesn't use a file descriptor. fp = False @@ -1147,7 +1147,7 @@ def _load_libtiff(self): # underlying string for stringio. # # Rearranging for supporting byteio items, since they have a fileno - # that returns an IOError if there's no underlying fp. Easier to + # that returns an OSError if there's no underlying fp. Easier to # deal with here by reordering. if DEBUG: print("have getvalue. just sending in a string from getvalue") diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index f9cb15772dd..81b87e012cd 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -131,5 +131,5 @@ def __le__(self, other): ] -class UnidentifiedImageError(IOError): +class UnidentifiedImageError(OSError): pass diff --git a/src/_imaging.c b/src/_imaging.c index 0c3d766f34e..9a9a51f719f 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -268,9 +268,9 @@ static const char* readonly = "image is readonly"; /* static const char* no_content = "image has no content"; */ void * -ImagingError_IOError(void) +ImagingError_OSError(void) { - PyErr_SetString(PyExc_IOError, "error when accessing file"); + PyErr_SetString(PyExc_OSError, "error when accessing file"); return NULL; } diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 0b22ab69547..0f287013b27 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -107,7 +107,7 @@ cms_profile_open(PyObject* self, PyObject* args) hProfile = cmsOpenProfileFromFile(sProfile, "r"); if (!hProfile) { - PyErr_SetString(PyExc_IOError, "cannot open profile file"); + PyErr_SetString(PyExc_OSError, "cannot open profile file"); return NULL; } @@ -126,7 +126,7 @@ cms_profile_fromstring(PyObject* self, PyObject* args) hProfile = cmsOpenProfileFromMem(pProfile, nProfile); if (!hProfile) { - PyErr_SetString(PyExc_IOError, "cannot open profile from string"); + PyErr_SetString(PyExc_OSError, "cannot open profile from string"); return NULL; } @@ -150,18 +150,18 @@ cms_profile_tobytes(PyObject* self, PyObject* args) profile = ((CmsProfileObject*)CmsProfile)->profile; if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) { - PyErr_SetString(PyExc_IOError, "Could not determine profile size"); + PyErr_SetString(PyExc_OSError, "Could not determine profile size"); return NULL; } pProfile = (char*)malloc(nProfile); if (!pProfile) { - PyErr_SetString(PyExc_IOError, "Out of Memory"); + PyErr_SetString(PyExc_OSError, "Out of Memory"); return NULL; } if (!cmsSaveProfileToMem(profile, pProfile, &nProfile)) { - PyErr_SetString(PyExc_IOError, "Could not get profile"); + PyErr_SetString(PyExc_OSError, "Could not get profile"); free(pProfile); return NULL; } @@ -655,7 +655,7 @@ _profile_read_mlu(CmsProfileObject* self, cmsTagSignature info) buf = malloc(len); if (!buf) { - PyErr_SetString(PyExc_IOError, "Out of Memory"); + PyErr_SetString(PyExc_OSError, "Out of Memory"); return NULL; } /* Just in case the next call fails. */ diff --git a/src/_imagingft.c b/src/_imagingft.c index f6a5b7d59b3..f1b299d9136 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -139,11 +139,11 @@ geterror(int code) for (i = 0; ft_errors[i].message; i++) if (ft_errors[i].code == code) { - PyErr_SetString(PyExc_IOError, ft_errors[i].message); + PyErr_SetString(PyExc_OSError, ft_errors[i].message); return NULL; } - PyErr_SetString(PyExc_IOError, "unknown freetype error"); + PyErr_SetString(PyExc_OSError, "unknown freetype error"); return NULL; } @@ -259,7 +259,7 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) if (!library) { PyErr_SetString( - PyExc_IOError, + PyExc_OSError, "failed to initialize FreeType library" ); return NULL; diff --git a/src/_webp.c b/src/_webp.c index 93cf7ae85f8..babea60f3ea 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -70,7 +70,7 @@ PyObject* HandleMuxError(WebPMuxError err, char* chunk) { case WEBP_MUX_BAD_DATA: case WEBP_MUX_NOT_ENOUGH_DATA: - PyErr_SetString(PyExc_IOError, message); + PyErr_SetString(PyExc_OSError, message); break; default: @@ -423,7 +423,7 @@ PyObject* _anim_decoder_get_next(PyObject* self) WebPAnimDecoderObject* decp = (WebPAnimDecoderObject*)self; if (!WebPAnimDecoderGetNext(decp->dec, &buf, ×tamp)) { - PyErr_SetString(PyExc_IOError, "failed to read next frame"); + PyErr_SetString(PyExc_OSError, "failed to read next frame"); return NULL; } diff --git a/src/display.c b/src/display.c index 21869b26ebe..eff9de5c40a 100644 --- a/src/display.c +++ b/src/display.c @@ -158,7 +158,7 @@ _getdc(ImagingDisplayObject* display, PyObject* args) dc = GetDC(window); if (!dc) { - PyErr_SetString(PyExc_IOError, "cannot create dc"); + PyErr_SetString(PyExc_OSError, "cannot create dc"); return NULL; } @@ -397,7 +397,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer); error: - PyErr_SetString(PyExc_IOError, "screen grab failed"); + PyErr_SetString(PyExc_OSError, "screen grab failed"); DeleteDC(screen_copy); DeleteDC(screen); @@ -677,7 +677,7 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) ); if (!wnd) { - PyErr_SetString(PyExc_IOError, "failed to create window"); + PyErr_SetString(PyExc_OSError, "failed to create window"); return NULL; } @@ -755,7 +755,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) } if (!meta) { - PyErr_SetString(PyExc_IOError, "cannot load metafile"); + PyErr_SetString(PyExc_OSError, "cannot load metafile"); return NULL; } @@ -774,12 +774,12 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) ); if (!bitmap) { - PyErr_SetString(PyExc_IOError, "cannot create bitmap"); + PyErr_SetString(PyExc_OSError, "cannot create bitmap"); goto error; } if (!SelectObject(dc, bitmap)) { - PyErr_SetString(PyExc_IOError, "cannot select bitmap"); + PyErr_SetString(PyExc_OSError, "cannot select bitmap"); goto error; } @@ -793,7 +793,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) FillRect(dc, &rect, GetStockObject(WHITE_BRUSH)); if (!PlayEnhMetaFile(dc, meta, &rect)) { - PyErr_SetString(PyExc_IOError, "cannot render metafile"); + PyErr_SetString(PyExc_OSError, "cannot render metafile"); goto error; } @@ -845,7 +845,7 @@ PyImaging_GrabScreenX11(PyObject* self, PyObject* args) connection = xcb_connect(display_name, &screen_number); if (xcb_connection_has_error(connection)) { - PyErr_Format(PyExc_IOError, "X connection failed: error %i", xcb_connection_has_error(connection)); + PyErr_Format(PyExc_OSError, "X connection failed: error %i", xcb_connection_has_error(connection)); xcb_disconnect(connection); return NULL; } @@ -860,7 +860,7 @@ PyImaging_GrabScreenX11(PyObject* self, PyObject* args) if (screen == NULL || screen->root == 0) { // this case is usually caught with "X connection failed: error 6" above xcb_disconnect(connection); - PyErr_SetString(PyExc_IOError, "X screen not found"); + PyErr_SetString(PyExc_OSError, "X screen not found"); return NULL; } @@ -874,7 +874,7 @@ PyImaging_GrabScreenX11(PyObject* self, PyObject* args) 0, 0, width, height, 0x00ffffff), &error); if (reply == NULL) { - PyErr_Format(PyExc_IOError, "X get_image failed: error %i (%i, %i, %i)", + PyErr_Format(PyExc_OSError, "X get_image failed: error %i (%i, %i, %i)", error->error_code, error->major_code, error->minor_code, error->resource_id); free(error); xcb_disconnect(connection); @@ -887,7 +887,7 @@ PyImaging_GrabScreenX11(PyObject* self, PyObject* args) buffer = PyBytes_FromStringAndSize((char*)xcb_get_image_data(reply), xcb_get_image_data_length(reply)); } else { - PyErr_Format(PyExc_IOError, "unsupported bit depth: %i", reply->depth); + PyErr_Format(PyExc_OSError, "unsupported bit depth: %i", reply->depth); } free(reply); diff --git a/src/encode.c b/src/encode.c index 41ba124c4ee..cbf1da3a9bd 100644 --- a/src/encode.c +++ b/src/encode.c @@ -201,7 +201,7 @@ _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) if (write(fh, buf, status) < 0) { ImagingSectionLeave(&cookie); free(buf); - return PyErr_SetFromErrno(PyExc_IOError); + return PyErr_SetFromErrno(PyExc_OSError); } } while (encoder->state.errcode == 0); diff --git a/src/libImaging/Except.c b/src/libImaging/Except.c index 515d85d1fe9..d6d2b218674 100644 --- a/src/libImaging/Except.c +++ b/src/libImaging/Except.c @@ -26,7 +26,7 @@ /* exception state */ void * -ImagingError_IOError(void) +ImagingError_OSError(void) { fprintf(stderr, "*** exception: file access error\n"); return NULL; diff --git a/src/libImaging/File.c b/src/libImaging/File.c index 6f014c1f822..117af9e3573 100644 --- a/src/libImaging/File.c +++ b/src/libImaging/File.c @@ -59,7 +59,7 @@ ImagingSavePPM(Imaging im, const char* outfile) fp = fopen(outfile, "wb"); if (!fp) { - (void) ImagingError_IOError(); + (void) ImagingError_OSError(); return 0; } diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 9032fcf0783..47f577139c7 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -229,7 +229,7 @@ extern void ImagingSectionLeave(ImagingSectionCookie* cookie); /* Exceptions */ /* ---------- */ -extern void* ImagingError_IOError(void); +extern void* ImagingError_OSError(void); extern void* ImagingError_MemoryError(void); extern void* ImagingError_ModeError(void); /* maps to ValueError by default */ extern void* ImagingError_Mismatch(void); /* maps to ValueError by default */ diff --git a/src/map.c b/src/map.c index 54e0fdb2296..47cc9d6de7a 100644 --- a/src/map.c +++ b/src/map.c @@ -70,7 +70,7 @@ PyImaging_MapperNew(const char* filename, int readonly) FILE_ATTRIBUTE_NORMAL, NULL); if (mapper->hFile == (HANDLE)-1) { - PyErr_SetString(PyExc_IOError, "cannot open file"); + PyErr_SetString(PyExc_OSError, "cannot open file"); Py_DECREF(mapper); return NULL; } @@ -81,7 +81,7 @@ PyImaging_MapperNew(const char* filename, int readonly) 0, 0, NULL); if (mapper->hMap == (HANDLE)-1) { CloseHandle(mapper->hFile); - PyErr_SetString(PyExc_IOError, "cannot map file"); + PyErr_SetString(PyExc_OSError, "cannot map file"); Py_DECREF(mapper); return NULL; } @@ -209,7 +209,7 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) size = ysize * stride; if (mapper->offset + size > mapper->size) { - PyErr_SetString(PyExc_IOError, "image file truncated"); + PyErr_SetString(PyExc_OSError, "image file truncated"); return NULL; } From 7bb51a4aca74ca24be7e011104441418ae7c6b36 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 Apr 2020 19:53:35 +1000 Subject: [PATCH 024/262] Raise DeprecationWarning on raise_ioerror --- Tests/test_imagefile.py | 5 +++++ src/PIL/ImageFile.py | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index e2b52857d57..649b0ac9b1d 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -93,6 +93,11 @@ def test_safeblock(self): assert_image_equal(im1, im2) + def test_raise_ioerror(self): + with pytest.raises(IOError): + with pytest.raises(DeprecationWarning): + ImageFile.raise_ioerror(1) + def test_raise_oserror(self): with pytest.raises(OSError): ImageFile.raise_oserror(1) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 2177dbf5255..8637a47f953 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -30,6 +30,7 @@ import io import struct import sys +import warnings from . import Image from ._util import isPath @@ -64,6 +65,15 @@ def raise_oserror(error): raise OSError(message + " when reading image file") +def raise_ioerror(error): + warnings.warn( + "raise_ioerror is deprecated and will be removed in a future release. " + "Use raise_oserror instead.", + DeprecationWarning, + ) + return raise_oserror(error) + + def _tilesort(t): # sort on offset return t[2] From d8213a245d648d0740cdd9f01f70c1998c312df2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 7 Apr 2020 20:39:40 +1000 Subject: [PATCH 025/262] ImageFile.raise_ioerror is now deprecated [ci skip] --- docs/deprecations.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 227a5bc823e..350a6002c12 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,6 +12,15 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. +ImageFile.raise_ioerror +~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 7.2.0 + +IOError was merged into OSError in Python 3.3. So, ``ImageFile.raise_ioerror`` +is now deprecated and will be removed in a future released. Use +``ImageFile.raise_oserror`` instead. + PILLOW_VERSION constant ~~~~~~~~~~~~~~~~~~~~~~~ From 29147e7fadfe2c1f9666c48bbc38c99495489d77 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 7 Apr 2020 22:59:18 +1000 Subject: [PATCH 026/262] Highlighted errors [ci skip] Co-Authored-By: Hugo van Kemenade --- docs/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 350a6002c12..203921c0b84 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -17,7 +17,7 @@ ImageFile.raise_ioerror .. deprecated:: 7.2.0 -IOError was merged into OSError in Python 3.3. So, ``ImageFile.raise_ioerror`` +``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror`` is now deprecated and will be removed in a future released. Use ``ImageFile.raise_oserror`` instead. From 5427878ed19027f5e701cd709c7dbcd633a3f9e7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Apr 2020 10:50:33 +1000 Subject: [PATCH 027/262] Updated PyPy to 7.3.1 --- .appveyor.yml | 1 - winbuild/appveyor_install_pypy3.cmd | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f6ac309c49a..e395f2ae6f6 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -24,7 +24,6 @@ environment: DEPLOY: NO - PYTHON: C:/vp/pypy3 EXECUTABLE: bin/pypy.exe - PIP_DIR: bin VENV: YES diff --git a/winbuild/appveyor_install_pypy3.cmd b/winbuild/appveyor_install_pypy3.cmd index 75a22ca59e4..4ec5384be4d 100644 --- a/winbuild/appveyor_install_pypy3.cmd +++ b/winbuild/appveyor_install_pypy3.cmd @@ -1,3 +1,3 @@ -curl -fsSL -o pypy3.zip https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.0-win32.zip +curl -fsSL -o pypy3.zip https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-win32.zip 7z x pypy3.zip -oc:\ -c:\Python37\Scripts\virtualenv.exe -p c:\pypy3.6-v7.3.0-win32\pypy3.exe c:\vp\pypy3 +c:\Python37\Scripts\virtualenv.exe -p c:\pypy3.6-v7.3.1-win32\pypy3.exe c:\vp\pypy3 From 417cbd155790c64783885e6cf4c850446a4b0dc2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Apr 2020 18:01:13 +1000 Subject: [PATCH 028/262] Document getexif [ci skip] --- docs/reference/Image.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 8af56f6c1e0..c824ff1763f 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -179,6 +179,7 @@ This helps to get the bounding box coordinates of the input image: .. automethod:: PIL.Image.Image.getcolors .. automethod:: PIL.Image.Image.getdata .. automethod:: PIL.Image.Image.getextrema +.. automethod:: PIL.Image.Image.getexif .. automethod:: PIL.Image.Image.getpalette .. automethod:: PIL.Image.Image.getpixel .. automethod:: PIL.Image.Image.histogram From f17f1bc6074adc657dda54f6dca750e1d9fa80cf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Apr 2020 20:43:49 +1000 Subject: [PATCH 029/262] Added method argument to single frame WebP saving --- Tests/test_file_webp.py | 73 +++++++++++++--------------- src/PIL/WebPImagePlugin.py | 2 + src/_webp.c | 97 ++++++++++++++++++++++++-------------- 3 files changed, 98 insertions(+), 74 deletions(-) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 1b8aa9f8a6a..f538b0ecf85 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,3 +1,5 @@ +import io + import pytest from PIL import Image, WebPImagePlugin @@ -54,15 +56,10 @@ def test_read_rgb(self): # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0) - def test_write_rgb(self, tmp_path): - """ - Can we write a RGB mode file to webp without error. - Does it have the bits we expect? - """ - + def _roundtrip(self, tmp_path, mode, epsilon, args={}): temp_file = str(tmp_path / "temp.webp") - hopper(self.rgb_mode).save(temp_file) + hopper(mode).save(temp_file, **args) with Image.open(temp_file) as image: assert image.mode == self.rgb_mode assert image.size == (128, 128) @@ -70,18 +67,38 @@ def test_write_rgb(self, tmp_path): image.load() image.getdata() - # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm - assert_image_similar_tofile( - image, "Tests/images/hopper_webp_write.ppm", 12.0 - ) + if mode == self.rgb_mode: + # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm + assert_image_similar_tofile( + image, "Tests/images/hopper_webp_write.ppm", 12.0 + ) # This test asserts that the images are similar. If the average pixel # difference between the two images is less than the epsilon value, # then we're going to accept that it's a reasonable lossy version of - # the image. The old lena images for WebP are showing ~16 on - # Ubuntu, the jpegs are showing ~18. - target = hopper(self.rgb_mode) - assert_image_similar(image, target, 12.0) + # the image. + target = hopper(mode) + if mode != self.rgb_mode: + target = target.convert(self.rgb_mode) + assert_image_similar(image, target, epsilon) + + def test_write_rgb(self, tmp_path): + """ + Can we write a RGB mode file to webp without error? + Does it have the bits we expect? + """ + + self._roundtrip(tmp_path, self.rgb_mode, 12.5) + + def test_write_method(self, tmp_path): + self._roundtrip(tmp_path, self.rgb_mode, 12.0, {"method": 6}) + + buffer_no_args = io.BytesIO() + hopper().save(buffer_no_args, format="WEBP") + + buffer_method = io.BytesIO() + hopper().save(buffer_method, format="WEBP", method=6) + assert buffer_no_args.getbuffer() != buffer_method.getbuffer() def test_write_unsupported_mode_L(self, tmp_path): """ @@ -89,18 +106,7 @@ def test_write_unsupported_mode_L(self, tmp_path): similar to the original file. """ - temp_file = str(tmp_path / "temp.webp") - hopper("L").save(temp_file) - with Image.open(temp_file) as image: - assert image.mode == self.rgb_mode - assert image.size == (128, 128) - assert image.format == "WEBP" - - image.load() - image.getdata() - target = hopper("L").convert(self.rgb_mode) - - assert_image_similar(image, target, 10.0) + self._roundtrip(tmp_path, "L", 10.0) def test_write_unsupported_mode_P(self, tmp_path): """ @@ -108,18 +114,7 @@ def test_write_unsupported_mode_P(self, tmp_path): similar to the original file. """ - temp_file = str(tmp_path / "temp.webp") - hopper("P").save(temp_file) - with Image.open(temp_file) as image: - assert image.mode == self.rgb_mode - assert image.size == (128, 128) - assert image.format == "WEBP" - - image.load() - image.getdata() - target = hopper("P").convert(self.rgb_mode) - - assert_image_similar(image, target, 50.0) + self._roundtrip(tmp_path, "P", 50.0) def test_WebPEncode_with_invalid_args(self): """ diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index eda6855087d..6c4f7decf7e 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -325,6 +325,7 @@ def _save(im, fp, filename): if isinstance(exif, Image.Exif): exif = exif.tobytes() xmp = im.encoderinfo.get("xmp", "") + method = im.encoderinfo.get("method", 0) if im.mode not in _VALID_WEBP_LEGACY_MODES: alpha = ( @@ -342,6 +343,7 @@ def _save(im, fp, filename): float(quality), im.mode, icc_profile, + method, exif, xmp, ) diff --git a/src/_webp.c b/src/_webp.c index babea60f3ea..dcb6c85cb75 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -545,6 +545,7 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) int height; int lossless; float quality_factor; + int method; uint8_t* rgb; uint8_t* icc_bytes; uint8_t* exif_bytes; @@ -556,49 +557,75 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) Py_ssize_t exif_size; Py_ssize_t xmp_size; size_t ret_size; + int rgba_mode; + int channels; + int ok; ImagingSectionCookie cookie; + WebPConfig config; + WebPMemoryWriter writer; + WebPPicture pic; - if (!PyArg_ParseTuple(args, "y#iiifss#s#s#", + if (!PyArg_ParseTuple(args, "y#iiifss#is#s#", (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode, - &icc_bytes, &icc_size, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { + &icc_bytes, &icc_size, &method, &exif_bytes, &exif_size, &xmp_bytes, &xmp_size)) { return NULL; } - if (strcmp(mode, "RGBA")==0){ - if (size < width * height * 4){ - Py_RETURN_NONE; - } - #if WEBP_ENCODER_ABI_VERSION >= 0x0100 - if (lossless) { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4 * width, &output); - ImagingSectionLeave(&cookie); - } else - #endif - { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeRGBA(rgb, width, height, 4 * width, quality_factor, &output); - ImagingSectionLeave(&cookie); - } - } else if (strcmp(mode, "RGB")==0){ - if (size < width * height * 3){ - Py_RETURN_NONE; - } - #if WEBP_ENCODER_ABI_VERSION >= 0x0100 - if (lossless) { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3 * width, &output); - ImagingSectionLeave(&cookie); - } else - #endif - { - ImagingSectionEnter(&cookie); - ret_size = WebPEncodeRGB(rgb, width, height, 3 * width, quality_factor, &output); - ImagingSectionLeave(&cookie); - } - } else { + + rgba_mode = strcmp(mode, "RGBA") == 0; + if (!rgba_mode && strcmp(mode, "RGB") != 0) { Py_RETURN_NONE; } + channels = rgba_mode ? 4 : 3; + if (size < width * height * channels) { + Py_RETURN_NONE; + } + + // Setup config for this frame + if (!WebPConfigInit(&config)) { + PyErr_SetString(PyExc_RuntimeError, "failed to initialize config!"); + return NULL; + } + config.lossless = lossless; + config.quality = quality_factor; + config.method = method; + + // Validate the config + if (!WebPValidateConfig(&config)) { + PyErr_SetString(PyExc_ValueError, "invalid configuration"); + return NULL; + } + + if (!WebPPictureInit(&pic)) { + PyErr_SetString(PyExc_ValueError, "could not initialise picture"); + return NULL; + } + pic.width = width; + pic.height = height; + pic.use_argb = 1; // Don't convert RGB pixels to YUV + + if (rgba_mode) { + WebPPictureImportRGBA(&pic, rgb, channels * width); + } else { + WebPPictureImportRGB(&pic, rgb, channels * width); + } + + WebPMemoryWriterInit(&writer); + pic.writer = WebPMemoryWrite; + pic.custom_ptr = &writer; + + ImagingSectionEnter(&cookie); + ok = WebPEncode(&config, &pic); + ImagingSectionLeave(&cookie); + + WebPPictureFree(&pic); + if (!ok) { + PyErr_SetString(PyExc_ValueError, "encoding error"); + return NULL; + } + output = writer.mem; + ret_size = writer.size; + #ifndef HAVE_WEBPMUX if (ret_size > 0) { PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); From 89e05cdf56fa3dd93df9c4301f8bd88399534080 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 2 Oct 2019 19:48:55 +0200 Subject: [PATCH 030/262] extract scripts from test-windows.yml (cherry-picked from commit 1a6ee35d0e5fa1bf4081649a6ebf67208d76f322) --- .github/workflows/test-windows.yml | 204 ++---------- setup.py | 2 +- winbuild/build_prepare.py | 494 +++++++++++++++++++++++++++++ winbuild/commands.py | 111 +++++++ 4 files changed, 634 insertions(+), 177 deletions(-) create mode 100644 winbuild/build_prepare.py create mode 100644 winbuild/commands.py diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index adbe979686e..6425f3b5c28 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -4,7 +4,6 @@ on: [push, pull_request] jobs: build: - runs-on: windows-2019 strategy: fail-fast: false @@ -70,155 +69,28 @@ jobs: Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" $env:PYTHON=$env:pythonLocation - xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\ - xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\ + mkdir $env:GITHUB_WORKSPACE\winbuild\depends\ + xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\depends\ + xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\depends\ xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ cd $env:GITHUB_WORKSPACE/winbuild/ - python.exe $env:GITHUB_WORKSPACE\winbuild\build_dep.py - env: - EXECUTABLE: bin\python.exe + python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py shell: pwsh - name: Build dependencies / libjpeg - if: false - run: | - REM FIXME uses /MT not /MD, see makefile.vc and win32.mak for more info - - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\jpeg-9d - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - nmake -nologo -f makefile.vc setup-vc6 - nmake -nologo -f makefile.vc clean - nmake -nologo -f makefile.vc nodebug=1 libjpeg.lib cjpeg.exe djpeg.exe - copy /Y /B j*.h %INCLIB% - copy /Y /B *.lib %INCLIB% - copy /Y /B *.exe %INCLIB% - shell: cmd - - - name: Build dependencies / libjpeg-turbo - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\libjpeg-turbo-2.0.3 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF - set CMAKE=%CMAKE% -DENABLE_SHARED:BOOL=OFF -DWITH_JPEG8:BOOL=TRUE -DWITH_CRT_DLL:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release - %CMAKE% -G "NMake Makefiles" . - nmake -nologo -f Makefile clean - nmake -nologo -f Makefile jpeg-static cjpeg-static djpeg-static - copy /Y /B j*.h %INCLIB% - copy /Y /B jpeg-static.lib %INCLIB%\libjpeg.lib - copy /Y /B cjpeg-static.exe %INCLIB%\cjpeg.exe - copy /Y /B djpeg-static.exe %INCLIB%\djpeg.exe - shell: cmd - + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_libjpeg.cmd" - name: Build dependencies / zlib - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\zlib-1.2.11 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - nmake -nologo -f win32\Makefile.msc clean - nmake -nologo -f win32\Makefile.msc zlib.lib - copy /Y /B z*.h %INCLIB% - copy /Y /B *.lib %INCLIB% - copy /Y /B zlib.lib %INCLIB%\z.lib - shell: cmd - - - name: Build dependencies / LibTIFF - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\tiff-4.1.0 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - copy %GITHUB_WORKSPACE%\winbuild\tiff.opt nmake.opt - nmake -nologo -f makefile.vc clean - nmake -nologo -f makefile.vc lib - copy /Y /B libtiff\tiff*.h %INCLIB% - copy /Y /B libtiff\*.dll %INCLIB% - copy /Y /B libtiff\*.lib %INCLIB% - shell: cmd - + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_zlib.cmd" + - name: Build dependencies / LibTiff + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_libtiff.cmd" - name: Build dependencies / WebP - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\libwebp-1.1.0 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - rmdir /S /Q output\release-static - nmake -nologo -f Makefile.vc CFG=release-static OBJDIR=output ARCH=${{ matrix.architecture }} all - mkdir %INCLIB%\webp - copy /Y /B src\webp\*.h %INCLIB%\webp - copy /Y /B output\release-static\${{ matrix.architecture }}\lib\* %INCLIB% - shell: cmd - + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_libwebp.cmd" - name: Build dependencies / FreeType - run: | - REM Toolkit v100 not available; missing VCTargetsPath; Clean fails - - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\freetype-2.10.1 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - rmdir /S /Q objs - set DefaultPlatformToolset=v142 - set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\ - set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" - powershell -Command "(gc builds\windows\vc2010\freetype.vcxproj) -replace 'MultiThreaded<', 'MultiThreadedDLL<' | Out-File -encoding ASCII builds\windows\vc2010\freetype.vcxproj" - %MSBUILD% builds\windows\vc2010\freetype.sln /t:Build /p:Configuration="Release Static" /p:Platform=${{ matrix.platform-msbuild }} /m - xcopy /Y /E /Q include %INCLIB% - copy /Y /B "objs\${{ matrix.platform-msbuild }}\Release Static\freetype.lib" %INCLIB% - shell: cmd - + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_freetype.cmd" - name: Build dependencies / LCMS2 - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\lcms2-2.8 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - rmdir /S /Q Lib - rmdir /S /Q Projects\VC2015\Release - set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\ - set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe" - powershell %GITHUB_WORKSPACE%\winbuild\lcms2_patch.ps1 - %MSBUILD% Projects\VC2015\lcms2.sln /t:Clean;lcms2_static /p:Configuration="Release" /p:Platform=${{ matrix.platform-msbuild }} /m - xcopy /Y /E /Q include %INCLIB% - copy /Y /B Lib\MS\*.lib %INCLIB% - shell: cmd - + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_lcms2.cmd" - name: Build dependencies / OpenJPEG - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\openjpeg-2.3.1msvcr10-x32 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF - set CMAKE=%CMAKE% -DBUILD_THIRDPARTY:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF - set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release - %CMAKE% -G "NMake Makefiles" . - nmake -nologo -f Makefile clean - nmake -nologo -f Makefile - mkdir %INCLIB%\openjpeg-2.3.1 - copy /Y /B src\lib\openjp2\*.h %INCLIB%\openjpeg-2.3.1 - copy /Y /B bin\*.lib %INCLIB% - shell: cmd + run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_openjpeg.cmd" # GPL licensed; skip if building wheels - name: Build dependencies / libimagequant @@ -304,22 +176,12 @@ jobs: - name: Build Pillow run: | - set PYTHON=%pythonLocation% - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set MPLSRC=%GITHUB_WORKSPACE% - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - cd /D %GITHUB_WORKSPACE% - set LIB=%INCLIB%;%PYTHON%\tcl - set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE% - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - set MSSdk=1 - set DISTUTILS_USE_SDK=1 - set py_vcruntime_redist=true - %PYTHON%\python.exe setup.py build_ext install - rem Add libraqm.dll (copied to INCLIB) to PATH. - path %INCLIB%;%PATH% - %PYTHON%\python.exe selftest.py --installed - shell: cmd + cd $env:GITHUB_WORKSPACE + # & winbuild\build_dep_tcl.cmd + # & winbuild\build_dep_tk.cmd + & winbuild\build_pillow.cmd + & $env:pythonLocation\python.exe selftest.py --installed + shell: pwsh # failing with PyPy3 - name: Enable heap verification @@ -330,12 +192,8 @@ jobs: - name: Test Pillow run: | - set PYTHON=%pythonLocation% - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - rem Add libraqm.dll (copied to INCLIB) to PATH. - path %INCLIB%;%PATH% - cd /D %GITHUB_WORKSPACE% - %PYTHON%\python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests + path %GITHUB_WORKSPACE%\\winbuild\\build\\bin;%PATH% + python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests shell: cmd - name: Prepare to upload errors @@ -366,22 +224,16 @@ jobs: - name: Build wheel id: wheel if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" - run: | - for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ##[set-output name=dist;]dist-%%a - for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a - set PYTHON=%pythonLocation% - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set MPLSRC=%GITHUB_WORKSPACE% - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - cd /D %GITHUB_WORKSPACE% - set LIB=%INCLIB%;%PYTHON%\tcl - set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE% - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - %PYTHON%\python.exe setup.py bdist_wheel - shell: cmd + run: "%GITHUB_WORKSPACE%\\winbuild\\build_pillow_wheel.cmd" - uses: actions/upload-artifact@v1 if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" with: - name: ${{ steps.wheel.outputs.dist }} + name: dist path: dist + +# - uses: actions/upload-artifact@v1 +# if: matrix.architecture == 'x86' +# with: +# name: lib +# path: "winbuild\\build\\3.x\\x86\\lib" diff --git a/setup.py b/setup.py index 1236b5df5a8..b55b1de62cc 100755 --- a/setup.py +++ b/setup.py @@ -120,7 +120,7 @@ def get_version(): "codec_fd", ) -DEBUG = False +DEBUG = True class DependencyException(Exception): diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py new file mode 100644 index 00000000000..c17af7b9593 --- /dev/null +++ b/winbuild/build_prepare.py @@ -0,0 +1,494 @@ +import os +import shutil +import subprocess +import winreg +from itertools import count + +from commands import * + +SF_MIRROR = "http://iweb.dl.sourceforge.net" + +# use PYTHON to select architecture +architectures = { + "x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"}, + "x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"}, +} + +# use PYTHON + architecture to select config +pythons = { + "pypy3.6": {"config-x86": "3.5"}, + "3.5": {"config-x86": "3.5", "config-x64": "3.5"}, + "3.6": {"config-x86": "3.6", "config-x64": "3.6"}, + "3.7": {"config-x86": "3.6", "config-x64": "3.6"}, +} + +# select deps and libs +configs = { + "3.5": { + "deps": [ + "libjpeg-turbo-2.0.3", + "zlib-1.2.11", + "tiff-4.0.10", + "libwebp-1.0.3", + "freetype-2.10.1", + "lcms2-2.9", + "openjpeg-2.3.1", + "ghostscript-9.27", + # "tcl-8.6", + # "tk-8.6", + ], + "vcvars_ver": "14.0", + "vs_ver": "2015", + }, + "3.6": { + "deps": [ + "libjpeg-turbo-2.0.3", + "zlib-1.2.11", + "tiff-4.0.10", + "libwebp-1.0.3", + "freetype-2.10.1", + "lcms2-2.9", + "openjpeg-2.3.1", + "ghostscript-9.27", + # "tcl-8.6", + # "tk-8.6", + ], + "vs_ver": "2017", + }, +} + +header = [ + cmd_set("BUILD", "{build_dir}"), + cmd_set("INCLUDE", "{inc_dir}"), + cmd_set("INCLIB", "{lib_dir}"), + cmd_set("LIB", "{lib_dir}"), + cmd_append("PATH", "{bin_dir}"), + "@echo on", +] + +# dependencies +deps = { + "jpeg-9c": { + "name": "libjpeg", + # FIXME HTTP 403 + "url": "http://www.ijg.org/files/jpegsr9c.zip", + "filename": "jpegsr9c.zip", + "build": [ + # FIXME builds with -MT, not -MD + cmd_nmake("makefile.vc", "setup-vc6"), + cmd_nmake("makefile.vc", "clean"), + cmd_nmake("makefile.vc", "libjpeg.lib", "nodebug=1"), + ], + "headers": [r"j*.h"], + "libs": [r"*.lib"], + }, + "libjpeg-turbo-2.0.3": { + "name": "libjpeg", + "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", + "filename": "libjpeg-turbo-2.0.3.tar.gz", + "build": [ + cmd_cmake([ + "-DENABLE_SHARED:BOOL=FALSE", + "-DWITH_JPEG8:BOOL=TRUE", + "-DWITH_CRT_DLL:BOOL=TRUE", + ]), + cmd_nmake(target="clean"), + cmd_nmake(target="jpeg-static"), + cmd_copy("jpeg-static.lib", "libjpeg.lib"), + cmd_nmake(target="cjpeg-static"), + cmd_copy("cjpeg-static.exe", "cjpeg.exe"), + cmd_nmake(target="djpeg-static"), + cmd_copy("djpeg-static.exe", "djpeg.exe"), + ], + "headers": ["j*.h"], + "libs": ["libjpeg.lib"], + "bins": ["cjpeg.exe", "djpeg.exe"], + }, + "zlib-1.2.11": { + "name": "zlib", + "url": "http://zlib.net/zlib1211.zip", + "filename": "zlib1211.zip", + "build": [ + cmd_nmake(r"win32\Makefile.msc", "clean"), + cmd_nmake(r"win32\Makefile.msc", "zlib.lib"), + cmd_copy("zlib.lib", "z.lib"), + ], + "headers": [r"z*.h"], + "libs": [r"*.lib"], + }, + "tiff-4.0.10": { + "name": "libtiff", + # FIXME FTP timeout + "url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz", + "filename": "tiff-4.0.10.tar.gz", + "build": [ + cmd_copy(r"{script_dir}\tiff.opt", "nmake.opt"), + cmd_nmake("makefile.vc", "clean"), + cmd_nmake("makefile.vc", "lib", "RuntimeLibrary=-MT"), + ], + "headers": [r"libtiff\tiff*.h"], + "libs": [r"libtiff\*.lib"], + # "bins": [r"libtiff\*.dll"], + }, + "libwebp-1.0.3": { + "name": "libwebp", + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz", # noqa: E501 + "filename": "libwebp-1.0.3.tar.gz", + "build": [ + cmd_rmdir(r"output\release-static"), # clean + cmd_nmake( + "Makefile.vc", + "all", + ["CFG=release-static", "OBJDIR=output", "ARCH={architecture}"] + ), + cmd_mkdir(r"{inc_dir}\webp"), + cmd_copy(r"src\webp\*.h", r"{inc_dir}\webp"), + ], + "libs": [r"output\release-static\{architecture}\lib\*.lib"], + }, + "freetype-2.10.1": { + "name": "freetype", + "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501 + "filename": "freetype-2.10.1.tar.gz", + "build": [ + cmd_rmdir("objs"), + # freetype setting is /MD for .dll and /MT for .lib, we need /MD + cmd_patch_replace( + r"builds\windows\vc2010\freetype.vcxproj", + "MultiThreaded<", + "MultiThreadedDLL<" + ), + cmd_msbuild(r"builds\windows\vc2010\freetype.sln", "Release Static", "Clean"), # TODO failing on GHA # noqa: E501 + cmd_msbuild(r"builds\windows\vc2010\freetype.sln", "Release Static", "Build"), + cmd_xcopy("include", "{inc_dir}"), + ], + "libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"], + # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], + }, + "lcms2-2.9": { + "name": "lcms2", + "url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.9.tar.gz", + "filename": "lcms2-2.9.tar.gz", + "build": [ + cmd_rmdir("Lib"), + cmd_rmdir(r"Projects\VC{vs_ver}\Release"), + # lcms2-2.8\VC2015 setting is /MD for x86 and /MT for x64, we need /MD always + cmd_patch_replace( + r"Projects\VC2017\lcms2.sln", "MultiThreaded<", "MultiThreadedDLL<" + ), + cmd_msbuild(r"Projects\VC{vs_ver}\lcms2.sln", "Release", "Clean"), + cmd_msbuild(r"Projects\VC{vs_ver}\lcms2.sln", "Release", "lcms2_static"), + cmd_xcopy("include", "{inc_dir}"), + ], + "libs": [r"Lib\MS\*.lib"], + }, + "openjpeg-2.3.1": { + "name": "openjpeg", + "url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", + "filename": "openjpeg-2.3.1.tar.gz", + "build": [ + cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), + cmd_nmake(target="clean"), + cmd_nmake(), + cmd_mkdir(r"{inc_dir}\openjpeg-2.3.1"), + cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.3.1"), + ], + "libs": [r"bin\*.lib"], + }, + "ghostscript-9.27": { + "name": "ghostscript", + "url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501 + "filename": "ghostscript-9.27.tar.gz", + "build": [ + cmd_set("MSVC_VERSION", 14), + cmd_if_eq("{architecture}", "x64", cmd_set("WIN64", '""')), + cmd_nmake(r"psi\msvc.mak"), + ], + "bins": [r"bin\*"], + }, + "tcl-8.5": { + "name": "tcl", + "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip", + "filename": "tcl8519-src.zip", + }, + "tk-8.5": { + "name": "tk", + "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip", + "filename": "tk8519-src.zip", + }, + "tcl-8.6": { + "name": "tcl", + "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip", + "filename": "tcl869-src.zip", + "headers": [r"generic\*.h"], + }, + "tk-8.6": { + "name": "tk", + "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip", + "filename": "tk869-src.zip", + "build": [ + r"""mkdir {inc_dir}\X11""", + r"""copy /Y /B xlib\X11\* "{inc_dir}\X11\" """, + ], + "headers": [r"generic\*.h"], + }, +} + + +# based on distutils._msvccompiler from CPython 3.7.4 +def find_vs2017(config): + root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") + if not root: + print("Program Files not found") + return None + + try: + vspath = subprocess.check_output([ + os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), + "-latest", + "-prerelease", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + "-products", "*", + ]).decode(encoding="mbcs").strip() + except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): + print("vswhere not found") + return None + + if not os.path.isdir(os.path.join(vspath, "VC", "Auxiliary", "Build")): + print("Visual Studio seems to be missing C compiler") + return None + + vs = { + "header": [], + # nmake selected by vcvarsall + "nmake": "nmake.exe", + } + + msbuild = os.path.join(vspath, "MSBuild", "15.0", "Bin", "MSBuild.exe") + if os.path.isfile(msbuild): + # default_platform_toolset = "v140" + vs["msbuild"] = '"{}"'.format(msbuild) + # vs["header"].append(cmd_set("DefaultPlatformToolset", default_platform_toolset)) + else: + print("Visual Studio MSBuild not found") + return None + + vcvarsall = os.path.join(vspath, "VC", "Auxiliary", "Build", "vcvarsall.bat") + if not os.path.isfile(vcvarsall): + print("Visual Studio vcvarsall not found") + return None + vcvars_ver = "-vcvars_ver={}".format(config["vcvars_ver"]) if "vcvars_ver" in config else "" + vs["header"].append('call "{}" {{vcvars_arch}} {}'.format(vcvarsall, vcvars_ver)) + + return vs + + +def find_sdk71a(): + try: + key = winreg.OpenKeyEx( + winreg.HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A", + access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY, + ) + except OSError: + return None + with key: + for i in count(): + try: + v_name, v_value, v_type = winreg.EnumValue(key, i) + except OSError: + return None + if v_name == "InstallationFolder" and v_type == winreg.REG_SZ: + sdk_dir = v_value + break + else: + return None + + if not os.path.isdir(sdk_dir): + return None + + sdk = { + "header": [ + # for win32.mak + cmd_append("INCLUDE", os.path.join(sdk_dir, "Include")), + # for ghostscript + cmd_set("RCOMP", '"{}"'.format(os.path.join(sdk_dir, "Bin", "RC.EXE"))), + ] + } + + return sdk + + +def match(values, target): + for key, value in values.items(): + if key in target: + return {"name": key, **value} + + +def extract_dep(url, filename): + import urllib.request + import tarfile + import zipfile + + file = os.path.join(depends_dir, filename) + if not os.path.exists(file): + ex = None + for i in range(3): + try: + print("Fetching %s (attempt %d)..." % (url, i + 1)) + content = urllib.request.urlopen(url).read() + with open(file, "wb") as f: + f.write(content) + break + except urllib.error.URLError as e: + ex = e + else: + raise RuntimeError(ex) + if filename.endswith(".zip"): + with zipfile.ZipFile(file) as zf: + zf.extractall(build_dir) + elif filename.endswith(".tar.gz") or filename.endswith(".tgz"): + with tarfile.open(file, "r:gz") as tgz: + tgz.extractall(build_dir) + else: + raise RuntimeError("Unknown archive type: " + filename) + + +def write_script(name, lines): + name = os.path.join(script_dir, name) + lines = [line.format(**prefs) for line in lines] + print("Writing " + name) + with open(name, "w") as f: + f.write("\n\r".join(lines)) + for line in lines: + print(" " + line) + + +def get_footer(dep): + lines = [] + for out in dep.get("headers", []): + lines.append(cmd_copy(out, "{inc_dir}")) + for out in dep.get("libs", []): + lines.append(cmd_copy(out, "{lib_dir}")) + for out in dep.get("bins", []): + lines.append(cmd_copy(out, "{bin_dir}")) + return lines + + +def build_dep(name): + dep = deps[name] + file = "build_dep_{name}.cmd".format(name=dep["name"]) + + extract_dep(dep["url"], dep["filename"]) + + lines = ["cd /D %s" % os.path.join(build_dir, name)] + lines.extend(prefs["header"]) + + lines.extend(dep.get("build", [])) + + lines.extend(get_footer(dep)) + + write_script(file, lines) + return file + + +def build_dep_all(): + lines = ["cd {script_dir}"] + for dep_name in prefs["deps"]: + lines.append('cmd.exe /c "%s"' % build_dep(dep_name)) + write_script("build_dep_all.ps1", lines) + + +def build_pillow(wheel=False): + if not wheel: + op, filename = "install", "build_pillow.cmd" + else: + op, filename = "bdist_wheel", "build_pillow_wheel.cmd" + + lines = [] + if path_dir is not None and not wheel: + lines.append(cmd_xcopy("{bin_dir}", path_dir)) + lines.extend(prefs["header"]) + lines.extend( + [ + cmd_cd("{pillow_dir}"), + cmd_append("LIB", r"{python_dir}\tcl"), + r'"{{python_dir}}\python.exe" setup.py build_ext {}'.format(op), + # r"""%PYTHON%\python.exe selftest.py --installed""", + ] + ) + + write_script(filename, lines) + + +if __name__ == "__main__": + script_dir = os.path.dirname(os.path.realpath(__file__)) + depends_dir = os.path.join(script_dir, "depends") + python_dir = os.environ["PYTHON"] + + # copy binaries to this directory + path_dir = os.environ.get("PILLOW_BIN") + + # use PYTHON to select architecture + arch_prefs = match(architectures, python_dir) + if arch_prefs is None: + architecture = "x86" + print("WARN: Could not determine architecture, guessing " + architecture) + arch_prefs = architectures[architecture] + else: + architecture = arch_prefs["name"] + + # use PYTHON to select python version + python_prefs = match(pythons, python_dir) + if python_prefs is None: + raise KeyError("Failed to determine Python version from PYTHON: " + python_dir) + + # use python version + architecture to select build config + config_name = python_prefs["config-" + architecture] + config = configs[config_name] + + vs2017 = find_vs2017(config) + if vs2017 is None: + raise RuntimeError("Visual Studio 2017 not found") + + sdk71a = find_sdk71a() + if sdk71a is None: + raise RuntimeError("Windows SDK v7.1A not found") + + build_dir = os.path.join(script_dir, "build", config_name, architecture) + lib_dir = os.path.join(build_dir, "lib") + inc_dir = os.path.join(build_dir, "inc") + bin_dir = os.path.join(build_dir, "bin") + + # for path in [lib_dir, inc_dir, bin_dir]: + # shutil.rmtree(path) + for path in [depends_dir, build_dir, lib_dir, inc_dir, bin_dir]: + os.makedirs(path, exist_ok=True) + + prefs = { + "python_version": python_prefs["name"], + "architecture": architecture, + "script_dir": script_dir, + "depends_dir": depends_dir, + "python_dir": python_dir, + "build_dir": build_dir, + "lib_dir": lib_dir, + "inc_dir": inc_dir, + "bin_dir": bin_dir, + "pillow_dir": os.path.realpath(os.path.join(script_dir, "..")), + # TODO auto find: + "cmake": "cmake.exe", + } + + dicts = [vs2017, sdk71a, arch_prefs, python_prefs, config] + for x in dicts: + prefs.update(x) + prefs["header"] = sum((x.get("header", []) for x in dicts), header) + del prefs["name"] + + print("Target: Python {python_version} {architecture}".format(**prefs)) + + build_dep_all() + build_pillow() + build_pillow(wheel=True) diff --git a/winbuild/commands.py b/winbuild/commands.py new file mode 100644 index 00000000000..2fbbc9b1af1 --- /dev/null +++ b/winbuild/commands.py @@ -0,0 +1,111 @@ +# builtins + + +def cmd_cd(path): + return "cd /D {path}".format(**locals()) + + +def cmd_set(name, value): + return "set {name}={value}".format(**locals()) + + +def cmd_prepend(name, value): + op = "path " if name == "PATH" else "set {name}=" + return (op + "{value};%{name}%").format(**locals()) + + +def cmd_append(name, value): + op = "path " if name == "PATH" else "set {name}=" + return (op + "%{name}%;{value}").format(**locals()) + + +def cmd_copy(src, tgt): + return 'copy /Y /B "{src}" "{tgt}"'.format(**locals()) + + +def cmd_xcopy(src, tgt): + return 'xcopy /Y /E "{src}" "{tgt}"'.format(**locals()) + + +def cmd_mkdir(path): + return 'mkdir "{path}"'.format(**locals()) + + +def cmd_rmdir(path): + return 'rmdir /S /Q "{path}"'.format(**locals()) + + +def cmd_if_eq(a, b, cmd): + return 'if "{a}"=="{b}" {cmd}'.format(**locals()) + + +# tools + + +def cmd_nmake(makefile=None, target="", params=None): + if params is None: + params = "" + elif isinstance(params, list) or isinstance(params, tuple): + params = " ".join(params) + else: + params = str(params) + + return " ".join( + [ + "{{nmake}}", + "-nologo", + '-f "{makefile}"' if makefile is not None else "", + "{params}", + '"{target}"', + ] + ).format(**locals()) + + +def cmd_cmake(params=None, file="."): + if params is None: + params = "" + elif isinstance(params, list) or isinstance(params, tuple): + params = " ".join(params) + else: + params = str(params) + return " ".join( + [ + "{{cmake}}", + "-DCMAKE_VERBOSE_MAKEFILE=ON", + "-DCMAKE_RULE_MESSAGES:BOOL=OFF", + "-DCMAKE_BUILD_TYPE=Release", + "{params}", + '-G "NMake Makefiles"', + '"{file}"', + ] + ).format(**locals()) + + +def cmd_msbuild( + file, configuration="Release", target="Build", platform="{msbuild_arch}" +): + return " ".join( + [ + "{{msbuild}}", + "{file}", + '/t:"{target}"', + '/p:Configuration="{configuration}"', + "/p:Platform={platform}", + "/m", + ] + ).format(**locals()) + + +# patch tools + + +def cmd_patch_replace(file, src, dst): + return " ".join( + [ + "echo", + "(Get-Content '{file}')", + '-replace "{src}", "{dst}"', + "^| Out-File -encoding ASCII '{file}'", + "> temp.ps1\r\npowershell .\\temp.ps1", + ] + ).format(**locals()) From 46f8729e991be208ec98a59c64cb66eb93a1f3e5 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 01:10:22 +0100 Subject: [PATCH 031/262] add vs2019 support --- winbuild/build_prepare.py | 123 +++++++++++--------------------------- 1 file changed, 35 insertions(+), 88 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index c17af7b9593..937bdc88617 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -22,41 +22,28 @@ "3.7": {"config-x86": "3.6", "config-x64": "3.6"}, } -# select deps and libs +# select preferred compiler configs = { "3.5": { - "deps": [ - "libjpeg-turbo-2.0.3", - "zlib-1.2.11", - "tiff-4.0.10", - "libwebp-1.0.3", - "freetype-2.10.1", - "lcms2-2.9", - "openjpeg-2.3.1", - "ghostscript-9.27", - # "tcl-8.6", - # "tk-8.6", - ], "vcvars_ver": "14.0", "vs_ver": "2015", }, "3.6": { - "deps": [ - "libjpeg-turbo-2.0.3", - "zlib-1.2.11", - "tiff-4.0.10", - "libwebp-1.0.3", - "freetype-2.10.1", - "lcms2-2.9", - "openjpeg-2.3.1", - "ghostscript-9.27", - # "tcl-8.6", - # "tk-8.6", - ], "vs_ver": "2017", }, } +# selected dependencies +deps_list = [ + "libjpeg-turbo-2.0.3", + "zlib-1.2.11", + "tiff-4.0.10", + "libwebp-1.0.3", + "freetype-2.10.1", + "lcms2-2.9", + "openjpeg-2.3.1", +] + header = [ cmd_set("BUILD", "{build_dir}"), cmd_set("INCLUDE", "{inc_dir}"), @@ -68,20 +55,6 @@ # dependencies deps = { - "jpeg-9c": { - "name": "libjpeg", - # FIXME HTTP 403 - "url": "http://www.ijg.org/files/jpegsr9c.zip", - "filename": "jpegsr9c.zip", - "build": [ - # FIXME builds with -MT, not -MD - cmd_nmake("makefile.vc", "setup-vc6"), - cmd_nmake("makefile.vc", "clean"), - cmd_nmake("makefile.vc", "libjpeg.lib", "nodebug=1"), - ], - "headers": [r"j*.h"], - "libs": [r"*.lib"], - }, "libjpeg-turbo-2.0.3": { "name": "libjpeg", "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", @@ -195,43 +168,6 @@ ], "libs": [r"bin\*.lib"], }, - "ghostscript-9.27": { - "name": "ghostscript", - "url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501 - "filename": "ghostscript-9.27.tar.gz", - "build": [ - cmd_set("MSVC_VERSION", 14), - cmd_if_eq("{architecture}", "x64", cmd_set("WIN64", '""')), - cmd_nmake(r"psi\msvc.mak"), - ], - "bins": [r"bin\*"], - }, - "tcl-8.5": { - "name": "tcl", - "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip", - "filename": "tcl8519-src.zip", - }, - "tk-8.5": { - "name": "tk", - "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip", - "filename": "tk8519-src.zip", - }, - "tcl-8.6": { - "name": "tcl", - "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip", - "filename": "tcl869-src.zip", - "headers": [r"generic\*.h"], - }, - "tk-8.6": { - "name": "tk", - "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip", - "filename": "tk869-src.zip", - "build": [ - r"""mkdir {inc_dir}\X11""", - r"""copy /Y /B xlib\X11\* "{inc_dir}\X11\" """, - ], - "headers": [r"generic\*.h"], - }, } @@ -265,14 +201,21 @@ def find_vs2017(config): "nmake": "nmake.exe", } + # vs2017 msbuild = os.path.join(vspath, "MSBuild", "15.0", "Bin", "MSBuild.exe") if os.path.isfile(msbuild): - # default_platform_toolset = "v140" + default_platform_toolset = "v140" vs["msbuild"] = '"{}"'.format(msbuild) - # vs["header"].append(cmd_set("DefaultPlatformToolset", default_platform_toolset)) else: - print("Visual Studio MSBuild not found") - return None + # vs2019 + msbuild = os.path.join(vspath, "MSBuild", "Current", "Bin", "MSBuild.exe") + if os.path.isfile(msbuild): + default_platform_toolset = "v142" + vs["msbuild"] = '"{}"'.format(msbuild) + else: + print("Visual Studio MSBuild not found") + return None + # vs["header"].append(cmd_set("DefaultPlatformToolset", default_platform_toolset)) vcvarsall = os.path.join(vspath, "VC", "Auxiliary", "Build", "vcvarsall.bat") if not os.path.isfile(vcvarsall): @@ -382,20 +325,24 @@ def build_dep(name): extract_dep(dep["url"], dep["filename"]) - lines = ["cd /D %s" % os.path.join(build_dir, name)] - lines.extend(prefs["header"]) - - lines.extend(dep.get("build", [])) - - lines.extend(get_footer(dep)) + lines = [ + "echo Building {name} ({dir})...".format(name=dep["name"], dir=name), + "cd /D %s" % os.path.join(build_dir, name), + *prefs["header"], + *dep.get("build", []), + *get_footer(dep), + ] write_script(file, lines) return file def build_dep_all(): - lines = ["cd {script_dir}"] - for dep_name in prefs["deps"]: + lines = [ + "$ErrorActionPreference = 'stop'", + "cd {script_dir}", + ] + for dep_name in deps_list: lines.append('cmd.exe /c "%s"' % build_dep(dep_name)) write_script("build_dep_all.ps1", lines) From b0885a5ceb46d41a383c4e97aa33a5e7686688bf Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 14:45:11 +0100 Subject: [PATCH 032/262] fix finding PYTHON dir --- .github/workflows/test-windows.yml | 4 ++-- winbuild/build_prepare.py | 25 +++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 6425f3b5c28..c0bd4e6ceb6 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -179,7 +179,7 @@ jobs: cd $env:GITHUB_WORKSPACE # & winbuild\build_dep_tcl.cmd # & winbuild\build_dep_tk.cmd - & winbuild\build_pillow.cmd + & winbuild\build_pillow.cmd install & $env:pythonLocation\python.exe selftest.py --installed shell: pwsh @@ -224,7 +224,7 @@ jobs: - name: Build wheel id: wheel if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" - run: "%GITHUB_WORKSPACE%\\winbuild\\build_pillow_wheel.cmd" + run: "%GITHUB_WORKSPACE%\\winbuild\\build_pillow.cmd bdist_wheel" - uses: actions/upload-artifact@v1 if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 937bdc88617..1492a57ee0f 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -1,6 +1,7 @@ import os import shutil import subprocess +import sys import winreg from itertools import count @@ -199,6 +200,7 @@ def find_vs2017(config): "header": [], # nmake selected by vcvarsall "nmake": "nmake.exe", + "vs_dir": vspath, } # vs2017 @@ -257,7 +259,8 @@ def find_sdk71a(): cmd_append("INCLUDE", os.path.join(sdk_dir, "Include")), # for ghostscript cmd_set("RCOMP", '"{}"'.format(os.path.join(sdk_dir, "Bin", "RC.EXE"))), - ] + ], + "sdk_dir": sdk_dir, } return sdk @@ -348,11 +351,6 @@ def build_dep_all(): def build_pillow(wheel=False): - if not wheel: - op, filename = "install", "build_pillow.cmd" - else: - op, filename = "bdist_wheel", "build_pillow_wheel.cmd" - lines = [] if path_dir is not None and not wheel: lines.append(cmd_xcopy("{bin_dir}", path_dir)) @@ -361,18 +359,18 @@ def build_pillow(wheel=False): [ cmd_cd("{pillow_dir}"), cmd_append("LIB", r"{python_dir}\tcl"), - r'"{{python_dir}}\python.exe" setup.py build_ext {}'.format(op), + r'"{{python_dir}}\python.exe" setup.py build_ext %*', # r"""%PYTHON%\python.exe selftest.py --installed""", ] ) - write_script(filename, lines) + write_script("build_pillow.cmd", lines) if __name__ == "__main__": script_dir = os.path.dirname(os.path.realpath(__file__)) depends_dir = os.path.join(script_dir, "depends") - python_dir = os.environ["PYTHON"] + python_dir = os.environ.get("PYTHON", os.path.dirname(os.path.realpath(sys.executable))) # copy binaries to this directory path_dir = os.environ.get("PILLOW_BIN") @@ -391,6 +389,8 @@ def build_pillow(wheel=False): if python_prefs is None: raise KeyError("Failed to determine Python version from PYTHON: " + python_dir) + print("Target: Python {python_version} {architecture} at: {python_dir}".format(python_version=python_prefs["name"], architecture=architecture, python_dir=python_dir)) + # use python version + architecture to select build config config_name = python_prefs["config-" + architecture] config = configs[config_name] @@ -399,10 +399,14 @@ def build_pillow(wheel=False): if vs2017 is None: raise RuntimeError("Visual Studio 2017 not found") + print("Found Visual Studio at: {}".format(vs2017["vs_dir"])) + sdk71a = find_sdk71a() if sdk71a is None: raise RuntimeError("Windows SDK v7.1A not found") + print("Found Windows SDK 7.1A at: {}".format(sdk71a["sdk_dir"])) + build_dir = os.path.join(script_dir, "build", config_name, architecture) lib_dir = os.path.join(build_dir, "lib") inc_dir = os.path.join(build_dir, "inc") @@ -434,8 +438,5 @@ def build_pillow(wheel=False): prefs["header"] = sum((x.get("header", []) for x in dicts), header) del prefs["name"] - print("Target: Python {python_version} {architecture}".format(**prefs)) - build_dep_all() build_pillow() - build_pillow(wheel=True) From b7120d56f194c7500ebe52b2235eafbfcdc1a755 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 15:25:21 +0100 Subject: [PATCH 033/262] add trace statements --- winbuild/build_prepare.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 1492a57ee0f..b277ae0b905 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -231,28 +231,37 @@ def find_vs2017(config): def find_sdk71a(): try: + print("trace: opening sdk key") key = winreg.OpenKeyEx( winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A", access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY, ) + print("trace: opened sdk key") except OSError: return None + print("trace: enumerating key") with key: for i in count(): try: v_name, v_value, v_type = winreg.EnumValue(key, i) + print("trace: entry: {} {} = {}".format(v_type, v_name, v_value)) except OSError: return None if v_name == "InstallationFolder" and v_type == winreg.REG_SZ: sdk_dir = v_value + print("trace: found dir: {}".format(sdk_dir)) break else: return None + print("trace: closed key") + if not os.path.isdir(sdk_dir): return None + print("trace: creating dict") + sdk = { "header": [ # for win32.mak From e0dd636e2cc047da28d71d36c0062c65510025cc Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 15:36:16 +0100 Subject: [PATCH 034/262] remove 7.1a dependency --- .github/workflows/test-windows.yml | 2 ++ winbuild/build_prepare.py | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index c0bd4e6ceb6..3ce9079312f 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -74,6 +74,8 @@ jobs: xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\depends\ xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ cd $env:GITHUB_WORKSPACE/winbuild/ + # reg query "HKLM\Software\Microsoft\Microsoft SDKs\Windows" /s + ls 'C:\Program Files (x86)\Microsoft SDKs\Windows\' python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py shell: pwsh diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index b277ae0b905..8d0ad9b8056 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -410,11 +410,11 @@ def build_pillow(wheel=False): print("Found Visual Studio at: {}".format(vs2017["vs_dir"])) - sdk71a = find_sdk71a() - if sdk71a is None: - raise RuntimeError("Windows SDK v7.1A not found") - - print("Found Windows SDK 7.1A at: {}".format(sdk71a["sdk_dir"])) + # sdk71a = find_sdk71a() + # if sdk71a is None: + # raise RuntimeError("Windows SDK v7.1A not found") + # + # print("Found Windows SDK 7.1A at: {}".format(sdk71a["sdk_dir"])) build_dir = os.path.join(script_dir, "build", config_name, architecture) lib_dir = os.path.join(build_dir, "lib") @@ -441,7 +441,7 @@ def build_pillow(wheel=False): "cmake": "cmake.exe", } - dicts = [vs2017, sdk71a, arch_prefs, python_prefs, config] + dicts = [vs2017, arch_prefs, python_prefs, config] for x in dicts: prefs.update(x) prefs["header"] = sum((x.get("header", []) for x in dicts), header) From 85110521ecf7b9d05b952b5ab9a9f800c0ad901b Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 15:53:07 +0100 Subject: [PATCH 035/262] fix default shell --- .github/workflows/test-windows.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 3ce9079312f..af30b98e937 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -80,19 +80,19 @@ jobs: shell: pwsh - name: Build dependencies / libjpeg - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_libjpeg.cmd" + run: "& .\\winbuild\\build_dep_libjpeg.cmd" - name: Build dependencies / zlib - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_zlib.cmd" + run: "& .\\winbuild\\build_dep_zlib.cmd" - name: Build dependencies / LibTiff - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_libtiff.cmd" + run: "& .\\winbuild\\build_dep_libtiff.cmd" - name: Build dependencies / WebP - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_libwebp.cmd" + run: "& .\\winbuild\\build_dep_libwebp.cmd" - name: Build dependencies / FreeType - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_freetype.cmd" + run: "& .\\winbuild\\build_dep_freetype.cmd" - name: Build dependencies / LCMS2 - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_lcms2.cmd" + run: "& .\\winbuild\\build_dep_lcms2.cmd" - name: Build dependencies / OpenJPEG - run: "%GITHUB_WORKSPACE%\\winbuild\\build_dep_openjpeg.cmd" + run: "& .\\winbuild\\build_dep_openjpeg.cmd" # GPL licensed; skip if building wheels - name: Build dependencies / libimagequant From 7b70265e69e9f26a8de24e810ac3a7bef3546ed7 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 17:34:11 +0100 Subject: [PATCH 036/262] rewrite retargetting --- winbuild/build_prepare.py | 42 ++++++++++++++++++++++++++------------- winbuild/commands.py | 15 -------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 8d0ad9b8056..454d80f4127 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -51,7 +51,6 @@ cmd_set("INCLIB", "{lib_dir}"), cmd_set("LIB", "{lib_dir}"), cmd_append("PATH", "{bin_dir}"), - "@echo on", ] # dependencies @@ -124,14 +123,14 @@ "name": "freetype", "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501 "filename": "freetype-2.10.1.tar.gz", + "patch": { + r"builds\windows\vc2010\freetype.vcxproj": { + # freetype setting is /MD for .dll and /MT for .lib, we need /MD + "MultiThreaded": "MultiThreadedDLL", + }, + }, "build": [ cmd_rmdir("objs"), - # freetype setting is /MD for .dll and /MT for .lib, we need /MD - cmd_patch_replace( - r"builds\windows\vc2010\freetype.vcxproj", - "MultiThreaded<", - "MultiThreadedDLL<" - ), cmd_msbuild(r"builds\windows\vc2010\freetype.sln", "Release Static", "Clean"), # TODO failing on GHA # noqa: E501 cmd_msbuild(r"builds\windows\vc2010\freetype.sln", "Release Static", "Build"), cmd_xcopy("include", "{inc_dir}"), @@ -141,15 +140,21 @@ }, "lcms2-2.9": { "name": "lcms2", - "url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.9.tar.gz", + "url": SF_MIRROR + "/project/lcms/lcms/2.9/lcms2-2.9.tar.gz", "filename": "lcms2-2.9.tar.gz", + "patch": { + r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { + # lcms2-2.8\VC2015 setting is /MD for x86 and /MT for x64, we need /MD always + "MultiThreaded": "MultiThreadedDLL", + # retarget to default msvc + "v141": "$(DefaultPlatformToolset)", + # retarget to latest SDK 10.0 + "8.1": "10.0", + }, + }, "build": [ cmd_rmdir("Lib"), cmd_rmdir(r"Projects\VC{vs_ver}\Release"), - # lcms2-2.8\VC2015 setting is /MD for x86 and /MT for x64, we need /MD always - cmd_patch_replace( - r"Projects\VC2017\lcms2.sln", "MultiThreaded<", "MultiThreadedDLL<" - ), cmd_msbuild(r"Projects\VC{vs_ver}\lcms2.sln", "Release", "Clean"), cmd_msbuild(r"Projects\VC{vs_ver}\lcms2.sln", "Release", "lcms2_static"), cmd_xcopy("include", "{inc_dir}"), @@ -337,8 +342,17 @@ def build_dep(name): extract_dep(dep["url"], dep["filename"]) + for patch_file, patch_list in dep.get("patch", {}).items(): + patch_file = os.path.join(build_dir, name, patch_file) + with open(patch_file, "r") as f: + text = f.read() + for patch_from, patch_to in patch_list.items(): + text = text.replace(patch_from, patch_to) + with open(patch_file, "w") as f: + f.write(text) + lines = [ - "echo Building {name} ({dir})...".format(name=dep["name"], dir=name), + "@echo Building {name} ({dir})...".format(name=dep["name"], dir=name), "cd /D %s" % os.path.join(build_dir, name), *prefs["header"], *dep.get("build", []), @@ -444,7 +458,7 @@ def build_pillow(wheel=False): dicts = [vs2017, arch_prefs, python_prefs, config] for x in dicts: prefs.update(x) - prefs["header"] = sum((x.get("header", []) for x in dicts), header) + prefs["header"] = sum((x.get("header", []) for x in dicts), header) + ["@echo on"] del prefs["name"] build_dep_all() diff --git a/winbuild/commands.py b/winbuild/commands.py index 2fbbc9b1af1..f78a596f1fd 100644 --- a/winbuild/commands.py +++ b/winbuild/commands.py @@ -94,18 +94,3 @@ def cmd_msbuild( "/m", ] ).format(**locals()) - - -# patch tools - - -def cmd_patch_replace(file, src, dst): - return " ".join( - [ - "echo", - "(Get-Content '{file}')", - '-replace "{src}", "{dst}"', - "^| Out-File -encoding ASCII '{file}'", - "> temp.ps1\r\npowershell .\\temp.ps1", - ] - ).format(**locals()) From 9784874dc6605787889ff888df9b8444b82b933c Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 18:13:16 +0100 Subject: [PATCH 037/262] extract remaining libs --- .github/workflows/test-windows.yml | 100 ++--------- winbuild/build_prepare.py | 257 ++++++++++++++++++----------- 2 files changed, 177 insertions(+), 180 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index af30b98e937..fc341e446f8 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -61,11 +61,9 @@ jobs: - name: Fetch dependencies run: | 7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\" - Write-Host "`#`#[add-path]$env:RUNNER_WORKSPACE\nasm-2.14.02" Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02" ..\pillow-depends\gs950w32.exe /S - Write-Host "`#`#[add-path]C:\Program Files (x86)\gs\gs9.50\bin" Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" $env:PYTHON=$env:pythonLocation @@ -76,7 +74,7 @@ jobs: cd $env:GITHUB_WORKSPACE/winbuild/ # reg query "HKLM\Software\Microsoft\Microsoft SDKs\Windows" /s ls 'C:\Program Files (x86)\Microsoft SDKs\Windows\' - python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py + & python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py shell: pwsh - name: Build dependencies / libjpeg @@ -97,84 +95,15 @@ jobs: # GPL licensed; skip if building wheels - name: Build dependencies / libimagequant if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')" - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - rem e5d454b: Merge tag '2.12.6' into msvc - cd /D %BUILD%\libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - echo (gc CMakeLists.txt) -replace 'add_library', "add_compile_options(-openmp-)`r`nadd_library" ^| Out-File -encoding ASCII CMakeLists.txt > patch.ps1 - echo (gc CMakeLists.txt) -replace ' SHARED', ' STATIC' ^| Out-File -encoding ASCII CMakeLists.txt >> patch.ps1 - powershell .\patch.ps1 - set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF - set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release - %CMAKE% -G "NMake Makefiles" . - nmake -nologo -f Makefile clean - nmake -nologo -f Makefile - copy /Y /B *.h %INCLIB% - copy /Y /B *.lib %INCLIB% - shell: cmd + run: "& .\\winbuild\\build_dep_libimagequant.cmd" - # for Raqm + # Raqm dependencies - name: Build dependencies / HarfBuzz - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - set INCLUDE=%INCLUDE%;%INCLIB% - set LIB=%LIB%;%INCLIB% - cd /D %BUILD%\harfbuzz-2.6.4 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF - set CMAKE=%CMAKE% -DHB_HAVE_FREETYPE:BOOL=ON -DCMAKE_BUILD_TYPE=Release - %CMAKE% -G "NMake Makefiles" . - nmake -nologo -f Makefile clean - nmake -nologo -f Makefile harfbuzz - copy /Y /B src\*.h %INCLIB% - copy /Y /B *.lib %INCLIB% - shell: cmd - - # for Raqm + run: "& .\\winbuild\\build_dep_harfbuzz.cmd" - name: Build dependencies / FriBidi - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\fribidi-1.0.9 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - copy /Y /B %GITHUB_WORKSPACE%\winbuild\fribidi.cmake CMakeLists.txt - set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF - set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release - %CMAKE% -G "NMake Makefiles" . - nmake -nologo -f Makefile clean - nmake -nologo -f Makefile fribidi - copy /Y /B lib\*.h %INCLIB% - copy /Y /B *.lib %INCLIB% - shell: cmd - + run: "& .\\winbuild\\build_dep_fribidi.cmd" - name: Build dependencies / Raqm - run: | - set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include - set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 - set BUILD=%GITHUB_WORKSPACE%\winbuild\build - set INCLUDE=%INCLUDE%;%INCLIB% - set LIB=%LIB%;%INCLIB% - cd /D %BUILD%\libraqm-0.7.0 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} - echo on - copy /Y /B %GITHUB_WORKSPACE%\winbuild\raqm.cmake CMakeLists.txt - set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF - set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release - %CMAKE% -G "NMake Makefiles" . - nmake -nologo -f Makefile clean - nmake -nologo -f Makefile libraqm - copy /Y /B src\*.h %INCLIB% - copy /Y /B libraqm.dll %INCLIB% - shell: cmd + run: "& .\\winbuild\\build_dep_libraqm.cmd" - name: Build Pillow run: | @@ -188,9 +117,7 @@ jobs: # failing with PyPy3 - name: Enable heap verification if: "!contains(matrix.python-version, 'pypy')" - run: | - c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\python.exe - shell: cmd + run: "& 'C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x86\\gflags.exe' /p /enable $env:pythonLocation\\python.exe" - name: Test Pillow run: | @@ -219,19 +146,22 @@ jobs: - name: Upload coverage uses: codecov/codecov-action@v1 with: - file: ./coverage.xml - flags: GHA_Windows - name: ${{ runner.os }} Python ${{ matrix.python-version }} + file: ./coverage.xml + flags: GHA_Windows + name: ${{ runner.os }} Python ${{ matrix.python-version }} - name: Build wheel id: wheel if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" - run: "%GITHUB_WORKSPACE%\\winbuild\\build_pillow.cmd bdist_wheel" + run: | + for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a + %GITHUB_WORKSPACE%\\winbuild\\build_pillow.cmd bdist_wheel" + shell: cmd - uses: actions/upload-artifact@v1 if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" with: - name: dist + name: ${{ steps.wheel.outputs.dist }} path: dist # - uses: actions/upload-artifact@v1 diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 454d80f4127..1354730ca63 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -15,36 +15,15 @@ "x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"}, } -# use PYTHON + architecture to select config -pythons = { - "pypy3.6": {"config-x86": "3.5"}, - "3.5": {"config-x86": "3.5", "config-x64": "3.5"}, - "3.6": {"config-x86": "3.6", "config-x64": "3.6"}, - "3.7": {"config-x86": "3.6", "config-x64": "3.6"}, -} - # select preferred compiler -configs = { - "3.5": { - "vcvars_ver": "14.0", - "vs_ver": "2015", - }, - "3.6": { - "vs_ver": "2017", - }, +pythons = { + "pypy3.6": {"vs_ver": "2015", "vcvars_ver": "14.0"}, + "3.5": {"vs_ver": "2015", "vcvars_ver": "14.0"}, + "3.6": {"vs_ver": "2017"}, + "3.7": {"vs_ver": "2017"}, + "3.8": {"vs_ver": "2017"}, # TODO check } -# selected dependencies -deps_list = [ - "libjpeg-turbo-2.0.3", - "zlib-1.2.11", - "tiff-4.0.10", - "libwebp-1.0.3", - "freetype-2.10.1", - "lcms2-2.9", - "openjpeg-2.3.1", -] - header = [ cmd_set("BUILD", "{build_dir}"), cmd_set("INCLUDE", "{inc_dir}"), @@ -55,16 +34,18 @@ # dependencies deps = { - "libjpeg-turbo-2.0.3": { - "name": "libjpeg", + "libjpeg": { "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", "filename": "libjpeg-turbo-2.0.3.tar.gz", + "dir": "libjpeg-turbo-2.0.3", "build": [ - cmd_cmake([ - "-DENABLE_SHARED:BOOL=FALSE", - "-DWITH_JPEG8:BOOL=TRUE", - "-DWITH_CRT_DLL:BOOL=TRUE", - ]), + cmd_cmake( + [ + "-DENABLE_SHARED:BOOL=FALSE", + "-DWITH_JPEG8:BOOL=TRUE", + "-DWITH_CRT_DLL:BOOL=TRUE", + ] + ), cmd_nmake(target="clean"), cmd_nmake(target="jpeg-static"), cmd_copy("jpeg-static.lib", "libjpeg.lib"), @@ -77,10 +58,10 @@ "libs": ["libjpeg.lib"], "bins": ["cjpeg.exe", "djpeg.exe"], }, - "zlib-1.2.11": { - "name": "zlib", + "zlib": { "url": "http://zlib.net/zlib1211.zip", "filename": "zlib1211.zip", + "dir": "zlib-1.2.11", "build": [ cmd_nmake(r"win32\Makefile.msc", "clean"), cmd_nmake(r"win32\Makefile.msc", "zlib.lib"), @@ -89,82 +70,90 @@ "headers": [r"z*.h"], "libs": [r"*.lib"], }, - "tiff-4.0.10": { - "name": "libtiff", + "libtiff": { # FIXME FTP timeout - "url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz", - "filename": "tiff-4.0.10.tar.gz", + "url": "ftp://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz", + "filename": "tiff-4.1.0.tar.gz", + "dir": "tiff-4.1.0", "build": [ cmd_copy(r"{script_dir}\tiff.opt", "nmake.opt"), cmd_nmake("makefile.vc", "clean"), - cmd_nmake("makefile.vc", "lib", "RuntimeLibrary=-MT"), + cmd_nmake("makefile.vc", "lib"), ], "headers": [r"libtiff\tiff*.h"], "libs": [r"libtiff\*.lib"], # "bins": [r"libtiff\*.dll"], }, - "libwebp-1.0.3": { - "name": "libwebp", + "libwebp": { "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz", # noqa: E501 "filename": "libwebp-1.0.3.tar.gz", + "dir": "libwebp-1.0.3", "build": [ cmd_rmdir(r"output\release-static"), # clean cmd_nmake( "Makefile.vc", "all", - ["CFG=release-static", "OBJDIR=output", "ARCH={architecture}"] + ["CFG=release-static", "OBJDIR=output", "ARCH={architecture}"], ), cmd_mkdir(r"{inc_dir}\webp"), cmd_copy(r"src\webp\*.h", r"{inc_dir}\webp"), ], "libs": [r"output\release-static\{architecture}\lib\*.lib"], }, - "freetype-2.10.1": { - "name": "freetype", + "freetype": { "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501 "filename": "freetype-2.10.1.tar.gz", + "dir": "freetype-2.10.1", "patch": { r"builds\windows\vc2010\freetype.vcxproj": { # freetype setting is /MD for .dll and /MT for .lib, we need /MD - "MultiThreaded": "MultiThreadedDLL", - }, + "MultiThreaded": + "MultiThreadedDLL", + } }, "build": [ cmd_rmdir("objs"), - cmd_msbuild(r"builds\windows\vc2010\freetype.sln", "Release Static", "Clean"), # TODO failing on GHA # noqa: E501 - cmd_msbuild(r"builds\windows\vc2010\freetype.sln", "Release Static", "Build"), + cmd_msbuild( + r"builds\windows\vc2010\freetype.sln", "Release Static", "Clean" + ), + cmd_msbuild( + r"builds\windows\vc2010\freetype.sln", "Release Static", "Build" + ), cmd_xcopy("include", "{inc_dir}"), ], "libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"], # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, - "lcms2-2.9": { - "name": "lcms2", + "lcms2": { "url": SF_MIRROR + "/project/lcms/lcms/2.9/lcms2-2.9.tar.gz", "filename": "lcms2-2.9.tar.gz", + "dir": "lcms2-2.9", "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { - # lcms2-2.8\VC2015 setting is /MD for x86 and /MT for x64, we need /MD always - "MultiThreaded": "MultiThreadedDLL", - # retarget to default msvc - "v141": "$(DefaultPlatformToolset)", - # retarget to latest SDK 10.0 - "8.1": "10.0", - }, + # default is /MD for x86 and /MT for x64, we need /MD always + "MultiThreaded": + "MultiThreadedDLL", + # retarget to default toolset (selected by vcvarsall.bat) + "v141": + "$(DefaultPlatformToolset)", + # retarget to latest (selected by vcvarsall.bat) + "8.1": + "$(WindowsSDKVersion)", # noqa E501 + } }, "build": [ cmd_rmdir("Lib"), - cmd_rmdir(r"Projects\VC{vs_ver}\Release"), - cmd_msbuild(r"Projects\VC{vs_ver}\lcms2.sln", "Release", "Clean"), - cmd_msbuild(r"Projects\VC{vs_ver}\lcms2.sln", "Release", "lcms2_static"), + cmd_rmdir(r"Projects\VC2017\Release"), + cmd_msbuild(r"Projects\VC2017\lcms2.sln", "Release", "Clean"), + cmd_msbuild(r"Projects\VC2017\lcms2.sln", "Release", "lcms2_static"), cmd_xcopy("include", "{inc_dir}"), ], "libs": [r"Lib\MS\*.lib"], }, - "openjpeg-2.3.1": { - "name": "openjpeg", + "openjpeg": { "url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", "filename": "openjpeg-2.3.1.tar.gz", + "dir": "openjpeg-2.3.1", "build": [ cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), cmd_nmake(target="clean"), @@ -174,6 +163,64 @@ ], "libs": [r"bin\*.lib"], }, + "libimagequant": { + # ba653c8: Merge tag '2.12.5' into msvc + "url": "https://github.com/ImageOptim/libimagequant/archive/ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", # noqa: E501 + "filename": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", + "dir": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971", + "patch": { + "CMakeLists.txt": { + "add_library": "add_compile_options(-openmp-)\r\nadd_library", + " SHARED": " STATIC", + } + }, + "build": [ + # lint: do not inline + cmd_cmake(), + cmd_nmake(target="clean"), + cmd_nmake(), + ], + "headers": [r"*.h"], + "libs": [r"*.lib"], + }, + "harfbuzz": { + "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.1.zip", + "filename": "harfbuzz-2.6.1.zip", + "dir": "harfbuzz-2.6.1", + "build": [ + cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), + cmd_nmake(target="clean"), + cmd_nmake(target="harfbuzz"), + ], + "headers": [r"src\*.h"], + "libs": [r"*.lib"], + }, + "fribidi": { + "url": "https://github.com/fribidi/fribidi/archive/v1.0.7.zip", + "filename": "fribidi-1.0.7.zip", + "dir": "fribidi-1.0.7", + "build": [ + cmd_copy(r"{script_dir}\fribidi.cmake", r"CMakeLists.txt"), + cmd_cmake(), + cmd_nmake(target="clean"), + cmd_nmake(target="fribidi"), + ], + "headers": [r"lib\*.h"], + "libs": [r"*.lib"], + }, + "libraqm": { + "url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip", + "filename": "libraqm-0.7.0.zip", + "dir": "libraqm-0.7.0", + "build": [ + cmd_copy(r"{script_dir}\raqm.cmake", r"CMakeLists.txt"), + cmd_cmake(), + cmd_nmake(target="clean"), + cmd_nmake(target="libraqm"), + ], + "headers": [r"src\*.h"], + "bins": [r"libraqm.dll"], + }, } @@ -185,14 +232,25 @@ def find_vs2017(config): return None try: - vspath = subprocess.check_output([ - os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), - "-latest", - "-prerelease", - "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", "installationPath", - "-products", "*", - ]).decode(encoding="mbcs").strip() + vspath = ( + subprocess.check_output( + [ + os.path.join( + root, "Microsoft Visual Studio", "Installer", "vswhere.exe" + ), + "-latest", + "-prerelease", + "-requires", + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", + "installationPath", + "-products", + "*", + ] + ) + .decode(encoding="mbcs") + .strip() + ) except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): print("vswhere not found") return None @@ -211,13 +269,13 @@ def find_vs2017(config): # vs2017 msbuild = os.path.join(vspath, "MSBuild", "15.0", "Bin", "MSBuild.exe") if os.path.isfile(msbuild): - default_platform_toolset = "v140" + # default_platform_toolset = "v140" vs["msbuild"] = '"{}"'.format(msbuild) else: # vs2019 msbuild = os.path.join(vspath, "MSBuild", "Current", "Bin", "MSBuild.exe") if os.path.isfile(msbuild): - default_platform_toolset = "v142" + # default_platform_toolset = "v142" vs["msbuild"] = '"{}"'.format(msbuild) else: print("Visual Studio MSBuild not found") @@ -228,7 +286,9 @@ def find_vs2017(config): if not os.path.isfile(vcvarsall): print("Visual Studio vcvarsall not found") return None - vcvars_ver = "-vcvars_ver={}".format(config["vcvars_ver"]) if "vcvars_ver" in config else "" + vcvars_ver = ( + "-vcvars_ver={}".format(config["vcvars_ver"]) if "vcvars_ver" in config else "" + ) vs["header"].append('call "{}" {{vcvars_arch}} {}'.format(vcvarsall, vcvars_ver)) return vs @@ -338,22 +398,23 @@ def get_footer(dep): def build_dep(name): dep = deps[name] - file = "build_dep_{name}.cmd".format(name=dep["name"]) + dir = dep["dir"] + file = "build_dep_{name}.cmd".format(**locals()) extract_dep(dep["url"], dep["filename"]) for patch_file, patch_list in dep.get("patch", {}).items(): - patch_file = os.path.join(build_dir, name, patch_file) + patch_file = os.path.join(build_dir, dir, patch_file.format(**prefs)) with open(patch_file, "r") as f: text = f.read() for patch_from, patch_to in patch_list.items(): - text = text.replace(patch_from, patch_to) + text = text.replace(patch_from.format(**prefs), patch_to.format(**prefs)) with open(patch_file, "w") as f: f.write(text) lines = [ - "@echo Building {name} ({dir})...".format(name=dep["name"], dir=name), - "cd /D %s" % os.path.join(build_dir, name), + "@echo ---- Building {name} ({dir}) ----".format(**locals()), + "cd /D %s" % os.path.join(build_dir, dir), *prefs["header"], *dep.get("build", []), *get_footer(dep), @@ -368,7 +429,7 @@ def build_dep_all(): "$ErrorActionPreference = 'stop'", "cd {script_dir}", ] - for dep_name in deps_list: + for dep_name in deps: lines.append('cmd.exe /c "%s"' % build_dep(dep_name)) write_script("build_dep_all.ps1", lines) @@ -380,10 +441,13 @@ def build_pillow(wheel=False): lines.extend(prefs["header"]) lines.extend( [ + "@echo ---- Building Pillow (%s) ----", cmd_cd("{pillow_dir}"), cmd_append("LIB", r"{python_dir}\tcl"), - r'"{{python_dir}}\python.exe" setup.py build_ext %*', - # r"""%PYTHON%\python.exe selftest.py --installed""", + cmd_set("MSSdk", "1"), + cmd_set("DISTUTILS_USE_SDK", "1"), + cmd_set("py_vcruntime_redist", "true"), + r'"{python_dir}\python.exe" setup.py build_ext %*', ] ) @@ -393,7 +457,9 @@ def build_pillow(wheel=False): if __name__ == "__main__": script_dir = os.path.dirname(os.path.realpath(__file__)) depends_dir = os.path.join(script_dir, "depends") - python_dir = os.environ.get("PYTHON", os.path.dirname(os.path.realpath(sys.executable))) + python_dir = os.environ.get( + "PYTHON", os.path.dirname(os.path.realpath(sys.executable)) + ) # copy binaries to this directory path_dir = os.environ.get("PILLOW_BIN") @@ -410,15 +476,17 @@ def build_pillow(wheel=False): # use PYTHON to select python version python_prefs = match(pythons, python_dir) if python_prefs is None: - raise KeyError("Failed to determine Python version from PYTHON: " + python_dir) - - print("Target: Python {python_version} {architecture} at: {python_dir}".format(python_version=python_prefs["name"], architecture=architecture, python_dir=python_dir)) + raise KeyError("Failed to determine Python version for {}".format(python_dir)) - # use python version + architecture to select build config - config_name = python_prefs["config-" + architecture] - config = configs[config_name] + print( + "Target: Python {python_version} {architecture} at: {python_dir}".format( + python_version=python_prefs["name"], + architecture=architecture, + python_dir=python_dir, + ) + ) - vs2017 = find_vs2017(config) + vs2017 = find_vs2017(python_prefs) if vs2017 is None: raise RuntimeError("Visual Studio 2017 not found") @@ -430,13 +498,12 @@ def build_pillow(wheel=False): # # print("Found Windows SDK 7.1A at: {}".format(sdk71a["sdk_dir"])) - build_dir = os.path.join(script_dir, "build", config_name, architecture) + build_dir = os.path.join(script_dir, "build", python_prefs["name"], architecture) lib_dir = os.path.join(build_dir, "lib") inc_dir = os.path.join(build_dir, "inc") bin_dir = os.path.join(build_dir, "bin") - # for path in [lib_dir, inc_dir, bin_dir]: - # shutil.rmtree(path) + # shutil.rmtree(build_dir) for path in [depends_dir, build_dir, lib_dir, inc_dir, bin_dir]: os.makedirs(path, exist_ok=True) @@ -455,7 +522,7 @@ def build_pillow(wheel=False): "cmake": "cmake.exe", } - dicts = [vs2017, arch_prefs, python_prefs, config] + dicts = [vs2017, arch_prefs, python_prefs] for x in dicts: prefs.update(x) prefs["header"] = sum((x.get("header", []) for x in dicts), header) + ["@echo on"] From aa4592c95184bc847898a446f20275f5fd03da39 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 31 Dec 2019 20:57:17 +0100 Subject: [PATCH 038/262] clean msvs selection --- .github/workflows/test-windows.yml | 9 +-- winbuild/build_prepare.py | 109 ++++------------------------- 2 files changed, 18 insertions(+), 100 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index fc341e446f8..910a5b92797 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -58,7 +58,7 @@ jobs: "%pythonLocation%\python.exe" -m pip install wheel pytest pytest-cov shell: cmd - - name: Fetch dependencies + - name: Prepare dependencies run: | 7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\" Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02" @@ -66,14 +66,13 @@ jobs: ..\pillow-depends\gs950w32.exe /S Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" - $env:PYTHON=$env:pythonLocation mkdir $env:GITHUB_WORKSPACE\winbuild\depends\ xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\depends\ xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\depends\ xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ + + $env:PYTHON=$env:pythonLocation cd $env:GITHUB_WORKSPACE/winbuild/ - # reg query "HKLM\Software\Microsoft\Microsoft SDKs\Windows" /s - ls 'C:\Program Files (x86)\Microsoft SDKs\Windows\' & python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py shell: pwsh @@ -108,8 +107,6 @@ jobs: - name: Build Pillow run: | cd $env:GITHUB_WORKSPACE - # & winbuild\build_dep_tcl.cmd - # & winbuild\build_dep_tk.cmd & winbuild\build_pillow.cmd install & $env:pythonLocation\python.exe selftest.py --installed shell: pwsh diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 1354730ca63..77f7716a5a8 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -9,21 +9,11 @@ SF_MIRROR = "http://iweb.dl.sourceforge.net" -# use PYTHON to select architecture architectures = { "x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"}, "x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"}, } -# select preferred compiler -pythons = { - "pypy3.6": {"vs_ver": "2015", "vcvars_ver": "14.0"}, - "3.5": {"vs_ver": "2015", "vcvars_ver": "14.0"}, - "3.6": {"vs_ver": "2017"}, - "3.7": {"vs_ver": "2017"}, - "3.8": {"vs_ver": "2017"}, # TODO check -} - header = [ cmd_set("BUILD", "{build_dir}"), cmd_set("INCLUDE", "{inc_dir}"), @@ -225,7 +215,7 @@ # based on distutils._msvccompiler from CPython 3.7.4 -def find_vs2017(config): +def find_msvs(): root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") if not root: print("Program Files not found") @@ -269,77 +259,25 @@ def find_vs2017(config): # vs2017 msbuild = os.path.join(vspath, "MSBuild", "15.0", "Bin", "MSBuild.exe") if os.path.isfile(msbuild): - # default_platform_toolset = "v140" vs["msbuild"] = '"{}"'.format(msbuild) else: # vs2019 msbuild = os.path.join(vspath, "MSBuild", "Current", "Bin", "MSBuild.exe") if os.path.isfile(msbuild): - # default_platform_toolset = "v142" vs["msbuild"] = '"{}"'.format(msbuild) else: print("Visual Studio MSBuild not found") return None - # vs["header"].append(cmd_set("DefaultPlatformToolset", default_platform_toolset)) vcvarsall = os.path.join(vspath, "VC", "Auxiliary", "Build", "vcvarsall.bat") if not os.path.isfile(vcvarsall): print("Visual Studio vcvarsall not found") return None - vcvars_ver = ( - "-vcvars_ver={}".format(config["vcvars_ver"]) if "vcvars_ver" in config else "" - ) - vs["header"].append('call "{}" {{vcvars_arch}} {}'.format(vcvarsall, vcvars_ver)) + vs["header"].append('call "{}" {{vcvars_arch}}'.format(vcvarsall)) return vs -def find_sdk71a(): - try: - print("trace: opening sdk key") - key = winreg.OpenKeyEx( - winreg.HKEY_LOCAL_MACHINE, - r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A", - access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY, - ) - print("trace: opened sdk key") - except OSError: - return None - print("trace: enumerating key") - with key: - for i in count(): - try: - v_name, v_value, v_type = winreg.EnumValue(key, i) - print("trace: entry: {} {} = {}".format(v_type, v_name, v_value)) - except OSError: - return None - if v_name == "InstallationFolder" and v_type == winreg.REG_SZ: - sdk_dir = v_value - print("trace: found dir: {}".format(sdk_dir)) - break - else: - return None - - print("trace: closed key") - - if not os.path.isdir(sdk_dir): - return None - - print("trace: creating dict") - - sdk = { - "header": [ - # for win32.mak - cmd_append("INCLUDE", os.path.join(sdk_dir, "Include")), - # for ghostscript - cmd_set("RCOMP", '"{}"'.format(os.path.join(sdk_dir, "Bin", "RC.EXE"))), - ], - "sdk_dir": sdk_dir, - } - - return sdk - - def match(values, target): for key, value in values.items(): if key in target: @@ -461,6 +399,8 @@ def build_pillow(wheel=False): "PYTHON", os.path.dirname(os.path.realpath(sys.executable)) ) + print("Target Python: {}".format(python_dir)) + # copy binaries to this directory path_dir = os.environ.get("PILLOW_BIN") @@ -473,42 +413,26 @@ def build_pillow(wheel=False): else: architecture = arch_prefs["name"] - # use PYTHON to select python version - python_prefs = match(pythons, python_dir) - if python_prefs is None: - raise KeyError("Failed to determine Python version for {}".format(python_dir)) + print("Target Architecture: {}".format(architecture)) - print( - "Target: Python {python_version} {architecture} at: {python_dir}".format( - python_version=python_prefs["name"], - architecture=architecture, - python_dir=python_dir, + msvs = find_msvs() + if msvs is None: + raise RuntimeError( + "Visual Studio not found. Please install Visual Studio 2017 or newer." ) - ) - vs2017 = find_vs2017(python_prefs) - if vs2017 is None: - raise RuntimeError("Visual Studio 2017 not found") + print("Found Visual Studio at: {}".format(msvs["vs_dir"])) - print("Found Visual Studio at: {}".format(vs2017["vs_dir"])) - - # sdk71a = find_sdk71a() - # if sdk71a is None: - # raise RuntimeError("Windows SDK v7.1A not found") - # - # print("Found Windows SDK 7.1A at: {}".format(sdk71a["sdk_dir"])) - - build_dir = os.path.join(script_dir, "build", python_prefs["name"], architecture) + build_dir = os.path.join(script_dir, "build", architecture) lib_dir = os.path.join(build_dir, "lib") inc_dir = os.path.join(build_dir, "inc") bin_dir = os.path.join(build_dir, "bin") - # shutil.rmtree(build_dir) + shutil.rmtree(build_dir, ignore_errors=True) for path in [depends_dir, build_dir, lib_dir, inc_dir, bin_dir]: os.makedirs(path, exist_ok=True) prefs = { - "python_version": python_prefs["name"], "architecture": architecture, "script_dir": script_dir, "depends_dir": depends_dir, @@ -521,12 +445,9 @@ def build_pillow(wheel=False): # TODO auto find: "cmake": "cmake.exe", } - - dicts = [vs2017, arch_prefs, python_prefs] - for x in dicts: - prefs.update(x) - prefs["header"] = sum((x.get("header", []) for x in dicts), header) + ["@echo on"] - del prefs["name"] + prefs.update(msvs) + prefs.update(arch_prefs) + prefs["header"] = sum([header, msvs["header"], ["@echo on"]], []) build_dep_all() build_pillow() From ae0c0f3d0ad558e9d7cd74518c25003dff77d65a Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 1 Jan 2020 01:48:13 +0100 Subject: [PATCH 039/262] cleanup --- .github/workflows/test-windows.yml | 34 +++---- winbuild/build_prepare.py | 140 ++++++++++++++++++++++++----- winbuild/commands.py | 93 ------------------- 3 files changed, 131 insertions(+), 136 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 910a5b92797..7120d7c3277 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -77,37 +77,37 @@ jobs: shell: pwsh - name: Build dependencies / libjpeg - run: "& .\\winbuild\\build_dep_libjpeg.cmd" + run: "& .\\winbuild\\build\\build_dep_libjpeg.cmd" - name: Build dependencies / zlib - run: "& .\\winbuild\\build_dep_zlib.cmd" + run: "& .\\winbuild\\build\\build_dep_zlib.cmd" - name: Build dependencies / LibTiff - run: "& .\\winbuild\\build_dep_libtiff.cmd" + run: "& .\\winbuild\\build\\build_dep_libtiff.cmd" - name: Build dependencies / WebP - run: "& .\\winbuild\\build_dep_libwebp.cmd" + run: "& .\\winbuild\\build\\build_dep_libwebp.cmd" - name: Build dependencies / FreeType - run: "& .\\winbuild\\build_dep_freetype.cmd" + run: "& .\\winbuild\\build\\build_dep_freetype.cmd" - name: Build dependencies / LCMS2 - run: "& .\\winbuild\\build_dep_lcms2.cmd" + run: "& .\\winbuild\\build\\build_dep_lcms2.cmd" - name: Build dependencies / OpenJPEG - run: "& .\\winbuild\\build_dep_openjpeg.cmd" + run: "& .\\winbuild\\build\\build_dep_openjpeg.cmd" # GPL licensed; skip if building wheels - name: Build dependencies / libimagequant if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')" - run: "& .\\winbuild\\build_dep_libimagequant.cmd" + run: "& .\\winbuild\\build\\build_dep_libimagequant.cmd" # Raqm dependencies - name: Build dependencies / HarfBuzz - run: "& .\\winbuild\\build_dep_harfbuzz.cmd" + run: "& .\\winbuild\\build\\build_dep_harfbuzz.cmd" - name: Build dependencies / FriBidi - run: "& .\\winbuild\\build_dep_fribidi.cmd" + run: "& .\\winbuild\\build\\build_dep_fribidi.cmd" - name: Build dependencies / Raqm - run: "& .\\winbuild\\build_dep_libraqm.cmd" + run: "& .\\winbuild\\build\\build_dep_libraqm.cmd" - name: Build Pillow run: | cd $env:GITHUB_WORKSPACE - & winbuild\build_pillow.cmd install + & winbuild\build\build_pillow.cmd install & $env:pythonLocation\python.exe selftest.py --installed shell: pwsh @@ -145,14 +145,14 @@ jobs: with: file: ./coverage.xml flags: GHA_Windows - name: ${{ runner.os }} Python ${{ matrix.python-version }} + name: ${{ runner.os }} Python ${{ matrix.python-version }} ${{ matrix.architecture }} - name: Build wheel id: wheel if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" run: | for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a - %GITHUB_WORKSPACE%\\winbuild\\build_pillow.cmd bdist_wheel" + %GITHUB_WORKSPACE%\\winbuild\\build\\build_pillow.cmd bdist_wheel" shell: cmd - uses: actions/upload-artifact@v1 @@ -160,9 +160,3 @@ jobs: with: name: ${{ steps.wheel.outputs.dist }} path: dist - -# - uses: actions/upload-artifact@v1 -# if: matrix.architecture == 'x86' -# with: -# name: lib -# path: "winbuild\\build\\3.x\\x86\\lib" diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 77f7716a5a8..c772a580fa1 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -2,10 +2,90 @@ import shutil import subprocess import sys -import winreg -from itertools import count -from commands import * + +def cmd_cd(path): + return "cd /D {path}".format(**locals()) + + +def cmd_set(name, value): + return "set {name}={value}".format(**locals()) + + +def cmd_append(name, value): + op = "path " if name == "PATH" else "set {name}=" + return (op + "%{name}%;{value}").format(**locals()) + + +def cmd_copy(src, tgt): + return 'copy /Y /B "{src}" "{tgt}"'.format(**locals()) + + +def cmd_xcopy(src, tgt): + return 'xcopy /Y /E "{src}" "{tgt}"'.format(**locals()) + + +def cmd_mkdir(path): + return 'mkdir "{path}"'.format(**locals()) + + +def cmd_rmdir(path): + return 'rmdir /S /Q "{path}"'.format(**locals()) + + +def cmd_nmake(makefile=None, target="", params=None): + if params is None: + params = "" + elif isinstance(params, list) or isinstance(params, tuple): + params = " ".join(params) + else: + params = str(params) + + return " ".join( + [ + "{{nmake}}", + "-nologo", + '-f "{makefile}"' if makefile is not None else "", + "{params}", + '"{target}"', + ] + ).format(**locals()) + + +def cmd_cmake(params=None, file="."): + if params is None: + params = "" + elif isinstance(params, list) or isinstance(params, tuple): + params = " ".join(params) + else: + params = str(params) + return " ".join( + [ + "{{cmake}}", + "-DCMAKE_VERBOSE_MAKEFILE=ON", + "-DCMAKE_RULE_MESSAGES:BOOL=OFF", + "-DCMAKE_BUILD_TYPE=Release", + "{params}", + '-G "NMake Makefiles"', + '"{file}"', + ] + ).format(**locals()) + + +def cmd_msbuild( + file, configuration="Release", target="Build", platform="{msbuild_arch}" +): + return " ".join( + [ + "{{msbuild}}", + "{file}", + '/t:"{target}"', + '/p:Configuration="{configuration}"', + "/p:Platform={platform}", + "/m", + ] + ).format(**locals()) + SF_MIRROR = "http://iweb.dl.sourceforge.net" @@ -61,8 +141,7 @@ "libs": [r"*.lib"], }, "libtiff": { - # FIXME FTP timeout - "url": "ftp://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz", + "url": "https://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz", "filename": "tiff-4.1.0.tar.gz", "dir": "tiff-4.1.0", "build": [ @@ -147,7 +226,7 @@ "build": [ cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), cmd_nmake(target="clean"), - cmd_nmake(), + cmd_nmake(target="openjp2"), cmd_mkdir(r"{inc_dir}\openjpeg-2.3.1"), cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.3.1"), ], @@ -303,6 +382,8 @@ def extract_dep(url, filename): ex = e else: raise RuntimeError(ex) + + print("Extracting " + filename) if filename.endswith(".zip"): with zipfile.ZipFile(file) as zf: zf.extractall(build_dir) @@ -314,7 +395,7 @@ def extract_dep(url, filename): def write_script(name, lines): - name = os.path.join(script_dir, name) + name = os.path.join(build_dir, name) lines = [line.format(**prefs) for line in lines] print("Writing " + name) with open(name, "w") as f: @@ -350,8 +431,11 @@ def build_dep(name): with open(patch_file, "w") as f: f.write(text) + banner = "Building {name} ({dir})".format(**locals()) lines = [ - "@echo ---- Building {name} ({dir}) ----".format(**locals()), + "@echo " + ("=" * 70), + "@echo ==== {:<60} ====".format(banner), + "@echo " + ("=" * 70), "cd /D %s" % os.path.join(build_dir, dir), *prefs["header"], *dep.get("build", []), @@ -363,13 +447,12 @@ def build_dep(name): def build_dep_all(): - lines = [ - "$ErrorActionPreference = 'stop'", - "cd {script_dir}", - ] + lines = ["@echo on"] for dep_name in deps: - lines.append('cmd.exe /c "%s"' % build_dep(dep_name)) - write_script("build_dep_all.ps1", lines) + lines.append(r'cmd.exe /c "{{build_dir}}\{}"'.format(build_dep(dep_name))) + lines.append("if errorlevel 1 echo Build failed! && exit /B 1") + lines.append("@echo All Pillow dependencies built successfully!") + write_script("build_dep_all.cmd", lines) def build_pillow(wheel=False): @@ -379,7 +462,7 @@ def build_pillow(wheel=False): lines.extend(prefs["header"]) lines.extend( [ - "@echo ---- Building Pillow (%s) ----", + "@echo ---- Building Pillow (build_ext %*) ----", cmd_cd("{pillow_dir}"), cmd_append("LIB", r"{python_dir}\tcl"), cmd_set("MSSdk", "1"), @@ -393,39 +476,50 @@ def build_pillow(wheel=False): if __name__ == "__main__": + # winbuild directory script_dir = os.path.dirname(os.path.realpath(__file__)) + + # dependency cache directory depends_dir = os.path.join(script_dir, "depends") + print("Caching dependencies in:", depends_dir) + + # python bin directory python_dir = os.environ.get( "PYTHON", os.path.dirname(os.path.realpath(sys.executable)) ) - - print("Target Python: {}".format(python_dir)) + print("Target Python:", python_dir) # copy binaries to this directory path_dir = os.environ.get("PILLOW_BIN") + print("Copying binary files to:", path_dir) # use PYTHON to select architecture arch_prefs = match(architectures, python_dir) if arch_prefs is None: architecture = "x86" - print("WARN: Could not determine architecture, guessing " + architecture) arch_prefs = architectures[architecture] else: architecture = arch_prefs["name"] - - print("Target Architecture: {}".format(architecture)) + print("Target Architecture:", architecture) msvs = find_msvs() if msvs is None: raise RuntimeError( "Visual Studio not found. Please install Visual Studio 2017 or newer." ) + print("Found Visual Studio at:", msvs["vs_dir"]) - print("Found Visual Studio at: {}".format(msvs["vs_dir"])) + # build root directory + build_dir = os.environ.get("PILLOW_BUILD", os.path.join(script_dir, "build")) + print("Using output directory:", build_dir) - build_dir = os.path.join(script_dir, "build", architecture) - lib_dir = os.path.join(build_dir, "lib") + # build directory for *.h files inc_dir = os.path.join(build_dir, "inc") + + # build directory for *.lib files + lib_dir = os.path.join(build_dir, "lib") + + # build directory for *.bin files bin_dir = os.path.join(build_dir, "bin") shutil.rmtree(build_dir, ignore_errors=True) diff --git a/winbuild/commands.py b/winbuild/commands.py index f78a596f1fd..ea6e73b4e37 100644 --- a/winbuild/commands.py +++ b/winbuild/commands.py @@ -1,96 +1,3 @@ # builtins -def cmd_cd(path): - return "cd /D {path}".format(**locals()) - - -def cmd_set(name, value): - return "set {name}={value}".format(**locals()) - - -def cmd_prepend(name, value): - op = "path " if name == "PATH" else "set {name}=" - return (op + "{value};%{name}%").format(**locals()) - - -def cmd_append(name, value): - op = "path " if name == "PATH" else "set {name}=" - return (op + "%{name}%;{value}").format(**locals()) - - -def cmd_copy(src, tgt): - return 'copy /Y /B "{src}" "{tgt}"'.format(**locals()) - - -def cmd_xcopy(src, tgt): - return 'xcopy /Y /E "{src}" "{tgt}"'.format(**locals()) - - -def cmd_mkdir(path): - return 'mkdir "{path}"'.format(**locals()) - - -def cmd_rmdir(path): - return 'rmdir /S /Q "{path}"'.format(**locals()) - - -def cmd_if_eq(a, b, cmd): - return 'if "{a}"=="{b}" {cmd}'.format(**locals()) - - -# tools - - -def cmd_nmake(makefile=None, target="", params=None): - if params is None: - params = "" - elif isinstance(params, list) or isinstance(params, tuple): - params = " ".join(params) - else: - params = str(params) - - return " ".join( - [ - "{{nmake}}", - "-nologo", - '-f "{makefile}"' if makefile is not None else "", - "{params}", - '"{target}"', - ] - ).format(**locals()) - - -def cmd_cmake(params=None, file="."): - if params is None: - params = "" - elif isinstance(params, list) or isinstance(params, tuple): - params = " ".join(params) - else: - params = str(params) - return " ".join( - [ - "{{cmake}}", - "-DCMAKE_VERBOSE_MAKEFILE=ON", - "-DCMAKE_RULE_MESSAGES:BOOL=OFF", - "-DCMAKE_BUILD_TYPE=Release", - "{params}", - '-G "NMake Makefiles"', - '"{file}"', - ] - ).format(**locals()) - - -def cmd_msbuild( - file, configuration="Release", target="Build", platform="{msbuild_arch}" -): - return " ".join( - [ - "{{msbuild}}", - "{file}", - '/t:"{target}"', - '/p:Configuration="{configuration}"', - "/p:Platform={platform}", - "/m", - ] - ).format(**locals()) From 510d010f56e9ccf3af5ec7b0a2935bcb73fa87e8 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 1 Jan 2020 02:43:00 +0100 Subject: [PATCH 040/262] cleanup gha config --- .github/workflows/test-windows.yml | 41 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7120d7c3277..7c188774500 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -26,14 +26,16 @@ jobs: name: Python ${{ matrix.python-version }} ${{ matrix.architecture }} steps: - - uses: actions/checkout@v1 + - name: Checkout Pillow + uses: actions/checkout@v1 - - uses: actions/checkout@v1 + - name: Checkout cached dependencies + uses: actions/checkout@v1 with: repository: python-pillow/pillow-depends ref: master - - name: Cache + - name: Cache pip uses: actions/cache@v1 with: path: ~\AppData\Local\pip\Cache @@ -50,13 +52,11 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - - name: Build system information + - name: Print build system information run: python .github/workflows/system-info.py - name: pip install wheel pytest pytest-cov - run: | - "%pythonLocation%\python.exe" -m pip install wheel pytest pytest-cov - shell: cmd + run: python -m pip install wheel pytest pytest-cov - name: Prepare dependencies run: | @@ -76,37 +76,36 @@ jobs: & python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py shell: pwsh - - name: Build dependencies / libjpeg - run: "& .\\winbuild\\build\\build_dep_libjpeg.cmd" + - name: Build dependencies / libjpeg-turbo + run: "& winbuild\\build\\build_dep_libjpeg.cmd" - name: Build dependencies / zlib - run: "& .\\winbuild\\build\\build_dep_zlib.cmd" + run: "& winbuild\\build\\build_dep_zlib.cmd" - name: Build dependencies / LibTiff - run: "& .\\winbuild\\build\\build_dep_libtiff.cmd" + run: "& winbuild\\build\\build_dep_libtiff.cmd" - name: Build dependencies / WebP - run: "& .\\winbuild\\build\\build_dep_libwebp.cmd" + run: "& winbuild\\build\\build_dep_libwebp.cmd" - name: Build dependencies / FreeType - run: "& .\\winbuild\\build\\build_dep_freetype.cmd" + run: "& winbuild\\build\\build_dep_freetype.cmd" - name: Build dependencies / LCMS2 - run: "& .\\winbuild\\build\\build_dep_lcms2.cmd" + run: "& winbuild\\build\\build_dep_lcms2.cmd" - name: Build dependencies / OpenJPEG - run: "& .\\winbuild\\build\\build_dep_openjpeg.cmd" + run: "& winbuild\\build\\build_dep_openjpeg.cmd" # GPL licensed; skip if building wheels - name: Build dependencies / libimagequant if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')" - run: "& .\\winbuild\\build\\build_dep_libimagequant.cmd" + run: "& winbuild\\build\\build_dep_libimagequant.cmd" # Raqm dependencies - name: Build dependencies / HarfBuzz - run: "& .\\winbuild\\build\\build_dep_harfbuzz.cmd" + run: "& winbuild\\build\\build_dep_harfbuzz.cmd" - name: Build dependencies / FriBidi - run: "& .\\winbuild\\build\\build_dep_fribidi.cmd" + run: "& winbuild\\build\\build_dep_fribidi.cmd" - name: Build dependencies / Raqm - run: "& .\\winbuild\\build\\build_dep_libraqm.cmd" + run: "& winbuild\\build\\build_dep_libraqm.cmd" - name: Build Pillow run: | - cd $env:GITHUB_WORKSPACE & winbuild\build\build_pillow.cmd install & $env:pythonLocation\python.exe selftest.py --installed shell: pwsh @@ -152,7 +151,7 @@ jobs: if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" run: | for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a - %GITHUB_WORKSPACE%\\winbuild\\build\\build_pillow.cmd bdist_wheel" + winbuild\\build\\build_pillow.cmd bdist_wheel" shell: cmd - uses: actions/upload-artifact@v1 From af322141ce885a183c2ff3c68a142efd7819c3ab Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 1 Jan 2020 02:44:11 +0100 Subject: [PATCH 041/262] update appveyor --- .appveyor.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e395f2ae6f6..bb1e91d0c38 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -31,8 +31,9 @@ install: - curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/master.zip - 7z x pillow-depends.zip -oc:\ - mv c:\pillow-depends-master c:\pillow-depends -- xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\ -- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\ +- mkdir c:\pillow\winbuild\depends +- xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\depends\ +- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\depends\ - xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images - cd c:\pillow\winbuild\ - ps: | @@ -47,8 +48,9 @@ install: } else { - c:\python37\python.exe c:\pillow\winbuild\build_dep.py - c:\pillow\winbuild\build_deps.cmd + $env:PILLOW_BIN = "c:\pillow\" + c:\python37\python.exe c:\pillow\winbuild\build_prepare.py + c:\pillow\winbuild\build\build_dep_all.cmd $host.SetShouldExit(0) } - curl -fsSL -o gs952.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/gs952w32.exe @@ -65,7 +67,7 @@ build_script: } else { - & $env:PYTHON/$env:EXECUTABLE c:\pillow\winbuild\build.py + & $env:PYTHON/$env:EXECUTABLE c:\pillow\winbuild\build\build_pillow.cmd install $host.SetShouldExit(0) } - cd c:\pillow @@ -98,7 +100,7 @@ before_deploy: - cd c:\pillow - '%PYTHON%\%PIP_DIR%\pip.exe install wheel' - cd c:\pillow\winbuild\ - - '%PYTHON%\%EXECUTABLE% c:\pillow\winbuild\build.py --wheel' + - '%PYTHON%\%EXECUTABLE% c:\pillow\winbuild\build\build_pillow.cmd bdist_wheel' - cd c:\pillow - ps: Get-ChildItem .\dist\*.* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } From 15ce881a2b93b0ae035bf757704bd58382c11e07 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 1 Jan 2020 02:47:23 +0100 Subject: [PATCH 042/262] fix appveyor --- .appveyor.yml | 7 +++++-- Tests/test_imageshow.py | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index bb1e91d0c38..645c2256fd1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,5 @@ version: '{build}' +image: Visual Studio 2017 clone_folder: c:\pillow init: - ECHO %PYTHON% @@ -35,6 +36,8 @@ install: - xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\depends\ - xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\depends\ - xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images +- 7z x ..\pillow-depends\nasm-2.14.02-win64.zip -oc:\ +- path c:\nasm-2.14.02;%PATH% - cd c:\pillow\winbuild\ - ps: | if ($env:PYTHON -eq "c:/vp/pypy3") @@ -67,7 +70,7 @@ build_script: } else { - & $env:PYTHON/$env:EXECUTABLE c:\pillow\winbuild\build\build_pillow.cmd install + c:\pillow\winbuild\build\build_pillow.cmd install $host.SetShouldExit(0) } - cd c:\pillow @@ -100,7 +103,7 @@ before_deploy: - cd c:\pillow - '%PYTHON%\%PIP_DIR%\pip.exe install wheel' - cd c:\pillow\winbuild\ - - '%PYTHON%\%EXECUTABLE% c:\pillow\winbuild\build\build_pillow.cmd bdist_wheel' + - c:\pillow\winbuild\build\build_pillow.cmd bdist_wheel - cd c:\pillow - ps: Get-ChildItem .\dist\*.* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 0d513a47dc4..c8c247dc2a9 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -38,8 +38,7 @@ def show_image(self, image, **options): @pytest.mark.skipif( - not on_ci() or (is_win32() and on_github_actions()), - reason="Only run on CIs; hangs on Windows on GitHub Actions", + not on_ci() or is_win32(), reason="Only run on CIs; hangs on Windows 10", ) def test_show(): for mode in ("1", "I;16", "LA", "RGB", "RGBA"): From c6a1c551d9840a79b473fe255d810bca7263addb Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 1 Jan 2020 13:02:27 +0100 Subject: [PATCH 043/262] cleanup build configuration --- .appveyor.yml | 15 ++-- .github/workflows/test-windows.yml | 4 +- Tests/test_imageshow.py | 2 +- winbuild/README.md | 48 +++++++----- winbuild/build_prepare.py | 115 +++++++++++++---------------- winbuild/commands.py | 3 - 6 files changed, 87 insertions(+), 100 deletions(-) delete mode 100644 winbuild/commands.py diff --git a/.appveyor.yml b/.appveyor.yml index 645c2256fd1..4d2cb81d228 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,10 +7,8 @@ init: # Uncomment previous line to get RDP access during the build. environment: - X64_EXT: -x64 EXECUTABLE: python.exe PIP_DIR: Scripts - VENV: NO TEST_OPTIONS: DEPLOY: YES matrix: @@ -32,12 +30,11 @@ install: - curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/master.zip - 7z x pillow-depends.zip -oc:\ - mv c:\pillow-depends-master c:\pillow-depends -- mkdir c:\pillow\winbuild\depends -- xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\depends\ -- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\depends\ - xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images - 7z x ..\pillow-depends\nasm-2.14.02-win64.zip -oc:\ -- path c:\nasm-2.14.02;%PATH% +- curl -fsSL -o gs952.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/gs952w32.exe +- gs952.exe /S +- path c:\nasm-2.14.02;C:\Program Files (x86)\gs\gs9.52\bin;%PATH% - cd c:\pillow\winbuild\ - ps: | if ($env:PYTHON -eq "c:/vp/pypy3") @@ -51,14 +48,12 @@ install: } else { - $env:PILLOW_BIN = "c:\pillow\" + $env:PILLOW_DEPS = "C:\pillow-depends\" c:\python37\python.exe c:\pillow\winbuild\build_prepare.py c:\pillow\winbuild\build\build_dep_all.cmd $host.SetShouldExit(0) } -- curl -fsSL -o gs952.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/gs952w32.exe -- gs952.exe /S -- path %path%;C:\Program Files (x86)\gs\gs9.52\bin +- path C:\pillow\winbuild\build\bin;%PATH% build_script: - ps: | diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7c188774500..fe958ad5aa7 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -66,9 +66,7 @@ jobs: ..\pillow-depends\gs950w32.exe /S Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" - mkdir $env:GITHUB_WORKSPACE\winbuild\depends\ - xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\depends\ - xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\depends\ + $env:PILLOW_DEPS = "$env:RUNNER_WORKSPACE\pillow-depends\" xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ $env:PYTHON=$env:pythonLocation diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index c8c247dc2a9..a9332bef43e 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -1,7 +1,7 @@ import pytest from PIL import Image, ImageShow -from .helper import hopper, is_win32, on_ci, on_github_actions +from .helper import hopper, is_win32, on_ci def test_sanity(): diff --git a/winbuild/README.md b/winbuild/README.md index 471b61a574f..dabbc359ac3 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -1,18 +1,30 @@ -Quick README ------------- - -For more extensive info, see the [Windows build instructions](build.rst). - -* See https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859 - -* Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command. -* Check config.py for virtual env paths, suffix for 64-bit releases. Defaults to `x64`, set `X64_EXT` to change. -* When running in CI with one Python per invocation, set the `PYTHON` env variable to the Python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the specific Python. -* `python get_pythons.py` downloads all the Python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`. -* `python build_dep.py` downloads and creates a build script for all the dependencies, in 32 and 64-bit versions, and with both compiler versions. -* (in powershell) `build_deps.cmd` invokes the dependency build. -* `python build.py --clean` makes Pillow for the matrix of Pythons. -* `python test.py` runs the tests on Pillow in all the virtual envs. -* Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, and 3.4, both 32 and 64-bit, on a local win7 pro machine and appveyor.com -* WebP is built, not detected. -* LCMS, OpenJPEG and libimagequant are not building. +Quick README +------------ + +For more extensive info, see the [Windows build instructions](build.rst). + +* See [Current Windows Build/Testing process (Pillow#553)](https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416), + [Definitive docs for how to compile on Windows (matplotlib#1717)](https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859), + [Test Windows with GitHub Actions (Pillow#4084)](https://github.com/python-pillow/Pillow/pull/4084). + + +* Requires Microsoft Visual Studio 2017 or newer with C++ component. +* Requires NASM for libjpeg-turbo, a required dependency when using this script. +* Requires CMake 3.13 or newer. +* Python 3.6+ is required to generate valid scripts, but builds targeting Python 3.5+ are supported. +* Tested on Windows Server 2016 with Visual Studio 2017 Community (AppVeyor). +* Tested on Windows Server 2019 with Visual Studio 2019 Enterprise (GitHub Actions). + +The following is a simplified version of the script used on AppVeyor: +``` +set PYTHON=C:\Python35\bin +cd /D C:\Pillow\winbuild +C:\Python37\bin\python.exe build_prepare.py +build\build_dep_all.cmd +build\build_pillow.cmd install +cd .. +path C:\Pillow\winbuild\build\bin;%PATH% +%PYTHON%\python.exe selftest.py +%PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests +build\build_pillow.cmd bdist_wheel +``` diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index c772a580fa1..e56b71eb4bb 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -95,7 +95,6 @@ def cmd_msbuild( } header = [ - cmd_set("BUILD", "{build_dir}"), cmd_set("INCLUDE", "{inc_dir}"), cmd_set("INCLIB", "{lib_dir}"), cmd_set("LIB", "{lib_dir}"), @@ -145,7 +144,7 @@ def cmd_msbuild( "filename": "tiff-4.1.0.tar.gz", "dir": "tiff-4.1.0", "build": [ - cmd_copy(r"{script_dir}\tiff.opt", "nmake.opt"), + cmd_copy(r"{winbuild_dir}\tiff.opt", "nmake.opt"), cmd_nmake("makefile.vc", "clean"), cmd_nmake("makefile.vc", "lib"), ], @@ -176,8 +175,7 @@ def cmd_msbuild( "patch": { r"builds\windows\vc2010\freetype.vcxproj": { # freetype setting is /MD for .dll and /MT for .lib, we need /MD - "MultiThreaded": - "MultiThreadedDLL", + "MultiThreaded": "MultiThreadedDLL", # noqa E501 } }, "build": [ @@ -200,14 +198,11 @@ def cmd_msbuild( "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always - "MultiThreaded": - "MultiThreadedDLL", + "MultiThreaded": "MultiThreadedDLL", # noqa E501 # retarget to default toolset (selected by vcvarsall.bat) - "v141": - "$(DefaultPlatformToolset)", + "v141": "$(DefaultPlatformToolset)", # noqa E501 # retarget to latest (selected by vcvarsall.bat) - "8.1": - "$(WindowsSDKVersion)", # noqa E501 + "8.1": "$(WindowsSDKVersion)", # noqa E501 } }, "build": [ @@ -269,7 +264,7 @@ def cmd_msbuild( "filename": "fribidi-1.0.7.zip", "dir": "fribidi-1.0.7", "build": [ - cmd_copy(r"{script_dir}\fribidi.cmake", r"CMakeLists.txt"), + cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"), cmd_cmake(), cmd_nmake(target="clean"), cmd_nmake(target="fribidi"), @@ -282,7 +277,7 @@ def cmd_msbuild( "filename": "libraqm-0.7.0.zip", "dir": "libraqm-0.7.0", "build": [ - cmd_copy(r"{script_dir}\raqm.cmake", r"CMakeLists.txt"), + cmd_copy(r"{winbuild_dir}\raqm.cmake", r"CMakeLists.txt"), cmd_cmake(), cmd_nmake(target="clean"), cmd_nmake(target="libraqm"), @@ -357,12 +352,6 @@ def find_msvs(): return vs -def match(values, target): - for key, value in values.items(): - if key in target: - return {"name": key, **value} - - def extract_dep(url, filename): import urllib.request import tarfile @@ -455,51 +444,42 @@ def build_dep_all(): write_script("build_dep_all.cmd", lines) -def build_pillow(wheel=False): - lines = [] - if path_dir is not None and not wheel: - lines.append(cmd_xcopy("{bin_dir}", path_dir)) - lines.extend(prefs["header"]) - lines.extend( - [ - "@echo ---- Building Pillow (build_ext %*) ----", - cmd_cd("{pillow_dir}"), - cmd_append("LIB", r"{python_dir}\tcl"), - cmd_set("MSSdk", "1"), - cmd_set("DISTUTILS_USE_SDK", "1"), - cmd_set("py_vcruntime_redist", "true"), - r'"{python_dir}\python.exe" setup.py build_ext %*', - ] - ) +def build_pillow(): + lines = [ + "@echo ---- Building Pillow (build_ext %*) ----", + cmd_cd("{pillow_dir}"), + *prefs["header"], + cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow + cmd_set("MSSdk", "1"), # for Python 3.5 and PyPy3.6 + cmd_set("py_vcruntime_redist", "true"), # use /MD, not /MT + r'"{python_dir}\{python_exe}" setup.py build_ext %*', + ] write_script("build_pillow.cmd", lines) if __name__ == "__main__": # winbuild directory - script_dir = os.path.dirname(os.path.realpath(__file__)) + winbuild_dir = os.path.dirname(os.path.realpath(__file__)) # dependency cache directory - depends_dir = os.path.join(script_dir, "depends") + depends_dir = os.environ.get("PILLOW_DEPS", os.path.join(winbuild_dir, "depends")) + os.makedirs(depends_dir, exist_ok=True) print("Caching dependencies in:", depends_dir) - # python bin directory - python_dir = os.environ.get( - "PYTHON", os.path.dirname(os.path.realpath(sys.executable)) + # Python bin directory + python_dir = os.environ.get("PYTHON") + python_exe = os.environ.get("EXECUTABLE", "python.exe") + if python_dir is None: + python_dir = os.path.dirname(os.path.realpath(sys.executable)) + python_exe = os.path.basename(sys.executable) + print("Target Python:", os.path.join(python_dir, python_exe)) + + # use ARCHITECTURE or PYTHON to select architecture + architecture = os.environ.get( + "ARCHITECTURE", "x64" if "x64" in python_dir else "x86" ) - print("Target Python:", python_dir) - - # copy binaries to this directory - path_dir = os.environ.get("PILLOW_BIN") - print("Copying binary files to:", path_dir) - - # use PYTHON to select architecture - arch_prefs = match(architectures, python_dir) - if arch_prefs is None: - architecture = "x86" - arch_prefs = architectures[architecture] - else: - architecture = arch_prefs["name"] + arch_prefs = architectures[architecture] print("Target Architecture:", architecture) msvs = find_msvs() @@ -510,7 +490,7 @@ def build_pillow(wheel=False): print("Found Visual Studio at:", msvs["vs_dir"]) # build root directory - build_dir = os.environ.get("PILLOW_BUILD", os.path.join(script_dir, "build")) + build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build")) print("Using output directory:", build_dir) # build directory for *.h files @@ -523,25 +503,30 @@ def build_pillow(wheel=False): bin_dir = os.path.join(build_dir, "bin") shutil.rmtree(build_dir, ignore_errors=True) - for path in [depends_dir, build_dir, lib_dir, inc_dir, bin_dir]: - os.makedirs(path, exist_ok=True) + for path in [build_dir, inc_dir, lib_dir, bin_dir]: + os.makedirs(path) prefs = { - "architecture": architecture, - "script_dir": script_dir, - "depends_dir": depends_dir, + # Python paths / preferences "python_dir": python_dir, + "python_exe": python_exe, + "architecture": architecture, + **arch_prefs, + # Pillow paths + "pillow_dir": os.path.realpath(os.path.join(winbuild_dir, "..")), + "winbuild_dir": winbuild_dir, + # Build paths "build_dir": build_dir, - "lib_dir": lib_dir, "inc_dir": inc_dir, + "lib_dir": lib_dir, "bin_dir": bin_dir, - "pillow_dir": os.path.realpath(os.path.join(script_dir, "..")), - # TODO auto find: - "cmake": "cmake.exe", + # Compilers / Tools + **msvs, + "cmake": "cmake.exe", # TODO find CMAKE automatically + # TODO find NASM automatically + # script header + "header": sum([header, msvs["header"], ["@echo on"]], []), } - prefs.update(msvs) - prefs.update(arch_prefs) - prefs["header"] = sum([header, msvs["header"], ["@echo on"]], []) build_dep_all() build_pillow() diff --git a/winbuild/commands.py b/winbuild/commands.py deleted file mode 100644 index ea6e73b4e37..00000000000 --- a/winbuild/commands.py +++ /dev/null @@ -1,3 +0,0 @@ -# builtins - - From d51380cccb779af1713f87968835a43d0d2825be Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 1 Jan 2020 21:08:51 +0100 Subject: [PATCH 044/262] docs, remove old files --- winbuild/build.py | 205 ------------------------ winbuild/build.rst | 122 ++++++++------- winbuild/build_dep.py | 328 --------------------------------------- winbuild/config.py | 199 ------------------------ winbuild/fetch.py | 44 ------ winbuild/get_pythons.py | 15 -- winbuild/lcms2_patch.ps1 | 9 -- winbuild/test.py | 45 ------ winbuild/untar.py | 11 -- winbuild/unzip.py | 11 -- 10 files changed, 66 insertions(+), 923 deletions(-) delete mode 100755 winbuild/build.py delete mode 100644 winbuild/build_dep.py delete mode 100644 winbuild/config.py delete mode 100644 winbuild/fetch.py delete mode 100644 winbuild/get_pythons.py delete mode 100644 winbuild/lcms2_patch.ps1 delete mode 100755 winbuild/test.py delete mode 100644 winbuild/untar.py delete mode 100644 winbuild/unzip.py diff --git a/winbuild/build.py b/winbuild/build.py deleted file mode 100755 index e565226bd92..00000000000 --- a/winbuild/build.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python3 - -import getopt -import os -import shutil -import subprocess -import sys - -from config import ( - VIRT_BASE, - X64_EXT, - bit_from_env, - compiler_from_env, - compilers, - pythons, - pyversion_from_env, -) - - -def setup_vms(): - ret = [] - for py in pythons: - for arch in ("", X64_EXT): - ret.append( - "virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" - % (py, arch, VIRT_BASE, py, arch) - ) - ret.append( - r"%s%s%s\Scripts\pip.exe install pytest pytest-cov" - % (VIRT_BASE, py, arch) - ) - return "\n".join(ret) - - -def run_script(params): - (version, script) = params - try: - print("Running %s" % version) - filename = "build_pillow_%s.cmd" % version - with open(filename, "w") as f: - f.write(script) - - command = ["powershell", "./%s" % filename] - proc = subprocess.Popen( - command, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - (trace, stderr) = proc.communicate() - status = proc.returncode - print("-- stderr --") - print(stderr.decode()) - print("-- stdout --") - print(trace.decode()) - print("Done with {}: {}".format(version, status)) - return (version, status, trace, stderr) - except Exception as msg: - print("Error with {}: {}".format(version, str(msg))) - return (version, -1, "", str(msg)) - - -def header(op): - return r""" -setlocal -set MPLSRC=%%~dp0\.. -set INCLIB=%%~dp0\depends -set BLDOPT=%s -cd /D %%MPLSRC%% -""" % ( - op - ) - - -def footer(): - return """endlocal -exit -""" - - -def vc_setup(compiler, bit): - script = "" - if compiler["vc_version"] == "2015": - arch = "x86" if bit == 32 else "x86_amd64" - script = ( - r""" -call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %s -echo on""" - % arch - ) - return script - - -def build_one(py_ver, compiler, bit): - # UNDONE virtual envs if we're not running on AppVeyor - args = {} - args.update(compiler) - if "PYTHON" in os.environ: - args["python_path"] = "%PYTHON%" - else: - args["python_path"] = "{}{}\\Scripts".format(VIRT_BASE, py_ver) - - args["executable"] = "python.exe" - if "EXECUTABLE" in os.environ: - args["executable"] = "%EXECUTABLE%" - - args["py_ver"] = py_ver - args["tcl_ver"] = "86" - - if compiler["vc_version"] == "2015": - args["imaging_libs"] = " build_ext --add-imaging-libs=msvcrt" - else: - args["imaging_libs"] = "" - - args["vc_setup"] = vc_setup(compiler, bit) - - script = r""" -setlocal EnableDelayedExpansion -call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s -set DISTUTILS_USE_SDK=1 -set LIB=%%LIB%%;%%INCLIB%%\%(inc_dir)s -set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl%(tcl_ver)s\include - -setlocal -set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl%(vc_setup)s -call %(python_path)s\%(executable)s setup.py %(imaging_libs)s %%BLDOPT%% -call %(python_path)s\%(executable)s -c "from PIL import _webp;import os, shutil;shutil.copy(r'%%INCLIB%%\freetype.dll', os.path.dirname(_webp.__file__));" -endlocal - -endlocal -""" # noqa: E501 - return script % args - - -def clean(): - try: - shutil.rmtree("../build") - except Exception: - # could already be removed - pass - run_script(("virtualenvs", setup_vms())) - - -def main(op): - scripts = [] - - for py_version, py_info in pythons.items(): - py_compilers = compilers[py_info["compiler"]][py_info["vc"]] - scripts.append( - ( - py_version, - "\n".join( - [header(op), build_one(py_version, py_compilers[32], 32), footer()] - ), - ) - ) - - scripts.append( - ( - "{}{}".format(py_version, X64_EXT), - "\n".join( - [ - header(op), - build_one("%sx64" % py_version, py_compilers[64], 64), - footer(), - ] - ), - ) - ) - - results = map(run_script, scripts) - - for (version, status, trace, err) in results: - print("Compiled {}: {}".format(version, status and "ERR" or "OK")) - - -def run_one(op): - - compiler = compiler_from_env() - py_version = pyversion_from_env() - bit = bit_from_env() - - run_script( - ( - py_version, - "\n".join([header(op), build_one(py_version, compiler, bit), footer()]), - ) - ) - - -if __name__ == "__main__": - opts, args = getopt.getopt(sys.argv[1:], "", ["clean", "wheel"]) - opts = dict(opts) - - if "--clean" in opts: - clean() - - op = "install" - if "--wheel" in opts: - op = "bdist_wheel" - - if "PYTHON" in os.environ: - run_one(op) - else: - main(op) diff --git a/winbuild/build.rst b/winbuild/build.rst index 1d20840447a..f47efb3632d 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -5,89 +5,99 @@ Building Pillow on Windows <../docs/installation.rst#windows-installation>`_ should be sufficient. -This page will describe a build setup to build Pillow against the -supported Python versions in 32 and 64-bit modes, using freely -available Microsoft compilers. This has been developed and tested -against 64-bit Windows 7 Professional and Windows Server 2012 -64-bit version on Amazon EC2. +This page describes the steps necessary to build Pillow using the same +scripts used on GitHub Actions and AppVeyor CIs. Prerequisites ------------- -Extra Build Helpers -^^^^^^^^^^^^^^^^^^^ -* Powershell (available by default on Windows Server) -* GitHub client (provides git+bash shell) +Python +^^^^^^ -Optional: -* GPG (for checking signatures) (UNDONE -- Python signature checking) +While the scripts can target any version of Python supported by Pillow, +Python 3.6+ is required to generate valid build scripts. +Compilers +^^^^^^^^^ -Pythons -^^^^^^^ - -The build routines expect Python to be installed at C:\PythonXX for -32-bit versions or C:\PythonXXx64 for the 64-bit versions. - -Download Python 3.4, install it, and add it to the path. This is the -Python that we will use to bootstrap the build process. (The download -routines are using 3 features, and installing 3.4 gives us pip and -virtualenv as well, reducing the number of packages that we need to -install.) +Download and install: -Download the rest of the Pythons by opening a command window, changing -to the ``winbuild`` directory, and running ``python -get_pythons.py``. +* `Microsoft Visual Studio 2017 or newer (with C++ component) + `_ -UNDONE -- gpg verify the signatures (note that we can download from -https) +* `CMake 3.13 or newer + `_ -Run each installer and set the proper path to the installation. Don't -set any of them as the default Python, or add them to the path. +* `NASM `_ +Any version of Visual Studio 2017 or newer should be supported, +including Visual Studio 2017 Community. -Compilers -^^^^^^^^^ +Paths to CMake and NASM must be added to the ``PATH`` environment variable. -Download and install: +Build configuration +------------------- -* `Microsoft Windows SDK for Windows 7 and .NET Framework - 4 `_ +The following environment variables, if set, will override the default +behaviour of ``build_prepare.py``: -* `CMake-2.8.10.2-win32-x86.exe - `_ - -The samples and the .NET SDK portions aren't required, just the -compilers and other tools. UNDONE -- check exact wording. +* ``PYTHON`` + ``EXECUTABLE`` point to the target version of Python. + 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, + ``x86`` is used, unless ``PYTHON`` contains ``x64``, in which case ``x64`` + is used. +* ``PILLOW_BUILD`` can be used to override the ``winbuild\build`` directory + path, used to store generated build scripts and compiled libraries. + **Warning:** This directory is wiped when ``build_prepare.py`` is run. +* ``PILLOW_DEPS`` points to the directory used to store downloaded + dependencies. By default ``winbuild\depends`` is used. Dependencies ------------ -The script 'build_dep.py' downloads and builds the dependencies. Open -a command window, change directory into ``winbuild`` and run ``python -build_dep.py``. - -This will download libjpeg, libtiff, libz, and freetype. It will then -compile 32 and 64-bit versions of the libraries, with both versions of -the compilers. +Dependencies will be automatically downloaded by ``build_prepare.py``. +By default, downloaded dependencies are stored in ``winbuild\depends``; +set the ``PILLOW_DEPS`` environment variable to override this location. -UNDONE -- lcms fails. -UNDONE -- webp, jpeg2k not recognized +To build all dependencies, run ``winbuild\build\build_dep_all.cmd``, +or run the individual scripts to build each dependency separately. Building Pillow --------------- -Once the dependencies are built, run ``python build.py --clean`` to -build and install Pillow in virtualenvs for each Python -build. ``build.py --wheel`` will build wheels instead of -installing into virtualenvs. - -UNDONE -- suppressed output, what about failures. +Once the dependencies are built, run +``winbuild\build\build_pillow.cmd install`` to build and install +Pillow for the selected version of Python. +``winbuild\build\build_pillow.cmd bdist_wheel`` will build wheels +instead of installing Pillow. Testing Pillow -------------- -Build and install Pillow, then run ``python test.py`` from the -``winbuild`` directory. +Some binary dependencies (e.g. ``libraqm.dll``) will be stored in the +``winbuild\build\bin`` directory; this directory should be added to ``PATH`` +before running tests. + +Build and install Pillow, then run ``python -m pytest Tests`` +from the root Pillow directory. + +Example +------- + +The following is a simplified version of the script used on AppVeyor: + +.. code-block:: + set PYTHON=C:\Python35\bin + cd /D C:\Pillow\winbuild + C:\Python37\bin\python.exe build_prepare.py + build\build_dep_all.cmd + build\build_pillow.cmd install + cd .. + path C:\Pillow\winbuild\build\bin;%PATH% + %PYTHON%\python.exe selftest.py + %PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests + build\build_pillow.cmd bdist_wheel diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py deleted file mode 100644 index 77857013938..00000000000 --- a/winbuild/build_dep.py +++ /dev/null @@ -1,328 +0,0 @@ -import os - -from build import vc_setup -from config import all_compilers, bit_from_env, compiler_from_env, compilers, libs -from fetch import fetch -from untar import untar -from unzip import unzip - - -def _relpath(*args): - return os.path.join(os.getcwd(), *args) - - -build_dir = _relpath("build") -inc_dir = _relpath("depends") - - -def check_sig(filename, signame): - # UNDONE -- need gpg - return filename - - -def mkdirs(): - try: - os.mkdir(build_dir) - except OSError: - pass - try: - os.mkdir(inc_dir) - except OSError: - pass - for compiler in all_compilers(): - try: - os.mkdir(os.path.join(inc_dir, compiler["inc_dir"])) - except OSError: - pass - - -def extract(src, dest): - if ".zip" in src: - return unzip(src, dest) - if ".tar.gz" in src or ".tgz" in src: - return untar(src, dest) - - -def extract_libs(): - for name, lib in libs.items(): - filename = fetch(lib["url"]) - if name == "openjpeg": - for compiler in all_compilers(): - if not os.path.exists( - os.path.join(build_dir, lib["dir"] + compiler["inc_dir"]) - ): - extract(filename, build_dir) - os.rename( - os.path.join(build_dir, lib["dir"]), - os.path.join(build_dir, lib["dir"] + compiler["inc_dir"]), - ) - else: - extract(filename, build_dir) - - -def extract_openjpeg(compiler): - return ( - r""" -rem build openjpeg -setlocal -cd %%BUILD%% -mkdir %%INCLIB%%\openjpeg-2.0 -copy /Y /B openjpeg-2.0.0-win32-x86\include\openjpeg-2.0 %%INCLIB%%\openjpeg-2.0 -copy /Y /B openjpeg-2.0.0-win32-x86\bin\ %%INCLIB%% -copy /Y /B openjpeg-2.0.0-win32-x86\lib\ %%INCLIB%% -endlocal -""" - % compiler - ) - - -def cp_tk(ver_85, ver_86): - versions = {"ver_85": ver_85, "ver_86": ver_86} - return ( - r""" -mkdir %%INCLIB%%\tcl85\include\X11 -copy /Y /B %%BUILD%%\tcl%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ -copy /Y /B %%BUILD%%\tk%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ -copy /Y /B %%BUILD%%\tk%(ver_85)s\xlib\X11\* %%INCLIB%%\tcl85\include\X11\ - -mkdir %%INCLIB%%\tcl86\include\X11 -copy /Y /B %%BUILD%%\tcl%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\ -copy /Y /B %%BUILD%%\tk%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\ -copy /Y /B %%BUILD%%\tk%(ver_86)s\xlib\X11\* %%INCLIB%%\tcl86\include\X11\ -""" - % versions - ) - - -def header(): - return r"""setlocal -set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe -set CMAKE="cmake.exe" -set INCLIB=%~dp0\depends -set BUILD=%~dp0\build -""" + "\n".join( - r"set {}=%BUILD%\{}".format(k.upper(), v["dir"]) - for (k, v) in libs.items() - if v["dir"] - ) - - -def setup_compiler(compiler): - return ( - r"""setlocal EnableDelayedExpansion -call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s -echo on -set INCLIB=%%INCLIB%%\%(inc_dir)s -""" # noqa: E501 - % compiler - ) - - -def end_compiler(): - return """ -endlocal -""" - - -def nmake_openjpeg(compiler, bit): - if compiler["env_version"] == "v7.0": - return "" - - atts = {"op_ver": "2.3.1"} - atts.update(compiler) - return ( - r""" -rem build openjpeg -setlocal -""" - + vc_setup(compiler, bit) - + r""" -cd /D %%OPENJPEG%%%(inc_dir)s - -%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles" . -nmake -nologo -f Makefile clean -nmake -nologo -f Makefile -copy /Y /B bin\* %%INCLIB%% -mkdir %%INCLIB%%\openjpeg-%(op_ver)s -copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-%(op_ver)s -endlocal -""" # noqa: E501 - % atts - ) - - -def nmake_libs(compiler, bit): - # undone -- pre, makes, headers, libs - script = ( - r""" -rem Build libjpeg -setlocal -""" - + vc_setup(compiler, bit) - + r""" -cd /D %%JPEG%% -nmake -nologo -f makefile.vc setup-vc6 -nmake -nologo -f makefile.vc clean -nmake -nologo -f makefile.vc nodebug=1 libjpeg.lib -copy /Y /B *.dll %%INCLIB%% -copy /Y /B *.lib %%INCLIB%% -copy /Y /B j*.h %%INCLIB%% -endlocal - -rem Build zlib -setlocal -cd /D %%ZLIB%% -nmake -nologo -f win32\Makefile.msc clean -nmake -nologo -f win32\Makefile.msc zlib.lib -copy /Y /B *.dll %%INCLIB%% -copy /Y /B *.lib %%INCLIB%% -copy /Y /B zlib.lib %%INCLIB%%\z.lib -copy /Y /B zlib.h %%INCLIB%% -copy /Y /B zconf.h %%INCLIB%% -endlocal - -rem Build webp -setlocal -""" - + vc_setup(compiler, bit) - + r""" -cd /D %%WEBP%% -rd /S /Q %%WEBP%%\output\release-static -nmake -nologo -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output all -copy /Y /B output\release-static\%(webp_platform)s\lib\* %%INCLIB%% -mkdir %%INCLIB%%\webp -copy /Y /B src\webp\*.h %%INCLIB%%\\webp -endlocal - -rem Build libtiff -setlocal -""" - + vc_setup(compiler, bit) - + r""" -rem do after building jpeg and zlib -copy %%~dp0\tiff.opt %%TIFF%%\nmake.opt - -cd /D %%TIFF%% -nmake -nologo -f makefile.vc clean -nmake -nologo -f makefile.vc lib -copy /Y /B libtiff\*.dll %%INCLIB%% -copy /Y /B libtiff\*.lib %%INCLIB%% -copy /Y /B libtiff\tiff*.h %%INCLIB%% -endlocal -""" - ) - return script % compiler - - -def msbuild_freetype(compiler, bit): - script = r""" -rem Build freetype -setlocal -rd /S /Q %%FREETYPE%%\objs -set DefaultPlatformToolset=v100 -""" - properties = r"""/p:Configuration="Release" /p:Platform=%(platform)s""" - if bit == 64: - script += ( - r"copy /Y /B " - r'"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64\*.Lib" ' - r"%%FREETYPE%%\builds\windows\vc2010" - ) - properties += r" /p:_IsNativeEnvironment=false" - script += ( - r""" -%%MSBUILD%% %%FREETYPE%%\builds\windows\vc2010\freetype.sln /t:Clean;Build """ - + properties - + r""" /m -xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% -""" - ) - freetypeReleaseDir = r"%%FREETYPE%%\objs\%(platform)s\Release" - script += ( - r""" -copy /Y /B """ - + freetypeReleaseDir - + r"""\freetype.lib %%INCLIB%%\freetype.lib -copy /Y /B """ - + freetypeReleaseDir - + r"""\freetype.dll %%INCLIB%%\..\freetype.dll -endlocal -""" - ) - return script % compiler - - -def build_lcms2(compiler): - if compiler["env_version"] == "v7.1": - return build_lcms_71(compiler) - return build_lcms_70(compiler) - - -def build_lcms_70(compiler): - """Link error here on x64""" - if compiler["platform"] == "x64": - return "" - - """Build LCMS on VC2008. This version is only 32bit/Win32""" - return ( - r""" -rem Build lcms2 -setlocal -set LCMS=%%LCMS-2.7%% -rd /S /Q %%LCMS%%\Lib -rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=Win32 /m -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=Win32 /p:PlatformToolset=v90 /m -xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% -copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% -endlocal -""" # noqa: E501 - % compiler - ) - - -def build_lcms_71(compiler): - return ( - r""" -rem Build lcms2 -setlocal -set LCMS=%%LCMS-2.8%% -rd /S /Q %%LCMS%%\Lib -rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release -powershell -Command "(gc Projects\VC2015\lcms2_static\lcms2_static.vcxproj) -replace 'MultiThreadedDLL', 'MultiThreaded' | Out-File -encoding ASCII Projects\VC2015\lcms2_static\lcms2_static.vcxproj" -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=%(platform)s /m -%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=%(platform)s /m -xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% -copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% -endlocal -""" # noqa: E501 - % compiler - ) - - -def add_compiler(compiler, bit): - script.append(setup_compiler(compiler)) - script.append(nmake_libs(compiler, bit)) - - # script.append(extract_openjpeg(compiler)) - - script.append(msbuild_freetype(compiler, bit)) - script.append(build_lcms2(compiler)) - script.append(nmake_openjpeg(compiler, bit)) - script.append(end_compiler()) - - -mkdirs() -extract_libs() -script = [header(), cp_tk(libs["tk-8.5"]["version"], libs["tk-8.6"]["version"])] - - -if "PYTHON" in os.environ: - add_compiler(compiler_from_env(), bit_from_env()) -else: - # for compiler in all_compilers(): - # add_compiler(compiler) - add_compiler(compilers[7.0][2010][32], 32) - -with open("build_deps.cmd", "w") as f: - f.write("\n".join(script)) diff --git a/winbuild/config.py b/winbuild/config.py deleted file mode 100644 index 93413d1e573..00000000000 --- a/winbuild/config.py +++ /dev/null @@ -1,199 +0,0 @@ -import os - -SF_MIRROR = "https://iweb.dl.sourceforge.net" - -pythons = { - "pypy3": {"compiler": 7.1, "vc": 2015}, - # for AppVeyor - "35": {"compiler": 7.1, "vc": 2015}, - "36": {"compiler": 7.1, "vc": 2015}, - "37": {"compiler": 7.1, "vc": 2015}, - "38": {"compiler": 7.1, "vc": 2015}, - # for GitHub Actions - "3.5": {"compiler": 7.1, "vc": 2015}, - "3.6": {"compiler": 7.1, "vc": 2015}, - "3.7": {"compiler": 7.1, "vc": 2015}, - "3.8": {"compiler": 7.1, "vc": 2015}, -} - -VIRT_BASE = "c:/vp/" -X64_EXT = os.environ.get("X64_EXT", "x64") - -libs = { - # 'openjpeg': { - # 'filename': 'openjpeg-2.0.0-win32-x86.zip', - # 'version': '2.0' - # }, - "zlib": { - "url": "http://zlib.net/zlib1211.zip", - "filename": "zlib1211.zip", - "dir": "zlib-1.2.11", - }, - "jpeg": { - "url": "http://www.ijg.org/files/jpegsr9d.zip", - "filename": "jpegsr9d.zip", - "dir": "jpeg-9d", - }, - "tiff": { - "url": "ftp://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz", - "filename": "tiff-4.1.0.tar.gz", - "dir": "tiff-4.1.0", - }, - "freetype": { - "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501 - "filename": "freetype-2.10.1.tar.gz", - "dir": "freetype-2.10.1", - }, - "lcms-2.7": { - "url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip", - "filename": "lcms2-2.7.zip", - "dir": "lcms2-2.7", - }, - "lcms-2.8": { - "url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.8.zip", - "filename": "lcms2-2.8.zip", - "dir": "lcms2-2.8", - }, - "tcl-8.5": { - "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip", - "filename": "tcl8519-src.zip", - "dir": "", - }, - "tk-8.5": { - "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip", - "filename": "tk8519-src.zip", - "dir": "", - "version": "8.5.19", - }, - "tcl-8.6": { - "url": SF_MIRROR + "/project/tcl/Tcl/8.6.10/tcl8610-src.zip", - "filename": "tcl8610-src.zip", - "dir": "", - }, - "tk-8.6": { - "url": SF_MIRROR + "/project/tcl/Tcl/8.6.10/tk8610-src.zip", - "filename": "tk8610-src.zip", - "dir": "", - "version": "8.6.10", - }, - "webp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz", - "filename": "libwebp-1.1.0.tar.gz", - "dir": "libwebp-1.1.0", - }, - "openjpeg": { - "url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", - "filename": "openjpeg-2.3.1.tar.gz", - "dir": "openjpeg-2.3.1", - }, - "jpeg-turbo": { - "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", - "filename": "libjpeg-turbo-2.0.3.tar.gz", - "dir": "libjpeg-turbo-2.0.3", - }, - # e5d454b: Merge tag '2.12.6' into msvc - "imagequant": { - "url": "https://github.com/ImageOptim/libimagequant/archive/e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip", # noqa: E501 - "filename": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip", - "dir": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4", - }, - "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.4.zip", - "filename": "harfbuzz-2.6.4.zip", - "dir": "harfbuzz-2.6.4", - }, - "fribidi": { - "url": "https://github.com/fribidi/fribidi/archive/v1.0.9.zip", - "filename": "fribidi-1.0.9.zip", - "dir": "fribidi-1.0.9", - }, - "libraqm": { - "url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip", - "filename": "libraqm-0.7.0.zip", - "dir": "libraqm-0.7.0", - }, -} - -compilers = { - 7: { - 2010: { - 64: { - "env_version": "v7.0", - "vc_version": "2010", - "env_flags": "/x64 /xp", - "inc_dir": "msvcr90-x64", - "platform": "x64", - "webp_platform": "x64", - }, - 32: { - "env_version": "v7.0", - "vc_version": "2010", - "env_flags": "/x86 /xp", - "inc_dir": "msvcr90-x32", - "platform": "Win32", - "webp_platform": "x86", - }, - } - }, - 7.1: { - 2015: { - 64: { - "env_version": "v7.1", - "vc_version": "2015", - "env_flags": "/x64 /vista", - "inc_dir": "msvcr10-x64", - "platform": "x64", - "webp_platform": "x64", - }, - 32: { - "env_version": "v7.1", - "vc_version": "2015", - "env_flags": "/x86 /vista", - "inc_dir": "msvcr10-x32", - "platform": "Win32", - "webp_platform": "x86", - }, - } - }, -} - - -def pyversion_from_env(): - py = os.environ["PYTHON"] - - py_version = "35" - for k in pythons: - if k in py: - py_version = k - break - - if "64" in py: - py_version = "{}{}".format(py_version, X64_EXT) - - return py_version - - -def compiler_from_env(): - py = os.environ["PYTHON"] - - for k, v in pythons.items(): - if k in py: - py_info = v - break - - bit = bit_from_env() - return compilers[py_info["compiler"]][py_info["vc"]][bit] - - -def bit_from_env(): - py = os.environ["PYTHON"] - - return 64 if "64" in py else 32 - - -def all_compilers(): - all = [] - for vc_compilers in compilers.values(): - for bit_compilers in vc_compilers.values(): - all += bit_compilers.values() - return all diff --git a/winbuild/fetch.py b/winbuild/fetch.py deleted file mode 100644 index adc45429a44..00000000000 --- a/winbuild/fetch.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import sys -import urllib.parse -import urllib.request - -from config import libs - - -def fetch(url): - depends_filename = None - for lib in libs.values(): - if lib["url"] == url: - depends_filename = lib["filename"] - break - if depends_filename and os.path.exists(depends_filename): - return depends_filename - name = urllib.parse.urlsplit(url)[2].split("/")[-1] - - if not os.path.exists(name): - - def retrieve(request_url): - print("Fetching", request_url) - try: - return urllib.request.urlopen(request_url) - except urllib.error.URLError: - return urllib.request.urlopen(request_url) - - try: - r = retrieve(url) - except urllib.error.HTTPError: - if depends_filename: - r = retrieve( - "https://github.com/python-pillow/pillow-depends/raw/master/" - + depends_filename - ) - name = depends_filename - content = r.read() - with open(name, "wb") as fd: - fd.write(content) - return name - - -if __name__ == "__main__": - fetch(sys.argv[1]) diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py deleted file mode 100644 index a853fc6f7ea..00000000000 --- a/winbuild/get_pythons.py +++ /dev/null @@ -1,15 +0,0 @@ -import os - -from fetch import fetch - -if __name__ == "__main__": - for version in ["3.4.4"]: - for platform in ["", ".amd64"]: - for extension in ["", ".asc"]: - fetch( - "https://www.python.org/ftp/python/%s/python-%s%s.msi%s" - % (version, version, platform, extension) - ) - - # find pip, if it's not in the path! - os.system("pip install virtualenv") diff --git a/winbuild/lcms2_patch.ps1 b/winbuild/lcms2_patch.ps1 deleted file mode 100644 index 7fc48c034e5..00000000000 --- a/winbuild/lcms2_patch.ps1 +++ /dev/null @@ -1,9 +0,0 @@ - -Get-ChildItem .\Projects\VC2015\ *.vcxproj -recurse | - Foreach-Object { - $c = ($_ | Get-Content) - $c = $c -replace 'MultiThreaded<','MultiThreadedDLL<' - $c = $c -replace '8.1','10' - $c = $c -replace 'v140','v142' - [IO.File]::WriteAllText($_.FullName, ($c -join "`r`n")) - } diff --git a/winbuild/test.py b/winbuild/test.py deleted file mode 100755 index a05a20b1892..00000000000 --- a/winbuild/test.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 - -import glob -import os -import subprocess -import sys - -from config import VIRT_BASE, X64_EXT, pythons - - -def test_one(params): - python, architecture = params - try: - print("Running: %s, %s" % params) - command = [ - r"{}\{}{}\Scripts\python.exe".format(VIRT_BASE, python, architecture), - "test-installed.py", - "--processes=-0", - "--process-timeout=30", - ] - command.extend(glob.glob("Tests/test*.py")) - proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - (trace, stderr) = proc.communicate() - status = proc.returncode - print("Done with {}, {} -- {}".format(python, architecture, status)) - return (python, architecture, status, trace) - except Exception as msg: - print("Error with {}, {}: {}".format(python, architecture, msg)) - return (python, architecture, -1, str(msg)) - - -if __name__ == "__main__": - - os.chdir("..") - matrix = [ - (python, architecture) for python in pythons for architecture in ("", X64_EXT) - ] - - results = map(test_one, matrix) - - for (python, architecture, status, trace) in results: - print("{}{}: {}".format(python, architecture, status and "ERR" or "PASS")) - - res = all(status for (python, architecture, status, trace) in results) - sys.exit(res) diff --git a/winbuild/untar.py b/winbuild/untar.py deleted file mode 100644 index f2713b2f27d..00000000000 --- a/winbuild/untar.py +++ /dev/null @@ -1,11 +0,0 @@ -import sys -import tarfile - - -def untar(src, dest): - with tarfile.open(src, "r:gz") as tgz: - tgz.extractall(dest) - - -if __name__ == "__main__": - untar(sys.argv[1], sys.argv[2]) diff --git a/winbuild/unzip.py b/winbuild/unzip.py deleted file mode 100644 index eb17a2e6330..00000000000 --- a/winbuild/unzip.py +++ /dev/null @@ -1,11 +0,0 @@ -import sys -import zipfile - - -def unzip(src, dest): - with zipfile.ZipFile(src) as zf: - zf.extractall(dest) - - -if __name__ == "__main__": - unzip(sys.argv[1], sys.argv[2]) From 98dd1b7e6ef2b0f967e7c90abc3db6b77a113c61 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 24 Feb 2020 23:00:57 +0000 Subject: [PATCH 045/262] add parameters to build script --- .appveyor.yml | 3 +-- .github/workflows/test-windows.yml | 5 +---- setup.py | 2 +- winbuild/README.md | 2 +- winbuild/build.rst | 29 +++++++++++++++++------- winbuild/build_prepare.py | 36 +++++++++++++++++++++++++++--- 6 files changed, 58 insertions(+), 19 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4d2cb81d228..8602293f7b1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -48,8 +48,7 @@ install: } else { - $env:PILLOW_DEPS = "C:\pillow-depends\" - c:\python37\python.exe c:\pillow\winbuild\build_prepare.py + c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\ c:\pillow\winbuild\build\build_dep_all.cmd $host.SetShouldExit(0) } diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index fe958ad5aa7..fe0fdc04cd4 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -66,12 +66,9 @@ jobs: ..\pillow-depends\gs950w32.exe /S Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" - $env:PILLOW_DEPS = "$env:RUNNER_WORKSPACE\pillow-depends\" xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ - $env:PYTHON=$env:pythonLocation - cd $env:GITHUB_WORKSPACE/winbuild/ - & python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py + & python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py -v --depends=$env:RUNNER_WORKSPACE\pillow-depends\ --python=$env:pythonLocation shell: pwsh - name: Build dependencies / libjpeg-turbo diff --git a/setup.py b/setup.py index b55b1de62cc..1236b5df5a8 100755 --- a/setup.py +++ b/setup.py @@ -120,7 +120,7 @@ def get_version(): "codec_fd", ) -DEBUG = True +DEBUG = False class DependencyException(Exception): diff --git a/winbuild/README.md b/winbuild/README.md index dabbc359ac3..45c37e4cb30 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -19,7 +19,7 @@ The following is a simplified version of the script used on AppVeyor: ``` set PYTHON=C:\Python35\bin cd /D C:\Pillow\winbuild -C:\Python37\bin\python.exe build_prepare.py +C:\Python37\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends build\build_dep_all.cmd build\build_pillow.cmd install cd .. diff --git a/winbuild/build.rst b/winbuild/build.rst index f47efb3632d..465bc2591c7 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -23,18 +23,20 @@ Compilers Download and install: -* `Microsoft Visual Studio 2017 or newer (with C++ component) - `_ +* `Microsoft Visual Studio 2017 or newer or Build Tools for Visual Studio 2017 or newer + `_ + (MSVC C++ build tools, and any Windows SDK version required) -* `CMake 3.13 or newer - `_ +* `CMake 3.13 or newer `_ + (also available as Visual Studio component C++ CMake tools for Windows) * `NASM `_ Any version of Visual Studio 2017 or newer should be supported, -including Visual Studio 2017 Community. +including Visual Studio 2017 Community, or Build Tools for Visual Studio 2019. -Paths to CMake and NASM must be added to the ``PATH`` environment variable. +Paths to CMake (if standalone) and NASM must be added to the ``PATH`` environment variable. +Visual Studio is found automatically with ``vswhere.exe``. Build configuration ------------------- @@ -47,7 +49,7 @@ behaviour of ``build_prepare.py``: ``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, - ``x86`` is used, unless ``PYTHON`` contains ``x64``, in which case ``x64`` + 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. @@ -55,6 +57,17 @@ behaviour of ``build_prepare.py``: * ``PILLOW_DEPS`` points to the directory used to store downloaded dependencies. By default ``winbuild\depends`` is used. +``build_prepare.py`` also supports the following command line parameters: + +* ``-v`` will print generated scripts. +* ``--no-imagequant`` will skip GPL-licensed ``libimagequant`` optional dependency +* ``--no-raqm`` will skip optional dependency Raqm (which itself depends on + LGPL-licensed ``fribidi``). +* ``--python=`` and ``--executable=`` override ``PYTHON`` and ``EXECUTABLE``. +* ``--architecture=`` overrides ``ARCHITECTURE``. +* ``--dir=`` and ``--depends=`` override ``PILLOW_BUILD`` + and ``PILLOW_DEPS``. + Dependencies ------------ @@ -93,7 +106,7 @@ The following is a simplified version of the script used on AppVeyor: set PYTHON=C:\Python35\bin cd /D C:\Pillow\winbuild - C:\Python37\bin\python.exe build_prepare.py + C:\Python37\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends build\build_dep_all.cmd build\build_pillow.cmd install cd .. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index e56b71eb4bb..475982da271 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -1,5 +1,6 @@ import os import shutil +import struct import subprocess import sys @@ -176,6 +177,8 @@ def cmd_msbuild( r"builds\windows\vc2010\freetype.vcxproj": { # freetype setting is /MD for .dll and /MT for .lib, we need /MD "MultiThreaded": "MultiThreadedDLL", # noqa E501 + # freetype doesn't specify SDK version, MSBuild may guess incorrectly + '': '\n $(WindowsSDKVersion)', # noqa E501 } }, "build": [ @@ -389,8 +392,9 @@ def write_script(name, lines): print("Writing " + name) with open(name, "w") as f: f.write("\n\r".join(lines)) - for line in lines: - print(" " + line) + if verbose: + for line in lines: + print(" " + line) def get_footer(dep): @@ -438,6 +442,8 @@ def build_dep(name): def build_dep_all(): lines = ["@echo on"] for dep_name in deps: + if dep_name in disabled: + continue lines.append(r'cmd.exe /c "{{build_dir}}\{}"'.format(build_dep(dep_name))) lines.append("if errorlevel 1 echo Build failed! && exit /B 1") lines.append("@echo All Pillow dependencies built successfully!") @@ -459,6 +465,28 @@ def build_pillow(): if __name__ == "__main__": + verbose = False + disabled = [] + for arg in sys.argv[1:]: + if arg == "-v": + verbose = True + elif arg == "--no-imagequant": + disabled += ["libimagequant"] + elif arg == "--no-raqm": + disabled += ["harfbuzz", "fribidi", "libraqm"] + elif arg.startswith("--depends="): + os.environ["PILLOW_DEPS"] = arg[10:] + elif arg.startswith("--python="): + os.environ["PYTHON"] = arg[9:] + elif arg.startswith("--executable="): + os.environ["EXECUTABLE"] = arg[13:] + elif arg.startswith("--architecture="): + os.environ["ARCHITECTURE"] = arg[15:] + elif arg.startswith("--dir="): + os.environ["PILLOW_BUILD"] = arg[6:] + else: + raise ValueError("Unknown parameter: " + arg) + # winbuild directory winbuild_dir = os.path.dirname(os.path.realpath(__file__)) @@ -477,7 +505,7 @@ def build_pillow(): # use ARCHITECTURE or PYTHON to select architecture architecture = os.environ.get( - "ARCHITECTURE", "x64" if "x64" in python_dir else "x86" + "ARCHITECTURE", "x86" if struct.calcsize("P") == 4 else "x64" ) arch_prefs = architectures[architecture] print("Target Architecture:", architecture) @@ -528,5 +556,7 @@ def build_pillow(): "header": sum([header, msvs["header"], ["@echo on"]], []), } + print() + build_dep_all() build_pillow() From 0e5a6e6eb1a61392f3bcc29340c717b495081cda Mon Sep 17 00:00:00 2001 From: nulano Date: Fri, 27 Mar 2020 22:07:59 +0100 Subject: [PATCH 046/262] simplify default arguments in build_prepare --- winbuild/build_prepare.py | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 475982da271..36730ca0b1b 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -465,8 +465,18 @@ def build_pillow(): if __name__ == "__main__": + # winbuild directory + winbuild_dir = os.path.dirname(os.path.realpath(__file__)) + verbose = False disabled = [] + depends_dir = os.environ.get("PILLOW_DEPS", os.path.join(winbuild_dir, "depends")) + 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" + ) + build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build")) for arg in sys.argv[1:]: if arg == "-v": verbose = True @@ -475,38 +485,27 @@ def build_pillow(): elif arg == "--no-raqm": disabled += ["harfbuzz", "fribidi", "libraqm"] elif arg.startswith("--depends="): - os.environ["PILLOW_DEPS"] = arg[10:] + depends_dir = arg[10:] elif arg.startswith("--python="): - os.environ["PYTHON"] = arg[9:] + python_dir = arg[9:] elif arg.startswith("--executable="): - os.environ["EXECUTABLE"] = arg[13:] + python_exe = arg[13:] elif arg.startswith("--architecture="): - os.environ["ARCHITECTURE"] = arg[15:] + architecture = arg[15:] elif arg.startswith("--dir="): - os.environ["PILLOW_BUILD"] = arg[6:] + build_dir = arg[6:] else: raise ValueError("Unknown parameter: " + arg) - # winbuild directory - winbuild_dir = os.path.dirname(os.path.realpath(__file__)) - # dependency cache directory - depends_dir = os.environ.get("PILLOW_DEPS", os.path.join(winbuild_dir, "depends")) os.makedirs(depends_dir, exist_ok=True) print("Caching dependencies in:", depends_dir) - # Python bin directory - python_dir = os.environ.get("PYTHON") - python_exe = os.environ.get("EXECUTABLE", "python.exe") if python_dir is None: python_dir = os.path.dirname(os.path.realpath(sys.executable)) python_exe = os.path.basename(sys.executable) print("Target Python:", os.path.join(python_dir, python_exe)) - # use ARCHITECTURE or PYTHON to select architecture - architecture = os.environ.get( - "ARCHITECTURE", "x86" if struct.calcsize("P") == 4 else "x64" - ) arch_prefs = architectures[architecture] print("Target Architecture:", architecture) @@ -517,16 +516,12 @@ def build_pillow(): ) print("Found Visual Studio at:", msvs["vs_dir"]) - # build root directory - build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build")) print("Using output directory:", build_dir) # build directory for *.h files inc_dir = os.path.join(build_dir, "inc") - # build directory for *.lib files lib_dir = os.path.join(build_dir, "lib") - # build directory for *.bin files bin_dir = os.path.join(build_dir, "bin") From 9aa42c3fd6a12a4069e38bd4744e1f4a3fc46b37 Mon Sep 17 00:00:00 2001 From: nulano Date: Fri, 27 Mar 2020 22:22:51 +0100 Subject: [PATCH 047/262] fix AppVeyor --- .appveyor.yml | 5 +++++ Tests/test_imageshow.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 8602293f7b1..a06ba9902bc 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -13,11 +13,16 @@ environment: DEPLOY: YES matrix: - PYTHON: C:/Python38 + ARCHITECTURE: x86 - PYTHON: C:/Python38-x64 + ARCHITECTURE: x64 - PYTHON: C:/Python35 + ARCHITECTURE: x86 - PYTHON: C:/Python35-x64 + ARCHITECTURE: x64 - PYTHON: C:/msys64/mingw32 EXECUTABLE: bin/python3 + ARCHITECTURE: x86 PIP_DIR: bin TEST_OPTIONS: --processes=0 DEPLOY: NO diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index a9332bef43e..64f15326b40 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -38,7 +38,7 @@ def show_image(self, image, **options): @pytest.mark.skipif( - not on_ci() or is_win32(), reason="Only run on CIs; hangs on Windows 10", + not on_ci() or is_win32(), reason="Only run on CIs; hangs on Windows CIs", ) def test_show(): for mode in ("1", "I;16", "LA", "RGB", "RGBA"): From 3dedaec32278715db477bd0e2a540c693d57c7fb Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 29 Mar 2020 16:52:02 +0200 Subject: [PATCH 048/262] allow older cmake to support Visual Studio 2017 --- winbuild/README.md | 2 +- winbuild/build.rst | 2 +- winbuild/fribidi.cmake | 2 +- winbuild/raqm.cmake | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/winbuild/README.md b/winbuild/README.md index 45c37e4cb30..d46361c9e8e 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -10,7 +10,7 @@ For more extensive info, see the [Windows build instructions](build.rst). * Requires Microsoft Visual Studio 2017 or newer with C++ component. * Requires NASM for libjpeg-turbo, a required dependency when using this script. -* Requires CMake 3.13 or newer. +* Requires CMake 3.12 or newer (available as Visual Studio component). * Python 3.6+ is required to generate valid scripts, but builds targeting Python 3.5+ are supported. * Tested on Windows Server 2016 with Visual Studio 2017 Community (AppVeyor). * Tested on Windows Server 2019 with Visual Studio 2019 Enterprise (GitHub Actions). diff --git a/winbuild/build.rst b/winbuild/build.rst index 465bc2591c7..517843a66b2 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -27,7 +27,7 @@ Download and install: `_ (MSVC C++ build tools, and any Windows SDK version required) -* `CMake 3.13 or newer `_ +* `CMake 3.12 or newer `_ (also available as Visual Studio component C++ CMake tools for Windows) * `NASM `_ diff --git a/winbuild/fribidi.cmake b/winbuild/fribidi.cmake index 247e79e4cdd..11217473139 100644 --- a/winbuild/fribidi.cmake +++ b/winbuild/fribidi.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.12) project(fribidi) diff --git a/winbuild/raqm.cmake b/winbuild/raqm.cmake index 88eb7f28479..e8e71800e00 100644 --- a/winbuild/raqm.cmake +++ b/winbuild/raqm.cmake @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.12) project(libraqm) From 398b8a64d67b35f026587ea85ff6a967d945f2d6 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 29 Mar 2020 20:18:39 +0200 Subject: [PATCH 049/262] use v2 actions --- .github/workflows/test-windows.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index fe0fdc04cd4..a5761a0e051 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -27,13 +27,13 @@ jobs: steps: - name: Checkout Pillow - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Checkout cached dependencies - uses: actions/checkout@v1 + uses: actions/checkout@v2 with: repository: python-pillow/pillow-depends - ref: master + path: winbuild\depends - name: Cache pip uses: actions/cache@v1 @@ -60,15 +60,15 @@ jobs: - name: Prepare dependencies run: | - 7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\" + 7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\" Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02" - ..\pillow-depends\gs950w32.exe /S + winbuild\depends\gs950w32.exe /S Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin" - xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\ + xcopy /s winbuild\depends\test_images\* Tests\images\ - & python.exe $env:GITHUB_WORKSPACE\winbuild\build_prepare.py -v --depends=$env:RUNNER_WORKSPACE\pillow-depends\ --python=$env:pythonLocation + & python.exe winbuild\build_prepare.py -v --python=$env:pythonLocation shell: pwsh - name: Build dependencies / libjpeg-turbo @@ -149,8 +149,8 @@ jobs: winbuild\\build\\build_pillow.cmd bdist_wheel" shell: cmd - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v2-preview if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" with: name: ${{ steps.wheel.outputs.dist }} - path: dist + path: dist\*.whl From 999823130f29daefb3ee081a2c73b474af0abafd Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 29 Mar 2020 22:27:24 +0200 Subject: [PATCH 050/262] use v2 checkout in all workflows --- .github/workflows/lint.yml | 2 +- .github/workflows/test-docker.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3cad8d4170b..6c5d81ac44d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,7 +13,7 @@ jobs: name: Python ${{ matrix.python-version }} steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: pip cache uses: actions/cache@v1 diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index b6e703fd9a3..a2cb70b2054 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -29,7 +29,7 @@ jobs: name: ${{ matrix.docker }} steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Build system information run: python .github/workflows/system-info.py From 79181c20a544c31e2e778e9cbacfae2f9ac3ba15 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 30 Mar 2020 11:02:44 +0200 Subject: [PATCH 051/262] remove more AppVeyor jobs --- .appveyor.yml | 12 ------------ docs/installation.rst | 14 ++++++++------ winbuild/appveyor_install_pypy3.cmd | 3 --- 3 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 winbuild/appveyor_install_pypy3.cmd diff --git a/.appveyor.yml b/.appveyor.yml index a06ba9902bc..c0a8a023717 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,10 +14,6 @@ environment: matrix: - PYTHON: C:/Python38 ARCHITECTURE: x86 - - PYTHON: C:/Python38-x64 - ARCHITECTURE: x64 - - PYTHON: C:/Python35 - ARCHITECTURE: x86 - PYTHON: C:/Python35-x64 ARCHITECTURE: x64 - PYTHON: C:/msys64/mingw32 @@ -26,9 +22,6 @@ environment: PIP_DIR: bin TEST_OPTIONS: --processes=0 DEPLOY: NO - - PYTHON: C:/vp/pypy3 - EXECUTABLE: bin/pypy.exe - VENV: YES install: @@ -41,11 +34,6 @@ install: - gs952.exe /S - path c:\nasm-2.14.02;C:\Program Files (x86)\gs\gs9.52\bin;%PATH% - cd c:\pillow\winbuild\ -- ps: | - if ($env:PYTHON -eq "c:/vp/pypy3") - { - c:\pillow\winbuild\appveyor_install_pypy3.cmd - } - ps: | if ($env:PYTHON -eq "c:/msys64/mingw32") { diff --git a/docs/installation.rst b/docs/installation.rst index a83bf5748d1..307a05ad3ce 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -292,9 +292,7 @@ or from within the uncompressed source directory:: Building on Windows ^^^^^^^^^^^^^^^^^^^ -We don't recommend trying to build on Windows. It is a maze of twisty -passages, mostly dead ends. There are build scripts and notes for the -Windows build in the ``winbuild`` directory. +There are build scripts and notes for the Windows build in the ``winbuild`` directory. Building on FreeBSD ^^^^^^^^^^^^^^^^^^^ @@ -404,9 +402,11 @@ These platforms are built and tested for every change. +----------------------------------+--------------------------+-----------------------+ | Ubuntu Linux 16.04 LTS | 3.5, 3.6, 3.7, 3.8, PyPy3|x86-64 | +----------------------------------+--------------------------+-----------------------+ -| Windows Server 2012 R2 | 3.5, 3.8 |x86, x86-64 | +| Windows Server 2016 | 3.8 |x86 | | +--------------------------+-----------------------+ -| | PyPy3, 3.7/MinGW |x86 | +| | 3.5 |x86-64 | +| +--------------------------+-----------------------+ +| | 3.7/MinGW |x86 | +----------------------------------+--------------------------+-----------------------+ | Windows Server 2019 | 3.5, 3.6, 3.7, 3.8 |x86, x86-64 | | +--------------------------+-----------------------+ @@ -474,11 +474,13 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+------------------------------+--------------------------------+-----------------------+ | FreeBSD 10.2 | 2.7, 3.4 | 3.1.0 |x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ +| Windows 10 | 3.7 | 7.1.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 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ -| Windows 7 Pro | 2.7, 3.2, 3.3 | 3.4.1 |x86-64 | +| Windows 7 Professional | 3.7 | 7.0.0 |x86,x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ | Windows Server 2008 R2 Enterprise| 3.3 | |x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ diff --git a/winbuild/appveyor_install_pypy3.cmd b/winbuild/appveyor_install_pypy3.cmd deleted file mode 100644 index 4ec5384be4d..00000000000 --- a/winbuild/appveyor_install_pypy3.cmd +++ /dev/null @@ -1,3 +0,0 @@ -curl -fsSL -o pypy3.zip https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-win32.zip -7z x pypy3.zip -oc:\ -c:\Python37\Scripts\virtualenv.exe -p c:\pypy3.6-v7.3.1-win32\pypy3.exe c:\vp\pypy3 From b2f187c4ec16948555eeb68113dfa76133b52c8f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 12 Apr 2020 15:11:29 +1000 Subject: [PATCH 052/262] Added multiline text example [ci skip] --- docs/reference/ImageDraw.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index d888913c34a..3e453dd6f35 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -100,6 +100,25 @@ Example: Draw Partial Opacity Text out.show() +Example: Draw Multiline Text +---------------------------- + +.. code-block:: python + + from PIL import Image, ImageDraw, ImageFont + + # create an image + out = Image.new("RGB", (150, 100), (255, 255, 255)) + + # get a font + fnt = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", 40) + # get a drawing context + d = ImageDraw.Draw(out) + + # draw multiline text + d.multiline_text((10,10), "Hello\nWorld", font=fnt, fill=(0, 0, 0)) + + out.show() Functions From 2df26d8ea64e83d60b17b72bca2c73007ea925dd Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 12 Apr 2020 10:18:49 +0200 Subject: [PATCH 053/262] restore library updates in master --- winbuild/build_prepare.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 36730ca0b1b..ad344ffa72b 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -102,7 +102,7 @@ def cmd_msbuild( cmd_append("PATH", "{bin_dir}"), ] -# dependencies +# dependencies, listed in order of compilation deps = { "libjpeg": { "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", @@ -154,9 +154,9 @@ def cmd_msbuild( # "bins": [r"libtiff\*.dll"], }, "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz", # noqa: E501 - "filename": "libwebp-1.0.3.tar.gz", - "dir": "libwebp-1.0.3", + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz", # noqa: E501 + "filename": "libwebp-1.1.0.tar.gz", + "dir": "libwebp-1.1.0", "build": [ cmd_rmdir(r"output\release-static"), # clean cmd_nmake( @@ -176,9 +176,9 @@ def cmd_msbuild( "patch": { r"builds\windows\vc2010\freetype.vcxproj": { # freetype setting is /MD for .dll and /MT for .lib, we need /MD - "MultiThreaded": "MultiThreadedDLL", # noqa E501 + "MultiThreaded": "MultiThreadedDLL", # noqa: E501 # freetype doesn't specify SDK version, MSBuild may guess incorrectly - '': '\n $(WindowsSDKVersion)', # noqa E501 + '': '\n $(WindowsSDKVersion)', # noqa: E501 } }, "build": [ @@ -201,11 +201,11 @@ def cmd_msbuild( "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always - "MultiThreaded": "MultiThreadedDLL", # noqa E501 + "MultiThreaded": "MultiThreadedDLL", # noqa: E501 # retarget to default toolset (selected by vcvarsall.bat) - "v141": "$(DefaultPlatformToolset)", # noqa E501 + "v141": "$(DefaultPlatformToolset)", # noqa: E501 # retarget to latest (selected by vcvarsall.bat) - "8.1": "$(WindowsSDKVersion)", # noqa E501 + "8.1": "$(WindowsSDKVersion)", # noqa: E501 } }, "build": [ @@ -231,10 +231,10 @@ def cmd_msbuild( "libs": [r"bin\*.lib"], }, "libimagequant": { - # ba653c8: Merge tag '2.12.5' into msvc - "url": "https://github.com/ImageOptim/libimagequant/archive/ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", # noqa: E501 - "filename": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", - "dir": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971", + # e5d454b: Merge tag '2.12.6' into msvc + "url": "https://github.com/ImageOptim/libimagequant/archive/e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip", # noqa: E501 + "filename": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip", + "dir": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4", "patch": { "CMakeLists.txt": { "add_library": "add_compile_options(-openmp-)\r\nadd_library", @@ -251,9 +251,9 @@ def cmd_msbuild( "libs": [r"*.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.1.zip", - "filename": "harfbuzz-2.6.1.zip", - "dir": "harfbuzz-2.6.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.4.zip", + "filename": "harfbuzz-2.6.4.zip", + "dir": "harfbuzz-2.6.4", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), @@ -263,9 +263,9 @@ def cmd_msbuild( "libs": [r"*.lib"], }, "fribidi": { - "url": "https://github.com/fribidi/fribidi/archive/v1.0.7.zip", - "filename": "fribidi-1.0.7.zip", - "dir": "fribidi-1.0.7", + "url": "https://github.com/fribidi/fribidi/archive/v1.0.9.zip", + "filename": "fribidi-1.0.9.zip", + "dir": "fribidi-1.0.9", "build": [ cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"), cmd_cmake(), From 13dcab0fb74f6a6d8e6f69471a4ec51dc89bf890 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Apr 2020 07:16:46 +1000 Subject: [PATCH 054/262] Replaced DEBUG with logging --- src/PIL/TiffImagePlugin.py | 133 +++++++++++++++---------------------- 1 file changed, 54 insertions(+), 79 deletions(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 4b04752edae..dbb165071f0 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -40,6 +40,7 @@ # import io import itertools +import logging import os import struct import warnings @@ -51,7 +52,7 @@ from ._binary import i8, o8 from .TiffTags import TYPES -DEBUG = False # Needs to be merged with the new logging approach. +logger = logging.getLogger(__name__) # Set these to true to force use of libtiff for reading or writing. READ_LIBTIFF = False @@ -734,29 +735,21 @@ def load(self, fp): try: for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]): tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12)) - if DEBUG: - tagname = TiffTags.lookup(tag).name - typname = TYPES.get(typ, "unknown") - print( - "tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), - end=" ", - ) + + tagname = TiffTags.lookup(tag).name + typname = TYPES.get(typ, "unknown") + msg = "tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ) try: unit_size, handler = self._load_dispatch[typ] except KeyError: - if DEBUG: - print("- unsupported type", typ) + logger.debug(msg + " - unsupported type {}".format(typ)) continue # ignore unsupported type size = count * unit_size if size > 4: here = fp.tell() (offset,) = self._unpack("L", data) - if DEBUG: - print( - "Tag Location: {} - Data Location: {}".format(here, offset), - end=" ", - ) + msg += " Tag Location: {} - Data Location: {}".format(here, offset) fp.seek(offset) data = ImageFile._safe_read(fp, size) fp.seek(here) @@ -769,19 +762,20 @@ def load(self, fp): "Expecting to read %d bytes but only got %d." " Skipping tag %s" % (size, len(data), tag) ) + logger.debug(msg) continue if not data: + logger.debug(msg) continue self._tagdata[tag] = data self.tagtype[tag] = typ - if DEBUG: - if size > 32: - print("- value: " % size) - else: - print("- value:", self[tag]) + msg += " - value: " + ( + "" % size if size > 32 else str(data) + ) + logger.debug(msg) (self.next,) = self._unpack("L", self._ensure_read(fp, 4)) except OSError as msg: @@ -802,21 +796,17 @@ def tobytes(self, offset=0): if tag == STRIPOFFSETS: stripoffsets = len(entries) typ = self.tagtype.get(tag) - if DEBUG: - print("Tag {}, Type: {}, Value: {}".format(tag, typ, value)) + logger.debug("Tag {}, Type: {}, Value: {}".format(tag, typ, value)) values = value if isinstance(value, tuple) else (value,) data = self._write_dispatch[typ](self, *values) - if DEBUG: - tagname = TiffTags.lookup(tag).name - typname = TYPES.get(typ, "unknown") - print( - "save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ), - end=" ", - ) - if len(data) >= 16: - print("- value: " % len(data)) - else: - print("- value:", values) + + tagname = TiffTags.lookup(tag).name + typname = TYPES.get(typ, "unknown") + msg = "save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ) + msg += " - value: " + ( + "" % len(data) if len(data) >= 16 else str(values) + ) + logger.debug(msg) # count is sum of lengths for string and arbitrary data if typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]: @@ -840,8 +830,9 @@ def tobytes(self, offset=0): # pass 2: write entries to file for tag, typ, count, value, data in entries: - if DEBUG: - print(tag, typ, count, repr(value), repr(data)) + logger.debug( + "{} {} {} {} {}".format(tag, typ, count, repr(value), repr(data)) + ) result += self._pack("HHL4s", tag, typ, count, value) # -- overwrite here for multi-page -- @@ -997,10 +988,9 @@ def _open(self): self._frame_pos = [] self._n_frames = None - if DEBUG: - print("*** TiffImageFile._open ***") - print("- __first:", self.__first) - print("- ifh: ", ifh) + logger.debug("*** TiffImageFile._open ***") + logger.debug("- __first: {}".format(self.__first)) + logger.debug("- ifh: {}".format(ifh)) # and load the first frame self._seek(0) @@ -1035,18 +1025,16 @@ def _seek(self, frame): while len(self._frame_pos) <= frame: if not self.__next: raise EOFError("no more images in TIFF file") - if DEBUG: - print( - "Seeking to frame %s, on frame %s, __next %s, location: %s" - % (frame, self.__frame, self.__next, self.fp.tell()) - ) + logger.debug( + "Seeking to frame %s, on frame %s, __next %s, location: %s" + % (frame, self.__frame, self.__next, self.fp.tell()) + ) # reset buffered io handle in case fp # was passed to libtiff, invalidating the buffer self.fp.tell() self.fp.seek(self.__next) self._frame_pos.append(self.__next) - if DEBUG: - print("Loading tags, location: %s" % self.fp.tell()) + logger.debug("Loading tags, location: %s" % self.fp.tell()) self.tag_v2.load(self.fp) self.__next = self.tag_v2.next if self.__next == 0: @@ -1149,21 +1137,18 @@ def _load_libtiff(self): # Rearranging for supporting byteio items, since they have a fileno # that returns an OSError if there's no underlying fp. Easier to # deal with here by reordering. - if DEBUG: - print("have getvalue. just sending in a string from getvalue") + logger.debug("have getvalue. just sending in a string from getvalue") n, err = decoder.decode(self.fp.getvalue()) elif fp: # we've got a actual file on disk, pass in the fp. - if DEBUG: - print("have fileno, calling fileno version of the decoder.") + logger.debug("have fileno, calling fileno version of the decoder.") if not close_self_fp: self.fp.seek(0) # 4 bytes, otherwise the trace might error out n, err = decoder.decode(b"fpfp") else: # we have something else. - if DEBUG: - print("don't have fileno or getvalue. just reading") + logger.debug("don't have fileno or getvalue. just reading") self.fp.seek(0) # UNDONE -- so much for that buffer size thing. n, err = decoder.decode(self.fp.read()) @@ -1203,21 +1188,19 @@ def _setup(self): fillorder = self.tag_v2.get(FILLORDER, 1) - if DEBUG: - print("*** Summary ***") - print("- compression:", self._compression) - print("- photometric_interpretation:", photo) - print("- planar_configuration:", self._planar_configuration) - print("- fill_order:", fillorder) - print("- YCbCr subsampling:", self.tag.get(530)) + logger.debug("*** Summary ***") + logger.debug("- compression: {}".format(self._compression)) + logger.debug("- photometric_interpretation: {}".format(photo)) + logger.debug("- planar_configuration: {}".format(self._planar_configuration)) + logger.debug("- fill_order: {}".format(fillorder)) + logger.debug("- YCbCr subsampling: {}".format(self.tag.get(530))) # size xsize = int(self.tag_v2.get(IMAGEWIDTH)) ysize = int(self.tag_v2.get(IMAGELENGTH)) self._size = xsize, ysize - if DEBUG: - print("- size:", self.size) + logger.debug("- size: {}".format(self.size)) sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,)) if len(sampleFormat) > 1 and max(sampleFormat) == min(sampleFormat) == 1: @@ -1251,18 +1234,15 @@ def _setup(self): bps_tuple, extra_tuple, ) - if DEBUG: - print("format key:", key) + logger.debug("format key: {}".format(key)) try: self.mode, rawmode = OPEN_INFO[key] except KeyError: - if DEBUG: - print("- unsupported format") + logger.debug("- unsupported format") raise SyntaxError("unknown pixel mode") - if DEBUG: - print("- raw mode:", rawmode) - print("- pil mode:", self.mode) + logger.debug("- raw mode: {}".format(rawmode)) + logger.debug("- pil mode: {}".format(self.mode)) self.info["compression"] = self._compression @@ -1303,8 +1283,7 @@ def _setup(self): if fillorder == 2: # Replace fillorder with fillorder=1 key = key[:3] + (1,) + key[4:] - if DEBUG: - print("format key:", key) + logger.debug("format key: {}".format(key)) # this should always work, since all the # fillorder==2 modes have a corresponding # fillorder=1 mode @@ -1366,8 +1345,7 @@ def _setup(self): x = y = 0 layer += 1 else: - if DEBUG: - print("- unsupported data organization") + logger.debug("- unsupported data organization") raise SyntaxError("unknown data organization") # Fix up info. @@ -1447,8 +1425,7 @@ def _save(im, fp, filename): # write any arbitrary tags passed in as an ImageFileDirectory info = im.encoderinfo.get("tiffinfo", {}) - if DEBUG: - print("Tiffinfo Keys: %s" % list(info)) + logger.debug("Tiffinfo Keys: %s" % list(info)) if isinstance(info, ImageFileDirectory_v1): info = info.to_v2() for key in info: @@ -1533,9 +1510,8 @@ def _save(im, fp, filename): ) ifd[JPEGQUALITY] = quality - if DEBUG: - print("Saving using libtiff encoder") - print("Items: %s" % sorted(ifd.items())) + logger.debug("Saving using libtiff encoder") + logger.debug("Items: %s" % sorted(ifd.items())) _fp = 0 if hasattr(fp, "fileno"): try: @@ -1597,8 +1573,7 @@ def _save(im, fp, filename): else: atts[tag] = value - if DEBUG: - print("Converted items: %s" % sorted(atts.items())) + logger.debug("Converted items: %s" % sorted(atts.items())) # libtiff always expects the bytes in native order. # we're storing image byte order. So, if the rawmode From 53a85f2a69d875aa670e4432b3db7e3a0629cd6e Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 11:31:28 +0300 Subject: [PATCH 055/262] Refactor test_pickle_image to use pytest.mark.parametrize --- Tests/test_pickle.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index ba48689883c..b95d8baca9a 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -1,11 +1,14 @@ import pickle +import pytest from PIL import Image -def helper_pickle_file(tmp_path, pickle, protocol=0, mode=None): +def helper_pickle_file( + tmp_path, pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None +): # Arrange - with Image.open("Tests/images/hopper.jpg") as im: + with Image.open(test_file) as im: filename = str(tmp_path / "temp.pkl") if mode: im = im.convert(mode) @@ -35,11 +38,14 @@ def helper_pickle_string( assert im == loaded_im -def test_pickle_image(tmp_path): +@pytest.mark.parametrize( + "test_file", ["Tests/images/hopper.jpg"], +) +def test_pickle_image(test_file, tmp_path): # Act / Assert for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): - helper_pickle_string(pickle, protocol) - helper_pickle_file(tmp_path, pickle, protocol) + helper_pickle_string(pickle, protocol, test_file) + helper_pickle_file(tmp_path, pickle, protocol, test_file) def test_pickle_p_mode(): From d62b9098dd80b7792f0d5478d4e67ac06154606d Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 11:44:28 +0300 Subject: [PATCH 056/262] Refactor other tests into test_pickle_image --- Tests/test_pickle.py | 47 ++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index b95d8baca9a..fe4e75646c3 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -39,45 +39,28 @@ def helper_pickle_string( @pytest.mark.parametrize( - "test_file", ["Tests/images/hopper.jpg"], + ("test_file", "test_mode"), + [ + ("Tests/images/hopper.jpg", None), + ("Tests/images/hopper.jpg", "L"), + ("Tests/images/hopper.jpg", "PA"), + ("Tests/images/test-card.png", None), + ("Tests/images/zero_bb.png", None), + ("Tests/images/zero_bb_scale2.png", None), + ("Tests/images/non_zero_bb.png", None), + ("Tests/images/non_zero_bb_scale2.png", None), + ("Tests/images/p_trns_single.png", None), + ("Tests/images/pil123p.png", None), + ("Tests/images/itxt_chunks.png", None), + ], ) -def test_pickle_image(test_file, tmp_path): +def test_pickle_image(tmp_path, test_file, test_mode): # Act / Assert for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): helper_pickle_string(pickle, protocol, test_file) helper_pickle_file(tmp_path, pickle, protocol, test_file) -def test_pickle_p_mode(): - # Act / Assert - for test_file in [ - "Tests/images/test-card.png", - "Tests/images/zero_bb.png", - "Tests/images/zero_bb_scale2.png", - "Tests/images/non_zero_bb.png", - "Tests/images/non_zero_bb_scale2.png", - "Tests/images/p_trns_single.png", - "Tests/images/pil123p.png", - "Tests/images/itxt_chunks.png", - ]: - for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): - helper_pickle_string(pickle, protocol=protocol, test_file=test_file) - - -def test_pickle_pa_mode(tmp_path): - # Act / Assert - for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): - helper_pickle_string(pickle, protocol, mode="PA") - helper_pickle_file(tmp_path, pickle, protocol, mode="PA") - - -def test_pickle_l_mode(tmp_path): - # Act / Assert - for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): - helper_pickle_string(pickle, protocol, mode="L") - helper_pickle_file(tmp_path, pickle, protocol, mode="L") - - def test_pickle_la_mode_with_palette(tmp_path): # Arrange filename = str(tmp_path / "temp.pkl") From cdf3c981037578c727c70a42693d418cd5347ec4 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 11:45:12 +0300 Subject: [PATCH 057/262] Add failing test for pickling webp --- Tests/test_pickle.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index fe4e75646c3..c5d40b06897 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -44,6 +44,7 @@ def helper_pickle_string( ("Tests/images/hopper.jpg", None), ("Tests/images/hopper.jpg", "L"), ("Tests/images/hopper.jpg", "PA"), + ("Tests/images/hopper.webp", None), ("Tests/images/test-card.png", None), ("Tests/images/zero_bb.png", None), ("Tests/images/zero_bb_scale2.png", None), From 2e6ab7c669603ba13bff3b28de56b2e02107ff93 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 11:46:34 +0300 Subject: [PATCH 058/262] Fix pickling webp --- src/PIL/WebPImagePlugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index eda6855087d..a24ff682cd3 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -38,6 +38,8 @@ class WebPImageFile(ImageFile.ImageFile): format = "WEBP" format_description = "WebP image" + __loaded = -1 + __logical_frame = -1 def _open(self): if not _webp.HAVE_WEBPANIM: From 5529aba441936167df6164a948b799f1c4c8d16a Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 12:56:12 +0300 Subject: [PATCH 059/262] Skip webp test when webp not available --- Tests/test_pickle.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index c5d40b06897..02212c7d880 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -3,6 +3,8 @@ import pytest from PIL import Image +from .helper import skip_unless_feature + def helper_pickle_file( tmp_path, pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None @@ -44,7 +46,9 @@ def helper_pickle_string( ("Tests/images/hopper.jpg", None), ("Tests/images/hopper.jpg", "L"), ("Tests/images/hopper.jpg", "PA"), - ("Tests/images/hopper.webp", None), + pytest.param( + "Tests/images/hopper.webp", None, marks=skip_unless_feature("webp") + ), ("Tests/images/test-card.png", None), ("Tests/images/zero_bb.png", None), ("Tests/images/zero_bb_scale2.png", None), From 1e63f772f838e5e26224260af94ac36db1a4aa1c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 16 Apr 2020 21:05:34 +1000 Subject: [PATCH 060/262] Parse orientation from XMP tags --- Tests/images/xmp_tags_orientation.png | Bin 0 -> 175414 bytes Tests/test_file_png.py | 5 +++++ src/PIL/Image.py | 21 +++++++++++++++++++++ src/PIL/PngImagePlugin.py | 1 + 4 files changed, 27 insertions(+) create mode 100644 Tests/images/xmp_tags_orientation.png diff --git a/Tests/images/xmp_tags_orientation.png b/Tests/images/xmp_tags_orientation.png new file mode 100644 index 0000000000000000000000000000000000000000..c1be1665fa74a9d24a89d567c0b30f7900230b7c GIT binary patch literal 175414 zcmZ^JWl)?!vn~)^g9LXH+}$C#dypWDYmnf!Y=Q@OUDyOjfZzmo0%38Npo_b+*aFMV zckZck?w|W(s@|Tb=c%5j>#gaonJ;=essy;yxF{$n1nO!^1}G?~D*pyPHs-&X7BU9J zKV!_r$kf+VTT9Z;)1BAG-qY5BH_+YdAC7_|Ef?rzW9R1J%V_K1^MCP5 z{`>yVG#@kLf4TU&$uOI0>oF>NdOI+R@Cx(tGt1&KN_*QoN*XAs{P*I2mJGAAudkOR zA74N~0B?X0ucx;YpMZpf1RuX3pP(SmKMx+CAP--gKpqbtmj6)vUk)V)A3JXsFJBi= z561s++Sq#f`N}Xe|HB#oo7^GL<$u{deExg;|AgTC55gzF%g^`!y8F5~{(s#6gZ!`i ze-=yXxdb|VFjI1IckuA}7lI74ps4tNk^L`f`v0Ipf};P0{)b6Y*W1P6--iAZx(u@r zGv9w?;rnl0{t3+YKMMad{wGHc9{&>K{VxL$)x^Aii6wVYRMb;fRAkii_H=al;DCa{ zo@!@pZJ@@@K4?Q?Z9O=~#ewS`U=SOdU|$ zZz%X^BtUs|cJ(TD)T!}%j7;U+1FAQ;Nj)hV@6Jax=V$c!1^ET7tx=++l&+hlCeIrb zVwq4BDmn5$_Y&-5i3&xUrU<_1i_^fwJUGG@cteDV`Su-VbRzE1cV!1XJ4&hvLH?JU zFHoMew4zXHX+bC`@+j&`Z;b*`jh^w5NB1OWPbG8S6SYu|Qtk8jLWh(14CSxbZv6co!ePY8QWHR{P0@apbWgoFi$m3y8P zy4+ShWuJGqh|9=9AoP$fa91eu1U^zD2SHw50d$^sx}TG0x}WYRN<9I^>R;DZEg-Th z0LW>Lr>;qMb}IbZ*RMEiwym=o(uLJSmB0n^Gi$9_p1G$Po8StBTi)^kbaf|)N-N@j z&G>wh#k%!#V0Z{kRLQ2;yAJYi+s`AcuC7*)!F`8pgi%~aB8Aw=gz`~yPKb`hbcbN<`yN+RZyH2%VeInyb zcpqUk3#FgJ24>b?Nfo?fUAdtF_3{Vp?*?AXixwIC?QCiTl2nSfBG)bKEU&Jl%|d3o zVAT=LG)t2K3zK^v>Mfs7zp2*);fs5Q?$V)BGGPc`#1#O!d~jRS4e>Y))9M0=%RpAa z^c6QohD1dA7nvP05UZYd3^f;d7H_}D13}USGZlfi@JF7PkWFoP)o4)g4Rt7=!1eOh zf*QH1+wRcwK`<1l0zbK(H*dVj=MM9PSmY0ciXN=qRjwbEJFiLwBZ!td!io+C1FsH?m%McUxT( zQ3H!$3j_>Z3rWg2yr(aI*RLEK2q!tFKyA56?=@-Bx6%l?QMn2O^!JtC{jhxhV9_}^ z%|$&o@1x|MY#Dg?@CQYFB0+i{bkZL`63|Rhn*S-@=B@#=h zc%5r2@blyT)Duczn|P;1NB=&Gc*xJyVbT_|kCP?b<)@LJJ&qPuhH*l%`sl6*aEelj zP^4@P850evFP%yxwV{dkV!N|O2Etm825ZJ2V5 zS3)XZdlo;mf5AEwr|Fu?d8Q8GpvLP%f@X=n`~;?v_}!WaBcq z(BEpS0>NcFU-$4erlPLl>2^e$EDXF5Hpj>JGxc)pAmw4LubfXUoX&nz~VyiD_xsThTGqnAd^ zefI&g-FM?pT%F6+=(A4*8SmB$mh0Zyk%s*fSm>IUgZuTdF`MD4sW3JQ()JwH9Y@%<;|3!Orto9%N!yKiTPk_2)D`y*@};$HCcfIovK_9}I{Ctmdw zCL42X;Oa?{pvqp;h?>R>(mqE_M8|rBDE((MZ<_h_A6B;9){Z~f(o_|}h36^DLm%b8bcy?4Pp&hw zP&S9$mS21#i@F1j;BoFZarMDt;}FalYT(?K{}Lxi(q}2!6bLBPm@FHsKa(bN+kp z^gcLgzr&3}*@40Y$368(S$wUZNg*`rGliK&A+VzJAy{?^q^$;U-P$lGR{l1O@ma&J z=JL0*f9^&{D^PYdx<`BJOem=|kfP{eI96J9yaN3-c)aME3X6g+Ff{3FA=~y+2UlB1 zcc;wiN-1sgMM$-*7r%$b#sdul>q@T>{aj7b)TOADFiTTDC2S^ISl=@Jf4MH#rkq>q_7ELJZf-t{&&`AJg) z3VA(X1#5E88H(*3MRs0-mOa34TFQz&KY^Ws1A*+nFAf(F;gs(JIW9pwwo2`p8~qV3 ztH#Fm7)tsK7QbpN@k(RD#9Z6l`Y%TKB+{I{#OvQd(#srAjB=S419XH3d+I^kMf$z_ zLgzP@AjIUD*}z$c^U0~;mN*FdX_KMLRjTFU!*4QI6`g}&&E7vd*ZDpw>72z_?-L}8 zHLqjbFeMl5&V_mEEjR4@8f{w-BNhGj-w2#Tni527@6bH4uw>_~yr?!PuW9)(^H0fM z=~~U{5+O)aCyZwa4N^c)pyVPvE$VS5#!q^s(fs?5k3R+u85#S$dYBs3E)*#9_qHu5 z!6x$(r*)&BU#?RkMc$r-EmZR|rS){hi5?bj`g}~V`*iYshO&NAJUrGLf1w!h5rRhT z`h|Cke$&R6_E=7=R-2IwrDbrs6Wh04f;A`Q0hcsAtk1ox=gmX8vcYWw@1sf}x3xE0)) zEO=7ltLp=Q!hQ_V*E1oe>Y=a++m6f{{52f`Wtusd=VOo9^_B~qt?Wq+Q`mb2R3KH; z$>B|s(=le902t8F3();)s%WBqN_cSNrh4t581C459}}P^>b4vnw%d!Xm*Ilt3)I4le({Hd1OMT zbYS9fRBDL?=UU2_HPY`bp*4ILuquMpigxR5g2lwjHa641&j4o{V9!O};PG{$EPhGZ zA%<=m@b-T32%h`iC-cL+17=i4xUSqoBsVn`f@)6NW!V04mdNzR5g_Ffo` zz7@Z(Cmcu&-mjR3PsComkt!l8F385E_a@dcPPkx40_8z}j7xx6<6xq?psBnsDXuKwYi*&!XB z=|_0clVHQo0pelpj*leFFQd=wMww%E3F;UwM+Z?5BZ1yjC!Sky#RXoC_zvUweT3kP z*LjT3WW4C5jHUS3JB`i-X-0*1#3ySd@QdqR#38(n{i6DD&rA_Q9NDfzKfovRKraog z5NxDFL!A>DCnHi-bLXllc`mZ<7Tadtf46)c8{d_H*YVKSW3Xc})p_O+CsuQzgHelwtY|pIb zRx~4n%pmgS*Ve$9fDePF%EgJL)(d-WXv6v3sa%)8Hm=^DBbdz?bxqom55wZ^syFZb z;K9&X%m-yeaAxldvCm{=wNhTwFuSL9I^Z!2IKZca@kkS+WQ*CYQPfw}?(5IL*;9T= zweiu1jV`9$(XTf4L+}uPqf8JMG~Roog{i-T?u1SeSYOEEr&jR5ZbZ?)q86K->Xkqg zN*{6G16_BloM zGDF{;(u_;>ve*;nNqJZ;RUkx7C=Az+NriNlQP~rsts43%R@Z zpYsaDs=M>8!yp?$&*vL(iXd z+9KF!O5c54lfohN$=LGA&ezZ5jQx+V7^Lp)&x7wP*BEDT-~)MBO1YL_fDZ zAlR_yJo|n8Z>n_@=zJdNjMg3QdN4e~Q4hq-jx_%m{us6r>le;Q0Q*SW^Uoqy_h|bx z_=1=9A7@$R|Wi)=5362=obPBdTI`EP8&Yet6Y2 z)7=~IFJJxcksyvrf8d-Z_i)%x+R>uCC)HeQp!mVS`7^4&cTnN4J=(Lp*iLmQUp?xj zAN)x~RY>Ahxd7)92}*hVq%{#{t6Gq}PH)*2r#+=W-_Fxpk@pE5mKA@k#TJ>N+@h}{ zQ?;YQ<4l7ACkGr0Za#mEP*} zKlJukQ4f5c{QC$exjteh1syeW@Silh3LR02}M+5C0Z z2eFojKKxX{fd2La4B8`mtxMcidH44Ce2UFBG07U+iTZ0xw>1kkx8pXf@d{m~YnyW; z0_H=!8PsQ`R2?;Qmi(^;UP|!G?WfglnWJ~C@mGKXzFAUUo25zHI8sh%$Vu<~9t8uD zkG=?lS#?%a?H(9R4zia05W*|TspsBeMg6m#Xr~bSs=q83)Ys;e&O$}Rk!DBv<~Fd7 zYWLwG;LXaJP=jNi+ovpy(y)PbNkzZ_p{01$(*PgZB;WJE#6HWaKm?nDK{AH2$$-%d zu4NuU%v8$?<>mydmEiWKVD7Kpjg9IOZ@lZblMeLy_|=jg@7{r?E?!j?pWCwsMzrc$aG#JBMEd2-QEz7*^`h_t(=#Pt@6_?C=6 z7~f%Qv9_S7ED$N%Y_`6$y#Ak$tT z&FZ$JFtvOb8551n)cUb*!iO`xO;PE$*iE~2bbz$fdD4lTPw;M86TuODgZ z7)qOfvVZ*jNAUR|^IWJm?^kO>*BBP-fUX)NeYY;>%!s^SyGmTFar57e`6AjqIGptY z=1hV8bhWX1I|+-LX#Tx{tYBsZ($_V9?7^+E0wjZuDFhEzO^+%enc9?v95c$1S|2+p zjap-cG&Rs{2fSi$eK6UXA}MYDTr7Q?e1}_s{W4v(6=k<-8-oI;=k0587E{X|4j22m zu@c~Wm#tx9(KGWELb-m=a)5`daE9Vk+9rszx6|Y>$1oK?v~UnXWy+MH#m*2;PpYFwuze1 z|A|sV$S6FoTz*x{Nhpm5!Dd$^P1e=zVnjjM$K(C+_1zQgif_2A2Zp-C6hRmsXOSdJ zqZ`nvf4v0gQee_?u;jy%`%MBm&7RQJMxHAHrZ}N0P~q$NdzgYC+bM!FVO?v>?M1rs zbW9#|zmV=Cf3=w&7X9uiE(w_I1)bYhN+vJyeaSI7Eaas=|1aR4#heLd*}0b zxwEv3>qSL+zIRV%DQui;OIO;Y~@)jtM*9!-?;;tR4LP z`{1dfP)F4T86Em%b`69rLmv9L7d>K5PJ9e7ms72MXwDVw+Dwg6gv!Eh*h8 z@y27gN4>r>C_Odv0pVYEr;g{AW)m)K*!4@YZOOhODK73$lr`Hsbi0|_n%Z>M+rC8H zpYgUE+o(iO62I~;Phyz0h6x!s-cxBmX+^ueSfl;mnY8K}arPwStO}abN5bZo1D-ed zz@1!#^oolyM`&pK#sehj^}RGxL{{|WQhi02h;KbLLjrGPi<&hmN8S&H&;`{LcPfUB zFgaCL(r8isgpGQ-TL!7(1@ora?UUCP5!+Ml4WksiFmH2&A9z57LNgoCV`a_W-|!ZP zEb}8+mn@?^0~{$msfQP|wxiTf>TFZD)Vo4X0zBVhQ)Z}}S9GrnAG@5OL2={xH^#7K zlo=QuWmL0m=y=UPP7z#D%rq~lZPH-OOmWU;8%6fGsWjBn;FP>DkdC1&OP8*uZ8yvv zru%h(;346a&wsLPwaFU)q|C{v8<`cu7VY!;t-J5h<*Ym%c%wM8K z+p3(0nByOczrZ|shh>Mr?B+1kUt7wtvCCoUFzJh>N_~x#Uihf>n&~bb4k{U^0Cc;V zUfa2UA)&HE8JLyk+DkPPKJf_#6v(YK|F|HME0E1-PQB}NZ=KL?mBxL(KdPZ>faySF zK~g|umDyelK*I~~d8eK-GQr{enIO|k?1My+#>ngN-A#`LC7L>6E#=-RPU?(Vx|56B z4))cbXd{VhpvSF)7zmP*-cuZF3_RkDRACDj;7}A%fbdhR+#kdZc6SxQKN0{`){(`G z(c|d9M7t-Z!^E_RE1NFr77(#)q~cZAOCaEDIjwkzTX3_*i+{EC2I7v!I$qRJSkS?j zr9kMeghceST30)fn0M7B0>Ar=aK5PL&iMGh!u5)}Almoe*0VJtjBcEO zam?W{9)>SHjK!AA{n<|9Qp64tP&y_s{=VSSZ#vjb<=Lq^x?Zh!_Ci1MFOBMT?nZ7v z(j@*cZ3KABDwFsvPx33pmpq{p>$If2yh5%#n^tPhjO|TAFB=mja@;=OX!CcY&%m0+ z5vy|yIsoKkV9SHp33l5l@PZwI3`X3K{Hx0l4-=)&S3BL1u!kd0s)3x`w?f=xboQmB zm62eSV&(2`RqUk)WBN;pzum7mJcg9Wl0tj)ockjxiaw2u{ahPoUu$MP>x*GtWhO4+ zPw!NLwxUyfiDahgv`n4=!vfO<$EH$rZn-d@2yCKs5-GQHvMa`7#{>0ml;6MPxO{tM zEsZ$YGX+`D)U+RolqY~(|*~PkS~b-DY3IAZ#c|mnOdE{Bap8*d{7}D8k3B? zbvDc2xNQz=uc@Y(l0a1RF6JD6dv{u@8E}npon2$}bA^}1f~sTDEm(PxvgA~GhN5pi zQDO26{m?Z=OD6rD@;K~Heh-}%$-8NDcUQPbk5#QQdhJo?xV zi>s$}^*BxMu_v}MPYplzz3r|xL-9NgNl5J? z93)M0MqEN{@fkNmJm~Gjak8YRd!I!l?q*dhJy3myceW|DYKS7-dwot7Biu3YCrX$? z73eYtHF%NRMBldWGn8!O;Na?@{0$=|%g-s)Vho!8mYagC=(blEd{pB*R46=bdihtP zlA7f|_$4C>f@M27(o;wURO@WI5k5>T#&UK1ZwjY0sj~Ke^e=e4*QTe~e<~Y^tkl0N&^_A!hE z#?G@7I38UQ@M`H2o*0rn;{i*G#kGzQlWsX2*3RLtAr(Ppgq8wjz@I_tsRHwmBW$gAu(9IqwXil|S*$*?mo@Vf=&2F+G8{A!8DM z+u4bX3Gp42U_>@;Uqw>@ge;c+j0wa*{{eS25+kYPmGW7OyhyNq4B?2X|8wvPYipZF zR-Qp7n>1h|9_)2PNsixuG$;Kbt1eY4$=c@zK-?kU8wqZ=&#_^S5O;|lA>cg8)GNa9gC`>H?55=P9_lj5?!fh0$J70E(Eqs+c3r^IZpi<(WGP>hqPS?URIiiWD zeqL^WralQ=F4bNSbs^$abop`b1A9$ zsd9@v8=-l!A>}op>aS>LC_dY!Wkvfm3$JX9Zg8-rFcc?YWf#{XD_=I2r{nZ$s zp4iG{3R-p1-M-F;E|-wQovVim(@hR~)fgA1i&+Q0dKYsZu=@$h=xSb**`IeT# z1BTy+eyg_?ll{(JHQcrr|7@~j)pcXV;6wHf8RiK%_>-;SU8itV49=)UOwjog8R;y&FFKK zgB=et?{WBqOSP~%&E=u-9iV$oC@#%46|jQeY@R7bB3$bze*V~YU2}Uzqp_z4dJKf< zb2C^0mI}!A^HAv=B$B|Vcde(JswGstDoPGz_9s|QA|agzKf->B*lp4}A+33Knx zir*i2pYVrG+vV@=7ZTv!9GV|-WQXv&ES4qF)>Em}K+_c1{RmEm>;S8f;=+WYx0jR{ z{cG{vEO1Qix$?}Pl$rVs3sTz+E%(j_I@xFuxW&E-!crGR!A zc_Z^6R&@OXg}|9~jnC@%mw1H>qB9tzkclb4BzX_mU8ddAX>UD2W6Ka3z_f{yJe~Vt zaa4F~{uckWGWe4xX3M3ysIUuV0(4XhKvi^3Ri`8!-B%TXciU_tF1JALfWNTy3Mul1 z{2_sts~gq=8(WgJ^7PA4y*r!t%dBI)>$_FcaAV`-J2UCdq#LutzB{%E3Un@29|0oI zZ{@&91jlC`(@{H%e-*!6^U<4N*0D%Ig(bkF2FMvi^`_8cUf1;SNm4sU+?gv~KK$pS(eNT{YEOk3UsWTEwX3nkcwwH&ZAv0HpQUi2(zN#A>~96b3hZiOn#-$5 zbc*Ha@RtPuPGPe@V0loc%#6yc>I{uj=Lnpt+Hp~qwiYV3ygzY9wdG^47HE0(_TD`^ z7v+2yd+%8DL|%{+Gs4VyC3nquzM1NWbwh~beM_+e3Gne<0H#pkTL0C<{92xGE8&j^ z@>{^3MHSFKF#CtX!u~kGLo}zb>pK3W`S1ReUHK*>tWc^+$#}WBqc!G!UU6wOpmHS| z&&i#A%Jy4jh_jorX2B0$7GM2)o8cmhhKi-zS^7!Wbd@wB=1vUGeZ!|69ydcd-%f!! z**!{>vI6UGeMjxc_fWMA!amiWN5PPUNoJ-`IQka%OE{4D1T&Hup7%?{X|s;rpF&-8?AnZWLmAvm3EiARV!Y=hc4$5K`Y`9 z(|bOA{qd4qFbp-8JM#jyz)!^B%;S(Vg zYuyiqq%z98w3Z6=#E4*QKZ=lVJf1t`v8%8|`Gqi;QwFDMO9!HpDR&5;utKsAyN!7~ z+NW4wpZ`1TEpw34^iB^xBn{v7BMHelkg6QWt53g1Lv2{LPBM%88!kna+>KwDXXsy> z?f|#)0iNByJv*PtUhw*go?w&ed_JaBGbPEy*NH#m$o&mmZ`h#>iB)X+0kjrCNHiww zZo-#qdMj0@z8gniaGdD9Kh7X-xa$ykhswiTxw)-UD9~rcaW2UFqirfoKpWEKd62I* zQ&!%lW78&{Jz&dPY(Bn$f3omk<27YxvKgXXxe{O|!#u`%?>#EEv$W`z#Kq9`y$BO)X_>-nP(R7lP7a#f3gVd0{))6HxRz73f=Uivprwl!?&YZ1U-FsBaXZT zx$2(JdtqEQlZ(Lqb%k@+6LbY|-Df!O8OKD9YFp^S`^z@MLAGCA#P(~E-?o@owzGBCEz>WLws&mzI(@ zqyj9LR#+JAp=GzkKxMhw+uzmNWGC5c4-%pBD%J+`>-C;Kso3zt<&Rn*#NBNvgJk5U zX7rSmPb;Tpe%RB_)dBLc`&Hs|w=NL?IWaRaCjw?YjLcvOX(0RZHN))jyvslAE*t+u zE{iR#T8)2KdXhDk+m$PZ zRja&~nJm`tPIWO%q;(#vwqpXaUS;cEwB-+N1@s#rm;spRgs{bvVh&2~5A&G*&J!~v z|D!P&%M&XA(CAQ3wWo=tHR}<$D)W#CsHXtM3q!G{gbRhj16Rb#1-wnnp$1Zd21W(a zGD*nQ!3+L0gM(?OJp;0#ykg|t$Me694%^<`E<#0du=EK;SpmVXAy+#0NYc^cgd^4h zaenDz*z2Sa+oPtGXBVw~LMECOKTjfOujMP3zgnM;n}x@pubcTwyBn77jX1!zcaH4HU0j}DnH#qx75mqp?}Jej$9^H~0{h97 z^*`C!NfZuz3R9$3GAiEWgR`U`VE9b-a|EL+9YSD?%r8G&~#0kiQ6IPkFp z;Fp{1fHU2*xSOWaN)6T~56>|7&drg7iEiZ4LH8P8-z-ULBuBj`mI_^7kni6X!RuEnxZ zGH0&4+hSH6P(wNW^&)acp(`HUB0ZWX-x?L~xARK-S$@OMYvshuc7E1F5yi&(IE-fG z!jgyXco*9VRYtt_sR&ue;p-Ce2G8U!k9FMN+XWwkJ&q3THS?*!z54CULl+0_sIg99 zSoGd(+2wqGoR)aXNX8{+-(8eVN#hggN0+`r*BZ-tPvMnF0{k(C zKXowslgNM*00CstKK_;<>1XD@FD+)_G<-+&0OEG&{d$Mt=p!MSvlqbR2qHTo~1>S^##~!J#iH99Y>)@sg_Y3*Y~9E4>Vcez2^9vN5sMM!8b)A z04@06)3BNDS3{4}KW*(mb4om9)F<529fDl>QAqg7G5qSR>L3JWyA56|zQnhe7}6t} z%E@F~xiFW0r^eIW$ECubplz(cR2=)Q*BiX?mtYi+SiXzg@>mHH_9pWk_5sxkB{f$S z+Pi4od%UTnk#>HBU!OLex5Q7bnLUG#jc+L6^|Rq2=uXq7W`T8BDZnO?|TTj!Q1%Qtd$2_gR zml_jtM()`Qu?IqWA^M1R0(u1>V>C{_8P@E$QK8oMiM>CVaZ?~4waURoMqcy3K39_u z`FAZ1lY-&5KYkaBAePnk2U|ArG?WE!;o*~B6x|e>VWGwPUm#*fCwu3AWd-{kQO42p z`xw~S3IEBOvrFNG%9qJ0R&?UVrQqW@S-Z$k%lqx={jQMhV|%NElqDcydvh{LGR9>g zj{<13`SZK%+ZpNt1q^j(=RJ_d__EG2kPEV%GUMz=+qPuGczsK~I^IaPLi0m#&|2ZB zOEOvqnp*(-Zr%|^#Len@}mGs-n)Jib4rjA*;e@5u!M>54v zP3C?*lU>;foDj7swY)h$9a+!WvYJkblE zIr+kuFH1`$xUM&?ed#)So2|L}C~Vrr(lB(r;6Gsc4UR?bQZv7#Mf&UK8CglV3vZdW z92|XdY59|t{b9pghj*G#JK#887_q+NHwHoGSU~&ST*Rna!7Sm!d6n9g;mK28>Qghh zKsVQIAZ)aP3Ra4qB_C4o=eRU!OwdiKJfPCdx_XuSuO{v&S?*_tzb^yNE{TQscM`Vw zk~ zL_!gbXQ%(ixH0~9!Ui{2NF?*Ua~dAde1t zbF!CUw0!ux0F_#9pg%%oZl58zto<~!;u+CfHe{!x;CW}(ttuQetKX^84#UbZUv5o5 zrK+n=_YAlmiA`9e*7!{{diYCl#!fJItKN67(rzzlrn4fOM2gQEik$mH9JR3J9N{P0 zOxH5!;?BT#?D$%#;mo}It_%A@ae!Z_<-6pzRfA`l7Hs--4WEDVA-N96$aaLO=ge`+ zUthRwZ+!_nrLK@qoYMyN@^W_uIPtPQM~$ zk15y;MTA5Aa=s$+wtzyhi=2Vx0E>~#B%eDgx~EuSv2P2njrtPSi4LObJM6y-CP*p& zIFt%b$Rx~(8E<3;?rzdC z@gpP=r5Wg56qwXY?GAxg_IFJ76Y_!y2zkNf68r`3SCd#9PrSx7o17RD1KfXCuAXCQ zud~f(AUYx1f&MVPgik$+bkpy~&c7(nY2Mkw3DJ|xqAPVX?#f@V7b0mbFCBQupC9%J zfzDZorZ*CRyp+~6ucc&Ahs@L7^W)3`=xJu`fI(6*6)$&y#B};T%Y#x6tRcKiAR%3Y zN~V{i{TCJ)*vI_vo^2y=^jrxtCA^5;6|(@)HR0=4=_bN)iHFe`w*wccA|ea>6!B(v zqEmW2^|!r&mJ4S#rfd7J7$j2NPiVO3z6wt%+j}cz_Hv@ez8WHU5dhRjN^XD}Err-x zAKVyZNY=o6Gku$Wc)9b_0-c#AAzh#Vr7vYhB|`7$ms$sj z6l)FgW8{^r&bfbgzye>mOp|;ZA(l+ zIkjwHA|J8y9hEkJ_1hX$zL_SZBLp6Mx zNXxrmF@g_b&hpMCGHSpOx8M51q2ygt^UF&P7sit_OK2g8*LCG3@0(yX2~1%-1>=aL zoI2bbUg^+-0#(Gx=IzYHEnPaG=vHJm?1TTQ9iDJNiQZE@w&>y#|4JP^;g6eJNA>&5 zD~n~48ww^bM#5W06?Y)X4;&p;jT8A5?21YgWpZM4N>To3uZEg@lMGXexl`xrYU$TT z*^ccY#HrBXE4`5rrr?urbHoRo#JTG|?UF|#q1ay}VFWuLn-xL?B5|y41x#|Oh>uVh zIPA58A7xsW1n&+256JT3uU0z$UZQ>g4ea>{KZU|xnp-vZ89whoo=@T3NWSNPKNPa> zxi~%Byp;X&1^z(~Sxv`fQ4QjO@Q*VUJ{0(Dl9li#BWChGDJw&ilBRe^vUFg{usZze ze1c*pen@}dfVsHrNG9M0^i!-irmjxMHt*vIy?9y&f%Y!#m3yURPD3NECG#rqGiell?<6zvc;tmQ8xh4cjc ziZYUwrR#x)^6=cREM5zgn9)Up$|Vi^udaXJbGPNLOH`aWN!GgBrlYp=zdZT*NH*eY{>C0Kw+E*gsbDB7Q z60YO&Mk@79-BLO9`8jXseQk*Co^W3B4!81I8cO90oK2=<;5XjWKGPf%b-VJW*vVoD z7R_M@BZAUvv|G0Ctm2lE)#t$ci`ex3giqS&#awMZf!Q!y@<9|Qnj~HQ?7CJzr>2Iw z_(%os=Qg#AZ;SjyK_m`lVb)c)lZh!LMMd=o_~WEGKS6GMUfLyt+G~%G3)qfDyD&4|#KfwVQg`&C%lwMzxR>} zSPy{~y@OZj3XxX7!6)PV6#l2y*!ejm-2yVaAv)`+`8Q_YFJwU{E5Cil^T|>b_JEE! zj3FuoAxocq5y{VYkPt6@6@4}1Zmg|=j;F`ds?=ETPwvj&TSCSLqyEUUglD7OeM#zV zQp2ihvvzEVqQ_dhpdy^Fv)cP=l|7x}Smr@bIYeMGI$9KOv+0mtE3NdFe<^iDmnmwtRj@ z&CU#_E6Q!72c8`bq`PjumNSt$XVKVuf}cy)9zTp0H;Z_tO4N@UEuSQ2Sn(iTNRe*{ zrOJ>EzbEJPq}j7VuJn8HCXqSUhVKEFWhx#KOsz}(>Q7Q9!-IrG8$?Cg%=*%yzaip^ zH?xrSfOvgxjd3EX9?;{(>OnHqAVaWk6w~#Sl$(q4BvUT8L2>r>197q*^w3Am9A+@8ZS(dI7(0xf7%c- zw2tjSQuW8iC!}3cmKFG@eQzLl1~gV|Zb3_DN&#UMCwr)9Iv2RX9$#Dnp|m*eK@V?^ zyZt-IZbVl=_nif_>;N)&SLl|Ren|&fv_%~Li*1)*KB@dT#0=-8HPPfaS2h<+IWGgo z`0b=zn({F=T%~9U>9I`tc`p)t?Tn6l7#&;}H(k&^kuL{R$a>dAduJt>N{Q5vgpq86 zG0!4<5lmnmp@F|Fkb2&B^Tnss=lx2pNhV~aGR%>YLRM+ZgPyIt!K%S&Ae6Thp7Fyn zix01jYJy?8+V3gjtrjKiVDK$8e}+MJ#RGOs&u_#4e=!lLbVp3WA_OE5yp5oOsdDK> zJxe>9G}VTstzLI>=5u%4pi2?tXNV`yXPYpz(R5aX`OZkHb#1P;8_U~Mo|)VFLcWFE>KI|tV`VdbF!)S9 zS2_{jj~2KY4zlk!N(^ra%#+GO5g63mNm6UTIDM-&l~6P zoo_wcBXhGhiZW>T)8@!L6LhOsZpsO@51q~f!JY7Bj~>X_mN5Gj7b!^t3Qs?VFI73? z`Hm1YceRasZ5n;@zXwy~_m=&R3e`m8R3*J7LRmJmJxFohHA9m)eEB;+*6Y;^F-7C5 ze98ta31#dUJ)LJ)p_qIx;tuq3>7hM(IgV*^H{X`$gAN{_8}Y6VVJY%-gXTkW8}t?B z@JnZMv1n)vHeK;Bi)7(J(N@<@&LlJIJjfhFj2o>T8O#P|SI`aA*l2$$uH!|8@()xU zfM$pJ-xW!Y67^OY>;PydCxIg>S=&iPm#$`?V*yt}DUBZYx*(`mnN@MRY|;j!C_IV1 zE9jn8w`n7^fk5LcZ`B6eM)P}@zc>n@?q%f1@~mrb{NeT_J}Hqe*X5d`Ac zA6^64ofwAB*FzUvdP&OrAEACX%@n;iQajW-iXQQwn2mEgDhC16YRwjv0aOqf~k=ViYnVsB?r{PDJg4!`wG!^y13XXYY+ z90K!$x1)UORlg)^rAl>+`!HaRndXzTLT%>9zQ4KNAbsvBua`co z_o4x36@N!1LbW}xmp#vZsV+?4@xz<&IkPS)t#Er(d{f6q1=#u0cAkUGeDeD%2`Iyg zir}_tgG(+E_LoVWjW1bY3m-r0-r+^3EjTVk9Wdqj*G+r#>Rc3>a5BF&h-Epfu$MmbN#F*lJ z?)J~wwNZEX32_4tijdsZ+oo@;FS5&s#un$xulv-9ji-J;p&y%%l!Zw=xAx)7CNPMN z^}mlC`OI$$J*M3_4#>mPeR+KS6e3+38lt@Ahx;Bz)kO6RtalKhBKaNKRc1e%DN}3!vY)I#4&KU2o%wuqNyy#DXjo#Ml02}gro9(fXDXOm? zHjQUTzi2p8yQ?elvG$#72~MWdk1qXdWNKoMq*)fWvt|3xkRSE<=&e#!xnbuxvFuE$`I zviLs$OF*>0A{jDIVP87<_x$wQl9x3i5hUR!M~+rS;|WNDX#F35&tuvMucK|YuH#K> zS1Qw6qrposrWDnTu|E{Wj8q%8Ae;yfaTwgpM(}@Y8IEMG?lJbMA=$S8{g!ty^W1r> zHBZn8D}rVKd!M&A+EH4^hJD)%-K7dgr8B|{&ugO9og=Up@}ynJ}=%_bbCt^(yA>cQihI z?zvhS9cq_gY73uE8G^H%Yp!jw6!k*;!1b7QuB~Lkq}=z2o?KKZxT2r8P#iBOEqx;0 z8%(2t;e7B(rpJ=%nGLN7W^-6MM)zz@wSfSdPEj%`bmk2{a$XzqbR4~uMBcVuD4#~P zrJ}D9K`DH;%Tmp?}fxxd>9d+UOphGBg@wm=+j7nA-F)IqcJ@5v`{4 zZgiLN66mV`l(7Q~k}{5YLCwaf6fWN%CO!gqWMhn-_tL((`)VH9y2iJ{*NV)b=&R558(4WI}yyBkdL9d_4tVPsFRkI}yJ%;J zCiIJ!G8IpxwDKGwA1nK=Pr@mj=JhdxH~l@xzQx#_S&s!Rim0C}&uhNDcI~IarNFZhnO5q#{ehmFuKnf{DaYxoytW8uReTVehXs>q-)iQioq8CF}nXM zhe=4^%cO<=D^HmoM1W!FE+des>^i4Wy$O%TwNbOZJ=|#RXsef*@`r|5pE_m7gRuz0 zLtc@g*5)G5>TH2~k%hmDb}Lv&YccghlM9az9iZB&MWLRcCzL22{8PZ75j_rX2D?^k zyiWqsN3Z1sNw9vdZlx>O2Rs1K60X6&p13i zT8=}(gP&~F$9Y58E0J5Nm?>A+ZB1L0R}^uqnN6N12k}#zN#mYBJR>f>L8i(7q39;; z80P1$OEuk;3*2+mGwK`$ojBcBKwC2`wfE3*Z{cFgtnGu`kcmh9GGsQ%CYdt_k=`lG zAs@5yMqYk!g$}mh7#ESZpCJ=LQ#rZO;6nDAp4Y?xj2W&< z#>t(#9PZzv+zs8js;!YKFB26*S#A=sdq-G;x;$k>C8PvW`;JapBqlKs;~e_A$(S)R ziWB1gtnKqGm^YxKd-&(sAA98Lr{8}~fGNOXROL@5urU%%pB=lCWSEiDuOYmgpXYWv z4-fbg5Rw&~7YWpPkvZWI3E+1~b^` zuRXn2$0($z+W1C<|5yZvHixmOJyBP`>9>3wo-vF->ubiu?zUR0&7Bh&K6~7-3-xK~ zlbv&V1NYapZ~2>J^n{Z!LEYqc``v@->w=_u$ZnwPV?0>lx?xt9^hv8yHlXn^K=%}5qQozp$OYR$K(tb z&!0Ag%7pD$Wnahr)_bT&ymA5}#yZc}W=rHP5Uq%ha#YqaMqk?UXXFKYIl|+~VD*y$ zxGEB~R!Ov~208tPkO|IN(dJD8_E;YB+7a?C!!Uz*ThzGc#2nHP@%rL>0y4@l3aF9i zsL>GzV-1%!bPccX3tnNU?AV;)7Ab9rEk`+`=3O%g8p3ROJ^4$5&ahWx-SmI9=5Q!3 zVbGSPIQ=_@f6tL&*k~-u&|fqn+*8s%U)llm^z!NS`O{xh{;v&B4ELf;Mup*!d_LAW zI;caWy51UQMaQBh$}f_3GPpYvyy{=2_{YRo_-1%0%hbYJKQvH!Z3XyhQljt+*BPhy z{9G=GFir;gU&P7iyqZYp&m$A$hS2(q(`tDCT5j-yL293}e5*$)oE!4e=ISy6qdgi` zk3w(ZGpCjjd?|9a{!cFJ>EZWy_<4CKFrP`K_$(qP-=lXlc_?x{@{QV#sI6%kfr1zd9C+ z2Vah4qY%6fP}>2Ihjf)!>y#vE5od8$yX#~N53r*sM$R-jG08%tw@$L(yq+We^u-EIn7|LgYY36^^&e~ zi25TF(mRadW%+7belOE^gF(+JpJNGF{WvSsuyyMbmB}$UQ}nU+o;wAKOwuKqKrUXS z)8da~&*b&Le$E+1qbf$L>PC(|&pX+CYz?&?VRckH$yQFqnGWrFm59@x->htX109%Ltw%8PTX>Fd<)|_qRW{?8}vmo}oaJPqvf3+v_ z`Sk8%N`s*RKgCW+_Nt*{;z)y(tB>k$IYq|D-flQ-&0=LmOFUfiv!and1hE@K6SVHx ze+@o@Ez!RGQ{|LO<0E7+L?@EUI2^k&f-mNwGQ+_EY}gVE=NWF{y4<7dQWI;tlDx9= zJn#&PYqf(9cDNwyMZ?ici;(k_GdwB8o@Pi1Q!030hy*ioS4DBxow+DNGb|8|)nx^7 z?!c2VIbe6gD;mJZdu79O4C+?DEw|Y09-#prGQcyK~sL)O=v#4(gj$#dAm+Q~u2f{AW#j5M?^ z-Oqc$pRy|)dGtKQ^4Rt5JilB z&;+lH7(94u>4|u82JYy6=1G|Q47dI^p_D;*2{#7++n1INm4d(zuxQH?xGe}@be&_V zM>6Ie$bJos@4K`@x_iV#U?XQo4oA*WN`3o}{Ru=E47$Kzm<;{W0n%T;op%UKC4=S5 z=kn5fd~SGF#&%Q7ueC&vG3cemyL4!%_#RVU%)0f8EFKJj@e%JDTU3F<3w!G;}O<& zF5470gy(@ml660Z&sq1dzL>xRwT?qjWJ557!?G}*OqZ{yQ%{YhSJnBI!`K3{~iVS@XmE)_I@bpMVip0~} z!;1kFny^Q)zEg&-^gGE-k?&47J-N?@`I{!0Zc209-#c?Jro|(z;6j&=8LpX$K9T})RBES{@?KE<{l>=(g*V@9f z4vr8JT32>{mSHTKrxcb`Xn)bhA|d!j)iuem)B(eT|IrXci~&VlE-d`EgaYwqisK}CK2b_ffvCvfsla1gc)4kC9ka1sO44 z9Fe7S}}aHjZZD%QT)uc)-Wc){oqhIvHJ44_Ubyr&@$fI z$gmc1_TQ9kNjcJgNJ4FZyN{6Bn!~=1sqtL??HE?qxg435%S$;F71svCD0+m?hW%Xw zRX`lH*jJJ5A!8!KyyWnSOydt{hp&{vaxcRu)lD+k6+z&)+-P*`9?$BPJS3T+6<(Jg z`)t_U+t3}2jS%oNleMz_00x~u;G^ilW47{$)Df19HH1rk6-P5PCIwr@5?~wEB$sez z2pp8WHGY(eF()g%r&mF^{AVB^4~{EZ>?x2=p4xJ*`X4;HiVx(_i_^Gd*n2H6><*#s zw%?+k{iq*w5_I@9X|diDCEbL1n+@SQzg) zfM|c#e1xHYl%4o_nBGQo>GxwPk%t0~hYas8MY6ws?gX#;U|myZmO{<)>x5CsWb}-P@(VG~X7ixyoT6h%4I}Z=_NS`^)Sl}cQ|~&JFKl@7Euk|LWjOFP zqM@bn{BuJtAv)$Vik^%7+@Tf6GNI#0E9<;Q#7RzzYsNGB`B4CD`qJn9Px%<&URc%wKgiT%tO*RRGJHeke zk5-~YQT6tG=Mc^k7?IoIm1>8fPY9|$rmB8huI^6$CL`GxBXZ(0+E*rFy0EW2x+h+P zWzmnD!C?o%L*DUAGsTR>$9Fw5x~+rPt#uSV@w7_?Xpi)uBjY`>>?5Jw(xPr1DFce> zN4#xlY**2Fv|6MGZ-SYyGAi&m1krcKIU2>+8Hf7dY$Es^!FEyjUh8PAjIv9XQ|8*0 zgJpo=g}#J-!_gcqIbHdN)i+n=4x3cjT2_vww8AmT!P)dx+jT;U9dRd8tnq$_HzyWc zjG)K_JflhXz_R|jT5sVLEK&s3v_Sx_CeNe?YOXT%Fk1F~G7>#nZuQ&{)uh3kdJf=I z+X6p5^suv*x1B$yBNBy=e% zN>1j)gZr3h(LzRQvPjO!b}S5D0NQ!>6yT5LLqBJH?%H|grG0dd1v6r$(!;Hd26B4w zgyVeozGo3d<}VqqKX$VHuaBp{mMQuF{ql7BJ-VA*VN)zlUmOxHYzRKUl|f{~1PVmK z(7B7#!@E9h^SrvXiXm9hEVAeq6Sc9gc!?H{m0x&Ur`u1QLK3uN7{FuHX~a4fxh6mv zI-Z2k-Za5-3~g#EqEH&qn@A`%o*>%Zr^oF$sLo>mBG96Pl#wz!oyY4-XFF0VeF~Rv zJGAx3AOEp~GkcKJ2rH=yr>5E?#4&RzViacQonoMUmPG$LBV$Ol1UsonXm;>z?WA5K z0z!i;wNs*g2|Gx5^l%E!DW|X-8f)1{g-|az2X9J(M#~i%`ehU&-~{@qC&KYU2Nw)I zF|ewJkcr**;kOioXME2n@@N18R|d>?k)Zq+SH=h~FIqnw4!kAFJ#ON+azbXp?|R@B z+&BCODkEDT@FDJlF(Cy%hJj_z*|7*bWH*7Je6Z{l-Ofd-DY(@G+KQX&S46#2N#5F; z3{+{mNCMap9L;&?g*VB{@j9Yrcybt)C*ggC_hM8sQ6snOTUxW(dIude9O}6?7Y*eD zR%a2ZYT@@>EXsk z7xgEiy-WufBNUumWk|lXp7iVIzr@Rq<7)jPS^e7ck$(H_&z0(kHj`6D;AEPy#)GZc z2wqzVjYX`NAY^;$Uc8szqcd9^9nbAD^EidOp3idADclSw130%CU*Q4;Mx3z_Nrp=N zs`c-EHmW1@Um5`-va(VG49wwt&q6Vkc$p!y z>VQt!^7oYS#aFp;v;+&|&GAVwNm<+mdqo-vN7qWQAD$4v4*b?3DZg|W^Z)quTSpZN zs)8+gUT04pLd4qVo8*x_AQ$RSv%|+^=w`ax(PN_Y!nqCw2}ZhCeZP)gjN&L1JfhGU z#^`Tbq|}@7z#61{m~fpD7(xNzg=xgIyIIcY(@OxoeI5c+!{fIT?7#Uk4Jqb z7=`9k>uW;w{{CGaD#3^-W~^)>bExDNi$}*<{Q75G?u$Bq`on)X{lka<)J~W`)m}z8 z1y~+2g{<=#SYf|t$Zu2NloVXnQrSM|+{G|YLQ)n=Lxg9CGvHDI?om2_!7YO6XTR!0 z4zlkQ)-2>P{GmW~RSD7K3`O6~=Hsy_uAjR;!kam-f`HVLlkd5ju8bjrJvLOT$_Xa} zHfHQfeFt9c?O|gHkhNe0bld?*Lc&lzSmfP$F6E0vt1H~C41CRfof(b+5YO;}yWu8cqqdVIq^GxNc{;b974d5uv5_{5(5l|Mh>K{_W5I-_!fM9#`?>kBxqcczS5uR&&IY$Ptg?zfnPWP-!|eISJoA zqa8lUT>mkTlflbUEoIs>C=GAr^x^|?@mLIt%3BXp`ANp`>PLMpK3+RyS$hJLoe_{= zF;q|XTBA=iUe0_xIqHE!w-ynASq_-6%5r7*cZ!CZntu zv_{dhe(;bn*PyWAF@4}1JPeOBmDhFXHJkd^Pmia+w4>{{Mvz;rlHC1!!%D|JhJihx zq}PS9_Kt5z9nOw7lL?1mejghu_NONJn!o3I#u%PSBz#)JZ8acj$I7TT!i0_WDT)wc80 zoQCtMC)&}9c_$tzrI2$l6MhxIckg~Y{mY;J<@9$S{#eAA#iulR%3~?aA*=*7vJ?pf zi~LYR`;zApJ@K3j-Fd?V%UK8o&k_psgIM++=4MtPzp+cB1 zpMnQg1uPX1{g+#;;#WDD)}uY0wU<3t?gXBzPT4U#jN`R(ylNw^4le5qx5?ifsP>yc z%lWOZ6ydV(wq14gz_)x9=Va-N{3oZvXzlDw3N8hZ99_nL>i+$2JC5o`OM8WncKUCh zNWBOh&Irf=_1+AA*BC41MZ(}DPYxMDIdw6V(e$F=uk9Q%gZUg?zO~dx-jGw2Yh@6e z*2Fl^K+hPG4-g-;L6F12YLG7UFWDn+%8}cZ9}Mv&kW45_kNp1m_tXFD|MOo?|MSoP z{q#?N_>h0j_=-&Jz|l~%^_~oPIN&OF3CjAkt5== zhQjQ+$4{CcBbIB*;$PMn0)7iAZDPPpxyKDxd5MsbTulV?0Ok9#?nk~Vwi9#+r(Bkq zTxSSB_Pmn2k1Yv!UoNScS$QX0i_Y@+*aj7{7v|6sI6ZDOHqPbUT(vBLZOWmWO*72v}SVz z^%b(ip#lGvC-oz^iq7@Hh=Zp#$C!+#R02l(cSK-@LTFI>A!W0a!Ghe!qQu80oxU$# z71iI|I-0DR;*6HSWB6g?CtDNvsubXvGU@cacGqmGmv$Pp}6DU`9 zOSMGj!uGl%hNV+#M{`g<49KDU_`jSXqZ=?>%r^3mHtS7|e^*a=1}J%u-|R1`0AYor zsiFR0CIQhc<81-*LBFDqa@;Q&p@;2|vE;*6vG6Lg6Wy3pwEgdR7jfpS99R+K!#f!3 zn}f*P9?l?qv5(mjAzDYf$a;9f(P)X`%|W$A^3!iWH|zZKqW9l^D<^mUt;1gp(JEW} z59J;4<2VMdNUz8~MEi$9^%5LRSiZf8$moqM9{MyrN!G72I)=@LlNKshr6}^INXyAp zMU7xe-ssZKJq(8cJ@BJrwegso3Q8uc>~QiZ`aL3OKMx~Grw!rRgP0A(xi_+BLgeA+ zj#5jQm*tN-Fj$P|24;JB9S-EKoab!@F|*NmlT9iWRQ=%a=5S(n zj`obI@HpNb0yP;UiEKbzjt#t3-T{Fr$@j(1hBk5nuDbU`B1m>~~@Gr+?_VB|qf}Iv(jP!%A5k zhkn)GZcBU|9;?Gmd{p;J1QQNd9(!PH!h{R%RL*`s;*MehCCLyzsyJuCP%jLBMbBbU_ zp2A@aLmgih@k~jABWEF^Ag-?&jN=fIpo`veUdQ{54)x=4qn4sVKjQ(B zH3Rd?0h#rW-%Du>h42kt^n>3B9JGc}FH#-&4wmjQyrLjBaBBwDiryj)v=Gc3=>f`G zr2OqK!BbR79@JNUIV%Q2XK}udP3nlgigumQbYA2(0#9hgBh~Fc&e99L9p=D5>F?Y# zZFG)~l@sw9k&qe2`LPuPk4?C!@Ta2n_u*#Zv46=#^wdHBo#)u`Ww2#@1F7a1X8a@@ zy{rRmRDo~gkYlBDhRj#hp}00wIH>mOy8K=i=cfJ7p3^czBfSFe%Ap&*Q+E>|BW_LO z4~ws40?Z)?jow3%fnpFjBt|yAk-tz!PxfLm75-lvzB9^S%L^V%qiJx)jP$f<%xKS2 zo$EfE;O=+TW6#;A+N`XZaB`V{D`#Gv`UVNdaNh(ETVlNXgJr+9DM9j;oFZ{*K2pp{vr`n8J z_0xbX!&-lslw#eqp!vFJ=iR%z(}zxqd7psY&hrFJ$X+_d2f^!y^#`{h^b!8oXAFDx z3>o;uTuk?5x;o1UXN>#2;-Ra<3(76HH|fK0H0!n8X-tgV30r+49)YTC{x0t68yF!O z{S1Q)?X=G$Ehm*ScOeLlmk7^Hrs_*_XZ&o9ay}*cdg9&;ru#)}qGyMB2=%pZi2KmQ ziY-ct_giSIdWH_%-i!mJyTjVFv9~0fv6R8yzoWC6}D6hE}|ud;OVJ-(r2y3n;|^zuP@P8Z&BpVQ{_mCfTatDTxCq${Zf?im+)%0R7mek#&r}ueI{3yKBZO3 zfj!dAh1xyUke?$kiC3v$qxg*gnh=qH42py1<48XSW7R9XBlHj=S{{qqGjy%7b(EO( zuQ6MP$D z*H^cD(^l{%hpd@ar+2kWKG7{6eeOx2oE!&Y3oki%2o}FZ;Co79-ydRyp)*6Me==FE z_AWfT);MC)8*sI-Fj9M)`S6*Xr2Ab&{9?=5T}U%4n$aJshvqzN=@5%IMf86cBkw4~4(K`!Oc>PPpDX zLMueKU^pTz?-Hzg4ORy67$Rfe5k)lot9N1{_puHCG3j%Ud3|n(F+bF>24frm8XYD8 zus(jox$s{?IK|x_)^`~ekHq!M05%MztVh%lpQqel?B2eQa^d0C_1g1% zU;`0Lt*$Igu4l@>q5B#q$fXKQ`WWi7;USF3hMY`<6A~cRHuJ96}=ca%<06E zQ8k6#gRanw{*fnekq)#?F5ed|J=`?0bNTgjU-{2D$6oG(*LA*__H-Vc0f7dMYyTU-j^jpzm_Q2|s>p9qH-zS|V z1J?{M*+>FK(IyztTTrI&jefFm3zE`@trkJ!-j~6?xTspa^0Fd~8sd#T*L7YqX`s_fTj zzpKec(H}W7Tx66DIf09Iha51zT3~>W2{29$ujRkw;^c0xngF{f$^W?p$~R`utFzO2 zYRj%9FYEox*xN{YofGV9{F)!m%`=5K3}*@6x@emZ1Fg6F_sf z!77I+Mdi>|SLZl1;Y>5=Bm5&hV-B_m2_Ya+S5? zK#40OoVaAt_AbXq4-Lxj!EhSh92Z^zNc~OT;a@;8#+&>NUp6ckwlem7H_ zY2E}H{fKeB8OwQ|L*W^M5uxPC9wHPYx22|f3y&kD1UI6W42+kA=ump=^sGOdk~BGB z5%>+y&@kt|gz3kRKeg;2gBarLWC{Qj@YEg-fz22oD5! zeJ95x7&hv^v;>N&J9p0JXdTwUh_^P%Z;1#c=TQlF6u^%P_hw@7E!vg$vNh4^BfDIL zPq;;jEC*p`1Xa(X*m9S-pbz1K(oYlqR+!E%tL6Sj<|NboGf zaDbCcmIjF-!5p;ki0)uvp#0l#w7i^s*%v90t$HGoJoTyS~+roUm3EPOW%TdhHl$w_j7I!F2jnhYUBOaIXX4bUAQ`mtBn`!POL z8!p;7U_Nw6dmd_MbBO%tb>m#dSnvVt5}E5nEZnB;;x zQ=*1!S;Gk=gb1c#n-&j*)v2)vV5sX5P&B2ITUhNgLnH#*aYdD-E+RR{Pt|RGg~Xks z%8D|Ya>s0E*;VX-12~1hhf(a>By%F$_n^QPN=x8-fond3B=zt3Ub`8GkMDnqAqh`W z2ahHcS6-6-d)RJv%kW~`=Yp+5N2A&nZ4ruuc3T`fTFU{t+x-x3kL8!1-*S7XO~4OP zJj#Q0!#ni#hxpY`k`ll}@kNMI7Y`-zzowj&YTgx|fdcj397#< z8QsZ483Y67`-~@MJKcLH+Vq`46|oz_ct(by9HpP(nGp=huKZtA{aLeRNqXLQ?wBJZ zGBR?ic>)R`&=idz*^<~a(}miav^HJnoa3R8=x|19$GsoksYc=fF)}+32q3_f?-F*`F0C!5+P2 zPiKNkPQGILqDeBs2YO{%OMQm^>0~_yxvFSAB z)09@>gKc*yvoy%^IJ%59KE8Zeh4H3c)?YV6AfCOx-^gwAO>b4^x>AzKY$5*4(Zv&b zdknODcn3#htnylOKD-Tz7IRFG-^+QtDQbV#bh(YtK5AKRQ@3ulMtKKM)l!-bSJ8^* z2Qt9y9zQqY96gJM!TyfVqPoz|;l9cChMCMz>O+r3j`Vun3jteyxRh58Nu{{8O_BHOH*|Wy z&{5%rgCftsN^1jv+BMloW;HZERyt#?l6cXI6^f^W`khh9cxyDzW(6~jc7J{sFF5rZDb?K^pa|$u zD?T385BpwD;Nkn$2&aP4s9$jNBKv*#7QkrT09(;T(BoGQIDN63%OZYsO13NHD2oUy zzNrMxSsI*-{-8FtxKX6vAkg7)X$z^q(Nvvt2>v;E>7eM*O(j*)4aA7;Vje4h*T1t~)9=E7a`Np|9y` zb5{<+vkgAV^7iBCu8Sn+`~1a|ZKz;c&6eM4DO4$LLk#&UNNeNJpN>@)ywRZHq(bUX z@GGY|g7y$}7}lKz)gHH8(0-=dS}SM$qKa1BU`_N|b4W`!rkB0Ii&fGQwI*=5rj2_x z_|c%*Pp3A=x%YD@XzJNDZ^@0u(6deVi^Zk{Dd%>}_wGJul69lVBI)9xp}Xdi#r)_IMz-%f7#(t$gD=%6w=LY1_#IAq#{GXfj3#-o)Tk49hV-qfG& z9&#L|CNimBJMQrq;$5Rlke4$yRiN$E7zjs^k;g<)3ai}i(yP(rTf*qq|y!d_EmcD6#u8m%*1f3<15hUfrQQ_yXFY!J#t^@$=820T<{+{vcC%h2ZTu9AzuwuTYNoxvMOWiUV`c0dJ zzV1^~C`~L9tm#rTtJD8}`w2e0`(cXF_XiC~)zx4W`fj!6ZQTPoqO>r$uuE02q<@Qu z7!f)-EA)uU7$M%qH~n(!eX;Z#Q@i7OyhdiT*oI*zibX{Q@e@)*ZWw0vkq?9 zctBrZJaSxtI*_`#9ACkBZWR5De>p{iP58CZ|MFm|L8}v0iFWN4m90ePo2IzDEK=4- z{w6u=kflFcwUM60(fAy!@uEV5$0n?BV(8?iZ_$3A90Mn0WykaM-+aEi;eNm99?tS; zDof+ir>4ovvC&}ssF?36{V`42WU30$=gb|25e`m1`^AoOgx7ten=qdYIUW`N-SF9b z%UJdP`^}uFMRwmOeD4<&ykBeQ@dxc6nzPhrI&A6|;?(iNpo@|p#Os5(WxfKYkR=HG z3O@r)Y?!R^YkmB~``B+7_M-n1EUG_w@o^Du_DX$?QLq)f1^KopNIU=G2eJ%EXTI3 zzo(D65p8=);+4e%n&s334cm-4oHaw)h00wyfz8-*b``w-y67tF1UlWwC`EhjiMpI0O7&5tW{2*f+O*|q2^G7 zm3$1K-p)Bbc)!X$*q_#v{zQaW;?r7eRJ|6^mSU=CM8UyDzrM!X8=w^>a(wLC&OUYN zVDjv?w1gQ?RHnO1w?|dN@~8yD_UQa*ia});4HH9Ogt)BuI;T4rxT8!lx(zuO-YQ43!@LK%m~+ZbAQ^>fMHU*9ko_j4Rqu+ak6 zc+w3960iq2>lS={VEtHA5d$e^d2wq@%pmm@G6c$_dCV+CId&vIN)!D(=VS*s_yj7l zu}0Kp0GGG*LseNks;lK?3Vb%&Y{Gm{w;?E=_vum2JM%a7mdwjn2#nK zZ)?h~xs)Tbhv=rEluoqv%{O0Gh7c;*GS4o0rNtbnPB5`?pyN8MD(d_0?B=6;M3v zeb}8SKN>bFU6)S;o_z7gU;>A^ii`uOqHs|gDtXVdbnRvnnQaLB;(i<{|D(Ele*cHB zF5iYLhi~^OPT85(flUuMI}w&CG)cxgx~bw1C;gRVq$$k7s907hRq*;=vjZc>6M7Kg z1AjVtH+|PV^`L6xFb4hvSEx$mR4@{Q^v6MKqJaq8 zo!`SfT165QXy28u;}{yYVz(2@ySE0k5_*Q&uW-l!<0BbyFsmTC-!+GL6nYF$Mv5;I z*zYhnbaP0_v4WV#rpSO*QaH|$U**U|$}dj7$_fWwj|G%^v{n8bRQTGQmw51VN}p00 zLEWHTxJy~4-s?{cDjGrRF;&!>Sbo1f6s2yEBzo6m&iGT{lrb>nC{0{Dl}vEed-`be z4%KHZlT7X}TD7GE|5YRTpBEiIfAwY2dfgzU|NP6}UjFdauXA#Z@XvWCli+@k0l#b0 z)>Url;>)(bGcR#Fs1JSpnl=CCIOv&aC==@Ne-VE8HaRD+Gg^#p!8M{ELz-dmhuAloIXA;{gyH9ydxtSvlo(j=q>+ALGF+7{9+~L77mxj-Xf#e=C z(?^c@*eAX-k8tbu_U!BUo9S6R9@qRBALDzJ>e?~Eo1cih(cQ1)U%?V6u(D!6Sl6cl z@Dx;sX9{_{=JNd}V1M}j&tiykG&ryzlygRy^SoTGOQo{Al*HgEa165`ow2NsYuG^{ zAQ)@4M9z3dgXk;e0DsRII?X^-ie0_b@3cV~3=KC%!(rO`eyR;!6n+^ihr^Hh(w7e~ z#wNe3GIUgr9udOu3jZ-bwUyR0JX-qx02z%upPJ_I!uu2#diuRE9i9vr`0yb>_~X@t zJj+;r0~H<|(M(s~!MbL=I28-}b2yqGmnr5O~zya~sm;6gBUGy{9@gREiMRWd(MC2ozK8pZB#_wxhSGIX|S4D=1R8b5M zMiDZaY-g@rd?@ejj!wYyIfLM0&KL;ywZf`n3 z>DRyh|7B<|E}wkzL33K4U;gliUo}nVNzr+GCRPr98c}{`iCeONbH7pKa4~s&Qpw3u zn`f=;xLs54Wm{sY49&dA*W?^)A@p5v77tXahu=uwbf)mm9Yl%=%`h&esn2)1kUnW9 zPbc9aYvH}#Dh^~KgXw{W=^ER^4A)caQc;GTKKA;k%w${?K!$5rUb&q;?? zmzbDcvC(2?3SNarq(98TTEcg~_nfBI)m=^KfLO&gKNfHBV7n~!W=<{!s`S=H(fxw9 z=ylPAiu%q`ug?=-`ln*(Pw(_j(~YQM-&M@;;gIK0>3lF2)khywaerIwSBibqScccP zoJ^-{O|&~Hi=DO~yw@n`y~pb}h>otBWNsLlw!uagTh_)IKKKZuME0-S|T0AU_>!+wQNs4POAax9zNeabkh}^3@27A=ty3IrSY!x zB9&{$$k0ub=?%8Ljd>JyPAxHT$BG#qCwG6PN8b@190or6jw8J?x}F+&-aI^!R>n9H z;(Zq!)S86l;sD&PdjmgW7ll(E7$+(VLj6fzoXQ4%coDtQ(kSmz4e%G?CNEQC=J;~h zsZO8L=LmOSzS&x`ScMW@`tN4A@nX?VG-k%I3V!UUdH32z3LK**V$8TzMQ4q$3UN+4 zV(8VHGSc;%GlHf{Y&@1mpW>L=b0N(b?oa8HmA*XJrkSK)n};TUmZclw>o32#{QW=t z?d7*${{HeZcpp46Vx2=uwnjra?r+*JQIvQ$;|I=&r|u+!PA_uw1~Eic)*$N_WK1f9 z4Mz2?F>yG3qT;m;@sj?jD5!9k2oXOV7d#!si^^3d8SaeZ?2Cl-wSzf*qeJmvya^`@ zExcVnUk^8csHoBy=(tgv)4W@oJ3CSHR(fX1pkpzf*SD`*;mNm8i{Rp4@XVDJ5x%Zz z%W^uM7Akz6#|Dc-3%x5g~?$P$)neK)kc$DM(#ODvjkM4 zchh$*Og3?^zS~vo0 zPUz_7?5?cfVXFh0vKto ztYGS=aw(QmRFHud&5}x1!YQKHs!&)Q8k5jbQRV>sKqC(d7g+e1N_xHp@RGBy107Z17CkH4(QdL>#rw zvvyW#u5gy-x(MHSQ;uWBhT(Ww0{)3Yikv6RtEN}l87$x!fwE)MYO>u9k12_~!6zr( z68+h-KTXWt<+7^nr;@0e^fGv_3GMk%+14-ZSUd@$+WfCJHpPIgKo zrAGgZy!Yc-PBwge7!_qzQ={{K{i627`^SNxD~YA_ra5r{bU=&0LYqbu8tYJpeK<0WPMSM;soTB3m$ZIwbPu9g@_&-3oqv$)l=8!E-+~8U1 zT~vF`+4R{Oe$M`mft;ed?M~f}KAe@+Xm{;;Q{Zwpnpf$kYcGw`M2*=B>jp^gUphV{ zxjcN-w5N(uafll4h~g&CPV0;)8dgh=`cYJH##eT6c*va7{B@w}v`=7*B!4(x$oNF z@&1RGM~^-n=2OIAzG>e>r^vl%7j~1vZ&jJ92XE&T-?R+#O@ng`>88p=gj~5+D@=LO zk!p|~r;V~HFxiub#A_Mg0zPm6$+EN}Q@E?hU)FbK+qKWX`1t>rBYwE_c z^t#o(ij~Z^d>6LCTvt%~Mh{g8J?>%?bfHhbO%I&Uz!i*H8p+bzKARfVi(|JUzoQ#6 z&qtq|>Amv#s{BvBF8nE+xp+q~YKrwA6)EZyF`?81;u&#s z_6U^_dQeF|VNJt_y;`W-IlkWMr&9AF3|d|yyw4)Nh=3CXIakl?69IEgX1Pj$AcoM> zG5X+#5gB&R=W4FfX)+y$qxnd=`T&pH5aFmh-jZILl;rSd?n@{}y^Z#=B z_ka8Mm%sjj}|zrFmIU;XOx zAO7}Nmw*2^zq)+!t+{+nacTv{FF*a><-;b&+o5Sqvm6Kbcj_;<)bW0wS3PT-xn-W| z(fVB-vC#;1T|@VCJkOJZt*s0qY^Nqz^z4Ww9*0;ir{BkUy`@|FOZCBLKSz(N6ykr3 zn?q)vxsB*WDPEk26J7l;Endb)x~PDw-%FQna?I3x&av;ql60w1j}>!Q#syxzE=tUK zJ8Ixvb12`XKP|*r1;Bae`xeFZRjy=JAT&G3nOJRt-`ACZff4w^&vYBn`W6<%Pt(dk zh``aUi$c>_M0N&~uA0W>f$RDU*;IL>v@-g`mt5%#P)br>h`S4CEw=J!A3Jj?%J>A) z{F$zk2F73ZM@I~n$^Whz?yT;K$asO{3l6UAR@2-Cd#qMgYREBW+#a?ohYemOG#aFa z=Lj|^qLx$8RqVOIB?N#uC*g63NbEPq$>bGGdP?yo0w{#*gsosh$a_f7d-aZ#!0LIJ z8r9^iC$O^5X$%*}NfiC1aD-V^u-`)=0%HocZZZCl2J*lS(sC`M=3zlt0xsrzd4>t^vH61+iG zwQ{#>AOu_gi0DGIY;bU3tn)9`8wguqiLyq-BvO9J* zzD;6>uJq9}T}~SXFAWRb^tJe1A+z@gihuN&M)bG>M`lUZ?boyIn+aa>1e8x@m%wal z;eKDkOiIyemW@jJIN;KE(F;1tCT;pF8;m;+Hv!I;D0<3+2|QLGFp^vB29#naz(|>- zdIrFvPtPLaIE;{4sw)K=tloE(VsPXE>_Uhd{)n2G--VbJvnM_rE_fqXTGe;Yi1H$ zu#fV|MP$K^Uk?6a@RoLd{q2{R-~HxqFMsnlf4zgQe(>?*oNXJCy#GOyi637+d}O&< zePLD^l?P9@ds2o(x9Ir8CLrrWf2up+>zcgLcD9ep5UZS?MbksV>az_CbQi@Vl`vM4 zAwHidg${|d^!t{-Z!2<|pi-<*z|qFZFI6VV+3Fp#-{4$M5hv+3RV{Pn1$_FKY~13^ zWP{`M=I9*mh>Y0KmSpx|-AbaS(ka`Nr#L*EZ{o5b(6H0W>o8mMrlKi#KGD>7(n9R&N@A8kMoN78rYsfwVeGKUpe!XMq^ZSaa1 z%g}ofgb(o`t4=QY3F=n09Kr)PK$TjtldpJ}QVv4bod`OW_ zROuKU^FkFJCl!RlfuW);q%F5v9w}nPcTJ3?3C6Cr=QxN}(2=IDjtSqCeP3|qt9GL<`Wubldu9ZjZrEs-9%)jGfNbKrjev6wj(MHa4gW=T5Q4Gn^)E>-U;I>h}znJ2v=D zR`>3HoBkI~)jeUmt@XXO$q8Ll`?nqI9K-7rk}hE6R)wW&1|^ujzB4jDQSxyl$HBzk z&P7?|$O@&3*K^b}(qQz@JWWn&CF-m1-D#zjXW?WHuD1dO+I;$km8<&3{~(9M`rm?& zPC*UFb*oh)+9Y!*(Io#p(|V|65#F72fIe8y%z+KfbP`1_({i>sN@{pEVS(FZ|c{rIbNkZICPO;NY-l#SMH)_nD7~N4$Rp&%zC~& z1q@7X<=1!I9hPzAYo#UA;e~f)XLri+j{eS`BFMj#vkH@-IfrU(^RFa|vqNAd^>YYm zDl^iE=Eq9dh0z*!y@ODNCN{Vxw3rpKyQW+{Q)4b8BiA|F@7>A=#$mQv3wXx_BnIFr z6LUiwff(0ym_9^yKN*}CISplpsHD*|q#rk2|u4Ix!3jW&HN}=gC?BSb>E&ji0B-%D*w)d_bwma_{8pA zb-1@IvHkJJ!{o8~qorIUSA|K*;g`bRsE=_xjA7)j+3X}cqwb=e$n7S{lf?7;GroeD86GilrA@{Zu7k^xY8~ zJ&1qgpF?V<0}-ew^h55Xy*3mvdEBa`*js6ENY*wsDD~7R-4;1n-z&1ySAtK|>p93| zs=}^};~c=b(y;`#1sM36A)n1@)1&l27P>+GImK1{=^0)HHdxs?9r8@Gw|ikoT9~+>-U~wv*C$s5?_khsv-p^5SMgj2rV3V$E?VMk9Nre+BJrA#* zUjg^vJDjx0=7YeL0A3+}{8RZ?gi0s=mZIix?@B9OKt1D9IN%vvc}c^i5+N>pblfs z2yPSQKhhL-_9oomk34-g0zZXN66WGvw|JC-Ekc~40mE&5zSkUU|^!)4S!3ev_o%Hm6d06e+ z+N*fE&0!_(Vms>-PZRioGbggV0mZ|ikpo53roK@gHZnr%%8N&@yo)Cr+zBQ}O>a4K zxXpR?Y|8{oD^kU%&4~@x4uG2`mN>ha?f0I)!=oL25WcN^O5iESBFDZdo8PqG#7|9u zdES7n4^1J6V8oHn+rP7=)f`cUvAUiv=r1i}Erm*N`N$e`5Zl{?fT<}(BfMPMjXA@N zDkQU^kOiakLD}*$s8#C=UNLGBQMjtDz+MeDDq{$a?HR{gIq0G~=2a-uPs*AhsHqn@ zrKlLZsTgGzA>2wKL^TSXl9sj`*bc_&EAks{(F8E1E!~7xS>@dOp;TT*6u0|kV8?jT z>DjJ?>fZ@!`!4#L;>2Gm=4gAref6SkR`}Cv2_cXB#WV|b)60I_1xW8$@YJAj+w|fw9ZL51-+L4vf{%`nrUv)gzU*t1DeA|lO3QDHikjMrBj)UA7G!?VXKw&=uf zPB}GOxvZDQ8IYiRr++n>gtLf$Ga3}#`_n`&dvd_9&BqL;QD*~qw;Iu2KoBiA%v4|^ z(=VkM49XV5RLQ>gtoKt(&kdgie9M0yu1&8Ed#Swdce)-EmqES;O zrYxe+EIQ9|8N=6k=iQiAQZ*-sP`PLIOYfu$hN&)K;)a^)IEjA7C9q7tnA0B*y3e5I z_4;On#W@LevI9vYB^{< zj3-Z@U!Ju*@bL!^F7H2iaru6S#ouTLqmMr<`q1Z=W1~}x@Hz(Jl|IjV!HNElKl;Jt zXMi7hO@)l7th+ZtUk!C@<~59EOQt;&v(UUXfOmE>N(R9o?)5#BOfsQc#t_cscQOm$7Cq3R=79>5 z=$=8T^{F;!MHf_J^8=hSL9?CNA&d=Twa$EgfKyNZ(-%6jE{g17T_#diJUMZ$zHbyQ z0H=H9yvw#Ka((j)I4V1MqSB?LhoxT&F-fV&IY~Mf631_Gy8UBE4$W$Ucg8MZh=g5p zw~T7GGQh^434UDFzm-r#6ZVh05femrCV?YSG3jAVw>qiS9vGR?;j;H@8b~#l%mHya z+($c(#GE^&BM>#iP1ZJ;SI(d`v?P%zF@p6=nd$(Z=(hKBIGB$O!5#>B#mI=K7=!6K zR7OTp$OFFs~N_qcBz1*Du+FAlBvVO3eDZL8L2&H~AykB2jynVx| zQ^?6C1Fsl#oJq}K^$BjeTH^3cPK2_71~f*G3J|WIJ1}XLX@)d((@}Jz1BkD~``2H5 zdHJ%_67M{!fA3WW*cxfmgKSRvpe}%)e)N;e=eNGT{HNdj?d9)2`~33$YMmcviJ z_sO=w`tbgvZHoHVz)%tA!{%Fl`1pf#;YH2!qRsjkA2(5bvlA-cF5x#jN%O&jrt;kA zIETh9Y#OX(`^JVJHN{P;e)wMaN4sd1qjwo-(g*hf&->6{vWZ7p&MHC=-!(LuC zVEFQ7hoUvf{#B0PwaTup5d(Dn4_r0LSpJ1v&$V2NDAHhAF~Lb{E1T&}0^ zf+yYKD|&BMg`i{Qc~*HeU*#hzPHDUENxD$h@>MYZ#V*->d~@*u*8IEJ=9ONO&N)a)El9zXco#+f^!a03+~9ctN57ln~7E$g{{8T`7%+qOdicb7GST z1V<1az?Tfb9A`2#eRR=p5;$J57zOqJMC##cI#4Z%GvIKH+D4*N_Hu3tNr+I7BcCeD zVC;S0DlM&No63?s#b4z}F`{AT&y0sTiFv@%hBH~bk~hY`sh`e7`lxF6>t`Lr{`B(O z&%U^PRp;W5e)_Y^FaJqfw!L_F`PJY4{_?lK`psH6fBf?wUjD_O{mJEbU$mo6(cFLT z`8O@`dlBs-z)wDEGn9u-rOEjj(OhH^9pu%Y`ej=o6u$R!eB=?TUFmj&-n;SO@&Wwg zw>iGA+KId69;K^KHhFTu8=;?^OP6hIgN6*RP$N!j)}}M@`9$)&SyQwJrz}x?;&|SM zCf`17i?pY;0$wEhR-QQGV$LvK5iBIp_>*zRujxaWvY}~OdZFJ;WP}brL3))NyMINu zJ!BUBF(}~{A5GtXF2o7Bf{35gAQa%rY zv6Ve^HJIF=G&5szuy$QbqkkLej^EQ8Fy!?+D43)bBtTuCJznf_@Ypq&{Z=Bcphbl5 zi;#2DreuxLz2h@n`e4~G>Bb~#U9D&ioJHR$gstOuA%rvRODb*w5T2M6v1%QunA{r% z&$E6~9JT5V( zf5q=b7b#{XPbTGz2c?1wS61k#$>l&Wcw3S2-_=wee3+oF zny}`G!pT|A(}j3>uitmmaRILX-_&pLJVxOMaeJ=JYl`5kR8Z z+)!tq+-`&7hxh3z9z4H%Ye)P}id2u?d=(y7a?ZSt>;mhC#;E8P{z#&GVp>EUL=UN{_z(I zxXBKvf`63*w+$e1^z$R3bLI3cQaf~vJ*;+vkEE@RUA6l*$ky4vQ)MerPmN!9>x7=4SL-Wm4lLd0tNn6}zx z)?CvxMva4W&)sK8YLV|IF9++|VClkP4yKj{jEHeDVu(%&_axLAo!1k7H`6F(UYR{+i10L=R=5T_?ijm|&W~mNt4A5(P+bYerN0@jn1t=UM)K%jakb;J^<= zykXG6m=31&bC%H(UF*-8Aq00YQ>2s;ty>CMe*B%D;#I$igG?!jezLc5l}t~Qt!HYv(#&v5k1uLNMd_kA&bMcnKY{iL z40Oauu)AU448UvPg&wRfJ?f1I-jT zeR!TFliB2{%-!E;I@7tz!Ev)!Di$ioKu^REPrC4?m+Mc2x_^~_`p-j}QzYl{GB~BD z&t@(xnnfP4{-l>$n}VG!c?D~MMR0l!e=?tgmIdC++>dZsz$IxZ*kl~U4JfLWe!c42V^n2!bW za6H@#5zYW2qDEyiSdMIWgE52hQcc-=Vf~tqm36Bq0ZOhh%8)GG0S6%n$HXa4xOmgE zbqLQ9iA?&fraH-o@4IR@5_sMGJ<|XjhLgSvFk9*oLz?Tyvtn(zuQs;qLrFFGje+4Cot-#+>L^4I_H+oHV3m%sS)zqtI_ zPkzyP)qv#EoPNpH+VL68zqWZRgte zjdo9;P-)DZ;}sL9yI01N6WoOt6flG3thNp}XSWJFTd94WKJ*@`D&&cRP@%7;&-Ei#U^stl(9 zZd2Bfh0RHVe;fnsdd30vSdql?h|WX-KbZ8h+h`UK0hA zuHJ!f5DE{KdtJLX6tv&M23uskzS%UE<_S`PZ=ZhNz`!>}0F@QLZf%2w);(9b+Qiry zI4VAS@ICqV^UEK;`0eF4pZ{GuX8*1VsX+x!D*pcHlOJ9_`S7#L2M<4rA2p@J#o(SM zzFm7h|MKh0-q{fA-Uy;?FN1JFRZe#NlTkZ10kAHgk=*7p2 z2=C|kZWjfaHu$j9^KR87rAfD(@Tg+veDI;>H)ea4|+&ULt*+lr(uWpK6;U zqdAPS^*aLkByx`^_Pu8lVTWxF+ z5APTK-mdS}PGWCc3G}YkfT>G>Eb0ep4gicZwT7+IRS_mqwjZO{3t|)=U!#Y^Ik-h@ zw4?FZwXTjT)A&X2p=!Wpt;t9_Zh|vB-7G3XQ^=OLv|wW;VgyQ5ogQipv9SdWD#_s; z4m~>oS6SFPUYBPPHyaS;@fT|u?$s*uHJJ&t|7*~U-F|b~eb1S9uVp}PL#ncpqjdo( zG$PdU!?p|*BE(UeMbB76bj)PhU=noSr;Q(jR#-+~)L41x-ZA<^1OX#X#)7fR+cbbx z52BD9bBrq&z&JKWbTdcb8xslAsAV}ZQG4!MdLsEsyuv3vMa;(k~|9OG8| z>S7>7%~?ydL;xoO1~DKMjT0+BN80}Ya>U7K4!iW81#hV+h5o{opyzx<<@`m{iEF zO7z1IZA#IRxSB^IfgCV-TD^0lxr|eO5hguz^(f-bC%kq~dB3{|_WHSpSNWnluw8U4 z{fl>5Vr9}X&ZK-Z0$C&kr}vXa@{xW53-*c#o~CO?&uJ;=I1x4h*43w1>;LnZj_3Su z)Wm<_xQ50BZhiIY@^x#z-?d?_y$e<9<=nNoi7CQAmIGy)7AU*wXs*!b=-qyWbAsVV zm*UaEr`O#d-Qm#a5BHmGt|Gp|y+Tn~&Xs+Fn^Cg9Np9vE9{l*F>w%ti!(}%+{f~eG zVWGmAYIDj)R2Ze3N}=QaRbDbgf<+D3I2RQ?>{39r-~I6~#7h(XYz`ELYg!+QqP>kV zbPDtAkf;!m17%SH&ny#x^V&lqf%IF!>r}0v?PHf>=*V1Dl>ipL24Zjr1sv`EoJ?@H zKDhEqQd)mk{7NX{xaI9N+1J8QK)EI(n-DRw{Q4If7hP}SAY zo0#;vDq_4Un0MpuVwfLtjT zuD^WFD9xd~*+#E5fj|50f}8IF#4#)gKoNA zE5J+()4b@uPdM({y*qkep8dXlNSUrbJsWKKj+0yTEjp_o*1aI$W)*}{__YNGto&@M zcZ7E%UK}9?Z-yPb*;Zgztn%XZ7+gM=0LA5JuaCJ%*U!Le<-A`+EsE1Q`pw&T*zpbQ zaYxOj7jr~)eUO*a3D@GQjI|`-Ha+fZfxyuN!NQhXmY>5Odg&K;s(XeryMbpe_SHXd zv1A9}^?1=DJMlIhSdf7LB?D%=9^#Z?6Il3oL;}d{e#5!IoTCfELjzy1d@H z{ge>>9IMgxZIL&B)(eb-|9FaiFxeIddb|WY&qlbO4X^srAd2~Fja`I!5JgBdjgWPG zQYh~TX^#=j5pWbKelJ4pylG^UTf%h319ZaMLnN@~1Ku=|R~6g?eFemecspizCK9qG z+WG=}Ht%1FrI>f0zE1hUKzQ$=Y3KadqLD%MqZIZbFJ5jN*(IM_1y|Z)z%yQ~VD6Lmwkp!OBu( zzbWJ4f4mOXn|OP>jV7FyxcQ8_8_H%qv95~K-4Ja#NDj;x$I_1JMQ{C9BR5Cb<8`J+#NTr>TOH_>1K$3T;oNz_Dhfct+O zkVscVQ(3K~x=}>5)lT#r9A`vBr1`@UA=gMKL)yMvbm966AIbZh z-gc%-pgww3RZ!Z`-qyDp58mD}7q!0M%C_?J%LVR;r54ZgDDDyLOS z`ZJvj-U$Y>kem=ZpZw&Xjl+#Rl7IF(Q2m)&=A^-z(+ix{G9u2+T#2|j?%p5xVi$e+ za4PX^rR*-jOwV-x(jw8I-)FTj>);LvzK8A(yI}GMYtcskqQ}zEAHknp&goNU zzEB!9DPg!Oag&4(Ln^lm;>-%kqRVWxg|mH6jac3} zz|sfds8;FB(*vxLO-r)w)TzCZ*qFcO1e(Lc{TXPf42}tMIfjig6H++m^7f5J_Jog0 zLW5ciZi@yfP=$JarV&I__=7p>-1nX{n)58Lk@zKH1qlxgyGr9o%bmAEs5v6WeYTUY zeEja;SW<>nT*(CmCEOCWBpPqXGHmO|gd9baXpvU*S{cG0RPc;t%Gv{ib~m;|OE2S* zNSG|}T%p@NMpH~bl#gf089pi-j*Vh7nHMSKi;h#kJNfQ4pmDcD){pTu6xB{vsLLlakL%bM7 zkI}+%(`_WdpZKPKTK+RVseC4#{-+y;r=Ptm-7{fu*E4>r3{aSy@ro$K0KMnjLzI(DG=q-X?_ zEGzRwkIKx3tZ+%&!-iv06P5^9stu08o+9Rz>p0g@Kg1Q%XPi=dsOHUm#LXNF1yH!r ztMwnC?s%D=#l}VqPmOgAAFvcF97GH2h!j=7%jXf;N`3eT@4ytn2Twmy>q~i$f$IhZ#mT=(w`%w zzQbXi$XWRucl>-4U(Q@f;MGv^$`xEJ2ZRHqh@9f0#S}_EknQ+6O*qVs<0T%h6T4Dr z&S~-kgdf2#+8QmPxyipN8WFfE{@^bK1xfVlppWl<+-SzZn>I@*QE9^=8b$2-M;VUh z%}xT0P$TS)RuFZ;%|MnFFUoSxh&g{xb9kb7^EY?!yE6MsQW^1^qL`O;F<1q(W^{Ob z{L!P!k6M!Vr$7BgCsTGRC4GW>6=W3VJV`ki=B4EXHqy#b#Y|=2uClgzr8R#2K4qPd zt+y)PXvV7(df3goS3z=;KvQjt=Iua^*xTLnHbWeZ( zMt;+)@eURerd;^FE|&5vlC0c=COuX>CPhUwEem>LD<>*gSbtwOK+dk?P+8a2KX$w~ zha2v9Zcu(eL<*=1mad$YaVspHNSA}TGUMrVIQfVkF@4<(m>(jBU@K1a15!#A5sPew zJP#~-qu6zJ7h#Qp7&{^mHVsQ?DS@ir8fue+HFY$R^N|lqce$e!;yKDB;M(F}1LMrAz_cp|$_vy}4th!3***n!b@m4%uXC%vBTb z0<$!gv96Pmw%)}>fw!xu9OPt@xdXDcJ6L>vceC?bQr5R`8u1J-a~R>K#=e7&o;ibN zyB3jR%1BmNeXl`*J1^Qu;YsJe;O{CKw4Z457~4^u7p-1-ULPgm9^}w|_~ECQ|K^u} zcKP{_{ydJg+jnK^T(8=UQ}HU&$X3OpnWQO&r)?KCM}4RHksVeuqndNk?+2+Vu43>5 zP8?};?rnA;O@A3X>W))#m)=F&ge^5iCa1B$pZH8c^kD-Qy-B>}{m7=!e1%Lem#H+R z&2Fw8%YqZdhXeVp?ieuT`FK~};qDe3mrT*n?>%Wy=Kcra(;Q9*z};=co?X9dnw8CY z>FB$rtDU1^n|fsfwjcX)D&*ShI9 z+gxifB!_1-ojy(7Csz(i1*=%P*FVc#+4Y*rqD*rc@L#z=656-@SAUbe|g(9o!3n)-f}I`T~PyPT-8)`^4@5=Zq`>)o1Z#m z&-YeV6CEigtm39O059-+;`@DI=$+ zRkS=;aG=2H43Ekq2NxvicJi1bi2$aee|276nw$W7gAg9<$$NS&07*v4V||pX1w1;u z91Y1{np0li{hTe0*2=imhP_s*jLu$X(_m@lKYYl++-t>9x*M5-4He7=ugbp)EZJ}z zaOR?rBH~l}W!?l%AWOWZVG{-c=vO=lJ7@>8w&gk%VEH+Zu4ACs8mu~e?X;n` z+6R#-h5j=F-zJznqYNjs?YSoT%A_m_u@6SAnK-8?;B69i_rhX(;oZK9kS3I1Y1X+H zK{D2j{9-JHl`mRE0{SGyBYBLz6C9)H67Ah@X;vPk|0aEhQ`Zq*?5wus6koPfOW)Q; z-p15mPk7-Dj(f^dxw;T0n2J!7@ei_SQktB5qkgLEzL+~TfoAmaYIRWQ4z2MorAzb) zmZDS{^ts(|9NaV;?zX#&1JqY@_iT=`^gG~OHIXqqynjY?gVLMc<>V;Q`}G|^itg7P z6Y%uutIM+{SAYKGH8TE$`;xHUCpOIMev#M4I(E!;P%?*0lz`Z;q{E2mbf3vf&{y%IBT})iX#r%i+FZ{uWw)27^B~nEr+xsG(m{pzL_C#1KCs0`_(7- z%S0EuPwzNFPF2n|yLGq3hb=T9;_Sd{@7UT_l*PkM{ma*Q5Ays@&t222%F&vg?#z~s zYn^Q+r?py!3pyqdP?q1HvJC&dBQx?0u!--mov7oh`-Z7f4qkLaX65v4DO{s@XX#h{ z0|?ZoFr`Y-H8HzJbWUa~Cjb}msolWn+QiEk%-F3w*o0CX0Sn^Y>g5wzn0^Y*q zD_urMX;yQN#+VAl{VPYXUe?TW2KJ4eW6ZJj96oCQ%6?O`(pFk{e7hzUXXO0}<3l1v z-tw~qxTaW|K{zbgvOp7Qhwu1=ZD;u*u2kaaPX=OZf3X+NO%0D zen9sR>U4)PAF)15RLn{vA*lP%bTWYt`NsR}$fMjv$@N(^Kk;$9*gSY&fAp=(AKv{g zT;qGkalWlDa!u!&;mWpUz0DbD$n$8>M1K9|&gK2~H&pYVC1Ltz&+;&%qOa=*;}>?Q z5=`Rl`znO=%t?TE9;%poo;ZDPppOo5QaKHqdE72yy=CQ=ND;3%xzmj4yWbeWF$Md~ z&kR@&PbD)Uaa^1so{H?HrAf(z4!tv~8;|M1xoBv~@ll}Q9!vM$4JEz8V{!}D(ae*E zv)(|=GR1aMCf^K}VMq%FxGYlSpnJx)=IDWu?&Lq;()MUhw%f`dzU&EI_yx&s$@YG8 zJD;}|n|(0Opi7xoHa4YkDb6bHbV*5c?@a3E*hF7+hGzD@cO2Q_xqJCfba2TSmY9<7C%JU0(Je0rpi$$qO|;v1b? zpFK{D4>>|@3YG;{Q5f!IV#5NJe%!S5Awi4hw!{chI7&1IUKn1xz zyBym#9_am=qe&F=^oI%r5aU1}k;=W!`arNC+1`a`dE#OAk*a8>rec#tagZRlF=|*5 zplJixeygb#o6OyS!d(MXMUsn&PgU8w9z`n!;B22i(~QXREE}9hmgw>DQO9Uw>P$eCzUqAN=g{_+9hUf~%!#6!^|P zv=+hYFsBSC?wv^1=;IVJ9*{i?utX3(9LOH|!wHBqlEK2cQmEu&S|7&L6@7WHK6D^@ z%Kh=9$F1A_lbqbgmoJ)s_^T&>KPA#PDMGy6evKk{gDmD4ngIXN!w)V${mCC)KKkUt z%m3#u|FWY_|6ZVUx%Z$E(k60$`)!lD)4|vF^@DMI)~x@!rGUTr>bF*AM$7j*)8%2# zFN0|@fHZR=Am30o#$j<()hMYmm={LoQWX8fQ^|J zJ$|R7wS070fB9enjaOB$X9p_$R{{5&h6@ZviAU+Sc_o}Quf8a>G*|DHc_o#ez_kzE zg8v*@%=b8_A>B<8fp_WFoF&v(TlBnp{q~@r3qs{unenU2pRLX2av0Vl-&Uz~ZuXsy z$W>{NFW_A33SCH%PM1}5(1P^h+5yUghOMi{ukJUXf$s1ZBvb{4MZf#JNfz>Ql=Od7 zg{H%p$_|zBkCzP}(15s8VU#A?!COf_!x!ApbYC~sN%y33TB?iogyjKg9iRWGT9dqm*2e~!z za&9|}DMqO15T<{E6d=TzDOUfXPIP)a=9cgDZ9=_Th7zR&Ya*B47V}_W1SNTyLT+cE zMV}ChU6mJt!OtmTAGC21nbEQ(fpHK2B{i~5flPW9`D_CKO!Tr#M1@AXIotKyrU;@u z5srqT_d2fa2qLIHJB({;Rw;xBhN0#~4)wD(Q2h_T`A?TGTM_cGem)15o1|=l__LP7 z-R)5Lr_UZ2aeW+JQ5g*nasUtNKU-IZC6#I@*CL&iaBXtL^ohP9&QVhs-y)Qhos#a7 zMl=MDJerJanq`nF^pxtF_+<7j***HWCVJ;AzxUulPW{#8+m@l(4$LWacF5Ujb;*)! z3HIk7|M>DR{`p_z%&XLzy#2**e%o5+5AtDmFW-FeZ7VgNUB3C+TtLm|xEKvDn=t(k zpa1Ie*T3N`TN!h=L$B`qAf9{>Pt%_wVlsDPoF#tFsM!HE1Ums&HmitdA=0}d+vfFc zn>k5neloeCMS;GOrBQu2B?elnV#->&-~n^s`lbxmpL8;PP#LU}5sfMr?bL?MrS%Lb z(x1*=$bj9E@i{h&VNY5#+abIa}oz4fie%^bV^uH+9~>opY$T zaIm|yzAdVATZA0V*6cD2{eTl92h=@YqEXZF#j`JqTr_(LXk0$ga@axt90;!B$WbOW zn|@)TZQoBAAEfI^y9nj5Oa?7ywTYycboA1rXWZp zdnuZ9R`LCo!`HwYNEZ~m+wRV967T(@?ef5NJef9nN1?AB0%@cy=-owN zL2f_m?>(WVnld}7af z!KxA=Qq#2+9RL7807*naR4~s64MXAzX3xQsXI|$zeY(jG(#7Gl2w6_DG99J)_ui|e<_McPiS(BfE01DeGVv#qLv3)p&&wDP6~Qa1)MaNQW$O86xLna8 zJ^W@taO*PI;i_)yc{qiqKt<580bcxy$Kt{yRm%K;N=ATiGX1aQsg zkmtB^O#y+c-{{n&jfUEruV0%+BXU&ZONTu!g6rekuhoPh)DP^xdEx2LI z|Lt!tzy7;#iku!^-aPyI^5pBgH8&gZc-RIDY0SrMH+A_mxH*ou(e~bBJF4fj8ReQ7 zDHjBNDt`q`zzU~vJ!e2(cMYxcr(Yox7{;U~*Ej5%u@bO+EnTDXBK##@8QuCB4)+=< ze(>PtJ6)l&u_k@4XK$A3SI)v8vyD_d9y^M?Wr@G8psv^59XMyq51>5%*J8Is+0Pgl|f-5*#teu*q*5b-cMF8V^VyaQ806_RG-h)XvG37hg0ud@ZE zY;VKL7+kqC=xuoy?Dbs+dmE^hc#eZkq~~)y92FT!n=Kq%!+W~f<3+QO;XpH9=#bOq z3L}Iw$wn%F1A1V)I1mx#jjO&^x*+rQRi%Hca;@cej487}d`W9+Ev+Js<})aoXhVtJ za~Ri7Zrx~eDrrdF3_)0ZF$}I)zn0{9(*ropF;||{cq=^1KIQ|cZ!p}CMe&`3HdDnc zifxD;a22RPX1-s-YnoN&!9Y42mtH@)C{eNY8pQ*)=%33Yx^N;=WnYzFg@uH5i%lpc z0(9OfR?wG|_y90LcgIeexEda!JCi3l2!35e2hMO63Ioukxw={g=sB4lD!E#VM0CGV z-g#;x!`*X~I^tgjZ;Bmti{8kWqyl{+8E|<19z~F*?Y-7H8?`p#s`eoe{f(y&w4%$) zUz%5^31s!n?fR!~-9CL((7s(XWC>jiFeM0&aC+Mo{7=4q(+)K?0dpqrQuJ31P<;M-n=`y^YDzmt7nQy2T#}!E z`tyeqHqV&E=DOA1tYFks63;mogL)!H5Ah)fxIsCBCYwGcsds~2iBX?BxyEZSa~ArbEl4<5iRca< z)VI1)89HY>hQ(KKmA8t?y^H+1;1}%m{mHzv-JI~L2i@ny|M7xXysYwn=bZF5(75rg z<7{OaObUIS`p|O(+jCAaA-T#=v>C9!pr zAVa}9uJd%cdxHM*b*-7|drs%9(AvNr-OG-ZLlqY(#yj+!Kqn`b{N-B&JHAR6O}t&t z|LbcJb9lOC`rrj;8*5gBzA54BBCN9~;Z+(+;{)EB8*C;TD%_?KoZ{^U zZfucc&_*B2)*P#)F|4kH7tg=S3FH(_a#mv{_#4|%IJku7SWivWMpw6O`e+D#%%>x( zE#u4ER+QKS^6Mr%KgT!1&+sy(hbskb08(y&OWoFepb!~FyRa@bnWUJ z{Qiibbe-L6S?9|e4I=h$`Z{>${TxfUtZR*b01HeCm>#iD5$haH_e-~O&L7X`7!_M$ ze4P$wNA&(E@`)7rpVLInO|dZ`l#{KljT)?#)8nz@if40})1Ok|$ME2<;FX^W<(+%u zo*a4)4?G5E4u2J4c~@$I*&G`WMZ=Z>p+p4<&Z$=^+Hopniy4h)l{{BkUk?YhVehL@ zEK?e@4LqE~n##V-%`l|5_&Q^&u+nazr&|p1kN9*9Yl=i#yzfcx;IRP=xhQpjj`8DN zK<#=E4maBE*t8kDLT_FaQggVAZjTfpkN|5^v@6e@rgzw2RTDx@M|AoRZIf?V+^a^4 zUp#Mn{GRDMF#&hWx8klQ`Ihlj^K33wMrK+P!K1?oduvjmD}k8a@Z#C?c9dx(x-PCe zMXWW(gRj{d?i^)gn5_EyFP>li*Z=RYE}y*r;pM-}Io_&ok+K5}HkllQ>v_2+;47gX zhnMnl0>Ry`LKv@)ttlr96)4RjC^>z=>wC3m-_f?prx^u%&w+?EAecNngXhDH>Zd>Y z#pS0z{o`dcp?|97i^zW@E7U*7wh-^IhfEhXnH1DYzYH0=7$dpXgM zA74J`u=|ItVELrU-k&tt`=gJ)cX|BLr}2q%kT3dXqh*8>8oM1WyAOT|HhJ0zGrbKZ zZgkOhG@CND=uJfl2O!cpu?1Kv**)}Q{0t0wAicz%vmd|XsmgcnRlFqSd0&HfB*%6* zXHq+rSB1{W_nx9(!%bh`Ho-{S`=hCOVUgcG=G5VrIIVx`f+RCJl(#5y+8Fr8F(DN6$7-FJh5$zEdBkuuY12cZTtly)MvmLBoUq1h$0hljx?7_Oz zrmNrk(TDZJ#rF)xbRr5M%6{+Rt;_d+@c8oK$1QKe(VVxMl=FxRfBIs{gdKl5HPLW_ zMCZ~_0%>~Ri*r9CO@_f)GmGHC$f4!wSJafp^B|G!jShj@1`ph<^=!cU;OFi zU;g<&X@YkHEwxha)R*{vBk~`#wbp%mJ{}9Zq6r_81B!!%wBfD`XB9y*;T-UwCVi?p zJ=l~hKkzMwZ*WO|1~kWB#W}bb=`%hbt`5FRghCz`?Y+UgbM<_V$gl3FxZT1>J}8rs zd+*7puh@Jp^v$WAG;>PQz_0I<9HC{X{LS@CXXeBKUs}apIT7XicvrW=+Xh87Y1ikL z-8k3Y z`E1)baQbYqLVAUr)7A0DeTY=%M{Rz|3=28Nqrrm9f=wCs3x((qyD3tIS1ZYb9a$i zm~A=;hEFN_X2GR7Efo<{BdsAqnnv^VAr;GoV*etdBMXQ4Bonf{H`>u)P(_4z`Wzx8 zSezthg%I|NXsbs?Tt#pCL*%-a57Ridj>WvuIl?#YJPecGP3d#_37zcTJpZ=&iJx`o z(ANnpw?OIOP+C#I@a=vzY0<*zW`F}{lh9JOTXBB}4plzkFu3PA$IrfccKOw3ZRVOI z{It{R{`J54mzO{P;~!l`Wqiy{`{9e=v@wCm~!kiBgeje zupzN`jDL|)6$l5p3E-F-V?@`>FJBNmLjyiGyz1;XFoJywx4BmEZBE0bn501e-c=Yh{!}=K?fAZet^WQe|T>b_7 zjgEfu>BpCU`)~fc%m4kq{l8wm_sJ(me}V_bF1&>rd|dmA9u)%q%{wD{*5e1w*^NA`H!&E(mwSsX?O3OeS*q+5q!IQDEGx(*K@!(BSj?AuOD z6b>qaE(vZL8#_8&auM5Brlm8`)UC7y1OZ@gV_7iWqwNGtWM1S(*1h{KFj08rKtX|8 zLag$cp%6KhQatAzQqB1R)l5v*Vc#i+oSv4RGJ^Wh{DU`TV0qDIdYKLw&7 z5%$(-kQBi8SpWi z6XdX4Fjg6k_G*MWN-7nH_iQ0}({XOsS0!4TFcQ2Xg!VV1Nrd?AH|;| zXF_w-5k@+9K`t0)L_4CX7kKVidPyge; zx%|)n?ti>|`oji)OkS=Z?QH`{*Q5Aq=-V|b2P~x?J!?h=ScuwJz%i@xkD+sdyAQW9 zo+p`x_bojoT6(JIgFVS4BU*$74u!cYc11+UqV7hMj|QD+>Q)i@?T3}W`5ujkH2HdS zI;XVY1b?97PtOb2d zT{xYnD%_%D9ry5p_w?inNra6Q*!I~MBX*B3SGp>B$}jSEodLu6<+X|?y}7!UHebbt#k2wS;e?Fw$P7&^nFm$C;BUZ)Ewo=pFG!9ZnR+LE>M%yo7I zH`)Z}J{%wC6+ZNn=D~F|1!fh;23J;C$uXuBn@Q2K*aaq6pwOe}c=W1nBfFsU1?)#r zE|DCRSbZUmn5q=>QvhYxrbMd5X# zeEcnSBu|!TmDyDubsAuNi+|Je_%b`kpPp|;Pxz7LPQuI`{O6Dx1a0U2|t(c@!6 z?>19N&{i{`a(#!rh3FX_47Zvn!|kN#ZE3@6p}Rh!VN!f)1xnR4W4iI6L4e@GXY5X_0Vmk^{lYC z!b#;rF!4RYP~Ck{W8<73n$!|GY04nd=Lk-dE*>P^hK2hB0d-~Xli3QBSE0*OIhxT1 zWEP#N*uc4(n68)xn9e9z*-8WUjdmltv8Y5e7X0#Tddgmsbr<|9Yb}vv{4T}&_|XTK zfAL2@yZqJv@zNczE|{+6^&< zlxBiaI4)%%a(qO8KUEkrG&soX)71vT z-|=UU<2NUs9=&O5lnL###onzOEYMbtecSP~jt@}nuhrMx7GVUEGn}r6%N!q+Dswhi zxhgy&8OeB7p6OrsZZ#WvJ>x(6r(10+X5{U;`Nsxw(PPt@wJL*SP5Mk~{EiOqS57>b zAZUf)?c^1>ZMiAAdp78mJAKHmdapbP9ysU5vo+;0#%t^^!EGBL+$~hUnPVX64ScMV zoucsanx=#t5{Qh5$!91Gz!^Md22cK3Vm&5#T#Dm(PV+!OJ;pXBW70 zFd4G^aQdc`=w7~P_n22-^e822PTso)DsJ9<&>Xs&-sOMNQay7fbA8znn&OE*o5;{d z0qmOEA-3sF(E!D>x1=@W^)0s2DI^;v^J# zqtlK?M|T)!ayAlQnPBMe>1g;8b}gb&hI^S=IH~owZV$!Z>9G4B{o)6gpZ)Bk%ip)O z@SC<9dtDT5-Xmjr*HXbHyv`d+hd;rw0mHze2Rz*?eZduKL$X3P*{ytv$H(wLOTkJM zeVIzx6P(Il<@>T1dAoCY-d>(Qsrv<9;i>;#lk|3jGCTh~h-Wt>w#!zs(E5ToOqXt?WA~>ieI4j< zOrHA}f~k3}Ns;Ju=hzS5U~VWoWIvK#uV3C^sQi84jL-a2SSJd6P`C;l7T4@LJlTNl z`q~2-Tmc%q?LcLqX1{~Pcb%CsBIQF%kKN=LQ_367(b6X-Bt@9h)vRE!2jd230zR3s zGW{S^1cml{_Z1w{Sg=c^Gv_1N|3A6qtwlZ zwjTcCerU5cne~I7M2VyxKuIJ-2!sd_Kw+rLtjf%oBkmCW{rt|o37R~@ue+bK_u6aT zYp*?=1jpO=K88cbunobOG?K`Clvbv4a&v|NCK|Tim?aQ^rsW_Ki(&gCcyOsL&&iOiEHPa5|A#t!!U z&V$Zt8`?XYGKIvF^9%{IokGm1%dIY7gxOk}P|F{-)$ef!BK+R(|2Q5uA=6=@_jb@j z1;dAtvU-Q+roIZgEOLplj5X3B6d=SLk5!ZAG5fO<>>q< zRE7Jgoj@h4o}AO#;TI4dtj+Y#D&u{P-7PwFux~&LGDV2N$8!vzVE7WR!RpWIOljs6 z0QIiIaC&%p@%;0i@%)>oKmE&Jw&?x)mW({?m@JFGb2{;Bh*gD*j_AlY7y4uv2cmuA zUGF)0<@-+ma{k8zOW(uwQ|nuG#9K^l%O_>o3%OOo&H@>RcHQ=#a#MwIRGxguUJL0Kau`1; zYtx*Nyjj`_rxF3!b}=Y z=P15-@%;3x)28hNdSAZKqGjjc$;T4rgU6qr{;Plb&rZ*C3g3VCs))A4cM}+!=vXkJ zXTuEyi&W58uULKOEK>BV9QeB&`J(FT-@o@JCu_JIO)=_@h)QGuGsalE_%?&|sT}^3 zUAuXvkm_MH!<$wor%+h^MUdS~!5ykF#|O%cjj6@s+VUQ3&P5MT*72AC7Y(?}kf%@W zY38%v;LdN^%g^t^YO2HjXb!aOiD%E+{ z4x{?iZYa6KyLGk)KJW~;!Gj~1{jn}E-tRjez&|kilWdPN{dFBr(unbDWvWldj_J;> zcM%Tk;r4zyYgl*Cqfz(!z_(kuu#vsg1HT`)PEna9)szX&zIRPWV>isE704ca6W~=gdSx+KIXi zA#-Bwt2p4SZg;~C91i*jg~8ke{Fs2z(rAurzsM=wf7n@q40%JUXp?Jch@9J-FyxFx zCm(V&7dgiBaJFz*xc#ubxi6mlIH&koy=Hr;L+x=#kA2lTy03fA@jIu%lv0@TR^9~6 z^UpqOsmb%|H0cvAA3xq}cS{dLw}d4+&)X*V;@Ouw2;w5VUcG*m@3Rv|Zikbl+(j~s zb`B(*&8BWf6;CbW$#JCc1~P`<4%R&mZY_!~TAh^fo8hMLBD}*8$&nf8nf0s`$8r{u z?B8T^@G}*wE#D1m<*d|=?-Oq@+-4x;^a@Nx*l#ipC7en z`XB!IXQxj+CY6DFeD{5I%ePWa9YX(r>0)m)=JAOYyK_6lopFYokqbhgn+|YtI~B&z zG8xWMsfp%*9@wHbSmD034y(Q~sEQu1Te84LJbWNE9@UuHVc%yMc*3xeo-(SVgPRW4 z#%&!i){#`7a{0~ExCZv>DdUJha zgO8t^s8lBTIDhm!+#fc%cm6P(L)tn*BgT(!+Xr8V{?UlB>zky6R&V|o8b=2iF41)C zE^#gATKjWwBaL%CShLBdzE7WKJF9xYN``q7cl%xa>0CO{$n|i~CVy+VyYN}3fJfeS z!j()v$q^1p^@(-;p+|VKgLUy@KA?NO=2VrgX*%FH`25!SyYgmJssJ+7iwx%c@_q|z zpQlo7uZw{Xaul@^(V@A>A?8;;%m>VNqU(%k$LYu1g}MZ-A(2Uik3&IK!YIZVF}=6k z2VDOm6k?dYmmb(P6jSu$>h|*M6b=8c@q1n&kR#&qOUqqENB*=Oym{_Ih?#j{g= zNC!*&`t)1>;6qy?&BFTMAF`hT08=|1R5C}V^K}r!?!otXIkNv~v)?XrP&A(|0p$<& zj^BL|#W|V#D14M1Pb(+N#FOc=vfyNBH)U~f@EEOf$;+2T-+l9%1H;cBItFFDd{)m3 zjlOR;3mpK=r~7EuPSolu-w843B`gInf0-b-dy=qg!Wv8y+EpjuY-G&8uW&6FC zud7^bI+@0^U<4=ZZU;$s|6MzwzJK|u{>8~#MR)y02Zu_P=A$^+?kr$&aW5RvA#q-Z zK26u+wX-aL;L8mQOt5XTkzCWQV|gD9J8Yf9<7()rQk?j9I|@ zucH+qdTj`=gQ^~1Vs`rI$m4Oc9?W1&4n$GA=kE%)cM5WgK3~^kUpB09@08a^DQP7-|=!E0sP;+>9OYm(xSfsap9 zzJo#9VpGetVM0HG785eea3BYaJfh2f$Mla)I>!El2#f!Q@b)oDgSDM6;1;f`Xd(a_q9&(h z6Z}0hd^LLHa3}#Y4eQGJ?h85MvYxd+yC%1?p+>Nl$nLt~Rbu>4%4G=3t2+sa)cYvooXL?9o9iW zFv#xKDUPV_wIpcy+sQRLIQiPyPuPYFLl3Fy|2^Nk(Cd~S-|?sWiVSvbVSsA%XqWfn zs1^?vv1&-flTK$omFjx(!sA8bu45TkrKB`DGAOi{kLcg9nr^QM{#S_}>;ju%10km= zmx;rxx?PIp=}GHwJ)!g9KS1wuh;L7q?_LEZo&?`m4BiHjN3^VkU}uMpZ8K8Aue)`! zXUWRp!{_&p)93|CN8YZ_@W)$@g&t$*vehi(S+XH7^x)pH zfHM5Tb|V=2a?2YN!C}@20dIMU9Tr2r>DlZ$_P`6Pfwa>MpG?#)V(-Qt496~qd#Z`& zFataL;ghHse7$EIwRQfXp2bE53yWWOWId+c?~J<#5r6M--gQDm3=QH8{SDE8QY`r` z#+XJgdit(m=Bqc~SE9%h4mm{5^?niM4xt!ha-ikQ7BWXnWRBr51bd6X&DA2%Spz-tp{X4))~WdI;0m@@|xrK;`Mc&Ph~GFXxesCRU#Je(0e|!@)K{ z5s6(k33tkj`-j7g8K;LidPjhrw-e;;>xa#PU!FdDUj2?d^I-WowP6 z3)fSCdpU~Fatd=AVZ0%JM)axcN4xOG6%Hjc5FN!&YtS|sbEk74KeV@6`r`3b@?s#B z1uK3nuM;eu+CJz2M8gr?qch-2hEHOm4X;J6Tamr!2E9j- zd6?z82;IASDWBJJivz;S`)}qsJpFiHl>M;C<$hZLfArOt4ecC|z~LyU%<(nC0P0#W zljZO*>`ETTD_T?jqgqr!z1NZV#tU*xE{azU3vO2>92ec_VsB=+wkZ4HxTl+}eSk-^ z8%PxyNUM8kW+?P!j-(%Q<15D=jyAWHMu#i1 zUT(1>?J>RTIJ`>}gz4BBhnGk>F+hz>%vcLdu1!MCc^yu1yotUex@)|ZL%6VKm43|8 zH>!!~AD%@+zhmHavY6xI8$wg$HF3n`3wWV&Yb;1n;b1?2xY?nR%$ zHG6Fqdv&7UW|Nx;WqpTJx6COlbcwfvTN?+T@GJbX2WNNEZxTJ83B#uR?nSjGr{L5k zv3QZCujj8{H5(gpADdNhaDiU7<$$LLcVG2AhB)EZFr=3V5yEC)?^!6_aC4_Nhs#w% zn3p-%|M2zSo?drPRp zLohZ86&{?>jy_tng<%OO7-uQ%vmPk))o1rxpxnW&@=Wp1*?xwXpWgf4Ydu3fo_1$V z$LCqXe%$1P;IaWs_quLKUk@!UVaQ`>q;w6Y;B_K~(?zK}S|N=$xg`RAUp)_?(hofB zsIs#h^bZb&O)&033jcL(@r%QXVe6QL->ySgBw0lJ_e*vFNGDdm*rR-+37bXW)jx8F zDk>LD#(evlK~DREeNeI}cn#HdJTUBD#QyB_vI-B{Z~Z&_+z=h!x5Lk5Jp^-m{QV2)sftsmiC;-d@W+ z8--+lI!`(z+*s<1<6R!o$mQ8h1V-NQ&IzUaSB;)_f>4(^oXdh6yu+L6Lwr7tC=1`p z3s8>q79RC4WJOw>&#@9f#KvB_pRReP_-3g`VveYCoFFb$@`fjUo6R*c)p@Dbvn**bNIpiyPYf@O@LooxcugCPk-A{ z_rHAmb*9w6dYvuI4ykY7-#>lPUfPR~uTP)lj2?Ej-klQSM?#j8p@JhcJf?6IAuh%c zyfjm6Ph(zZzc+c%eN+7exaBn&kr^opbFU-z^;DRoKJiID-wy`|KRhmH`k*814gW`U z!nq@dmtte@j3=#(eG^_Fj@ONWi##mG{_02N5JT(Ud5Xlz!;@oKgo*KqAZ3F=7l%hk z%_&~Wxy(S_(<`RXhJL|5q9Il4IZ3qtJE7>>EMsRl)`MDP&vN8VFFH}^^2*St-JZ$r zhehHk#ScS`U>vWw5uRXXVMQIscd}>P$0G5rW$>HT#lReI2p;(N^WYB|wPsXxXaC`K zx#>IiV#u5Z!n=7J@uU-@7ma%*yCRpTPkIvIkA9~SVb7Imaw}lHyT<|aee2AWhjYBs zwp{CA^Bl)C=mGnNrU`92Rk3(5c<7rp;O~&`gRJ^FKNY>cPswY8wS!N9p^^gjlfG1U z%&UxLRg0XDyyxzB??|Vpghp@Qj}DOW&iHEVnAPpp!NdbBe9?;A+2WC7h9G&=@fup@ zFbpT#w;E3EIp30;MiDIsQ}eEU>*a9$5SicI;(N{_2UqSdYlxN6m7H|0Tk(-18;`MoJw7_xMA$7Jet;Fh6e<2al2XK+p(gg!sGV}?2nVf zIYKtS5nh#yvc`srZ?d)e>&Wppd)cgAg^rw`wKclxfL{KeC!8GnwYsN{)A>y)GX z^swg&-)rvp&FNLMxi*mpt%^Zj4CyQh+J4st2|GYC1m)EmlxBDEbUU&JG*kQ6}{t$Dm#*fhy zL2~&?!{MFu11tFmGhWGufmp@Ab$#JE!w7sA<|sT|>rT-jdIL-6x3T{!96Y9#;>|E~ z1bQ~Jii&M{Q4O3eYGY_-`WqJEO%dxGL!5;ED0!tmwZ#Z}!F^x$qu>1}>jUaOp>{Y- zy`N4fnrb&7;g{gcW!-Uj?G#N!+kLaYPI)QkxaXb(V+4(18l>fytT*^AM_Ek^I~+_S^rGgZS8{|! zf1HdC{BG-eZM;b9(k1!nz*m+^_1KnJBA#+*=(Z1WvD7U5000(4vig#FA=9V?32YtYB9{Cbp7CM`Lv!U~i0g z*|5+9pC0$1Zn-rCJ}6I7MzRTy9_ZB4n7{a&zdU{O?XOP1@#8N}|KP_zneea5Pk#R> zr`fYjzV4aC-xISi>Wka0J_!Br3bno z;e;K<|@DoXurwJC)z|V2NT0DUU}KpF23eAbDCKmLf&j3$;} z%r6|gj3#0T_R5T*Xbhry$Y1>&B2@^)UVUzjW1qVe^N0Yub_VNsCaSw}n%!Jpx6;u>13disdo_ z{8^6ib!DEs`Rw$%%%fM6~=~aCWUvPEjt3#17Hjd_RUOqTL9H(Fv;0DZ9FR z9nNRV59)~p8kPiIH>Ca0Dd8V4e|>s*(c!tB0CX0;)0|QlqQg5m{L2}|f!le}tsanC zP}UCDEQQhRe5bn_by={!TvX4H*->lZdMI^Z4GUN;hl7?eg$BA#%>eE@8eG%HdiEaw z(4Jh1b$#}>sHIg6(g&jdmMH!3qGet{#p}Ok6W-~R4kcYX$$YO^<=(7npS7!L^$DwX zVpcF2REBJVYqF_*hPWB)$}D74uj>5OSS}Jk9(rnhGExWn4uQ*3+6^H-@9-h>AcWX~0x|gBj1IuyhNOm@A9lBAM+2^b(yK<;QQ73+7 zV>&I5l-P)&Ye++n6)5oI9H}$CUeq6Mc*NE#IN5Dy8m7;|!y`R0g36wa=8%bRqP;oY z1h=TSCalHfB>AItJe8ZjkB6U%N^d8U0vbF#i--Hsck+Z+a#C)OL7t2&kiF7zdCfEW z9uey>YUl4etgcKy&^O0eZmvc=+(t#aHXL?9(LS_hZn-+M1W|M^VyqQ{?1v)7mv7#l z{;suWfB84y`x9ic8^Bqmqw}+adm|(Ho;(gBT)V}Te_5KeZyy#xa z4F?W8CwXnnVagT>fney-B$|LuIO|Z}#$S=aS&R4Y-!G@j+Hye3{P0=?f+z6m#uSM% zbAnx&L3Dj}w9`G`8D>N4v$LG=MJo`B9FNH2;KjkS0BDml^A!Wwo+5q&i}hi-p(?%V zjq{w)?Y6H?cvWy)1x-RVk+)>GT$_CC9G6Zi&>Px9aJ^x_eW$wzUgOh2Om({^qTKJ} zK3p(}ajnXnb8TBICw)Qw^e6)y5sU<*Rz3b4gx<8K90$;Sb(Gl~PToE+V9fF3=G}*a zZo#qZ*Ev4h|Mt|%o=tRzJd?%YJPd^!R%bu9Jzlow`z$ANEW*#xZtl98oIg5(s^d3S13I1TOR!WYKT+Xv^N`(m4#^zfG2?s%(*9G zqZD2HSTuRrGfUnVWxj3S^}C#QRRNq-8F@diCw;!{fHh zZD$I_%YYlkKI$-1OIdCgW)9^dSx*sLds}x^gxIIa6m(jQjX@@a%rZakIIl-fgwD9# zdyEX%F(yG4+1PCYcZRZ~&*m(_Ih5$|m^rTS<4m{9LjB5bfoxw1cXB0wz8pi!+FKXL z(FD72@YtKHJcjpCwY03C6sjCfy`;6E9=Uo49`XLN*+u!QtM|F>ho3jw{8gm1EJ{An z5{(ba%NH3o_?`k%EbTGE7;{+)N(_f`YRmn~Ci^*@__$~dVzrTq zR@nU%kmHsE$3xM_tpl&aSaee~z&IC0Wb6SS9mBOYhFjM*v=eDie(4YnkR?uLRa1|U z4r2PAbAaD+2=3FP?kk6HV-#lLnNKkN1gC2ZR@v!DH+HXbT|c(v;cstn_CAUaZ~Al7 zVcDgkBRW0EDYn|;7Wb=081I`45ba%)e=;%=ioUfR9?lLeB*5-xBGbAb#^-69}S0toxAE@cB%c^*#7>(bWgQ| zZ^sfLww$ahr*jeU8J&n6ZQ|HVJ8ap{=|?4l4VK?%62;E$cXis*J@Q9OzKj04S3AQn zUE^zB*rU8gDLp(=c4^+=< zzwde?$a#Lo7#{Pc*r+D3ggG4#00S@?!D%Y9Xd?a?1NU77n^&3weKHHg5dCI+l#wBA z=pEQ-_j`5|*A~Il&{@O6tA?dh$b_-pd`e)O+;?;2_dDQWJcvghQAKWaITT#W_s(h-|%TNDe|a2_NI*jGA&9q1enT&dzHIO|XuW;D#%1_;k&wQrpR7#avR1-v#CSEp0~V>|Fzp*hV@^~|G? zfr*k08wplWc!Td!8~`%5IjipPrENqUPGg^7Xb0VuJthjmx1x-GP7=ND3lb({b!w2+ z4{>}3w^t69+%z*1rz1z3M|pfx506#ol^$$Zn&Iy!+R{1XY*;B^h(`IaO?)}s$&q66 zBk5s`iOn5K^6Hy{k~fhpBFaWA8LOj=p}~nZBPMAQ?Wza=n)H*37814F%VRxSuGeH_ z8WU=}8@!vD@9{PDiZ|4k&I+PLWpiFZnTY4al{?B*dhk7Bc!5qlHECjqi_gm$kC#BD62AVSsg_OQq`M$x=*HS zI(xZ_UUt1}8%~36!sUKL*n2sjhedtQ9)Hm+_2&`R zgPWRprlFS?=ciwP|NZHgFMoY{efd@F{i24Bi4qD%myg-Z2+61(u5nwHdSh_Cf)G{$ z-?E{Wmu^{*;lbV7rbJ^5CmFtG*{~h?x4q%RmU}#Y-2MJXm11a*f&I<6k0ZI{E6X() zdT)_y+A(+uiqykAu)RckDeDxB(H`*7vbB5|GsP%kNqILcm@#Q97?aA4kx?iS$F5~; z!H5f$V~m6pPDOv435_On0XHef~cZf@_n&Z5rpx+P!o zX}7XCDv}vW8I*OH0LI*u`hZO)@uM|(lffdvidc}YXgFA#cir|jM!zL6E(X)%R~-|! zS$|H+xoNV60hLT2?6kp;O_Hz= z9a%&z5UOq%XU@TbT7NcK^aIbsrSHc=a|I4r-nMycKZN{&2dTJM!RX21!Uuk}ajsp? zo?iKGH97}nB16f$5qs7ohNrVVeeAg#hQx)w6@JhJ%!|(+$Jl!fsfDp6c|T^H7gZdO zG$F~GqSfG4IZF(?Tu+=Z4E-2NUY{VYml%zq9ba_ctR>-K3IY>^bz=;7iB(Rk`y7Ob z@nN~ADHy67lCDQD>O7uQXC^O2uk9VlRoz@%Bgk9f1>54~jWgg*Iio)sw-cq(lWMTn z28I&@e%=x%N33mCynY7%8pYJD&xl5(D4i2cF9t`a zk&$KZ{j3cRWQNf1apVR^A6|7*bc8|dlf%+Et!zN5zon{=9n};sD7x3l>St|p{n!Ml&dT|R9`iBO({CM<(c}?v9-RZd z#>=zbj_uMRf8;6+5|CuKcE4Dnq%LVXD8uVm>XU9%4($G8MxV72 z>~$<3I^z$aMtnU)IY7lLeYfWCanBuj@!1z`_;0(`;|`W+U$F7_@LVL(@r)ziZ;YFeQ97w$@2v{Dx$r0h&3*q%&(jqNkYs4l9i1 zn2BUct5~q<>DpO%+V{%s*}oKS%Oubp4#u@f|1cI`NwDG*TD@jU=vmlvdfGLq(Nc?h zEnm5G=KrEk5hbBAP!3$WL&TK8$pOcz3jgges1Er>S*1D7D6I~q^q5!ePfb#mV?COl zJHF*aHmSpbF*2Wc;-!N!v?@2bC0!-WGn-u@yH{i@MRPq52y@0DLYk3Tw}PdV5#L&wrO z-aAAYBk-J)89zB6#)gRK|4&~$dvfqvr;9&coZbupjp0>gcb5NEh9k$8_~YYP4%COvi(r%-6Yu3-k}BeT z49pa>cXI-w*PLRyaIa^sfARFi>8xdJb8LO)UYGy?KmbWZK~(9nR9VE{$n0>+_)kwQ z%wCW+x#Y>>FIXf;^hdA~uF-(QWJULl*f>*;eRCLH57o)#+%U}+yJJmL1dNE76~gob zqPCiOI@{wWNA6oZO#mfs9~E^xYj5j|4gt7)*V2IY>^^<;dAY!zzE`h!%q$Xhh(Hu! z3yp%=G95h$#MU>ZaLCy6Qpz*f@>c5;nEba@|9X7u{}6QE&Y1_TS$tUZ@wlPDnVC_- zUoWjksjbAuxsg9;CIt+cG3$rnS?veCHJ_s$H3dg0#=<6zu#kdd=nRT}-`X-+U+ z*L72t+u6-Aj}_o=^6>i9!~m{-x@b{x&RXqU*|Az69vntLv#D@$j_&JbQssLo@BxJ* z2&CD_j?8-d^6&ax`XD%W%)(1KSgh>Hj2VijJDxgo^UhVvnlgktMXDmLzJw3{haWW4 z2}_HXYwkXj(Z;QjAL8)uD2&&A{XrM{iguS)wm&z{Ku!&HyFS5a2!B?pJx6?ykwJQ=J_7~p&D z$h+-eh&n2r!EKaw1S2rYJ_RGjKhvTCX3fCDf>PdzIo^QNo`|oRCkemzjnM_^!fhI62SPyDSAqw zXC85I)=S^Z*uE{|d=XEc6+QAnmvw~qtx-<4x#B+YoqchH0v+;9u5_p}wioVNZG~Oe z_?PbU%GooWKAa(GyABpO@hN!cj@^>fKY+#ffHTA)T7L1YA*wvz*}e7^w@koL@2=e$ zfxFvcvw2-e1F2+zg!#LpEka|kfV>=f$g~#0tc}IQq>HcOEbBOX`pM=%`s9T#O zN}#c5BV1;nm1Fgsk7E5O%utS4uQ$#1q*rL|-VEcBvc5HPEE#ISWlSvZV zqa{26@!HTmgDQ#^ML6l_tTmJPt{29?o&2N2x{(RPi>UCS*4W_XF2B@llMA~(oYx(oqkU#le>dS zZyf|uq*0caMHWPds~^7H&`MbkBDz+6hS|pfw@bSZ*dg12=?@W$T|EEdea_(B>*!C{ zv?n6exxKH0c)viRVsUXh!Q!>y>mDPJ?!siJTg8!&J5|7iRdWV#?Ar*!51g?w6`~(unY^G}+^}cD{R?zSYM2Z0FkHC?W8qz~_ET;ePeY zuTQUDI(+7s?BFXn?q7Ar=rQ7(%KqGP2q~988-_H-1d+MP9$!^O0OycF-#XBFrpQmqpMCsEt5hM zcJ~dvhQC6!#aVD#u-Shdg%m^|NB$JzV4xumw#%n*p=I_Yu$hU1c-Mvpmgdg5Iz_%f?NVO=zVZ0n7et=J&F|Ybb zq^ROpseky?qGgGM%38#@tx@Pe|N z*8YBRdVKvN*yR{)-z~5FwxwgzPxo?&PaEZZ$l?9Ppa1dcZ+}_-w|eix&pP5a%^x_x zS)70tYoOt5()NROgBuReiDVQ8bWGH@l@#QfN^IB<%Xs&_$hs#be%m<&j|!q5-*-;u z+3820b>MSH{4F9(*8|e`;QGheacr2t9)mkS5dQ=YI`(xY5Ev_Adf3|WxPz*|vr;|U zAYh2eu(&8oWaNkM71BN^8k7jVU-;~(FSezZ`5^faE59K+}DL#ANu9}wl+@lrB z=MYZ`Zux^wx8cB!h$b!L5DdR!)S>VZ7_5VF!>*B4pDFB!pW>wO_1=dHb$!a6Vp%St zw~c`oxz@;mulukh6vGLQ%>$J%FH;;7!1sl*r958iBQV_Ta1JLHR4>KI;6gD)E)s7E zkYmgwusO>ujaY}J_yGr4_Dcs2Q-#A3J-COBn{mUh-nT{wWl@fMhrzf2vEK0weaj=l z5pooT@wX$Qem2=Ltgy(rUDcV&)l3nS35RHS!rq2kUnuPlFW;AZucL_{b}hx!Zbzjp zkxuYVr|uR7f%&N+)T~blPCQ)9-h0s&&ln*;Lpe^g%;xVDrZMT9jSN$-lz#LJr;91pFvr7Q8d zGFRp9-nD{5dPFpHVQ4RM&R}(^*TEtSQTMgg5CmAg<5c$A&Qb0mPBp1@x(FREM&~B# z7xg)1XPp~fL}Oyh|B#u;JNS4d_Z|*sJ9!+A-QQ>`xvrDyqtV;_I-L8B2p%`#ipNHY zi!z&O{`wa$PygG`|Mv7YoBNpVW1(O|TF$b(9R!;`*owFrV$HD0^qoFjdsio)R$GY5 z5rO=rkqW>l_dO60)iD= z#)|n!hSJ*s6UTbYWIe9bUbp6RrmG_F9HWzUE~nrWp92VJGmTG+&dxc;hR+0anL=9Q z=pa$E%AB5=O+&tCkA9;(=#!*QvRkj-CdGn>PcKVBG-Q8&*}CaY0DIP?un8eB6x<4w;2-Rw zCFs`XAy7JMSnqZ0qxM}J2ah|bw%sb8JePIOXhY~;QQCb3nGLF;`@PNVzU<%#J*#J_ z$c@NJdESZVcbuol0B7bQrIC<|S%)L&=gn(TV@|-1jrEXup)H%K9!6P$Vshs}v*AyB zKdwhuw9_>l(-R=DMtVoc7D-YKq-!Jo^9z%7T9cAe6cI-7yU^PUjBuUy9gL?i zwqHr;ZdSD&DCyVQTf6FX54gecxoK()4R*J;=5jobAH1F-YXgSinXnBrIXJ^&B3nch zQ0u!kL1d<`j^* zx`2JBEa9@WMKJJ3XXYF}ozB6Yeq&P#Y(e&|nr-`Jr9{Y?!koh16hCT{bt~hcE&w(z#q#cf{`rB_F z@^ku4_%t6}r>pa7$IyXcAT(O%R{NrOhEBHAs1O>g%FIzQaQupwMhT)ML*0(FNi6c4 zb>IlDoXrcOo%cmKSIsQzE6MOClpBMti^}FaCfD#pP_LEi>me5om(4_yjyt-lHGa(u z;_*dJm#j9!&f!hRbN-mBv`D!+)KJHOm%fc?O|D#@HL@9a)z(?YoBnSJRPFr5&;RQ5 zSAW+U%JgHSLhveQ00w*JhS%s=XJ6oTuh9=%*V+V@{%u7>Ps-##)A~k&O}Yq@l3Q*d z)zER<*BvB|IvJwP83u35e+n3^{(F_`Tzyd|6Cal=p5ZdDx1`X zTV6=5LZ9sK(1Y{$4RJ3b>|+A{n1l2%r^Dl)$|;mv%m7t5ymzPW)bqW0|8l@B27lIB zji&lN5}{3fh3+*|6m$qLWI}+0f*rNVIN>x&g3)CP$DtGv2x{Se zw_eoHW0BV#7c0xzjj$X1DIz>*?i8#x)A zr3PMiKETJEvEOr02^loXaO_MW3s<6R?|z2=QXJXj>VzMj5KEP2Ki4Myy4r=Cjr`l-ZvsF?}_AtwW_%TG}g~ZNYf$%`UolC#U$P1E~MU|K)!= z{q5JkETbYS#z$)&!Bz97^C(adVAu&}MSw}Bv(`i7bpwCJG6PPEZuII;eR<$Wr>pT* z>h5+6--jlx*dGVH{qWI$kx%%(PW4Sksh#I<&hEU(xi%jX`1A6i8;wSej@cOx5Ogpy zC%oZ2UWUlTTl-9atj&5;y7RZ2ND+8-H%1Ano@780x%bTiiy){HCS{2B@xJLnImznu ztQo-Vcdj4i!7plDZ+tvpz8po4YSZg8yl`uXJ_U^U^?Z=T7xGBycJ%DnN(#>2G1n+C)1Z&U?gbOCOe#i+|RSSr|)n zmQQW5aD?1V*u%Hzw-#)Q<(?MQ>fv2Mjs{(iTtGvj8!RWLcRw7}qMSMP{JP&>!TO7Mr1h<0%;=@(? zYA|!hY<1tMWh`JmDI0F5^{^ah+>aj%M_6Ce@zkJfhZ6-wE+|&UzGsp|d<4 zSx7Op{qln-(Rx!(RD{{+>Tt4fsodBn#B2f^+#XNddbCDnvQ!NKh)gd^fOkD8Y!`}E9@Fz1xfubZ58$qYt)1;4nKKA)j-SH}E_sao0BS!tS2glifBTEm zzxks-ufB5|H5$&^9|G0uA5zdH)uzLh8ga&>6#|CDmI2yLcWhjU;~bZCNpM2f<&K}}qpGzCC)dHo9 zc((py>_J2PsBJt)Gk&9~hhbfI3|JmHqzvuurL^~2uPEv2+{G={uJDmlylfgi2WZJk zy~KwWEE9Q|AC^LHE*!7}Iq_n}q4> zacVf{9W3xtz7192B)7?_f)mfTp#3Ilkb~k_!1bO?w7IMF57 zGpelxYzB1)x-!t5uE-Hz;RU*=LTaLJs9##eiU=u(MSn+6@|crahZ?RGbNW1Qd)I-( z4V{;E2(DAsFIukh?rpMa$T6o8tG4gBdODcfNOK)uhAS%4Vb$F3ElPw(@TOODWL*dj zc=&LPo=6NiT~n8hh5K|TZiqma;$if4e$#vUY7u^oaj@ViGj4EZ*&@1wxVCTYb>}%I z^MCh8|Gv|-eznuqIYILc%R{n<mFFFt#bjUR`_ zOy(8m2#uK3x6I5aL&xK61alA6?;;2Ku^`R~<7WkNMzBosD&O_C)d-?q_}E$VvElG% z*Uy>=>O}jwk`NhBvJQL-KG_b{_@@oOn9ykM%lY>$y1a-XcWX$c$4`suB#@)Zab$~1 z){Dl|j8}peLmk$7|HjjcOm+=noY)MK5K_UeS-sAQV4`757fG_fuj~ESQKh4uuG0d} z&3+N27XjTyN--is-RaF;=WIqASd_WXh1%7kGES@6K4O4FX$rY7*m3(-H_Y=g>v6&D zpQNlj()Nva4S0B}hH){WFD#~Oq$5AH_ks7FMH}#~3}cAELkyKoD1>x;)_z~_Hd(N> zdI@Ms5$_ql6vLKaG=%rui$p&hB#uSq$KqOe86bcoiW5a1a*!pq)I#!`B0zo4ymgXt zVdQ&@$3+R7kG&bM2t_n<-tliFTQ3$Q^`k=mFq#j39^@ax>u7*7zT_0Hdh7v3W0W5{ zhx0?vM}69}U5pey9~S7yNai3i{!j5vFJZ}u zjz_wneOS{oxj!o>>=?nz(VK2_gZ?w-;ZU#05$P!WrjQ&Lep&uQwj9k)gsy`)mKSxM z0@Mje8JHOQ(1WOKtGg%DoJqt{Dl3h9Kb|;#OvXdVc z(V;~^+1~d2!wQp?JE~SF9?5xz+c6O+=jnHNpwlLYHLTuEr4WUqNYdwlS6^;kI^AD4 z;qvXvmmSpdxbqOw=?dvsX8G~%;%2!YIq)O&?Yw;7qT1N{u*sEYkDs3Y@lSv2^o7G{ zs>ux>zM&|x!06pOL07!&x(?pK(VX&|mhPD7eO*xYF`>NcywS^YxAMCUw&KrOlQ5NO z{pE++mqu#^=IQhHhTmyd+=DF(qsvBC(U;C0#}=})^nIO;R`C~4;_J&d5t1U@5BOQb z*K5P7elIziLp1w%KLi5j`za*>@3;Pp?9)MWNw`i5mh@G8Uq9u&kcb3!lQ)D)hT3WcjujI^30=7RLwC zZH{7-EyLyD6GyXcYY9E#auiK!CG>fcwpDK5Yq*{_9J=U84bju5P69G9V|X$4hQDPR za$X@ZUTnEYQ3&kK=w3$8nJ6h5j}A+uWqC)l-Qjsl(t8S-Q?zfJ-`=9{9LRM`Gbk@( zrtIjQ1Cd9LUNRK9jov;MB_}ViHwhMMA6f)_asA}<;Q8mJTEIN^hTComPm#=268Lmx zAAJRl0$bes_tz@VxJPoIjaDYcj6j4!$=gttvlIn!Xdlukx*JX(zsu0Nbhn{zvRyZm zemU#U?jSm%bL`i2j$vbl)3C=Bm6aho7;z%iw~qEh&*8Z&|Ms?=B6#PO0e{=(!j)c7 z33?9op&$I=UK?g)fn=0;8j-<1oF3VW-Vgrsuk0dkkp9up2#OEkB2Nem8}d6S^fs_J zFZbb1FKeSMoA}O7bYAKxOD7J*k}ckcJDYg-@?Cb`3XKAn>zv@W@exNnu)TLWTU96V zZ-q&~c7U!UXy<;tq$<>w6^x3idAic#^MO$XmK zAS&P#ZtA5V8^3UDfj=TC(Z-?JoZ$}}-8DjE73}^|#z(N?%|*GWkMBF^H{-QD=WdOkx*=kpl+6&Lnn6s^(H2AMEstwVz_4(9tu95IQPnnkuwZ%*QL2nSnVdhqD#xdX zHnZPLhchSA*xp18h2GGt`t?1N-O5Qd2>%t_@ix@dH$1mz0C+U>lg0+#fo?E1SN zkx|3kYbw!weDnez&r;Yqvh;ArLAh_JkI&&LdRUqy$LGE^dy~sOn8c$TTvP@RPH`mt zJYDAqKiYTw4IePRT`g}J4xAel@CX_qzK4&s4sz)M9llEBR@o4Y()2Oji4x5^JNE0M z=-4I$ZI2&DHgiG&?Ij1iln1#~#!j=9RR*3!2=BhvKy?ce*7tK=;Y8=VZJpv}_Fxnx zh~PX`J8TJY-l9tpzB(LnI03I^&YffL`T!DeO-_C{s_hcNA4BrB-cLT^qFrvuBFCh& zHIh6gt>SqOM_aZ9eiXz6gA?^hNz5=mfAl=Z_~j0vk$W^u))kTY>3$P)VH(w;8T{lJ zvFl9s(7MiI;MAWzdvJQxCy{*<{olO2YBHE(uIzXr9}b7B(|0YVf8H$hcds6t{Ht*D$L@L2dL= zv?X5JGK(9*oUhA|EePqmoiuagj61rh17K5#?r(T}qkJnc2Mwoqvp+bIT45>y9a!`` zoV&$2PO>p_j!aK27w9(yOSE&4qQUs5C)W{-9#!|!nEN6axNC(2&yYI-<->>X5~jy1 zaCTfgrOct%2@%$v918i|Z9%nY9-T{XptUy&@R$lC)hyg16~@$%iy}x+pS5ejR?Lsd z^rFcD9S&tT6qDl;CGI%46B7zoO34W1mkAVQYB7(;S`o1Tpbaf@Bkv7jNIz$tjk^&L=ypEIm|507$6`Xa_Y z@m@H}8F3<8c@Ygk3b**!7c}!*hJXChaPG@e=(S_i7L??bj;umI%;=&$N6a3qiR@4H zD@)i_{zUgENagSopOPRFdIw*lH=~+E9vmj;TothUeni{5P^DX^*ewLaP-<{tE~2fP zJL#&6I|++pNze4xVD~kD;Ax!Q1aT`uu*g z#L$;7;1J}uxFOGnj=aq{E*C>VE66!e!sx5#6QN{U%|sn$ALA32*>2A8$j_vJF_n-o zNo00g)}^g2N+BugwkPE*w(vQ!2<3JgpPmw4jEizmfN20F8f=uJBs>W`=I?iK*BPu` zsyOrP*5MeY(J_ot(v&^9&e9!L*HKs$^{~K_s?Q<6i zHd)rWiB3J)_QNV1OKmu0e9$git7Sq=>B$#43vS82a`JTg5b}zP(~~I_oV&Pahm)v% zPT+0(XYG(7s5@=(a-@|G3qIIn&nx87vvf^ttKowW;bF&=-*Sg`Nl4EqGDEDcgFy~= z*wxDG&@Fk{(xdROKUk!7b{_5V8=3cu%&!~rt?3weJbkiq$$xqffplO|X}GO}s-CsG zi?BEJ8v~*#TZmoOzE0t7tBiO4;m@&Nl#>W=d^OXi*F|%@G~27bvS;+qJ)k*Q$X0F( zx3fhv`((VWwd+`~(xmX?^!dT-@Jp?pbzse_`0{J9ezQy$*+H z_UA(lUA4XKLp{Ca2bVQ)zljl^6gwRFxHWPqx!yfhtQXajM;#+Gl+YNcAy3xA&IyQv z=w;$25o{PoxtnW>5fmEvsR_pOLn0tOX2WT1Nw%9Pf(MVko+Jlgf#+4il@9o!M_3d% zi~|%BaDz+l>5i7dZ!cCtp=)2Sv17V$9xge~8pERSxmCPdK-Ke=*p_?iH^VpFNHNor zZe=_?W^i;E9+Li1Uh%SridBzkL&P!^(a2a&*ohdmNA96z+4iep~hDcnSJ zo7JrB(D0|GjQsAm311xxy&V9&^Dwm)F5_TKxLTVjUf=P5!#s{CILjxBN{FmK)}8^s z4#ziqDV#W#&xpbpCb~16?!9^aeLCbY)ymbPrC^6Q%3)RiSSAs#{EPQ?R*_w(@1!P< z=5;wE+Yj$${I<(Emyr?P4oy7+hpAn2h9Y)OO*GfU5h5%t*^-|e$EQ|NSkt*2Xb#b{ z&zEw@_?BDNk<|JgGXZ7|jZg8*_iYUo@zE8!-d(}JPyCwPAy4(mt#Zmr|8U;&>_=(@ z&Bseege~xh^aBsScU$Y6b@ar8b@%J=j6Mo1=xaPO!mw9*at#k1j0jd*hSMRFb(VTj zbPYD$3N~i;+QGwp-Ez9>&Lh^kCr|$S|M;h;Kl-yjIsM|hI=MPD|KudeSWYuYL=}GV zq@QH_pg_n;IbU^N>p%RRAD@2w)rgCRceV0@IwkYv!DH}F?+34;=YaPF9?(I!^bg4Nh9c{yx zjX+p>X6Y~VX2UxAMpDPbOU6qle)?2Ii2Qw&BE+1-c<6nMiGiRUgwC_@!t z+Mjv8^Vtp|Gb&Cwoef4rj^LOZjLl+(&xQ{v#-dR=-HV&&co_?tMn%SD(!~DTK_3Tm zkaPG|!iE&jP-v4ne5ds>a(x;O54oK6+%ul8t?NTy6la|8-+NqvW4EeYwGFfCVW(IG z1ynag$P3rd(~$V=s?%fcH5}Y|j7P5*%PO053?J(sMS=FB!qafiY_}yi%X#MXyS%co z><6A7ib?Uh>3*N%w|q=Lw(hN)2mfzm1+{oK=Qgj9_slp&G|t?6{pRK5r)Q?qTYL;g z@Rz>|_J+(kUb!j#Az7hY;Q!!RTf7qJMKtL|mOso8M4{Y^b3q@rSJqmcyOp^tSGzsY z?J%+L)*&4s*fmN~?)&UTA7;0^W|A$vJMx+w*b$*@=q56)lY$B33P|6iH5hdKi&mqi z@u<7BXl2t0x{DRyPX0M0oz~b|8|sQ6+1zSQA4@x^gfSdl|H$(Aoeo5g*=~58w-ZdK zQ*@AygmbXu=eu}tRvXbwFE@J98KfIf zqPzktWa0HDknkvK*=tP>hw#z5Ey}LZr475oSpahzx$g+WCTpLR^E4W{-+2vRTs&*` z`ttPZ!)HBWrKgbYFyH)DY9P4~plC{lL>ddxZiud3cpUWC4kV+aQ;j-BCk1L3T;rXDCol#IQKiH^_TvKO3d>fG>rj|RBz zyLJ{eb}MAoQ-tgxzIq5s7(?Xnh!DdHJ7W-LQl1=8TM^~r7Ci(5E?#q5oXK8T04}EA ze5VMm!~hZEmJM}FLs_y%=C32@qN92vIvRF)O{tdOP-_pv#%7HfhEAwQi9v*n9mUhhkqcrvcWdo||&!IK1S!{xjL5#oM5D z!^7Iw866!(1oWFB8{RH(pa(f4ujx7Go*uHLIYPY+=N4GQe%BeE%VY5LR(tRYBAHFM z2C}+UP;}*zL!H`3d&twpN9_l;7IRwee*gB;Rx}=uROJxSd!)IfW|nJ>#$lZp7VTEi z$jcB_XRwU{>Dw(G%`S`&fAN=pe){MC;$LnD&;5=9dz#vH=uUOB&z$bZ(r|BkvS%WX z*B)^ao^6f%;_0)~Prm%@^t(U#^7QO!>sE8t6iA0~tlLb>q-Jzl0+2a&fXSnekcH-b4jkdRG(lgO!lNR{hvE0^;nnF!b&9{)uDMrlAD4UV@QF@qvhMn!4)KO% z^nEf;Hk+mH2EHD_8L{UWtu{BA@?|~raAfOl4yI%L5knt>uqc#c!>m?|CIm;a z=NJj0x66j^5X9H*j95qqmu_-`hBus|;f&#aLqbt9!&;r{8v0(mv!1U9M+M)F+|+Sj zl&R-O!`jeZb<+^EZ~t#4bs!s_%U7km7nbiJJ>E~LQnWi=;M7V$Cmzx1-*5}REj3CPkAqN8r>}qh7pFh^lYe`9_2yTnKlq2g754OoOQ~!$y?gy_ zMo(Wq3z;XulQ)d_<8_pnC(EIK>YUD7<)1!~WA)|wHas5DX1ph^P`x$OZq&JxQg@Kh z*JRXh6u{#cIk*%Z6_7t$S&{VkJm6gmRPAnLk`quKz1PW#8}p)42jtpHujLhYutqxh zsEISX&_sWev+^RxY(O6Rb|TWJYdR4D1O1%CUJC?zIB`Fj&$cC^CS0e9(JNY(VtD!e z+tZ)_`QLQ!tcdS7;&J$rEkv_v*U8<^lm*s7aB}y(<`iB$d3yTopM6>SM@k6om{)u8 zQMcrm3A9CnI_t{NsUy0klX0aw#59@pg*9T;`09TDsI|Q6R&2!AvkuR;yXNlG9=P|e z-BsE0n-1Uo*re6-(n?!yR9(^VVBvFM4b{Qb#+)Pk9;8e=1UAB7Jaxoa3C&Kjc^JbV zlst8^uxMbz#VJ)xOu2UuYR(Qpm_J9BGc+^%sXf0sIGoGw?dp7dLxjhKMuc z8k5~Gv^LW@wyTmMId76ML|1dV)mzkf18uX69b#Kwb2R4Zb}6C-r&lF@<;WPG`}S^c zyq_n1+1rSNByN%^$OY?%+f zhBLll{DuhqUj8T9sfKze>v$+&Uwd_<@3*f-j%k~++D#>5TMFY^-xqPNR%1aLXY2}TpMN?xobzN5t7q@45`Nlcyt#gcFFwVAN|qk|NGDX#p%y}{LR}Q>r z{o+OYrUj41NJpE)P-6(}f1Mb9kO!WUALHaKL||K~!JuyNq;2$wSwNcpqrl1CF zJiKM^wy=c(XM7Z>o;}$DjulrvKBn_F2v|9A(UwUQ7+z<_w5_=V!lHkn>?9 z$vKb8hz^e^=WzBOSwQ#Rwh()IQ-Jg!-H(Cs77s*b^tq-2!rbpYt-o$xP9bq;K~u?QFezTCs6*+|`!zT=<0M#@UBUm_|5l`{4SX~t1M;b2rEzFR>=ZcOQ>svwP z=oMVlobqvgDcxm>eS1AGGCqr=ZG)ROiJt3N?sbC9WuFcO{n%0OpROHjKrAVZb~*lf zX$ttM$u18b8(r~O@Ii=PalI}qgXcV>?W`qK*EwVllVZfTuKtHTxZ$!q*im`H9-sto z|LA3P(0Hgwjy9O|ZYP;kX1Tt~+~(oreAXGJ+|nyY?(#4O%Ol`m-KwEk~+RfBV0jzIgIw zGmht{*Xfp%owa8Hq9@x)>d>Kk4_ZXt&I;SloDT2gZ^P^vGrc04bfu}p>2HXIb9m(t zMC&^onX)8X_eAT{CAh~QQt)0rk`oH1ldX4Z&G1RCOTQE?c5kz`%^orrFCggQqUb1> zhu&i-=z#nETSQ-k_~v!aJQ+U!>}dun>gq8d_*`TIju~z<)91E8c3o$=hm9czW5XZB zp)AT&=|I2l?2~iq0{Sbb)4eYD{idVD{_Vf{cc*Wg@Ott27pEuX2jTUw-1dh3eHRej zZ31I3`yHXDr))04f4mCMXt$P;t#N2O!~+xoM#brfMd~-maU91e2XhwT!sxhtO!mfV zdNuqH{{LVY6&nQsW)~dl9A+JdPKGG&mx8#TFVf5FAGCZl(53tH0*5TC>x_ZPH<_Hj zXl3~GCnCAR;8v$UZ4u>pE6s(M$4nM(XNW1vhNm@hIIw<)Fvq$4VBht&1jXwbVngv8YmiN?D?Q@hxYP|_*!_96uQN_P*BrqN-hU(n_45vkIzNd_m__(?U z`Sk4~<9ok7zh*b9AC6}?Fhn8tiQf6B3y4HBpurB(_|=_+$l)?Z3Ll^2m-}qe~f?@RCRHuxXL-zKD63 z*5=mG#iij#N&6m>_+5#WUtdfgC~@zBiEsNFzmEmo^u;jmCb|JTn8U43=@0+izdQYZ z{+mBMeV-WYu=+24|94Km+mP%|9g64rkjcyB6vgo+3X2>W>gC0+PM1aGSMNJ<{&}aY zc;-+}agQB+*yDWLgmBfFjCAX|?TFV7me9?|qVrZ;I8Da_cqtrdnX~kF`BOwpzrr_r zC~O(s*Eu|Te@se6KABgSQKd2C4_>2PBce+@6>&3M@}>E7WkcTX&t=Qercc(jK5c2q zqfV|eY`vY#lQ*_J#&dx-+E|3ePKyj{Pr$f8;kL(3&;r+M?Qn#?M^iz`iG5}-Ic7o_Jc$xlwq^GgH;@MoCzQ4oc*IC+U+mDl6H4J-lx?1gi%x(4%rBV z3|zCVRcAihlkM2RIhufB|5g5Eg^5=35$AHn{S!fYszDQC1Zh?K!<4ZZ>UslSom40M zRn$<8$l>%4ew&#dfgv7ob;gx+zq0G88BNP8>WvMZ&pEUvcXg&VKB*c zv$8)hFry~`B$uaIq8%7~@1J4W{f0Sz`RD)P^#A_<{mauYUVrN0Yma;CbSfQA;vEf9h{RgKo^{2d$O{uLv)C~ z^kN-*V3Ixg0oWj9baLL9y1a9A1HK_;IYtMt7;RZ;VE9a59z1(de(rg&gB6wkaxlb?To`p^Hs?04{U5}RBCIk=n(ec1l&Zp)dPytT|q+Z=a)Ly^v;TV0&)&bGQV zI0rw+Ga+5?Bi9?i{twa|n)0hL*wl&MSs}x=v`2fnCH!`f$EW1TId0On>jE2mxqUG= z2DI&LJ6CxK7?XL?vE1)3kiRoyOuF_tgxNtvZ;0LN6BG$)mFD(;#{zACsG_SWq9+cyjvBf zaED=8JlE!=HlS`I9Lp#iY=I-Voi;4Mvq6kBWFkZptLGJ9eBDv84Ey z9A>TEo;|tVlQGuDa-kmLmGHORBU~(+{|HynG_W^DBl=kCqui!wVhi#s&*(M^fNlZ^fw+5m^`tFWY-Z|C30L;p-lh1w(wm~fd|pxP=O`7 z(`J(g*Y(n#<@2i3X#VT}?hj9|(u-$bJ?(UwH>cnI=@+Lz_^qE*HDKxA97+7)L`CQJ zXur!rn6IY$nq2Cp34p7+-`DxPIz4L<{*wn^Rn}=PO#nK2?9&&g4?W59^20abYzIp+ zjCsp7HKS@|gbiCQkR10)rq?mF zIqlWwZ^(REsz`8RNPoWvQr$g&7Gz74*h+d`XGym`rdZmE^&HEeC|&?71KxI{zz0L< zA)O5p$NRDT{Kcntr+2OSyY3Y13wzjekZ;>M_w9FYPJi}`uTOvWH@{qz`qQs|`}En< zFXIhQLcRxVOqTc70j6r>xpY`x_hWf^>4@%(=E}`ByRs;=`qO2~>brUrtJeY?UJl@O zuC=kC2L@{x)hmaIJDn@~cCGuM=)Ax@qUWRRRy=eu3c6<`)hj2+!PD2naH`Yqt*l5k z{6V_f+N64NGs=S=Qj6lMG~y#puWqQZ$5+C!G8A0Yh5>>Vy@m$&B(msUt|MdQ6b;ci z+$D>7ee}m13byTUMV4l5KNSU>l{+h2kx+#19%Pge7d_ZXah%3nj}U+{fedhjgT~Bh zZg|81y*p+Yp6A&|xS~f6rt9wGOE?<>8!lp^i57V%YvVG9AYe3Fb07_!+Ys3m`M#Pq z1g_o9sNM*>j8%#Vb~R%l{!CmPvtFhS;3|i;d{j;gtSCKbqR7>?i^A+5yNdnsrp8hd z#!+)~#umWB$#YXIjXJg!Dr3EXIyE0ZmKHb&53@tY(i_Jx_~6ySHgZ=GwmIXD38%OEC-;13s|n5Qn~=xgzK7k3|z%{`Ft~{PguV z-=2Q|H^1ybe=X&@^Yrw8{ez!1eCBvOLp%8(2u|S;sME6?qo@^|KgL`9mJ_vY&N@cU z?bUaGeY$-3w&?4#qQ}pZeVtlqn1@fAWqp+4*Eu-G?sj-}82cf6^4BqrmPHfMpPtB3 z=hA{zd$qIu$Dww$VOo6P$co~u`<>i_F=r5Gp}!?4j8DgyQx?Gmth`;iUi#p=x*t80 z`E8FNZ8GEQ?@iV{Y)RddPP2b{`uyo~xUnHCy_MWg_s|KyftO|LDZS%lk&#=?I3 z_^u^lAKPtpS;uiV=Pgb0p>1>@YU83*)vI^kr4t{TP5#O0w|}F@lzE8|hxyX*8dDpzTF>znUWoxZOF>9#{VIH{GU?=gNk*#Nl1xab9N`%J!8q!^8? zZuXHGK}Bzv#`zNb7<`yrF!~O{aHNCk*UlXI?e~fu?I;f$a7+%@IXYFxUPiOi99z0u z1e8mdPFO%HN08)Wy89anLogWf12g=3bVFuv_fVS*I4t*DsBcg8W}?Cx6E=GkRn||* zTTP);(mYH)E&=3p?Hpm44i?=^QNtx5DQDt{yCE8b;k(efOO&Hu{LwxkunC@dm|cu$ zj+vlyh$%lOz>#7Qal>W3i#npi*v@f9Vc^uwk=-~5J!GJ2C2?X;Oi^~N|0lfG!;bI4 zJQPj6;4>Nql@MKLIC#kT_HeRjGpWO$>O?H4Hj{RggKsxl%lRpVw}udwJ2DQxRnp`4 zv5roZLfMB%*OJkpdf`xe+fy6FVivW7Yg-5XxLq0L zx;=^@M`c6(rVFC)?PDh2 z>Pxj4`D}84n+)9O?|kGN44^ZuBVdfOx&-82jPH(&qi^v8eoH>Yocaar(k)`;ti z&tIJW%YXb&PQUs2SEsLXlJ}#B4b>%VANb1_H?uEg>7CskVNz$DZX{pnCu@=~vf1}V zswV$FbTZKQ-~Zj|^XGR@|M~B=_AnM-qca<%BXpBvS(p$%5Ne_PZum{Fyy#~19}3!K zIzuhS$23E|{<+pVzBymG9MRiReszJ=vxV;uu==_vDW7rZmG->pFPl&1)iH-a4i5lQ zK&`*R3EeD@e^|T9(Q!8H8d-G>eMVKvbFSol{=Aty>y93FM7!^)aKckL#A@t1!)5%Q z66b{FX)c5ismO4P0f>lodhX+w5Zo>LUo|A0|tCOk5mA@>=Eaxd^+Vmi5}xKRVV1Cn9-lY7ei~0MFCI1-_Fzu(aOwwwIvPi|T@@`|HhjMN zR1WRDwY1Sj8J;|Oo?*y;=S*^{PK>fiVWcJ>us*dWSBm4hEsocBTS#s#Y7yU#zDj0G z8^lYP>C~{pE*<9!Skv3DzSlW*gC09~lq~0XMRjz&`+*q$>C;!AJvsf;2fuy#o7NQm z>EC^G`pd6>ar(1A`-{{6_&@*K(@%c(MUmZ)PoKZ|QFxx8UUitlpZ@7zu&UGl;dg%b z^k4jg-)(L3(>fU=m_xH?2Xz+OyeOwxGGgt1^Xp%vEmx=i(?9y1({Fxd!X>6AT=JRD z(CKVqHca<=>p1K-^9YE3^%;q1Q|CtE=^i<<&pvt`dTYCQl@B<5-CRaxjqXIMTX$Mn za{!`LIp2_)KHVS%*c`QhMxz|QWo1YDh6~RZsR7sSe>4IOruDNr4D|IaJys^?T$xQO zcg?8rT<@}(E=s*<>X>3bauxv9U_G8OJ;fG%;DMpuwoXNHcWVD|Mw^XIF)Z#BMVb_l zPqM7zc&QKsy-P^Xv?&(WQIw3PsobdUGe!AiVu6ztN~jZ2+MgQ>Bo-`c$q+Z>4iZD) z>^M=;oxX1C(h`OviSCL9#xp~-44?EWrDT*wTTTU|urAt{YvJ5evPG7LK^Q)52rYgA zALl?ZJH_IVkOW(8^#{M!%XH)R9K{K)>pQuq(k7M+z0^2pB@erin(&do3f3_^)nmgg zWlDKIQO#-!%@!~_Ub0?>ZqW6dK|NQ!O!A935$CD=lba;D+dSp1Y`OydU<74+shL~lP z>*l{5fksyEnwkE~zy9A&Uw`}c>B}#Fqa(3?k|QY}nZvr8b3<(f>cLTle zUAy1+%c`LsAE3Ld&dHM*8Cym~MrIkGCIwM-F;d#fv1R8K$g|;9Bz>wu!#Ax=+Z5L1HdB3ji{O5cAjtj;|sNu+A2KxY_8k9BO;w)7?HEs8CO8HLZ= z(GU(W^<9bT8qXO0P*J?!YDMkgtD3lHYe0Hrj<-MZt#Y_5Sam!Zbha30|th^J2 zq2yrFTEamjaU5vp$&aBqO0i3#t(NdDyT#wTV?35{bRO_z-7By=v#l^?u#|{_3FSxY zGWO8=1_-`9M7+68N=^YU={HI^#|s2fW9I1~&v_q7af6YZXb2AFN$GZR@v;2!hzWKI%_1Uws#~{>&ALKoGUyjuis7c1PPBSFTmy|uL1wuIn^bQBa zXRyV+pQ4Z_cbrT}c7#v>;1Wg49#o1@EtT$P+Wo>gN;gK#4&i(O)mfTKL1Kmq+!{9ul~^WYtJH*DSX3z4Z3l&&0Qy`&{Omr2 zo`+)GCrX*R9qEEk>Tn&XDKiRn6eEr2043sDGk3j-6@ufz`yO^QWwtr9XKEvBl1XI@ z>UCfX)ACof!Z+$PwTCkH*g`5aT{!S7KC(|DE}9C$d_`koCdHdDPQkpw!?cwu`8-3Z zB~?O2jHMFm6*$#CP80+N6g!9t6UTa|X$Q3mmw?J_s(?A96wk6V@TrAbV>rS}9l~un z=^o_|{`~C4qohA+5uo6mxSMc*)eV-(EvYp3D1^j1p^2dZt!pRtoOGah>az+eVKAxu zR`Dw1i_ejY1uLx$UOfATm%%5%s%f>roc-4$P$*EMe27Bw4C{tGgwRvCyg7JV{`UX= zjakYo%>-5MGGGvkr3EFT{!#d@IOuRt9#Bd!9=!>ap z`7u3>pC-AxSvm(F$z>|nk#TGXOL$G*vJA^x7_^)@$7&32s_b?1%zaDQZVLssyTLl- zjith3AMU|VRD%N(c#I>ugO#+o1m9ESZj~_E_9_!kapeh74Ne(g&gh5R-El56Yxh@I zQS%#apN9-7#}GnGD}z5!i%I|#dfM?zn8TYAn>L{Vr4vV$_vvd##j0ZGe!%lJe&Pkm zv)02;^a&`He`76LdVyk}s|%zd?>GP%iOb*_#i#XGc`rN(ShisWb&`*|=55d&>Vl`J zC*e2wme0zn^`(EIk>%X5gJpXP1UTfGfC@Y>g+NxKa|zKQmWWAwfqFE5HCF>9fIHBs zObqHvSj}k#R3=l)3%J0k6k;XJ1<9pibv-a;O%;Oi6$JsB3Ly2E`{$wIndw5QWkiPg zbwirCViO1;lbJ4|)lyerh)5Wa_rrvCOoTBqm{O6+AY*TgL~uUF1w@3vE>)W}>Q+}t z(eQoxZ(K+L$Uc9jdE_7_lq|JM>?!w>sswYUTo6i&!Yy-ve9;eGqr5Vqfi-AerukHW z4;a7#?&A_LNOjn1tlwk1F=rsL#^a{ajqEy1+{L7DcNx-ZO}q_7GrB`iov?sbDj;!0 z%>c=F_o|!FUVjY*0_3!jEv&T+#UJfQsp`ToV98vSfn{2Ff|`s!8b)#Bjktf%H;K-M zDCnJWHHWDoFgVdL!9twPh#NB?g?VF@ijnXvih)lmaqufYX}QYmW;0|HR^T><7XG16 z#`%W0${pg2?5hm^5LN~QdBPmkC`jtAm#xqib*W5kg93)G%jG45B*#T$Aca4*W{z2Z zeRA>->^=CVY?BB1AHVxm`Tp7WWf>(L9+uctA&+O`4MieNv*ws*Ndwb`rgxy88*b{% z^v-ar3;@Le!mZ1oHV-M|Z}`jpI^klR(-)(lAX$|qC9Nt3`Kv)%U8lQUezn3u^T#`7 zY4xQ1ku;lq4yod0$qp|zn1vpfH*e3%^R2t`%^r0d_a(W#FgxA!Xjvz-;Qt;LuHlCc zDNYS`Lgz%D@T4117}jwaq9K9sKYtft9t!ap{-9NLLN5MDC*=XGfLr*98bMao4JtV0 z&>y@-`z=0I9%z=xsj_ChmPswt9X?by;aT#ey3O#ItB68BWh)4`EU)rrU58Cb&!vg# znt8JtEI!RysPD{e+70s$!!e{#?Gx^zO z0!Y5mmV8bDh0KoXUd27XN4u)Dhex zP-C=hlne?WsX4+R^ZFO~tKiu#F9C1%c|*9e(gZXIvA`;%Dq`VsIhMYUBL}Jn!V%4N#&8RKyLe6!DTbWhCC=3-~|)f*jQZ7!qIy352u8G#qI~RJ8=%2TnA*x~GF> z&}WOOKGsOf8D^{;``#tSmY&g`t_vd0SCm9tIv__ej0lT(_)|tqU~%hxe_ZYu*DY~d zRS<2n=DVH}$h)ITe+lcz??4oVN=ZLCxX=UI^P6M@eh3}cUn>gy8wzo@b6~m&6ADHL zGrLPa4bq!~=iq0)oq`p&L`)A*f9<)Y(tAfs80b6LnByJ|5`CMIUI35|I!q8Kzvjgtj?4eF-YKWNj z1vV+e1OB2En2Fa&@TAPhji8Eo! z`|yoz9g_m}@#?VLO@N(@&++{k?7~bmd?Vi~1Iuh9mf3Lz`fQeha&GE!tEAmHnf47; zIlih#m=CFg0=RV07cTzWn_N+RkW^e~C}prqUn_r$FH0wEriIGekqrl7DzWd;GxbY8 zD=$&Fc}|-SOg;x+^Px3$^{5oko`8|YfSqD!R2Xms;$yKvNcvab2?Ai++G-lms&KpWwmy8V2|@@o zJOR>WAC<_I!_mhqOUkxnjJi3PNyLJHy2Mi&JoARt;(Rx6da_r+^OSg@58t-pFm*#@I3HPi&t zBvU|Z5u_@BmEi%$=dmKitG$&5w58TUQF-JBjp3HFzMuL``1X?53Q5c=R0kBT#0KuH z0~^0U2lX-&eR_US{^gH9lv}K(oOMpkzC)2HNkcwb=RdxB#~e?SB=L56PSW%8602yy zO9L+@76x#t;Gzs@!75@=qdoMllp#f7X=wv~gm+cI^vBdI-8xy&AvGZjl|Jv-OZys+ z4F!<+To{RpXiu{0VTlrF`aOf)4TwJ;9F~idGxRXLKsg5%2i{K?*X8xc{qn^MLm`{PTB(CMjNuK^hZx74N>oZLW%&gc5)w zv{29RwEjb`5m0rN0A)f)9i|KfHv7SS6(jI7*(>u|sS1q-`SCz;2_U>lq*fye2#8KO zUUqvQm7|^XS*$0k3Wy?`&yEPR1F1B?XcTOM47I)1f+PKmjC^P8H0{Up)7EO7NSj9T zXuaspqC7n08#o>D9f3{xaTQ7kEEv@3KVisktqx;Ae0P>mgx{;kF}G|Rg0S6lw&wmTSACyTh>?Y$(xUaOo3^&G{WD#wLSTlYTb3Tl}yk8Oj(e0JrdK(T>W${P>a> z;|csr8W977M{mX=4gb??`lvM|PF}n{WL4ODS^Mj4+*Xc%jRghosATDT;-$czi|@L0 zF^K7Vl?iv6St%o2x<%dZ|kDX(9?D;Fp)J)bTEbQgtwL8{N&{o}Ip1lOCUnr?KVg&hkBnM3x% z*JKD7$8<}7<~3;=oVs(Ah6Q+2LE1Ms7mo@>iahE~>D5KPxHmP{odFdbf=f$VxjvI+ z?4>{I6el;Ub;up-4-UYB`bXt4zd{#j2Ln1D@R&XW6Mr?#Qn$3SJ(Ugj?V6VywQ$RM z3s8hbZl`@{tGDC8?IrJ)$V~AXxEJXwgGkApD+u;52_-lKU;dBMnz<-D{GW>?F_0TRHh-BF5v2sj7Gl8 z=;4_tx@@9Km`&f9aWsKUk%0rKjR#eiTtZ1H3BzZoCBrJ{nKgq#*l8e~wYyjiODxJ? zT3sb46D6Ut(podV$Y@M+VTi2x6-y$GHpQ3!=LPu87vrqn<1eoTudxtOW@*=ZkTr~9 zIhfO$+RU~(p;US*G?WPN(QEnzgJCACfFjJcO#`}Ex-p>(?z+Mew+iKwb+ae}g=ENt zO8yeOlT{l`SaeZ@^~@TnV&7D=e1Z*3MTv`MH^7;h4Bi9DJY>+-bxz0Z1253a4wj$T zbmmuuqC0K+j4L=sDt8;fBDeikhq$aec59`Ah#bEQZC&AX!%ozH`!~NWC#UQW@L-*H zEsr(CVNaq~MXoih2#L4z)9doXkFUzFzx^d0sc!8!0|I!YO!^cEfmDqsqPZ04nsZ8* zgMSnY@YzQ_6Uzy*JRL6wdbr%w&$1K`_%9y(s|8#u_K`xoSefD$b}Q?h^2L*l^61e@ zxjwu_!8rj$dlbIMbJ;(*E}!?=0C=52vxnD2^U}|)|C_N2rMLA%Zw840R<{Z8ZPw$X zVxc)o54v=LLjpE2oSZ~zv0Ijvjp(-ns`wJVI>>+a~WN@Xfq1{RcD@S@AAk||Iy6BhsJ_=uDObJ+;i+0k6 zW$dT2#QT!n!B@UZ8V8n@){l%4rgii~t;*0^F>zh_EWv_tD}wk|Cqhicv4a^d{me(L zr+^9oQ6KxFD$n|h=Yji(oLw8wa=rI5u7Bz){&emo^hghF!I?f1c1vcA$ zMnRII5v5KS{cD`TE2e$mQs_YvM0OI#7>B%&5zm+m!Y~pB+veSjK){PHhP0>{!9gnY zm+qrhE+19+^iy|IfprOHSP^`GNtE+ouef@JLQgFpglIO~(IClXv*=1Y6lYFr# z1A()gXrga-b2Iii@^-5+xI<(!%MOz zG~&#JE*&__ay4U7w5d{wBIP}EC*91{Wt$8wC_wrtZB_P}nXPfO)QlvIQJ!!+u!0|O zX>ZIg^&JgRd7`>hfiR!tLuset7J6PtIrBB0W$$S9@XS;(Et-aLV>;31C|{RJs>CZ3 zV#!k8vy32N`RVm5R+D@vL$V*t`O5&w`yDn=aXr13zE-CzSUfhsK~VhP80KMMwZBPC z0r4t;8e7*eph@u<<)?Ua@jOb8FQFlSqtJM&JlW^kZ~7;2)M=t}x(jZ#s#F#l8Bqc} zm{YpePosKMDw+pajbA-`T;89Y5E_&&IP>6KQeQyQ{j*`&V!&Bqn?ZmPI^Kg9L@m4= zV*q8j;502&Pgm5vH%==LIQXb=GDieXMj43EvKid46~p3#u)|5?h_WRzE#X*P?|n9eT8 z!fJ4q*0ZdeEfSoWO&dbow(goU+qMA#>(=RzR$-@|FtkYH-K^Utp4Vh%*tO8cA64Xz zgkeS*j?vN??3#P&Kp`QVoN-S;6tyECrCe%j){$vbOmU7)=!c zCqG~Gm-ceOAU+Z7>R!SupvIhigiHL*KsLQaVNlBVuR z?bvvD{;u4NpOj^iyA3r_X>bzDUcAz724UU_{zSHLg0~Ub069||C6gtm6jAXQrZHto z0vHwwZR7z_&3$9cH5O=}kwM-QPoY=BMAlCqD7U)2Q=aTSFPm%c$_eM+SXW5Pdp9WA z7suBe8Ns$}Yn`%=#{eL_D{fU})M3A=-w;f47V+2`=4BEC*FaIVaMBKciG2f`mi~lg zgR-TH5A20bIdX(jb|T83a+#2fQ376RBQeCL6JJ!E^3)N1CeAgCC?5&tvkK9Eq$G8cqlz^|y)tNi z!4$@;WHObW%D@gL6~zj)z957N!_-D)mJ@i1XnPVLxESqJHhmb}nMupqsEw&SX0c4D zFGv-Ml%wvX(ok~9&L1t~fdD4BkdnFq-g;0(GMF0-n2*KOBJE*G!n8%EB@Ls^R_1oz zO;%@2>@+B^t4u45AizxgsnKE!DzN4*Bc{r@9lwC8L|U$=btYOW$*DfEe^@^$i(93r zYE@@8oOeb+v?bXAy}wnq_r5I4tfnw8Qo{2gc(p|EA<^mw9|f8bVRD6t!a5n_65~=s zUckVzqA%`NBXBun3pm4EJU3@Gal|N8umhigcpooag+E^8) zaLl_b+!}u3q4rYPc#L&#jNAB_NBo-fon4u*N<5p`0FnvaPL@URiFp>bi90;IE-zlc zFCDfno1$2{9w1MP!5diYss!n1HcXJV@SB?gj~E31&&xlQQTML=;^~*=^VMhM2X65y z+@#i|fAYM1Qe(={kQ?3`ptvtinG|ukCb+PehsG%%DmBJZcu}_xJZeD!Y6ghVvG!L5 zsel7fl#J{sm+06>n5lFbgW&n&M`eelvu7Mx3+bqjQoJ`JG5Ulye>xqo_v)~Gz2gcQ z2S?O}$@#z$xu75JT9km#hcu=u-`JQEs7#BI#fk`PLTU>#S z_NyX8dE#l9BZw{MLj;ABz`?sQguqPShrwgYVw2aedz58MpYc zKh(uDU1B#qse3cPue#W@5nV@ljSxjfy5(>&h{5aJ10NKp(+GVL=BEa&DSF>=P7)T2s5(E|5QD`VUh8pX#1(j2hsJS=7 zZ8mRB${O!Nq(X==4Gbz1HrQN%9?}5Kk>+4K4K^o`d2&dRJ07#CK#mYl0W;!htt737 zncEIZtAUGa{+U*d``|UH+xQhArS8P)w18!PMoAC@!Wb2)PZ!g?#q7Z-@f?8D6aO-@ z!bEEuEXP`ZNL?yw>g1P9THzLt^n>4YJxyA|FNPCpHov%3Y0!W)2{m~`N%B%*)Zx08 zq3TLh>g7Xii+fa4FKNJcM^i2pkaaO6@nawT$35Sb{`O1U%Upg=Jz6o~ge7^2!vS7) zDXK#}ZPw@MBXyaBYN#S%7UPv!121#ZzKw{h1v~-|ABKf!*Rfy4n2z#DArD|Yk!y@C z>Xwg~G%!cf$)#93Dn*3`WiX}gdt$0Lmjkvoo0M1YK9x78q_0qB-9-%!7OBrco&F>2 zImep2anl(5J+{~D{Pe#3-T(UMvj6_D{FlETl`qS)?AQ|;NlAFy6<^AU7PeZ^iKxrV zt1LgPmL2p(&VqCKY2J=dDA1YA5Dx$GzQi+Dn1PQn*#}XSDBQ{%OrV=>MS5N9oYVB| z=}y_dJ|@)&c(^~NuM$wM`N$5yFL9+eR{CX+b5YbiDi7t>0XQ2c^OAqTBCjD=@W{mQ zi$J2#+|XJ)H!wcj{i=biHk#llRs$8P{Q2yVn9z&DK^<@vw61(9ZzWDh4(g(BAupJb z*Aup}`}|98tsaNp!4Z$C-(R++ym1d)1c!XLA5I9uZh*pj!Yqo=ZIOwTOu>{DkCB(a ztd4*>;GKb&xA-IUq>JQ`8XCNh3ypf6J-TVMpkfnlM&s~wrP$T z){_m}57&mOAd?7g=eT6^nLz*%C^8jN0>7<3c8nW`qSQ=%sQGrl$>hlpD?y|ihvFe_ zlCF~-hbWA`x^oJ6P2Zp&lo7FRSlVJHxao`~md}8M*Wo>httLcco$B@*Z*8-ohL%9K z8>7Q26A9o1(SVrectqL@>s#%&xKk+_FRLj`jNa=HaGxK@tu#!>MQko$359))z939< ztb;cQ2+k~=k#_z@`Xu8z$~q+KngCPE)5l!um|Q%gxmcNek9Fw%l=HocbHB$$fwr|R z;ov=EP7D^lUsGVfuND}LNf*H<_oK+P1G)Isbdd6)3NttOE3@&>*;lQMC zVrVU16)ted75B<_6nb$^QLP0Pid>O2Et{dd$Mu?CRoEuWFwca^AH4z-ESzbUH?$OP zSuS_P(W4(e9hA3k-I^1Z1& zuiu@p#PN6V!I%{&k2rRL(+WvPat%J<33L^D=rv#!jjq0!i2(3S5&|^5t5U-<5$Ey= zeXRMq;#lQGd)&=W9i>v<_oEa+P~Zc$7#-BrXM*qX<7Z{_!^d*ZLG#&ZK5}bC9yLZn{#q%-d{P>^o) zF(l?#d5?SciRa2Q+KF0%o%AQ?|L_cs5Udy)@{9O)yS9Y6Xdy9jN~t>%QV=QXDTtv} zA6#-vSywMduK=<7#?Xk~i~3AgTMt94JfSKpq&){QQ>Y9(&?_o}Z-CYMp^s)fM4>X* zt15WLY@(HQZKy2PpupJ)kQ9KZ3UtW_5n4OtUW_UbH-4xSAinQ0shHtHZu0;fcRZ9A z(E=n|Kp^&On>3Fx?K&IWV7HX)mq)F}!aO^;AOM{nvu2pZv;>iiDQME`TG?Ic0dra} zowl=!36u_d@U@rrxYa24%uWum6ixHMagQJwKl3~dtv(v&H}HcKYT%QKnjUM>I%4LY z)q`xxx`Bo3s$Jf-zCs!fF*x6<5c#_jRs80@gudzNtC>sS!eKMtQMcGLt6|eOu)hM;p>Gw)NN;-h>M;yrzh_EaYN(qWZ5je|a*CMOo zxtRz~eT9hI=`Bgw;yL^XNg2{EWIU_FbX7YrAkG5#6kozd17vX z8YZ=1Hn54CHTN z`SoyFqG;?m3}-cQi}-E>euor%hLO?{8KB1GGD~hOCQL!tcLhY?8s!kEF@bkoU@SL} z1aMoaF-!S+Y%E{`x^>O?E8@CrYt_UG>@2grJxDe0Zg4|zo3 z@eH%AFznxh7zse{Tao;Lip~FWNsU z@PV|N!=U?{SeYfoQR+eYp#XNbA8@kY^YRlj@3+t_IfIn9Yq8$=e2i!G(-{^v6Ijn4 zKtl`*Ljs=S*>tBBJ3i1Y^a*f`T6QR}0^Rc`_dHP{#J1x1TSS;qWlP9pT z9BMsW!lIak#wvdCTi;XnRoSQR4CKNqtz3}83-Y4OrCtiqyy)M|0$U3eBnBdt(;

zdg2dNnWAE`?0!K{P1$IJ>}Af8>7ZIZjt)4mp5f5Z$xXmshy1`>+u^R*9h&-olQ!_-Ugr2ejqTSnufVexjO_LGl zYOpoem|1V^M%qDZ1wjxSm}P8+2Hs~Rgi!LNkZM_uIH<^V$r35nDF{~>P3@3i5@U&t z3bYn8T7?4qq7EKGAU=n_-s5KQDRHBei(qSvbcE_zWJbNJda#Yo$ENsLZ#} zW`379K+W}8rvU7Y8CNAB!C`a1QstoJa#R{$%d5QBy z!BIXva!}DKqP+B%pB&q@BAd*3QaSH)&-y8+684fWyrLijuDk^;;S13v|D+!Xr^+{* zR1ojVHhA!124UzS4&8oDOLlvMGh%K#<)_!&0~R-C_Q>GcYw8!@l#PoA8153SXA_u( zPdI2dD~+(A+{NaX-)@&@kMU@j8Sikf%^-{($`S0+IaZ)Cx>#fLep-!{0lSJymn|U; z_(_udxz9Ey4}&qSP5<&EuL+8X5O(iZ7tww?tIdg@OO5h;?`e6u_gT5Pxgm653Yuz~ zeo0bjd_(;G&C!S%_BKZtEVE0Xa-xlwo@>o_QKffZDJiRFkI_aUet?g3s`d zcoZ?*O~)$^d{_Ls6FI|wh46QeHe*(SzM3`V^V=!5sL}G7`gjM4a?uLwZ1mOrI=68>D7-99mEN9Oq z?xw7J2Vxy*&3Wsy5krRqku=FitRWr^IB1L19+h%t)ZkDjdwSWuhnZ}Wv1N>!k4LF= z(dBgHRC;!_ZP31b%xEq4P!KxT9(CzuxD@WaPo6y#Oz;JY_{&WKB0 zj>w6F_!$^2sFHVUGV!k>uS$_ed{6Ark6uwU)H7#}+=``18{SK2sK=4n!3;V?F^PL? z7gT7W0<-xAh(N$Ulo$An3l!xCPtZ>V+=ONG>0%MkZ`-oGeGcPCIlzv#i)F8SYJh)H zzXPI&&#UNsQ`*J)lyNK+eiP5u>cF9Qq0+H^%J>fLI@&v6CrRCM4&GR^Z50>FY6LD! z6Iy1iv300aF>2x+nQ72QxV3JKOO1K2T8s9BVFH-6MBMFW3pezaj~^H3=10Vwv-9@N z1&4I89%OwBYjh=pv-x^XJjG%H9{caaqzeb4ZzVlYs^ILVVH^>U1lB&&o&#HEUgams z#Zu(uSj~L5?^Mi>b-3uFQMR^L%U^u`yd0h!l!IfH8wLnUFhNFOFm>vh5Y&r9mK8D) zv%B+U*?RCDiw}0vXS4zJ;)OBTjaWu{-{ep+jElR$0lUSYBBK~2Y;tQSM$pmtyx}PJ zP`8)R$@js(tf1M;WnI!YgS2qdpIGG3LzYQUi5H^$?Tas?XjNvJRJAU;nF%5IOFm5M zO={->?ZP4INl_S_B7QcU>hZ24S6gYk+ zK* zTX1`O3WKu(0{Eqk;Q$#%g%-g^@ahOn!Z=Ce3ZaM`v8UeX7WbS3gy>vc`7t9*VoKDd zAde~6V?jO@0B1#R+JTz2n{%A`CBv9~wwGT2-)FxnP`=_Q46spFlwT;E(8gCqq2=aR zl#icnCL_3&Sp`ffzk11dp<(@6+ZUR7{eg?VLaW>pS@}fwbeXtP>s&E85H(!{fQu$` z*2HZUol3zvfM0<`;EWx&ShgmETX*WRT)t7#zUCd~9Yo?y)}k zub%HRfuYRdsc_E$^Y;3poL-L0F1dniSrvumIYP=*EEB<}-Apb~*9`}L^>DRY?3QDH zb0I^59I05~fg3fy(!OYzsN}Vl#S?u6vJ7siz9fOefi68*9lYY5%$`g^9Yn-#Bg%V4WixpZaQHT-<#EPHF*h35>J&s z6MsiOSjuahSNFQv*x_By?V%-M6aOg-?C#Vb5612SgMCdLR(Pn-@7_0?LD`8caWCN# z-{G@;6{lv;7TwOT~V8srNz=f zm{Os|lJK65Sy&BfY$daWs&|xtN)i^LQUAgmQJ6lf zr38i!I^uXl=EU`|V z6drEty-anVsl#QdLyl8*VxhxGk~~5_8~2qh*+w#ir73>t7q>ZHpD$51$_h;3@oBs7O5}+{~XuCz?0x2@@P6vmPeu_ z>8ZOGDTlg-9b%0Sx3`*V>#}V`2FGJynOxik`n?Q z7?2?gN*Bs0tWGEz+FB{wY&3j~tdi4rD-s zW@dkA1sfBU1$D*c8O!pYws;4+C8R;G98^<}{REM*pe<;P$`~*j_cfg<#smzN$z+gd zy~?E%=_+|AlPN{dXgIi~ab*hJY0%UQdFGWc3Loo33(p_hG@#ZO;jXwV_SnmgP@(BO zi*r?+88SIj4ee~I@830UOY3gRF2rnHi-K6J$9BO@O_Wvv-QnUA`-5RjgMsUk{-C=% zyH*G$5RLQ~1jiB6ONOy;Q66(-!MD^!E+jF;DOSm7bOFP%{kthP#2`T*Fi9R5p3z%H z*di(1)Q=7;Rg6Q*4l;u$tF>YLyW4Zl)A+=}L8mZ)L1>tp*oq2y#$=1uwwqV9mwBJ? zL7kbW3JB-!;B~7nh0A(@*MYzbD@J1ce1XqsIQx~s2~GyLRZ@85;9+_D599I6o4iu% z2%lJ4$m1`{s1alqrIL4b$^Wcu80e%E3~x~{AqsYR&z+BQk3vzV(wZCcL*l1?$!D2Q z=0wmKOl&z2*&mdd=@+i9k{OerpZ9=WW)I`?0{5KVl)h0}j*2QbEkKzToTNwGOYMBu zDkW~geS{er)U%d(hfst>zlOQBVETh8%>K_77<)=s^7s01Mlj2`S!c-aiKfq602~45-?pv zzJsfXK@Q6`7S<)ZY;SHb@Y}P5FDkyolLMeV7KI{5S;>8A1&k5ktV5;`mHe>E99LSM zytTBtUYg=Bp#xBo;#4F`FjP8C`?QR~2Cs6kEr9Nn(pLwpE%aTw0B;vlX?xb9Z}D&e=l# z?uNj#Gjz<%8H*S>`|D`QPnV-1Vr7yzZmy-cbmy0BVMZI#e^|~Y;L-wBqX4)c>`6F( zGJ5exvgRK66j)6u-^mlDuzt>sya)Lqblwj&{0vSOw7#iJR0Uq@aa%3)V`i*P`_a9#fV~tVJqafo^%A<=@6b;bDQ*F)XxioY;;L*z z>%HB_rH4D&?_QSU^JA8)!hayZb8zDBgMf9;k6z{M<~Ff%I0oxUt4P_WpB{on4sful zJc3AZnH)QBDsQyk#@H~QPux~-PIOeSyJbPnm@#vHR1>Ke2hO4e&l!EHFrE zDD2(IY5C3Xe=J+OJLP*0#OtXHZ5^Dbprny)FbB`sGoQtp&Ok=LMopGjUZ1n7$t~?@ z8<}FCJKjMd@{VwuvIm5GrBCq?kXEXf2W&@oTDY^L5#QwtEnD&HWgUylH&l3r=K>e~ z6vkNil(Wjj-FY58N_~+#{9G;nU{a!7-i)sTZPk@9n2`;mPZiXQ*k&XMT4a4Mchp{Gk!c!)=3hXnHCVL zjHxJ8R}iQ?B!+F7Y2R!!($Hu|2rLWZd-fsc!)USaY=Ba{IsL>aeabsE6~{`lQUy3I zFOprl=4PQy)=Fm+Q{xc0WTMKEF6W1!1aB`tmg~y{cK?CsShMc_E%5;s@U$rCmvIM0o5{Ju<@nj~+MAb)<#IV-f`4n!@? zzcu2J+pD|mrO38up#?G_!o; zz*ydG@%m+j`f^*o(cmDRCkg{2s7YQfQXkL3&05;fK=_n-TBCfm(lmZNO*CQO$oL#TLnX^Fm)r{rN&3``_m*21B8>e z1yGApf4=Qem+W9tg^u@O6k|Gqq_yg-vGu6A!|;5z8*XIK(i$TG?53_-T$MpU4@{H| zBB;wa;N`^u@x&9}GM|#?z}S%ZSdM{5B}H9b1ZLuS9BmFXGIlA4d&zXfIo$z!aC=hD zPu`Ope9HT9AZ-wl$Ay4t%!w2p4-m6o=7E8DQznu_M@JL{6^Oq|5l_tS%CebKYnY#{(@i@S7?Mv7_bk4hT90NjNi$%fv zepLyGl9cni%&dU$*plnA|Ah6#A_3clKIsG1{@DwXhT@l-Xe1So=M!JDr{OV22W)))0RCX>HdbUg zJy<6_2=Acmligo` zeTI?3**17Uiw&EhcA8RyA0FM5fBNxF`S-v0w(LDv0~GO~1t?Fz`|xo#S2o-s5%K!{y5{-qLi2Q34)CrJ=giLujI=^`R8Osfw=h0)L^YdMK>sO+MR(Xpl~p z7Z+4U-OPRiuW}H&QKshEqL{fSuh_1Y>lLb$z0^Ueao$Y!p+$d~Q35t8pjTpf+Tbgk z;NEI90wcKC+Qv|r!HX85iQXzI$Th#m>@1BdSVZ$8KyHlb)=~m%@O!9b$zKU+4APNT z)f1rxjGX@g;A(qkE=53)4RARfPr;+}NzxXfC@^=y$%1_$LD>yi>NPXppTgoSI41C3M1tyo@DHx_X-E)Y1d$Ud!xqu(^#Nv?%ZG!(2Nto!!{D(ie zyyqpM_@i>uouD5v5BwbNs4#=HFjI0+c|i+Yg_767yB~vd@R73gKhODHX;E*7!Dy;7 zk%Jm_35s~+vt{e#OJzoBm03jFgtjSe_&vgHP~6_hrMf!xOFCr%?jsz)=N*+% z*2)8>*#yP~y$^x-CG%==z~JHmyxDERbMx9EG2{t(tSwWpq(^&=)lClxaqJ?zCLB|e zfw##{*B(OUk*r#-fj<&NUE+r~@_6FyjvD32jY=2|XmE4#dgi*NcFJZD7H35ayhJ|h=1Lod*K zgfdIMsJsb|o!M1DvkHseH8@8`y(P?A+f86J{-?EO-3?6J9o*x;din)N9egUEIPk0D z8fs?&1Ufk3pJ_Q|5+)nS=nkWp^`59X@X$D5sYLw8ExyCr_ySMiP3~tf0e5<~?#jjdf&v7vRH$X+r3RPb zYk<@SxMx#PKnqZ7!nRZb$G!)W002M$Nkl@w_ zpJ?XPm+tTsKzI}D3PsCjg)y_;NRC#P7HEUEIw;y~UVC?j@;M~O3hR(9+1)kAdPN2; zLp0i@ZR>LMm*HrVe(GMW(jN&S{1g-tUPz833EX$*N9FQlpRSTXj{HWMVs&&;q)?pYimPN%+-A7Lxff(Irl&XYFa9)z|nQp}Wb`7?#CYIu|MTrC?0bF6_R({ck z0PaKUm8tlto5^z1=kp&iY6<&l{6)7jeNzbqyNF*jG+$$J@|P=$&%gPB83D}ZWW+$l z-&hP4LSLW_JOPE*xA>lwAXrL@4HeLuEXZdRPTXSd%FDWT=|&#W%DfGGB?Og8?V(Bu zhKArn-a%Gk+l-VpuVWnwrI#?f5RVNJtR6OMB~* z*1SP~fh#gbA5h$S%2Lh=GTYT~u<7=|Neu|#OAEhUq=wlgnA%Oov|EpfP$u7+HxtD}YpqA5e=^by5s=N&d>9A1_QLV5_4{rj*6 zb&*G`mmc37Wwnbt-PmgxLUDAkMWyn~?e$s0w4k45c-VG}H3*_OOgSKG#PN}!Dl1wB z%s!8q&A#KLIF~_*JFU(h3bD7sOeAqc4;@k|)}tX+^t8^%Zy?(G!7R#Dr77`fTmT@9 zNyWzWm?}pB6SSB4C@&txn=r^U*~OaA=>mULzEmQTxIj?qLttfi7$qh=fM$Ise*-h4 zW}VFyhrA2%y<`+g8Jr}b#yx1axZ)D~g?mC|UknRqsfos<$Oxy6z=6ISy2A%o1mCq* z9mEnh#uAF#r68z4sYj()2O{d#@&N{|CzpMt*(i$IK9J>|fZ?rJG=^hHBzL(dlOH2O zC-=8Uz=cx_k2P3r)WVW)bT@cTM@6?55f<0Q<#{=Hdq8`)WvNAYP8``kVU3(okg$+x}RU_o!8Vfh(2D{r`Xf6jU6XF~<74p$@>Vp2tA@`%O zEDa4pbMO%_jrTPM+9;3?+oefKW89LthYUV{{@bwMNWV~EThl(MIQZL6KYdy7qO8>HUpK6?!CjP$B3 z@uMy4(L+tFRN0xcq43H>@}KhW;47;-?kpy!YD`gC=YexX04J_Z591T>Q48=2vkFfO z($B_@t=}^FYr9ZJa_H^WTx(^Fy0Grsbt73S;(bsMM#(l_e7R?L5UW?_b8*iVDnn8b zW*{-drvnDBm0>$mgUAxJMS8<30p=A(4ipM83S%Y41nU|_5w36;r^E^s0Ek$P88H>{ zOh=jhh&4I7myw)xR7pY@5r<;ZY-cZ%jI=VoIo@vQAObSVS)1_Pvm1ChfKbGp5K5`7 z>uxHKYhA6okUHf4cy6WNVdl`Yh;x*u%qtR1(Lo4Rezv8ev>zE%_?zWSv~2vePMB~H z!Omu?G6ZX_C&XvE-~?4|jg$sHaU8`5p!^LU)x19CmW{{E&lkSau6=UAS}=hdb;50Y zmmz$?N51R_VbjV%nCJsvxxf!N48v2#dwh*m<3gy;K zKyX`|wB2O|RF922#x5ldf7xqjf)#Ott9^R)k+Y*~9-{_Cj`OfxV39{$0b@IG1K-kX z-~e6_{B-+K`Om-jb@^)VSy|y&3azyvn^D|f_saFn8m=zu^Rbx5@QrJZ=feoR2rg8v zly5-uJ7RC`HcD`^NvN6_9tHrqi|@G9Sa{)2`;Gvpw6&siCS(lPBM&)$RSQ=9sZdlv z4yJW!o_vD$YaXO&T`W#;DXbBE$Hf*$6nOGyLP4DCa?1cQbua~N7(>)lYI*sg%Ag{Dh*bHF?lL( z6DOT;%rCT2NpXjNb=F$yN{^f%C~$LrqQSgdW6#v0JLofUA#aEWK4-TrwW1Bpy;~J( zF7TRA6S^WiZ~eAMhoa2kqbdir-nIjgC+g|?+G#a!HA58|bq7MgLFq|)hA<6A$Q%n$ zH5kuTcDxH^hafP3P;M2oe3jAG_0nqj0RqG%t~dqZn5!x@K&$Z$?$f_4E#sa-5#y21 z*$~2!pHWdpN5k@-7N_nO#E`kENYvE?VQZxfibHqHPMDJ9mS+QUB|FS8w>|U+3)U^F z9EIs=gj)tM<)+Ir!WJ7(cwo9KT4uODuKCW|Ue14U^f75$OAr)M1>6V14U}ujN4YTJ zqY3R2L0ixr@{%!3D{&vm8Hyq?b)0Q^2gOfUIwLwf$nq7>-IkN zYe{;)iVL`uq5ZKa5X%@U9qaHrn4=0y^WAbh)>2GI_#{T_jPQ)vS+;!xz5#=bFf!_paT@J z)C0(HUxz0svePo_J%Z2J*o3xv@Ie$Iawb&SOw|Dh0Z{3=sjWP$mFO-;_k=9uMUUAv zVSK=<8@Vv}$$&^5;H`_t(r3Bf7`%(UuYQn9W=%bC86s1q2wne&+I zi0B@4=vjMApdN-`By1uKgbT$O?V;X)T{pTebwjvOmev%8Tz5p%1p9h3F;KQOca36hBIw} zC7?u^@Hll*86Wa3OeiC0-2oYQ#d-al*e9RkmiQ{|80Q^eQM;6FfFjUc$2h1~UEb!4 z@X&^7O%7VMDLP91W-$!0Fm?MgV=2AtBi5^fma*7`BZLL|dp@-w`F0ccRo5#54E_eh zg2iQ*c&`e93Q>$zAv>aKgA7oD*Q`{sqwaF!WQVIJvO102>OQ z*8GUL`42B%m)%X)MR#}04!jRbAS3h-C9r~JHIT1IT5|9V%F787m2x)0m7WVc0`G(r zup!Iov~+q)(6E8!NZGbb#x6avUa|>rVa9q`POi?#gXAn|`4@)r$Ogj-8XOz#EjFG# zD%Z@iyYu*x%l43+KGy5%G8^$q9G0*)rg}HT4)KC|z<)~0l9QT-X=oAriec!koC4RY zkEi9}{JK1S2wXWn*XKTlwiL5G!%md}WefZ%Z=D|bnCOYpBm;! zB75=$kYrC*J&&}dJ&z87CYfBLY=E81LD-S@wL;s2#O_pSDk+o+Op;Uv!C$Ot1!1%g zUbF%b>|1_QX?C-=DL!XbhxS{<9M&+cw>bE{OG`7%dznw|FCi*&D*_}A{1T)ELM<;C zxhg%B0waa76FZvN3=9G!HFyehy8J9Nn!uMr2+fdG3FDcL;%%2LX{I6+vOta5p-kf* zvtJ_ZiBO5>5-y5lX4wJ4db2qyNV9sEuGRB)wMGy!Tp^KNI|P6zip&lJgMBeho0ONX zc!i+ccXV=A)O3jnrye@;XC|A$At$@hK82`KOoKa+E>jp|6}Y-N0+k6UNReJ%j^ffl z#bzdfk9{(xy{4lrOj@ncT0RdU#GfaI95lc^4}xpHMHylp4&VWIq8<{)Fj3!PS}%^& zX;($UOQ?pl>i{AORPe@^6IbGfIjx2&%nkrG0YQFM&AVF2ekbNhU(zr8q%x#CsSP5? zeXhYbt3hE%z@MjDEt#mXzurgD-rSI}fQKNwZF2|lV4iHOmJjDQC`{T^8L14N450Za z)bJk-(zX!Zpq!46j&Rd|%ULPJ(UzW<2gID+X3Z;yIuXy)Is(J7FbBA)Lw-g}_s~>c z5o!l&`B(!4D-ZWxkEzR^i%!s(PSo_dipGH+h2-FKadli?zIwq?1uSv^)&X?Tfcfgn zugf-8^TEdhEKH0Re+<_kA1in$Uw{6L9pssXm(NI@VRy8|9gpbCo1;VAXuL$^Dkoq9 z0aT}YC`kRMN4Mo8%JXJG%+z@p`W6q5x}~?2ZmDEMnvom^SYID>kpicu6EY=Uw{KS zf}q71&5fm4O;~`?`k|5psHw%Zz^*J%k!C3z50@A@)=6twCAMfEvfVq|G|3o(s0En~ zIAg|ErdC6%?L`P(3!{%YBV&S4@?ts{WrcEeryb=;7=_PHv=BT6+Fw)lgv*j6n1Xn7 z-sJ{;bAklKQZ5q#5KC8ZWZV&@=BTG&Y8fY0NOu%e*oI8(y6Fb4MHly1LDkZdOd^Dj zl;NJ#%Pj}v=PrzVLb3{BRpx-&|SbudvEB2{Y~CIMFg zTqdn8*bVUHFu?_5sD$|y92jtpg$cfN@6%^SP>cA!Ys?ie)HFXbA)n{Uz&{GvEi<@x zwf}d+ofF6O-@^2ue zU1pQRW5Df-1oQ1ixQKKYx_}$_!*hAeny4q*A)JIcQWx-PWoSW5m$;u!qB%L^jI{$O zG<>+r!DOrbvcv(;uRdLKa-J5G$f!7zh}FH~7J{d>?3p(5W!f0!wY;6z> z$682$6=4C=#MtV7f;Lr|z>Xx=fY#RqzGmf0;(`e75bI>@j30S_?tEire-fl2gywNR z>kf$sxsV@~RflpSOgA}820?#fS`aoCBRKK7YoAY!4oJt~fS^rMFm(N*WmFQh!DLZg zJboUK*X^ny!SnF8`M zm%N&a!&^f#7Y6Kzq}5s#ejS)r7%3lb;bs$`Y~J8q(1APl=p2Jz$tN!*PDx)Xz7Q84 z81s7b>O=YT^iBC@hx4j|!@WMu*PQU~1dB(lx5Pe=AeVRGXS%A}&M-k;cul7Ez%L0U zFko>PxWy~<;#(%|6jbC$!un@-YpXnd@UWcSU@@ZP7kC}AfyMn0C5()9F+`@*9`RKA zh?Q*H*Tg*`Sy|IEP%Ywe|MdFz<=w~k&=63ObLB)F>eU&xus!8mopMS#kFvPMp<(DU zm_v(F<-Oo1@E%&C#MP=Y0^uMHq~463dN^WX$o>SQDax_EQMw#ZHyOXDUzfD#fpnfj zj!)!jHU~xS48db&=~>S$MuaE%hf(5cA^<@zxTr*(WoXVpcQLOJp67Y+i!Lydpwfr4 z=z#LTC;H%}T(}7g1cB$h<6j4QKdOw4UsKpo*5xyv(V-G>A%{xKU@y^w$eYvlZr>DZ zF;=A=0XT3%y_nOT@?c}VJlfgJ4kQLzV*x5$xE?A+nK+#RScOP{1;m)$5MUgQhK%+y zmoPe-$|MQi1C%j6Qz%Ri07Y~Ffumk$mK{Z4Mat888WOB1GDhDV{0<>An@5=;I}*S9 z^r5`pe^2~~Gzz!{JqWVl8rudAEkLcX%vizYj-0y7h!pM1MG}rCAe3!s9clSyvVc1g zFrGV8cyRD3@u-KmUApWd-oZsOjhS(u5a;cJrx1o2L~|lNaZvo3nv<9xWr2p@;sjh_ zmDp-7xtSl~tv#J_C6C_Zt3PYh*d-5&}VxR62s2fsm92nD%ZE=OY5JEtt zNe7yGR|`^_=DT>KeT7F$E9d%Zet@VR?wyv6$}pC+P^xgifK19(Bf$&aI&R}d`Tfg3 zlpo)`NdDdODzPhyEHN-W;w&9Q4qcS9Mzq3G`AMr6ptz?pz9IFB1V;!epztAmv+ra0 zr9+VL>7$*p!+>}V-fuYt@s73BrmdO4eKY^8veeabt$mhHqP$1UMo%Yfib6v*HVbYs z4BBj{+$4slm*p-yBj~H^8|v7$28H*aQ3hD}%u^Wkrr>U|fx^;SLjJf@`^APAY#RIQ z@i*o08rS@q&0<|ug6s{jHix7NZLqJPa ztM_9(6iz$*<9<0f!>SaictNP}gqva@LCkx+v~xDNxFJV$T9|+&WTcCm6=q;F$@`#H zJrUW5KOI$KS%M2p69zC8E~eRd%zANL?7Jz)N~jKR_c6X)Q_bE9hORtVi(?U-Wmdn= z$Vv9pPgb7u9nHgBfN{?Tv0iCm-QJJE1zyzw@}6)=54DDacU_nw2cyG4yHZ1e@UIhZ z$+y14ea&znp}ax~ho#1C%j6 zFDF=!2d9^8$8}N8$UC$Xjazn`W|ntjb*0>~D~*di*HLUMC?U@{PO2B>fC~OV2&nUl zE#@+_?LZ9ue5UeoK-1;&EEN`U(tT%}wR}-r&~0cw=H0NoM&aB7ze?+t9dqt(*)RhB z=rbsf+3D%{1DmuyzQM~d4GliT+Rw(bDE~gz>#J^!+-->#B&#NvZXmcCwc<~qvg{7Tn# zOo+g`^l;kc20S01oq-3p8k?0>X4H4Ki8(HBu~YXh%l%%L-?P)u4{wi%pJIf77bhw8 zqT+o3!$&53E(v8B{K8qMjK%`Eik42nMnCZMP;#sO#QS|g%A@SU2 z{zN(ZJnjSaL-#O|B=mJ>Lajj8!^Cg85-$K&f&oCU%*vHG9=NZoE^LI>f!DgLp8nS z6oQg!hz_{Ip&M`GG|pgfjTnfzK_z#HrkTkK(>y>F4@AN)Zdmt-Uh)4I_IN z&n>$l>2c_mIuRyqBWyqf6cL65`|St-W(Rs`;jC$2m!5%pg$a{#IVs1S4|8Lsx_ zBd#|rKfQWK9F^~EF019b)WfY7KJKX$bEXhp0a)N3xYT-4NyuAF2u21_H4cn+$wYT! zs!nQ%<9Jy9g(#{|+WI!O}r_F@BW+-b{fzCYUBzf<02uG?4WG z9e=LLL6%&J@$%Pmi1r?SRaQCHcXM^W?5ymU|2cWV%=QI15(Wx6c*06IOs?v^yAcPv z5Z(}9d>4<wp|QL7N)z2)8mEPg^F>;gU>9^%^nRPNxJ z4$4&?!^Z$O&gT2JIt2#yK71pd$GF|jOPD`AbJrmip1cliG=>5kzlBJ7BxL|OKyd@N zdsC_)0VF&OxdUHx1p};MrD`-qTc&+sKzD%*Ug|Ij(10Or%-LoecO>v&ax=jfD*|jR z=@pEYHl`c91fwi1v)>}Y+8mFGl9gdpTxOK3sKQVX+}UaR#&d<1He=~vi^#-kO%M_n zJxYOI*b#m?j?x?-ogJ3Rb#d&p1rR8Rk(@c&isDf_aM?*2&t-@A2+rBbF|p8I`D|~K zokX@dBLifsT~H2Wra~0ri?pL9AE!DLupDl8SeXq@Q+@q!F!jM9guepq%Y9>AMSGc^_mO@M@Zw!kPJ zZKcIWdEQHW3roSEyI0(PM%8-BOuT2NKlLS#P>`u7F0OczMzmr7q=k&lhrDOvyKR^< z;>@BOY+SK{?=A7X34XA3OAp;jKE&N0z}!=8 zkO))16bL7jq=w^>c89ImW|uRU*YJEcS4L(343`fDo2&==b#h4pcboT{#L1RFLbvbA z*4i@;x_<(iP>M_{beRw`hfum^0s+{>R~aw1;W?$dC%gTt`!*lvVk%tyKjM+q}Dhf%6>m zst1hj81=7jUg27wmrEve3<0?Dh1TaOmis*xbVrRz-O7PHVY->Tm)T!JWEv~Z?240aSkTv#OZWtNCN0UjEH8&O`7S5wNh!1O27noD6Xgg6L7QoP zOvc(NU_r>8ZYB! zw&C(a1D^xZ0nCuURiFS&E2)Pv^$@BW&(Of763QEoQt4^UX2lBkJH#atEEjg_rv&wb zAYDDjGZ1BGy&RzLM(6C}{H}B-=jGR5t#if&%eaV}4al*)zEOo!o=b*|B_Wcv82KAl zfoCo?yZ0PolvRiR$lx|EA^m9IDNL-VDyUcQ-+TTh@XdvVp5@cu9fDY&aH{AnKK9DXLWJiUuf>m=(AKZ^rtJuU#X|`>rxU zkow!a!^tZ{Xn04?qUW#B2zA3N@Qs_vf`s5EkZVPN0Q>6)|0bayTx)49W1VpDZ7je$ zCZUAm^h%}BWOIl~d2>81M<|e6EZ=K{`q|!Dd9-$e(s}~@`{f!d=myu@1HUH8ZG>*T zD|b2gAf_}3KEP|82=JNT5oCD-+$(b732DGTn2fxAm`0l2fj&{0? zc2dM@IWqmk56@5zF2?S`*y}5-hh{V|1;ZGFNw*@+(N?6zO0ny7QurKL*hrb|as&l8 zoPii7B@hf%C2l*D1E?esCId4HXSR8c?9`{yq76@btJV{prE&cBpmM?nv|%n8GoE8cQIoe53c!3*DM>ShQXIfu5v1Wzq}#5{LEACi z5r8zP5|x+7<^1xZynJ_nVBMAF_2tA=wFWI0S0A|JqM&r#3v!IYb47=-50hYTX?K|< z*e39+oEcwfFC>=JsY7PP2?2*ACK_}DGvP!1y4D#zQ3P6ozGg@uL1#J@Ma^IPR%dOa z{;41O(g72pgL`n2ckC26H5O$G%<=Ib%hl0q@OGJSM;TEb#iAHEiyJ7-&4b}$fk4*@ z$Cj$3n#3a&0~H&pkx%qb+a%;2qYRglL*#N+KfUlAw1#aq8M}nB$FUwlQx}&Pbf%tm%gjS^m=y90enopBEr?& zzbmKL%%UGU0JTtkM}*>Sf{}d95yIrQg+} zQEefd$dwkC{RSCwL|{p$!6POE-+>iehJFZZ_=~=G*bw)Ny(f4hW^hb!_eo+Wo(-fi zKJ@%1#^R7+lf+m0DllrKExA-YOw94a!&_@_m#?sr*O7$oS~^v+}rb}Im%T?V6x${ohi=%9mk0@@=m>HzxYQr#%jJnN5bPVv~Me7b7jJ`saX}2BCNBmk|iIoZcC_qsP84q-aFu96<^c7~Oue#{S34?1! zUO!u&OM^|EXPYr#ulJP!FXamPZ(%jbOmTVVk8B_Te@$dxtYw4T>T;1dLfyK@t7y0l z&x!!q0|Z-uxJ}(E36aBRVFQqGfptQ9D&{^6Y%B%^7So*g-q4^gmYG3Q)tsZi7$q!W zVIS@z*Z|}Vtd>TCsZ_0%mwr>$qYyQVUBi7(koVo;3*3V9^4ZQRn*y+f8Eze$LD6p@ z0*PIyD~!%gYh}1(NEl=7(5`N-6EB7wL|k?qhXI2a?g*Jb)~6eHimUVEpI(($2OqHz zofW5E;&16X)}(HK9f)`r=F*C6v7~L*!zJXb9JC!}U;|E_a~4&L#Su>Brj_it7e(!N zEX4@Cqd6lrfPfPftV$1@7=wWp_DK9x#Ub{2hH^%uMbXC{p@fVYtIZw(w*#f{P7(Ma z%IEmtMY%e8!%X3IWLkF2q5zt9#$DwWJzA@#F8F{l3XRM6L_!8k?zv05szU3@$pri1 zq@EU!iVIN>^Lt-jX)%bU|MbW0xQ@`D%3g>nL4!W%58Xqc^?IpM$^UIJDHNw<)9 zU_!Y}KHFwXwXe1r3}KZA%S;AISnMdzE;0^o3YuP=%Xk;z4F`!RX!!?PxUHFagR4x+ z^jYw*y)-P}ZZaTK=_T9dnR>LO)kW*PnYL|(m-!Fev`Ov!6MlOo?^G#wV+pNa%ke^J zr_!5X)w|JeHulx+2M5ZsJ+vl0Gh4DvxQ=%nP}D`KhWqL`exs;i7~=g{#wuwa!*mmO z3F5OfhU)2i26kjADDqVbC%w<-S`6IN({k~KUsV3$m{dQI4MN3kMSvrkF)0N@&BL1@ zGcR$^+(6@!D#aB>uVq%kXVnP6n7nBaJTok!3*fy>eNoBiN+@V1YulPt*O_D$AE@A` zOX@?x%~n`cGO~FFAId6Fj-BQRW>n#MND(4&55$Vah45T6L-}DJLB821rucv~qBR78 z6bHM+yBU!|d>5`ng4P=H!gZwY@@MbO_Gxdreb_vM=Z_K4Sut^cq86Ee2pKcBRra%Gw>RR#AA?8M*_q9#x%*r7o+3 zMsYjgzxCBl*$|VgWSNcJ1<3xOsPs-~4?yCH_~Nw9z|FrXaq6sQkCKQ&fehaIvnLYN zAoHX$-IEGNZlkek4VM@Plyw4Cw-*)2eQU85zK6eo8M!ejJv<7JJI9f8r%~=r>Lr#W z1%Sx5z#QeX5{g=4K^kg^XH3oVxn2z~2q(>@JtYckgL~TLp0<)onJ*xMN^QZVYL%&Z zX|Ar*SrG5Yt@~=Ax`$${E-4DhhQa~-b+m(sB0Uxe9hTAI0D$Kq!aiiOLjgzcfmhG~ zG;uNLxR#F4wqbPLLXjL7S4BwM!k{_aByNQWR0Rsdn2YK8wfEG0#>$O%9}db1lON9? zt(2Y32Xq{345>Fo+A#;r5xmW2qAH+R0AVPY3xp~Pc2xx?^J$%!remhufVk(B znAyMygNY+B`CFh%_emC5C<_+U7>M#18b{dHnPCUM0D zn51vOs5>HK$`#N<3&0uAQxwo|e)D&+828wKca{D9)MHs0LcIZ;>sH8O1VCH+ugj-kI$-O(LQG&+2G|TmL{pE7 zVUut{CNZ(> zKudU5nq?6I@5xnqL0}yO02jZxAu_;)NZ~$Th0(HoGC+E}eYVLXksZ8snNSoC`EAfg zSsL!)UoPd4_p;%wEl@s#t2_uUwVmHk5Vy0!eaTgsGb>npmvZ0pDG$ z`-zq>uujNz^s?U}0)~Nn63LW**?`(z)dTb-V6hzt)Pr|{j{0ODJ7bG8Y z#f_n2T{F#6tv6#p3X8Lc8kn&hMIxRRdjv%~VogMISRZpvzRLLw{z zNgWY$x2Ltc@gRak~Az+sIE(G!pt zmh&D0YVxmc<0iA&4qOpZt{E%E1Cl<%Rc5_SaAGxyAKIM(4JE0`3 zz@$ocVo|r=feX?Jd7g!zfdvHfk>}t;yhIk|J@3QA;@Nk20{=LuQ!6n>`7hxZ_|5dX zg>tPr7CQ4T+KD!6pJ9UFFUE`tQA9|ez@W>^Cp}26nnOtaT6((wy4Dys=26l0STk?#$VRu`teGW+SC(H!wh|kG z@7kCh zAk`vUWOETYCdzDv^FpRST6!fj$xORurm1FAB+v~Ig+ifFSyj33^ZopuNQgHxBJO>z zFUQaB$B(7r_t%iE}o`7}H&8VdA1`&<<@V;7)#a5dvm(qsRECS!QOh2H@BD`^AXGD)#8bF;##F;+`9ky>3TQv z37(TD%2JZA!w34*7}{4|Y|lc0!-2C4mF3MdOr#(nVtVI1I{ItJ^pEH24P&Ns;?C2Z zys`5S?zfOf5%8RYqVl{3?Zg#RzEdL6a|7!s&vY||#f@vq7b#BXGV+wJb-01^JSi4& z)*DT!ug1@R+RWpl+tONIpYC>*ft7rx#9TBMsUv5_J8$Q`Quw@%rM8)xMe~;#K1TIw zu)69%JW$RZ@9Ir4^{Nbxt)zyZDWcFoNtjkva6GgSKzQ4QYvF>oe(bWUAAD=nA^k9f z*tq)8Wr&RtPF5=U9e>Z0LZm#aWRiSrXHl{{LlIvEM)i4Z{)AWatfJcI1Xu zo(C&KV~$bf@I_;zBV-c@4}p2{{B+0`gMy!kGY>dweZse+!TMw|sfztoH+0mNdtjQdmjwL9>3{jle|!4(Uwt3r zwOnQrKP4yPmPOu~oQ!KSke4bVyx% zsJ{rgS4~UIew!Ek%GIvnlR|cK`9;(AH^sqTZ*@{?QLrVD`)LYxMe=)0#_QVDyj=>Z z)%d(d6OECHRHi*Yp2Es^OE+X!={3@$pho_@Jtu?3I_uC4sSO5sYpLfhs(Nn z*};Y%eey|{OVw9K{Cc4QcO-3shpo+)ngM80F6oPYZg(KEGbQmN`bWIeS%5!lRrW?R z2)x}A$Ap++n$ZR)986L2<{2$;^n3i7qfxIu_WO8kyh(E3yGGQD6QCY%aj)9sb*e@8 z9R5bUEjAo4Qj$UqT%o0s_1CP9V$NnAuliY=!AfsXM)F3$J#cO>i< z`0qQ13_O#3oOh%TE4*^E-nF{vp5t4cM&y+$tG%M8eGgVScC4RMnXgiagu2gfn=ZU* zD)FlJUbYPmQy<|Duo9-?DZy)Mf{vJRbRal+H?!&)hIs@LX;}dYvM|GoZy%lhufO`m z>A(H&{0l(qU{TrdF@r2j)A=(>V3;thguQG4^YWZ@0 zgbBns9T&z^fE&zcT7Jx5>wBP2X`=g6K*P+!VL$t^)zpv(YXkYn^wkqW=}iiwgVng zIJ;Kh5QOnPWp8%m5D2_j=a)Z10yR&%va!)rYc0A~T;EF_qk%hxUHuTJ%idOo%!7^0P&D{pEugH~+adqz8JDr^p+@`V zhfH%g(oOFTP;E0laJH^$P@RP;I1jtL z#Rcor*Qmba>!ufI(pPnOc!iZ(*>zoCyrsilsNcX5TL=A6tjDTyWhmbUZWZ!qlVS}? zfq5wI@W`sIy~?L7o}{dJX0NIvIQv1zOug&5n-F8U!DaB<9|{pQY#M|y!7+ujp<^zw zRg|MPDb9l-=u1jvhxgX1mX{1qKnG|I_ku$3aVDNTNr{zLcEO9Qtt%+mW+YaX#Ng!{!EgNb6??jH)%LQ3>7ZO35A^~RYKnnKT8jd562 z9zay=8H*nbwR6@uESx;+(1Gt-Wci=|^}jj&cmK;@H+3$7zcV$@zild%5wpKtO2fO_ zyICqrlfaGn*nyA{in*P+Xdm{YZ`&DCM!-d<3EjHa5m+6D@Mg*@1sP6})xz4x9Xj$& zO68$&KrlQ{;V(lV*ccnQ48rh|@{24YphMFg(=SQRD*Uj+6Co=`6|o$^AIjl zkVMHe3iB-dQwcPrtd5L?;OC~tvr)EwGDeb>Q)=(*drys>wI%Ipcagu%qo`_sgDfTZ z{kP@iMT7Ad-b}EZRCVPhie7qhAfhDUAxpA@RqOBdd1*ZzTFXGv3(l|<4zSOefHNqF z$w*vn`U(s4@Gjfs^7|1KPT5uQ5&F6bv~!<67TvPFYF>R7?-;$O!m@ALP9l%3>qf-4(FvM^)#A9 z%Lslw0cIR%JqB=>V}-WmJnFljVwXCu6&~EQaiy^a+V-w;BS;Ts^+Gh#jerTtu{=+By{cV#L^uw*4X_lGV0C^$4IHYK z;vqzDQzkpaAjPs6-2468O(XNm2I%x(pA3A~vi3H;uW1T!URB69HeN3w?5p6e&z?&f z-e^2*oHxpM`>+E{zj{*QGjBRrF*H}aGSeX$xc%I}< zK7JM>1p|S6>8_m^@ooxeCuct{DK;;43j`@2Sn-mF#;{y(O8PQ|M7dje+mtxL5SZR- zbC#c=Sft@)FU~Sr0i#1*I}r8%{Oi9u{jY!hH>dyMFMr+1Dq4?LkG{)uYw8hiY^7R& zy;a@ow_427Z@BHx_DOfUSbUknjnQ7jfLEI#EDtmoXRV7pZnZNU!K&RUpFa4MsGR=l zSATnY*fD2>+)RWA5a|Os>YH-K$h>Ib9EK5AOOAK7=q zNn;=1)q-Q)cGl#0fKoAAiSGSKmY+~IUbvcW>KDG--syx3zbC<^##mtKAcu}ed#3;J z_B2|EX~`9&cnP`Jlp^>mM{t*yTANNJf0?qq9&YCmCj2~^y{ZpiRm#9wgkxNcPdns* zkvm?L!28!`Rt$T6^U%E}XR)5J1pN41V__2>l}H9SLy(V76jQXYxH4Mc2{IAz?zedz z5_rz@PAhvQ+P#iXUzFqe%1w!#rgNU+dYkb&6>8d9n$zWZzrmeK66zn?(PK4_~9ex5JjF|Eo%Yh5;jWUS$ns;psF(~gHK8)?Wv9Am|^v=Xz zJ#sWf%87_*p2JuS6arFytle^@dTe?Yg~!U`F3XqNa(GoP$pME$%mApXI!DuL)@}a~ zPmr`e(5-&wRj@t_iUTv|o>J;PhQQo7M;goG4t>;SP!4#5+_WEI8-S^f6M^2?Bboro zv*dE4hcpQbIAV$v{&)uxH`P?GMUjM3Dg$GLS$J)G84Col%6++n>Au*FY?Zs+O6vz7 z#L#6SV61f3LA>Crvr49tLj*nZxVYW#9zH8==tYP47M4g+*wuo$pFQa^^bS{WukTmC zZDb^=;F;aBuW>_8iIub77Jgu(!AG6=&`=7Yb`9 znWIs3ns;zow0MTZCyjHdaTb*7-}FN`O=_s`facn|)q z?G$5+Ry^oRB_IKT$rIw|j95l-e2U)ToXz18Kok#Q@e}i2wjV07*naRJbXr z@o7rZFf@l}r!d10*?>EL83fhw#+;ntfuc^%8%G7VHEwM-hEUS0@0$t>`S6P2_c%bg z@70U%cz&FmzRuG(o#oJU5XY`N$#|22_Oidb`f=n!92>PC$!h|+YkWY-@ zd{ym;Z5GSs?NuQGH5Sv?VD0-pq&#Msf&eRS3yi+ zMC+j`Ks~H;`ynlxoBmkx-doyRRX}%g2cl* zBc7I0iX_8SJfwIL1mj&CQ7?H1PV%Kl8fK8x@&CGJ05qO)WhF@fBND- ze1H1Ymyb_3+U)<}!&hw!Ou@vMV`W6EylD$pgvUsLjMTj!#}x=X1Wex#3|O)4{icm= zYkSv}{&9JBUw-x5wv+vC3oU>1HxJ9Z3;ysT*6TI6_iF@`Hi?;-1Gq`Iq7Q`i&U8 zc9N)Q1OK(zXS{WsYLtSlKHlQRy9|o5G#$E`kvLWy(XNWvtv9ct?F>tv)24)2p|OFD zqH~kq&Rxv7=x~BzY;`>0s*QJTT%foEBwi3mIO_o7&e9~-J#!j691AB&W_B?g5Edr~ z1|^Pu{?#6*0KOkl! z@Ap#nfM{bU*WmRv_}Y5bmQzx{KKmy z-@EXuhuSt%QL>-^H&+k_;&Ja{I!n` z@U_+FQu@+Tn%xgH9kU9s9wYW_@PxB;e+A&XHWS z+zTVFVF2`*#c@1PkdG*9e9Wa&Run`Pj7T%>G!RC*1b8`02Nl-ttCW%ZDr_TUeRwwR z!re8Dl@nk`%2B?iL)ifzq)-zER=HQz-+dw&yl1=`DGxk{D|ZfMUE__%%5aFTr70Hg zit)Y5%hcAGmNKmEc?BVCOa3Fa=h7F#syNlW(S*V3cT>oDt97_3`;b_Z5FHMV{6lc& z8D8nUz(*;7XB{3Y^&kd$(e8{t{NWF$KXf3#Zy!E7{j#(4zHH~hgR?tLX`7a&uWsdv z5qQioG^0tlhg!7ZaYWh_Gz*MAdHFF>K+F>G+TW(EzU^e1m+dP0w#$wrAzQS#dM0o# zx04B|-Rds}$%AX0Z~99yzkVKmW74}_Ky`y+$XEzRTy1gUIF^5i52xg*GxPenbLZaZ z`P5V~PeNLfnZc`+%wUJBjK{SuCAlcgMXo6L4vu&m?c(9f zu`(Xvgkg@C7?KHFJhl)-_&Do^y4Yf9fg@a|%}o{iM4;y+RBC(heIKA=TZ?&`V1X|W zQFug3)@!?9;u|qV2a+sIQ(HUPoA=z9Nf1ROEs1{d(oetlyao>5O)!P9z-v_-JzctW zwbZhM(%KqHM}Pc9kgKf6J!3p#D^2&fXM{ceC(B22bT|wavctH6mojT$(JIA(kNa+^ zX4N~4t?~y17SP-U<=jo_Vmq!5f2KTl2toz%QZ zDrOBQtWH49Llk({U6xuhwCO}csY(QD1c<^rvwi7V$W8ugt2R4ThUF*plCI;5+5tGq zg{*{)5?#Aha*X-&;tjCx_1 zc@Y@OT`r3`BT~~zJ6LXaATbX}VQDSbQjS(-4;Hkz(=h`#?sS{o-NqO_OWnDCkpW3j zOijcv7}72lff{KocW>2y3o%wg35c*xB-l?ZHyF`LeDiwz_kEr(14wBST1WJQ(_+>2 zjyap>mJp8_;#-G_pzjoyu|;F?>4QIcclaCo7LdiTgkVvR(6$)9l_l=CeUNm?&?NQ-^1s&ON+j1ykJoLUYQvkL>- zVNh#*MGp-gk=HsUwL*O!(Hg%&`CcDf-3D;DrIqx)&NRAJCjvQz<{_4=Qrg)r6$G%& z?8+>81C=+Hpxg6#E#|#>n?hs=RsP?C!MT_SlF`@Q#!33vctd)fCdm+0I` za; zb1%Vv_VnTDC!c;+Vl}H?yCkPR&PqUr-xnozf7v}A&r{GAyKg6r2hk^W=XsV#{igiDc_XnjdSAbFr`7$Er@KSscBja{$Y4IV zS2|$}qAtZVPkiwzx;s4wfB0R7G zU1KmdG0_;hJ}8?6JGiJJ={}6y``tYbe%|tI!Z1JeL-*r*L~to+rRE~|MALtkA9U~d^Fhhuf<)j zW9m+sW)$&s^(aOQlfrd?MYH5QhCqnMXJ4Vmha%Y9ta6M~1$BV!_bhD{FocJ5w^Mw+ z-)io>2_9Va;zTF;9cV{I4XgdP=dj5oTe5hy@|bwG4S; zSunPHJz_fE##PgT@C777;pG}jOt_$QZRvAqO;e7w*E9Q(-xf3Z^?&GO?_YksJimwE zxADI{xGoU8-F@7w+4n8P!1<%L8$R#ndOIyx$vWu3(ar}#ytUcm_#xU*@D3i4rt~Zi z`BpeSFWLD~3qrroqu%sp40`ZX1F!zs>B0T(YH82=zv+MusY|Aj=fU&A?fa*XKKyKp zVb>A>J8Hh++V|fPZeqQJ@MXqmLZ2r&9#0rd$>mH=$zsNsWHThjEduD-6j4s3yK8oO zO}O$G24z5ivSDxfJY2-@Jo|08B_EVfbRUoIvl`(!cuZUIGu%bCx<5h&zIOn{_^A_m zXmsRvG~E$%tb~ia^4hF+U+=IIvl4MTl7;7ij^}jG2p$V27E#GY-%oP%*~L@I95l;@lJFgpBEX-^JW__+rj0Kmh-02G=hfV{#BQPU9reh0)E0?`_Z~)cE&?8 zkhIiRtwKWm&nOxxL((V`#bEc|B5iUG;L2Uyp|l25-JvJkRKCxi@SOU+HYpa*R<8mY z*~b{D?<#N5GX2*<@0kfxuEjJ$rlv=&VlwA>CywrqUQsRPH3e=P+KYB_yea4^an~h7 zrocSNzHPuD?41P@7Ob}YWidxgR&FrnwG_u-y`3BPsEvWaF7?%nHf)gKfi$&-H4@l~aTpv&#N?av;3e!6@6 z$F zxrx3O2x5uK)ZX~mU+|>VQ=B38kg2A#&s#yaXty|9bh&fy_UYzX`J!UAk|u)Ze?5JEa)9(?PNfETC5I5?*q$P*d$v@`pU6@1mmvsg0`E zrV>Y>S^GZ9v64Gy*9$LTntnjz*gBpNRX1dKZ((8!G@kFJi~&T;l-soc5~tG@HL4AY z!FDW*7OzW`eHmj&eCF}o$SZo6H*;)q&pO0F$KjXN3V{kQwsLMCD@xBR z=8E=WT_K2axp)8e>CrcNycApGz?zs5!@!e$^B)%byjnj~eDK}y%n&HMcbQ&Z10@hQ z{1F#~Z?V#098h3e;0O~(a>tZ{#ZZNp&SAAqk-l>Ju2da&IpaGs28N_g`rAN) zjl*i(>DFWs=HvVNj19t9pKHUHaYg`nSOuP06{QrlZzqLFX{uPc#_+fpwWHKram(kj z@UdA_hbO;|N1ta{Zr>`&y{rmyNzw>Ea?j9_zxr7^*OUo7wU|YLgfq^K1r;GBAri8W z2c+59bMNz!-`lK@B5Mpwzo{q6Hf|bCca&gUz@wj-HB>Tlz^jyk;lKKlxsNhEul$^# zP{BG~ahFW^eBHGC{N_j7VdW~bH-(UR^;b&ZFJrjR^~0j1(B`}}=H$f=Ej- zZC@p85@9D?J)|HiR7bsE=P(poAj<1p|DMgJrOBt%A}Hj8kA{ESN~o=8p)Vv*pn$D@ z9*Gp1jiiO1!%Ed&C2!WoM_nNFO>rp1ebF@3Dl3o9ODsXZ1oPN;9DJA>kt#>w9z%*j z4{-Or)y|i$H6Y-5s#YLh*#<~aHYUoA$AFL+pK^hld9ajnjBSkCe%i%Kzy0#j=}}wl z2*u4#Y5BqDpPv5w@BV!Ie;w#*wHNwc#NdDE2(_!7LjJ6JzxwivF#EDpp1WO-`QY@~ zy^lN5g(ntv8saM}&rLV%X}{Y+4iwuG;K5YqQw|hFION$o`18XLEqbM#c%)%*-c>lS z3bdq1x(nrQ2Pi!FB!d|p6aaosMbiY|w>N{i_z z(?Fwiw*h{7`a3`P`RUtm`NcP1&r7=1H1|gj9-RK<2R~}s-#t~|r05DA%3iqJRlWBL z4P4lE*o*)ja%_XA{mt!Ax!HjykGDm>KI-9B{Cao_;UZET?*l!0q7ytNar2U7QQW?F zw>;8LDa>o3Ae5PB)p*|1ow455(Z+L;B160H3XN=+O|)GOV{(C(`egJm7FZ$f{oO@E z8$4*ZL`#c-hhRn1!VNFFvG7S(C)o4v8!|@u32RAKfBO8pJk@yp;*&<-KT5E62*=Cl zO%ZIdt!i6D0!Fqns6`6p%i-_?8g<#2XFR8<0&kWmrTiR`d+U) zX{oxNZICG&1~`<4^AvMg4INtj43z#i@EFet#*~C-&_Y`6|CKCjilF^t z=#-Fvr{O@9Ts$c;2`np>B1s-=wyAKyZ#u#v7>Ry0RG;{Zw`%H5@mLA_pjF&&zl({U zJU_k78#3jh%$LhnJpx+gn1CUmlxtN8z!W{|nTBs?1m)3p;!E6X3L3P$f!De9FB3-0 zz$=LFV4=U23&Tfh#4xzNd@Ft8yJCL7>wXPa9uOMJ_-=yzpZ(pRo&MRM|0g{=wL9ka z>C3M_ZMV!HiaoZKs&f{9`QFkpX5z;irMMjgWIS3H$RTJd72p$ ztA-Ajn3obPlf!5L*TyCUV*1460F!XUhk2?${=DlsU)EnY#2u9DcX(dC*WFfk_oLHK ze(-rX%%D`WK{lSoH&pJ)a6z;w3*^~p%h_}@3Wu(ek0bXHoem>1{eVXj^s6^-oId;P zho^s(Wxt)5_|2nlMx&qq_~%_W@RQT8e)YGf>z$@^w@%+QJ-qXQe9(_-v=|dEnp7X~ zpM{(oH#^}c1Nf|+DHI;XU=f6Jde7f6MqVJraqz)e0{XI4vg^_0{)ZpMgBhL5Z+aL$ z;8VC|m&es-oe%f#XH^ssIkEtUtr@-}W_id2%}rmJ%1^ll`BsmW&E|;hR)yIa?rjWs zm6!Om-A_`xb_`m0d=V`z{;2Efk46zruXiVOa?q@!+=05U(csykFuiZ6y^=!?fbSZN z?WW`bJ;<$vRlLjyfG_Ix{j^-q8w+KVtWiOBTRH63iTPWBu3oJ`Np;8yWce+e*0^eT zNBkw*p4+~O?#GL)96H8#JCwuc7P{bqA+-1u3F7VUfo%34nk7T=dIOOU4r*u z@+`}y&#h9u?q_j^S%frxgk^XsxT;sDf0zza)i#8CKgB^9;sWml`gfv9WliTX$mp05 zLpojSeh< zhrC4bFys31XP=(_-cSEzaXFaO1H|F^tPJjF5?|0VUjm~Pk zd6uViyUWxaPMVQAZ`w^0Q8KnvU2RXVe!X~XEd59tem2c1X4^dqV+^MlJ-B^-`pCzodQS`F zefHs}r;nRTU$?SZ`B(Ge#r^8XbhH@l%~trI#RKBf#MZ*syx89J%632mCDRPrNkwli zLjoUl@zm|E`@DMPZqM)*VLB8eTHnlgI)d-JcEdbv)^dZw=ykm`rPI6bO4t`ai^IBG z<*H-M>fSzGCP{l?sEOEtUola!Sh#@0!PL26Fi}@{J5b<$X-d(lPm^Koqa!_sPo>3FYSVPG z`b@f1#-`Nx4v6@EJFl*hH}S{nTogVyWSyerWYu?c*@g@99PG}rWM(aLD#L+F{6SFJ zRI-Kwj^`3X?&(IE71GttQgUY*^m%&&x;81rAfD&kn>hsy#vwR7{8UNw zP)7UJ=P1NPj01dhX#*dt#G3-3<{>CgV?oSZZ(d0_9#e^lT+17T-~*GC zSc7ha?+G&3)mb9-IBz1#97;C@7y_J-J(oPjId-trR5(Eu2P!b37$0HYdx`{C%w}7B zg4GTdY-hDl+c!(!5I6=_D6QYI!Vn!M`&(btahsYuJpQj=eRulJ_fHZKS2nym{jgmp zAKiJ;k@>~_T5)yAtZjrBB_oR|y~30c`bA96B^R(ay@5yLACK4X6s48hpqNss;O!Pz zOcNvszv!042Q7+xa8qvJRQ1MtQB3dRe(|-tEu1|4L&AUWbpLwO5_)cnL<#5OMU+;@VpZDX6QCEt9t>Y8MD?x3$L7~i0Uo+X9&kH!Im+6kzh(p|G0jC^jTqz z?n8FU(z|ZNqrAXpq0u<5GC+Bt*DxYp2yO=5(Q`@dW;y!0!9Yl_3TKCKnmLgT_)l(< zpZ$$?kf4l3NHX@U>9bYyW5JKJP~Gslc;KZ_&rrux^K{U^x=-7ue5G;SDEq*ncD!Q> z!a0;9S5L_W1_ATZsPJs>8%B>0dGo$Or=)U5l7F&st6W)Rei3}TF*DkpXJp?jud}dM z<2vH3Dq}Qb$NPQPx}O7LrRI?jSsqs|6ZVIX@I))&&4kPU;A6z;7x*{(z#{r+3Lbx5 zg!tPjMvmI^V6@L&+kQf9a4|ZSr}QS5hcP%4>Osk#Z^5(*cu1y=Jmn?M8vvb-RJ8P1 zyoNPMsp$hy_CY#}(4tB_6yTIv8~XZu)WN{Vs;4srL3bX&u|#zsJ7Ht4!mR0>1ZCU# zj#W&W8&c|5V6`^{Y6pf=-Z2D4W{QoNwVV~JrIaQGAXQ*Pt$*9??lskX(&am1i|?Yy zix}Iq|4lI(du1hnzsf>Bd3p8p*T4Dx^q0T>u2tEXlR&3L@85Y@-rto|`r`D-ovWw& zx7~=?R;?JFg0~~35NN^h!}3YJfGd_T)NJgVvRn$z@EBv-2W-U>!`mGZH5yBh*S7-^ zh)IJ!p4Z-ag8xzQ5bhWEO0ErO*PCYEZWWT1-_@zLKp`$Ex;h8$FXJXDJ^S2w>lEP< z))}dJE#W2xqX4D!eAGVfpL`yJz5eQC_8B57A7#;{G7@Uh2Qv|lrBsA3o(qFY>WrNb z3ev;nS#)2>q;IyPMq8KVFuwM8@P;vC-0Jh_tyFHh3q{r8rQNnwGtZ7DH8pCU94v83 zjQlD^E(9WVkvDt2t(sTg-Z}lWDHr}T6L{P$kxw%+&x=1lZGZa1LM^uP-N{40(;%koc;Q`_Usrm5Hhr%z-U-D*xo8}nveDQb7jH~<&U zE?PKZxO%%3FY$GYP*-~{rhdy}kOH-G)y}K%WF&zzg>%dRlC{d;i0_tX80622Tb~u4 zxlv;K&D+)iawRAXhy$8do!7<;lRBvTYwCIV2_c)M{>%L$1_%m zC9Hc-@0gaqNx6mX4xUJf&)^@*i@ZB_tVV0cl)843Kl|d}wsLpt7X6U&3|_c~AC3Zi zZBdPLP#+GX@H{}))c7b|@aE||v^0H)`wpOV);AT7nXRHF7#j7m%NjWy!Cn+ATK-^=zj*S88YqU-VuBPM$A6)?J6?EDGTUF}$`U*T4dfw{cY4Gx zE6oiM*b6qEXgY1gAy^dhV7Iy*BV%G7;qwIYO~=YVesP|VJ7nVRast2j`s>qI51+U2 zQTw&^xc$%HwF5JeExrZK3DN1qBJVvYTg5Qy6Cfspg1|dOx;pWW9YqYv)vmF) zo);a)w`gZ&k>@?0$tZqUp60XeN&fb{+Ya-19-lov-MjbE>7$Q6DW3RA7eW=D=#=vd zxv#%9hS83_ed$VqC&M&sOeiQ_W@qUp@Roimn^?c;zVpp8LiU zfkVq7$aznM*xZh?8IJaCV{H83sQ2g|4}|LFnKtq*`t%JCa|#VTHA6JXpr@6$5}g1l5?2}OXI1ntEGY6`k+;M zhP^g-YI&nczD^MWJ;K2kqS=_sHG9Hioggo-!zvgy%{rg>LFOf*c1P}G5) zB7UA{_`8SS7SFqK`d!J>4_ncFoE!9qZ(T4&$;NC6j`BPi?*A+X{_DLy>ojt79cu8F zvUd7?ah?FHU&+{4N;qz++wE#ELnZ+<#l~o+H9P}K)*T;n<7p+9Fd=e5`m@H2sUZH} z{pQ!FkN&;e^PYF9(+`@$wfD2}TS2d~{|Psx5R*l0!gD1fmDmm*VTh><9Ak0B*Z@$u z#l^z?ru)HXPw&Q7jVZ?9QjK4q%X<2#g`K}hK_5xc1-74llj12~(~X7ovrCLJC<}!I z%iya49t%~qT(LowkoR}5gy2wU$H&nRY=LO2UJY+dA!{QF{3!%XYl=P(v%gPGnQH4$ z$&BRDgDG(?&{myI(;48z;=qaN1l|r-Q@$&ufl(T7-Z=5+`su^_AGPJOmF;4;$7w+f zUC^Ro<9Z7wvyGnFNip89TQe225r$|d@hn=Fk&o}5w6I1QzIfDBy}q9{6$k2aJW(w^ zm8yn6UW8kul0swpKS719J3* z#&kWyNm12tFvq{BP#sRf22mS}PuI|)92uVE#`vPucK;~TdkNkuUn|9plVs+>DD`x> zFytLO7H#D`UoQk^JEl6Q&*^v{V??imTY8pPO1^fAeK>oW=PmpK&lV$(LquW#NSFU- z`hwvpGI@-fJ_O8CXR@%zrt6;fy}?wl@4{izSITfFxr7u1e9el-B+h6%a-LYsI_g`g zHoLk&>QAC@D5ZmN5V^R?N@nQ@AHjmy5N6?pXgKs$Ym}&XN+cb@c1z%s?@N;YeQ6Wl zrySp${@s^PPv1KP0m4Js5}RiL2<#fHplhf%3 z_wN?mw^G*F1L8PMJmnpO&hx5o9-911o)UqWm5jT#@=ic%&3KLjJ_0pic+pgifTK4U z4c6`=Ov}(v9z^kGH{;ob^CV?>#C1%Yq&~I%&kcbYK;+owfXYwmYg3vJA>}=UzLZ+b zZ_18WXuDuqMX+H^aA4s$ytly~P6JLzo@ec!@qq7jiSTk+54_WZDI`j>I=o7JVTDx* zUi4zG8>BWX-*YKrHxk?{9nkUM>W@!%ogvwmrpr${_s=!9H}bA;G&S6PG%1o|LD5W1 znbD>CaW}w&JY@!C#!n3K+sEIXe)-E^g|}9{;AkP&BRZy`c%KIn4!~9`$m%d{Z?)$AEqrp5 zruC=(0|{eS&Vs}Bh$|4S-jX;ISaCzz_dpO}F!`nOsqXUv)3zGbV_&LDadP#09}|tC zOe6blJ1n@mDQ+X4H&uCp!voqyQK4k_LN`zgFs9B*1}V`b?Q-~Op{ zRvONW)BpH?{<8a3zB~P=Kl$wRXFvF?7-xHuYiG&O+5<~%?T}Vpb^^A8T}RYl4YqBS z6tEu?4TNw>fl?lnV2l*ac()kM;RKY_ytrBiOTg}XxVl;D$Gh@SpSJf{$m8|f?))iV z?xJmhn?gcUyt0#A8oz60apV+MZSBf}DS>0L3ll=hF=ec1eSxwkgL!Em;ii_xZ%1z2 zG4=qM@<5XX_=PWwJw?1-P-z(8Gj(XbcH@_ZQJ3|Lw<=E=pNT6uOgH8DJntJXI`su? zu9|S990hjV34ToN`Z9++zDxSV*U3zFrN`o=uTQ`H+b;?|xDYL4ml1iAqJG?rC5&Z5 z6W-c3&K(O^sAqRv_rxqn{_nBEAKcZ)Ularnjc0Q;8C%RX+L^*1UVpqyImWj~K51iX zu$xi9QSB`D`<@qiHcvEqNQl32>tAW266RJZoL15Q1|N5Y8-FIT1N z!|EGC5Z=|D`y8M#n}Jh8U^$SqM`QX~(-_q8Pc>TG-1deO-X#2fT2k=q=T67|wz!y> zVMOc8ozh&HZ9|G+3(#VKUb;M9ASIla#ElsNKczvye5@`7{CbGYlW5#pA%r|?-Xt_) zc0^yyt;d(xm~zs{wt+Q`;b}cA4dvVJ34Zjr`(eW6KlziUdZl}4Gc;8h{DSpxdst$j z45p7UQw`@Twj$Q_>qL&PK??;OKIErfRBz)jjAuw?McsNay`fA;US(KR6^c;vEtM z$NN`(WrJ*Ed_RNo%?vX@@p%={vrm^VpAjth`^#Zig9bERQJc|YM-pDv(v%!ki@5M` zDEgy0%Fu$W><#4(9&N;dJVOz2`sOI?cZHUo*IOYvp&C7G##hY( zAU+k4#^kk;LK+h?xxl_Nozqy~PRMZ4PWk9N4X(a1M~!WaAEO;0VpeW8{e|Hv4Y3n( z8>?}C!fu3Zi&sRpGb1fF04ZIHK<5A1)igt^YJwoZrwr!pOb;uMX&{otIqk}U^UqsZ zYua?W@%8C9DV^`z#?|b;GBH(*#p9V^23wB~L2g8>HKjmdKpG~5cq@8GVsE`QMoeak zbKK=CkFg|M!rFLYW?o9%1YTBfo@CZtJW?OeQ#6>20l7L)1kTSoK=$G3w_pGM^k<*9 zrSHbQ;Dz~K!Emh16G-*ozP{=J16r`!sVEiIx9=Cc@B4Xh=o~{h(~v<}T=%^~uRUzn za1nVpbUx;VRHN!Ya}xKIS~N;Yobh@a?;mFcyzTIB(LsDFU!wkdn@5gu7k=5a8c)GB z!4AIO2VMV;@lX^p7uZ+uS24K4P~!nJ1)@H0NL}R*UW$ml$X?h8uK1-X|B(+@O9&jz zeD|{R|DJZL_|q;;+l(l<&1SBb-V(E}KY6i^!g3bk8<$&62Jk^`=%miwXDnCnX;bFk zef?E4ffVw**B00E6u&Ak{dN5R=-I`o&~2FxS6X1R3?j}H_*qrA@k4nHYdM#@%Hvw|Ajad02r>!c+|xYaiN zLaLoFGXkb*@D&Z<+37%NG-&cy(c5pi5Fx#99KCYNU#j_epM8V zm8yMz{ph~Wy~v~xWrYF(9(u-j_5C3SLEFP)7f*HTbJ;O5%$hLn@Wx@X!)B-Ogu8j6 z3Mdy&k6B?n9Up=L{WsKO|zsq@)N#n{ziry8;OByss zX9hoIu$Hs|0PRNb%lA7pHQ*M+4tWFu(5sh%1mpyw@1~fe_u^bNH}@UFcsPw`=lg{v z|BfDzjAHCb0&MfP{O$dwBZd;p5Y9JJvrvn}=}-nC(EbsKGH~Q16SgVw7N? zXH#SQSDVJ@mO?K|;H(poK73H%K0LDSv*LZnAn^Jw%FQwj5O5zqR28{Fyj;bmZ}0n~ zEN^#r$i1d=cW-puQi;iu%ir`DnlT=OZlP;DHQa~0{*BP}DR&brn~D`P+Y~qa&XTV@ z2B?Vy(!S+=A3acsLE$Z760lBCL$td{DB|a>{Lu@nuI+V)qJfF(Vd(0KZ}B4R@vdYn zqC)NAo$*-xq$~rWI`9j&L&*_>#oD6c2pCLQQ8^21!ZP@Yp=FHcg$EEG!t7Xluj-8F z5~zLKI7#FZsDqnAf6>14N5ur^MKstO#mN{wFfu-b7wyHb&f1e(g4F3{d4(t!w6$m2 zPD$i_mKL*>@su5Tq7VWoh1nUC;o?#8_V41ip9S}|Ym$jms{Q7%4`+=R&xe@}yJ2GX z#f9fl)eoHGYg;#sZDEjRCFsO+HcdBbID{)k$26Pwdb7CUMW^q`!mvnjyQ?YgxF5bM zjkFrQbV|wPh+M<_=v!k#5{Du)ql=Rir0x>=2!&FXay&A zu`mwD2i)W7S{v*KR)w4*WzUk&*a9L8Xtul;0$tpT?OmEc92-&w(t_!%g-C^ zbXQQ1rx{TAG4LZSIL--<`W(**8$^ZR;kUFWn6^9OBbWFJMxiuahRQLxnpcWn?~Ibesk8;;Pl ze~ybHg4Uk;Ue;X$2ls7FZmK1sO+dGLZy-eHkN<5REBoxE2+q6nJXz zJwsNnV&d>nLmOjcn;>~s>pspiVu$@q}BmFH-T8-0pHOqEc+eN#?sIN?Azx-9vs`ARYBO3>;7^fEd4{@KgZ z7oCsEn17t7_Cb9TMoO5BpcNchw5tJw2$-tJw{P-3-_KNpF^+Q!hWL4`4$YYIK~R>f zCeE&<{7S~YRf?DW=J;y>1{oSi77;fe$3W8X)mW;`ah$UfzA2E+HX0+H01LeFw|N&* z_0VkeXbcS^`s0)2J~@07pY(9!VDR9Uvv52+-lt;c;o|9I#0Mei7X;&oK?i=D(Fvz- zQ_%W64ohzAYQ`rUcetqQ;JAdF!1jJ2w<>|7fKRi5YDE`L7T&U+vOV{e!OP87d4Gr9 z*w|{FRwr--MDi$2YfuDgOiLm5oW{2sgr$R&&GW%Ogknt0VGxKyh>ron5rz-bAulBM zeZxJ3vbL6<&}Ym5Ia_Ks)o36Bwu?Ik8%q<3dk!|~LcA-MCLyeOQi@q`v={i3Yn@K> zrd#id8U4Dv#os>a+RF}scvBoQRT}KaxYdDl%-W+~v3L&-&p4$M;|Z1<>pYouU`P=- zbA+1E9D?9k8lrNGkzl}Z8YsiZgOZ7V{*%9V`q}5D=7hiV?Zm0QI&`d;98mFG!R}oH zP{5+SS>i>|mK6cPYRW+#z3J}lO->b!-u7B_V&!=Fn;Jl*f?7QRe zg!=BKKIIQDrb{!LT~F|SiWQQg6G31IqdAG_uVb^Miixu_$m_3P6Y@ZQ{`_&foxUlT z^xM;SWiUL?@ZT%<_DYLv=VE{D9(iu7Sv)Q|+SK$_humImL5AJXuTyr8OVo@nWfku& zfxUia5Tnt;9dHrfK9T>}%KGE@_htAzFFhz*+L-VS!7U6US23(e1Rt-MlA4(Ho( zL@|fAku5_b6t;ds@OZR}PFDJ$?UNtgzn{0*bbs}0Ii-Rv>!SLp!1!e}2?l%{!VXj! zB)a%Sw5M@AI#!U$A+Pi>u=u1-tFJ8vgC7&oq7ztq)t^kt7^oKOi%i9rW+oDTyw}y% z6?!t|X*`YD^ndbPQT#AXh3|v^m<88b^q?~q8;2Nh#252KgUf=X?>z@48L)qxWnJ-j z)OS0Dsd{7^{=szlYG`$S7PIe&D3g7bq}s(hYrJ9+l`ziQ_*`uW-t;mCCX)uHO$udc z8Dn}1I6~AZZ!&2c@`I?^CH(agZlTz~%TLJO<_@`o`B{nc=UYi{ zx@Ca%1GcBHzAKM3Z!h?*PG4~%cYFZhTVzRr$VYrpoMcnjLJ7h+*Kb6drp3oDqS{L7 zzV0{}`uJvE;MMPJD_>_7WJ~RD0zpDSE}!+IB)pOy4~66<2g+kMg`=YIBCPw&!XoAC8>C z;MDx-q5!~?WaB)1>xcfZ&zt;jHmtts=e(Lr&w^q*R)h~`ob=Ryn8uqmXlMqgL0Y*^ zAhph|E}!RFoOb|&*Q++==W|{Tgiu>K%>A~fu_=t#T-gATWNEsI$fhk6=iKRPZ)v^h zqAMm{={N~4C@%PoH#T65SUV1^ z{7&-lF^z#7N=!=4(=0rX>)eWB?PG8p;4i&PIVk%QWA$Js7zcv|La_}5uv_3-X$>EB zp4{L2_-Cg-`|(d>{)Aaw3|NbiuW?_pEy5u(hU&$W^KJ%WqJ0s|&o>xtn?MIiU>^$@aXdt;^PiLc$KhSyU}4Bt+JoRk{`4k z@LG#6j2+|8L|yB2akCy=e?8A~DQM61c?9Yyom=I8ZfjzS7(F-Z2_AeAUU=^LB`QP?GtL7Pt(KWAH8``?_l>%&0C>isGra>;F~?j~916<#&ZJH&oQT zkwI|i$@LP~?>uNXN-179I#d_&Cj6H)LKM3@aGSbb@UP!PA@;Bbi?<>C6g568t&UN} zAfrzPfs(6ti)4QcZ2$&L_(1!O1^u)=W94sj2K7Ata5DZ&JB}?6Xex^zYBvEPTYJ^! zn`I?Xg5Wp?u&`CQpj_z+{rQ=v2?%BTYd6o*p};82(PA1S3!jR~3l%~lQ)2Ywq#02H zld4_}AU1%LAxfilGZ;45rm!)n`(6pAleM|=J1Hb2F1DDbF{Wui1j9F^22b)nt-wn0dC^#sEluVHI6YV_ z=IHO~llGC8@5j4jnIzhp)|rOgD#-mwLicw+|Iz7(H}7ZJ8*5BrR3RzQdsn+V_9&R` z>0P=A9I(*n(0|%*UTUzs%i|Cv--#rBiH5^xI9#d-ja|JlQOeQxtmE+A?;b?2%f1b0 zFP&W0Q>4|(V{uaZIEu1>|GtD*aG}fl>VVt_BkO&Cc>i&9rO>}&Mx!#Pd|Usu84Z^U z*aIgsGm^eryEw%d`nP@HHGn?Uso0+j--S-Jk*>~rs$2%kR9vbD#Y7;_o#LG_#(6G? z((l`1U)QgF&{>Y3A5-99deFIxw>prPz#GS6$azMu6UaV9+Z1g=yA#lFrplkTMHXz8 z=TSNDkMbnYUK3hK2ySCK4BPa5%K7E#+cxX$#=ZFJ-aET)8cO(r0oOM0U>qFsai^Al z_QM|(=a+YBY{8$xe%n<4T5^Zyo%DY5gNxJs_RpUMC&dudAPc8()MuoHaJ`cfajzUi-VqP<;I2i(4dSDjz>N_Kj-h2p+OFC!Zs(36i8~a>YfG}MC-qXz&YB57>Xr5wpCZDC|2E`T(h0z5oC~07*naRDQc$mb@KMweN6?czVhJO+d20>rI>K@09F)-my?Wy7y`4CH}Bu z!|rW&fH>*rAANAT8G-1DV|o#;SpLR1Z)(KGsK%Y=6eA5*#1;R;HI1QcNFm)a|l@j0s@{w^~Ta0Z`NZnow(b3~Y~nhAof7 zf)F0zN!3C{8<02;eywleqRlwMsZ<)qTN?~7CF!7r&px_1ef;3f?!MW=N_B`^t?kWD zE5er$(oDq(PckE%yd!3=q_Nz{<9qU`lWgLnH+jPsd2dVHZ>+qvDRz7uozQkd*!ZLd zH>1w;dv|)&l=VvTb0dR^CfnQGSnqU;A_Pe%f6ZXi0{cE-fA+~*l3QA1}nAof$+1?%=pt+j7i)c9@U)Zl?Xo{e;I98+#CpE7~y0f`U^)9zB4CAu7`_KmjG zo~;ySq?j7ykGTZO^&!@V+ySI%5ht(Vl`9X_qm%+_`q6QV2E%JvznM)-kDWbiy~H9x9|~h z31zi+KHw*vrv0D){Xg$y?L5O6%#DIm==Cv0XK+0-afCMB31Qfj7mULtdQ=GG88?7? z9lr@UKZS;b8%i{S2cH@T^6CT5;i-pu zBt+YX7aV#UE6X1hCWySnB)1xPkAT8rOdTx45ibM&uypv`R>8b*p&6+swglep0NZ)K z#S9Kb51;zgtiEbB+}F|Y7}f`Wj$SfN*qgc*cl{va=ZXpGNjHlZUcY76MPZ@h)kj(l zW7@cKliwu#UifqT$f~w$>PbeI{9WtVE+>GS*__4OfA>#*bowX%{2!e@>0C&+7=o=0 zjRzjxb<$NBuV2YpXw<>8Lt9b~@Z2p~JwKcUq|u*o$-@=C!57Obg|Uhouko1Sa4I}m z!hf(_?V_nSV=g8@3{~7cQ7Gs!x3eA)0wbyuz%S_A)G?@@eBG03|88Z_U%JYDh zn@)flBp61N+D^&5e^C;h=Xofz>Q9OG3{sEWCBk{6vJeOs-<`b>CcroXZ+!hSgkV|f zPu`szx~-^F{J~`$+iF-o9vC6pSI-KHk6FT!-5r?KxGdOA1XI8-Td7P}4B-N{`rvU& zgVE`OJ0G6@@qhaFPe1xNMs7S?9GZg58Z{O;SjxwcVawLviQU2!8@1*Gxm{^F@OnkF`A8+B00)V0UQ66jtctk$3FtoqiT7bFd(Mfw zzP-^cyyWTGW@&NZdV;Raqt==mOf|np8&fcwnrt;ZgaCVsGM5ZqWAUAH3lr*u3iyPL zRp&>{8}6Nc#(xw5yh3iTFycU(zN_IDy=rARSey~AD>EaS)qTtIrn2U})W~_$%^FTg zFw%WzF`)|JrWWDV=7Nob(7+@4s6VU>DvNm6u6}m|fhIeO8SK=D0 zbNwE4{e%N_EgT+stI~W@2jTW{E8$xf9ygoeDKi-y@{MKT5fVD;G zOo^yReEREzaEo4V;6u&&HRUFkj63nD&CXUdheI)Q4>p+1F?eekIUY zd%bVq6gNJtK=1sk*!pjpyp%sw)0-J3eUj}~PO>u_2DLhD|pbBvBX1V}d`x}#AF;)+_ zsa5R-0G?0pD7=J!Odc^YB=1bzzz&Wh@%tFV4%9_Fai|D_(O#D42qjlaCe}731(yVS z1WEzB;Vdm!VrxXLO$QA;b09*0zbi4e^^vLQ}C?Rc*L`oH)`r+@IHt{Avf2HRg<8N{WG#Wxq{tWIDI?w!Qk z^F9o240$jFA&CYnd*Dg|V`rQWrP0)j7Ew85Qu012M9o=K^j*UUlbHJVg-3ZQXgEsAmr6ZO}-wMQ9khuMP2 zrq0RDh$&>?@RveI8LngNZZkv}Hbsx_ZeA>k9%hlMx|CdXVQe@njJC9)a6CNY5&f=} zvD{Hko8iOZ8x5{!2ftJ>{gM&mffmM*>9HA2GS?I_T-2wOrF4XulBcHSy!Im(H%NG( zeS;_P0ObvTczrxlU*g@?8>|)J?KvrZPfvM?VD%?P156JQ{MZ6S(>CA$xthzL3Jc)l z=&)ybRPwHz(Y9@U0bsG=tkq~L$@FVTirE+3jZq;E;RLXGVosK}c!Aj=@`@GddXb{@ z);Wh0;7djhafi~3krN7cV7y4VJ#$G^BM5u-ZQS-~u zZ@Y3*ufKov^z=<>I*&OEDON}2Z!ra4LdjV}S7BSzT3PwhTSKu=%?p3jUKJal_xXOBX*K`sejroF+)t?thz^ZpqT+=v!hAq*1SEDPyrPq7=!0Ph)$fs04qW{5&$|C76P#32juM{sa__8o@tnhc~&Q?e8x{xY3a z=gJk1PjrYE$PvW|2MsWutsQ-P;k^ic&6%$$^@DecL+dYm8Q&a}@mexUkylq=+EykT zeR!MU&!H1`&LG*`$Hr0<1lE&7pnZFsppo*yU=^}Y_KFq2TH7*sKpm$9hva$#uW8@} zk#`Y5Uc4964#+i5$_CRd-UJFntuiLsVhybpB6%sMGj9DerF~i8`AJFC-oy~7 zw74Zp$({C!c@eb+Gd#zt=)Clp9iQrhz`sACm{Qa({O>getBm^fN*Ih)Aiq+>*RkiD zIg5eDxZxpK<{eyG(&GZAP7~@tgn4FFugR4s>=@aLQte^5%jhW(AmBY>ruEnRO}YB+ zQ2rH9E6?j}T3aM{^sZFA9S*9`BwuL?cQsFB2PK44@bf_6%wh}}_QFuTFum8V&8Q?4 z6CH}pH?LAGV#xD$)QvA+#@F`nJM2K{CJk248HQ#WS#84#&boxl#wiP8^z9iaLc$c~ z&C+JB=S4=I(t%PEU}}t}syv&8Sc1q@`o3!!f&(pYXJtR?z!2F6ciZt~Emfx*OMOrX zPS1Cr`J1<;y2(oDvDdck_5C7a)p_?ioMnuR*384gDiHD8;I18>^!fR{#pEaj2T|`_ zRM!qvvPw`;|M7x<3`=+&C@F8e<^Yw{(Qt$GSIP_^PF3pL-t;}E0*?jsbOm@1odLyt z-#>mZU58u3Cx zU(YC40$QA?g^ogQuPSTdi4NOl2PLY~3sAHxCg>OD1||lCl$dw1u%6du$^^n}85E-k z;0AMzwPh_*5ZYT_Qf+Z{F=Fa1H=TQX2owR7_ed8kw$p$ubmS^5hGen9?vJxP+NXJY zVlu8^qy)Ek6D$C3y!c_n)OcDqFBwS_rj3C?ih2fo>H!eaW$U^2?D<1Z`Lrfrl@1*+flsdn~%o@9@s;o_6Qmt8$z z*JEQaHZMFVj_ed3;#VV~qw(O)p~mzxUaxOyJiKs*O?6%8zFCkU0;1JRJW6KT41rmW?2Unf(F8G zrnD1;UNcS(E2)olRDT~svly*nJG4OHN#q1H| zo_}9!AF`0WR*(Is5jsw!RXPBGhO?S*&S zqpD|}z66J6!<#ZE7mKsjkwvm8=oJiiwMXg4g_n%C#_ObpyfEe=SCj%5nRgXWrdb5y zk2!tbUi&cZVwe{g)7cF$&J7VedJ0=YP$tr@fhO!VPKi>cJ@0QI|FORp0_9)`5j$8l z=H7w{7yTAHXhq>;^sLE0jnr0yn zFBwzT=m_gsDEG1EN74g~p7UuazG5HYcZ!796454%5dy=i|6Vz76}RsJN`Va>fF*H} zmlD(q77eof#&GU@VMtUsic)T9ELN8Q@C*P~Gixcl-H?~r2Uj}S_xqx{?ylMNwW40{ zlW}*ZWo#wZ>i%nR4c+}Z;6z;g^?U#f6Z7t_Stah@)jS#(9YLMf7EJ~dL5IM`n-UTv z=jC0)+&rZI&H%wa^<$aF4e#|s38pA^VswMQ(vkn}Jh^&a z`ioEl-tipF2jI0KeFlF7Sl%oim||pX2!3VJdJ9SwnOubvN_Mx@#c&*fgF+R6sNf^K zPCs%+0nR&=LdHnUFR#uW&oVsl27vstE^r)dQu`YpPwG&(^Ty-bS7n!2@OsrAamrhK z&G`c2iElcgU+N3I;DrMYp_<+|?&EZu{-#_GkCSH~zJu|Vcwg^lL__VHQjg%sVIrKO zv+(Nl|34<_4}Uybd=O9IMTFj8@40kQESTW}*|t47T6@2lXvlQ1%BJ7iz!Smp{*IY@ z-GT0u^5-4k@;trs%C0ss%HZ6MkB!s$3&H_CJd|W`Ek;=%P>{lG5DAe0t-YZ~QgH|v zL*Pb;Nnm(xR%eR`E%z`$t~#i)?;#W;NL`TLL#eWiZ?&DLGNHi~l{Pgxj;oI;0&Gj@ zn1ZKSKZ3q{1&>(bVo(u>``u{pLM~s-*WZ^ZNPAF!`|#Q6t4Cd1(EewaII#x1XsV(+ z-U;G*9nXR-4LBkP&;HdG$n-S=*Z4->ppHDMo<}&k=Ffiq!_&X`M}P11aXTR1$eRpi z3Ttt|+6l^I?7gQCjnQig4$$v4MT!VB^kgs~uK47Xz;*x;42pG?6Tpnna9DY z&Khs;!!?hCLM9xPtM_Bl`i<;81xw>!r`rF&U&b)Yo}t;4FNLvW`ik7nThz{rrclqy z*PF0iwrRZj;rSUO!k|BIehQcItIXj^1>3&wS>xaLeaFj%7jQ42-l9N5sa@k@(VLRa z>+V@?gXLi23aQ&rMlZjI6hl}vV>`dZ7f*7uP&PhcRGdxV zp;Ldnq`%m9$Qyi9%#FR!10{f7>#N^ekQ<+yHRPFkPblFgW9wYVH(hh;)NabhW%X{i z=g1s~QNyD&ygWRi9ZH)Q$34#@I^vQBAN*Lib{1o#6lwjs1NUoNhQsRxUhVV3OF0S+ zUDe^kH20WQRhCg{{PP4glTkUQ&GEGgz3?_33tN5SAOSBdr2HBEQK|3AuUM${ijJu| zUA!*ozHLpNX0TfhLsN}$w9rPMT1O@C({bZNyk&#PN&^~_d(|OXE@P=WRmuAbs>4f6 z>rS~8)P@8(LxG$SaPuE$6`HoB=?wt`LBrvl)W(>x_9!jZtaAEv*5ZUHDA#}OiHp3d zUml|Ax1?A6{@%SGmSZB9!~gQP&riR3_^QjsQT(zVDqcsM#ReaM!6Dja;gHeRsVr5I?>fzeRW(ra5lE2po6?qC+y z2m{kSH5BafoZZFM=(5ukN?vC=FQS-|r@@tJFjkihn4;V{iS==$xIXQ#jY?ZeZrzJ5^*ky{;6Qc%zYfh*TIAoWm+F=0#|uQtX=roBE;&XtOR`Wruq zK_L7DkW}~c$)|Tu|NJk0e)@m^vwzrW%&k)Yg zhhj&*CVT`>-{Vklu8(RMO_<;Kc7<%hE_Ac>q6CUiq#bK-#2d_n3hCZ#`OtWRr}xXl z%z#_Fxq1Cg#ycJ^X2*kk(N2-LufF;+KwQVUmD2=X-tLumw=#h7$;1LqqDPty{_rUL zP4I#Zf191jn_MVmQ@=j1;Y92b$MB2T<2iUnqZH9KN+4J)jGRwF!rbvfSG?-zvrQeF zo`R2}gu!Dmg_E+ThdtRLVCjGNW~ZF+vDjB|y?Fhk_l>jPkw0d)?wpu;f`Axwk4>#|AY4?zy{kx0;!V5yY1+%JIVRd5VN=M`vNoV6dV6 zQAg5$(J}I0KI{t8_Ev2*uKv|DKCU+fV#h#T)USsK50kZRf6(=*AI4x9GrTwDw6ZzaRqvT!h(xrsjfpEzTQQY?F~k%KOCOFr zWAVX?q32yjoFLj~Q#3DucuKKSs{^^b1%j~Y?z(Hto+-uKFy@f_Uh$y~xc4)dDjDr5 zN^o24>~TFO6o84T!Ih}>zLSBvPuP)Y?zPsYsrt6Zt&f3O+h|+6YtyqtgfQ3Ma8T!m zP**Mn=bd0+-I=~n7A*LfIFCVlVxkuJcxZ7zpV7PT>ZfYerCfRw3%=}p#KpsUpVW2O zdxo~a`AaqZJHDu-0`mFSyu<1 z-_Is?QuTNwNO*)q(ySo}-z3LYoEJw-kx_zIFFI%fUp0oF)K!_i{Y?m>L^D9M!LMLJGfY=1woVSNv6B ziBxHQ)_)C%ssE&?3OOX&bX$0I<7oAHBx#+kiW32iFeN$i6Sd|b~erHySu$*^1SXZKc z`)zkd7d61>rLbI!L8RRzU%i42aYBZ41jqw1VP)|*b&N|d!A;002MiSKDP@ZbJ=uGL z!g5)G{`rqSJ>6>oVsMNxW>MCk0JJoa+483Uyk{$`+k_t+Zm>)3EM#D+#^qnz5dhKQ zeMAPUdI3Z^jd6Om_}U5tW78~2&lBQc_M+Gvf8QvFL(kqH?rHFw0#?SCi$$_Z{fCPs zG%JrT{T1I-bTQg+F=Y_zU6jaty_0O7m-qQ1#i#-BIW(10aVQ43dF*hH33*YCY-5Ad zjf*mvw_wbTCjhO`<0ox4+>Nbe#Ofzpm9?wqJX!8S5avCv7eG#bTC$5vcIu2T7#X;MSn?kJDB$<&0n=>5O*9YB zu&h1FHNKlr&qP9>x2`U(d)Py(2Ds;#U-76Njps0` z9^__c%!iRNBnH6na}*`U6Am?S-X4p5+>G3J?_Y1;RfF^F>62ClbV^-el5=v;&swe0 ztc&>>xSAYdZ4QLN4nm*6c3)IJu)Cx0{X+P>yuZ7qG0U>PeA?11&`DIsCoB`;7xD{f z$55n1DLNA)-1lj%HZ?1mJ^MO#b(CIh=@Xpe`RSMf&CHL-6nBkhWGq?=Pj<(d&UsS3 zMbJ^QjdSv_EarG4F;%p~7d*{1&hCjpALIe}?7Q*e5-R-&-JDgjVCq|8>)h2~`%Agn zY~f+YE|_@3kP2gykLW3+h7SfHPm;2~6C|DKuaT}dD8bX|XYg59BY5r!j%>^b&?tUS z)sMXCo{a@lYH$=&)FdFuFJt5B*i^-Mq?KIdLG?w?1Q%o1a|8bX*E2m&X(*ck z4|?m*FGDhdz6e))z6-y-8@1MRHz|jkl-o^5a@@8;heBY6(Y{W5Lf;K9eway<+BAcW z-uV4A2gi<0cHP-)>$}I#ewDwGk!z;b`RF#a#W>_rPGA3LmGexIcX`Rl<&H59j>|&w zyz<>V!OLJ;$V&OtHVM0~M2M2CkwA(E8P0JPyMkuH!7i>X@A5PoC6(s8VQTn`4{oJS znrt+2!Z^?Owa8FNbI2dh^2F$cZuAsv;46HT^_@>s{uB&Kl7=qf!?H|yU9%|5&h<8XR?iNc72cpCAmvhf0ZmiqSrBNDTxu?4cTOc!YH74r3ZKbRmTz zRivqYZj!4#VrP|61_s~t@qXrP5bkjd1h8Q^Lrh7AptF$q=IF!bPj6qfQss24&X>vFaxC5 z%<_)wv7V%G^hEhjv3~E3R|qJ#Y{}Z>jWB(E@K`4bNa0M*6Nmc7^YtrbG0MOG`!xOc zMQ!2%5foro?HxKY;Lf@`g9_A+jPL6xmPDn*HA3wfp6WC7Z>*u0cR|dv- ztF>-?yfgGz^qc<5neD`{m+{_s+$mo;ntUN4?d~<9Mjm+1T?<&e(Jo(IgKJ7$gwdJfn91gvN02}7p4%pIC{Zuv1G-JYMwRqnP^91nAI&&Zp1w{C zG~VdYB-HvAHh7Yp3#cl3=$mfu7rh!&@JTty13wr8wC;lgyNTaNSlZd1$*o1>o#bGp zxN7;%KupMvDf-8FFE6zykxt`m1j8DLM>43a#e`C|VQcl>)^#>N7qc5U2F}W65iXjD zA+U0eGg6#s05OGGzS}Dd*dUU|hr3pS5S&uR-0=p%rSI0UoEnf}QwN=##suA71Nt3=PYS7-9J47mqjp;a~sN=9lFb z_d>9olNrZ5_8p;O#oz&+Ml<@{XpE1NjNZNJH?&RY9DbGs?4B-Cbn=!;8_;5MJ;Uf3 zl9+~pi3@s$f~ncsmD>~cdr?3ba)h$)wK`VVGq}tvoO+Og8?R8m2_sti91wlI1mC?q zIi9@8!OwoHG7cS{RLh@_D}At&cT0rlCEDnl35`eimnXMoGlPXE2|th!Lz~RN50i?a zoDFEe%be!HOBC?AlVxZafe`(`+x6=RLJ5!Y z5#IHb=ekrUz2hX*sh{n5+Pr~zfd%IN>K@q;p4KSbT6;o^;O}2 z5eucUj(>oIS{@|;evXD(0UgT$SBJTEn1}SNR7e|}Y(E-h`T6Aa=HuHRM(I8I;#;h> zxo&ZU(w(!busCH<+!?4$t?l?^U`rYn%IITp|VKML2Kr$JK;R-SX&w#t%<; zG+d}*Hiv=dc=OtZL2BKH+3+@0db6G^9tU~-jhg}_4;K@_4TLLLVs>E z&?aV%TORfHcregf=@hm61$rPb9M)IEA9#cwQFK24?Ju8h{ zMh6UiWNg7GM4J0*i*h0$cnRVuF#Kf+*}w(XzB(*_N9jyGZ2j6(@}{s^o^*FMOK8w} zB4d7yGtY6m6oX}Sd*RRy@xoX9D;K!iFK=dYY>>jqU*dG zhcFG`a?xR@?}8Z-hdZW-(jgABp^Z3T^uV0xXMWhl-mzBiq>{;B&ibkRY6`275BHN$T==SMH| z&fmv3;kqLC-9ruzMW(0ZCIuHKG*W)3BJ{3LsEoe-#*edxxuH$yTEFCGJDK3&Eu$Dt z!a4aZ4vBZ*BsI;VyMvL@4-agVN}=xRxo z1I8c2AMfMGZtPLYI(+uoN<1X7YGxdTaNoD{R1d}w4H&72is|@J(bt_nk>x_bW%=h_ z$PcLmwMJYqkX-<;k=91Ui(w}02)WuC#?VgJM^R9SAnIPSy9fa7Hz~D~%|E_+)3K8g zZmeN5qiq52w6l0%Jc9{)W4OuH1m|rF`coyF7tMf|f8Tsr8PDge+w(`|Aq!p3W1{yh z7yMK{@}x7Sq5Hl#bt!-~wkKJI|LNcSZu39v@iTl};GUQ%`2cE?U zY~z4;p~)&hP4N*h7HgD#Sg5J-*0L-F&TE#Sg^LH}Zu&Od!YQI-EFQPn%r&dZnknHJ z0(#gB4jYc}fPz|KLl1cnUix02a>T4=JY4&WX2WFJZLl z>`YSi-adIbfWr&5+H-NRy9r5nF}m@vW~{(T7^2-M`izp!85KNR!OwG_C3eEEL0sOW(z19#4V&Duqf2@AmN;&w#6~$%D6XX>(BrgF z{G=5#QwdQ!gbl6X-FA7x)NvAwcRmMau-!m}S8z-&EJ8P|d4AzUbP!IA?Jw0DP!3qU zNLHc|1DVHa7Wu46lgn~?wnkHIZS#q#X5gt2Y|5)t!QSEH+BSLfFawY>OKHWZK^PWq z%}BS~_{FDwKc>V#r$}>r+6`!{TEDC6kB>h5sSx^Y&y|~$-wPl6IXR=XY6D*6&gq2* z<>+?W4|3ofdiQfJ=>LnhMKSxCAR0=2H-f>1Fvyrz35+q-CnXY$v%g~B!x_S00yUI8 zfJxFvgEV~42TU(35m-co#%vg3&77_ZLWH(*VK(BxltT6HUo+kWu1hi7&>?~Hg1+S) z2h+G@gm}zhUxd?W1QxGlxCnJP?R*k-Y!n|)Z1(W0FF8hQ+lMiu?d*XWym0F!{N+h$ zcShwu;}37@*XR_R6oj0ZGCcG%HHPR?D-)u{8(RcvT3jB8qXFg{RF`T5Raoy zdZu@cr7*es)bpXyuF1n#e>ZQ6LfOfnd!4sA$%o_DDbkJ>$fJ6`^L496Qlh~MKVx9X zM`NBmr_jrY8o&HI(wTs9iQJ4KfNX+E;25VF6S|@W*x?S0MzMIPC*m24AC5{3*dg2w z=En3m#g}(jK4wAa@$a9R1L^O^lf&ySiw?#%|v!uzH9FR&+dy9uG^8 zd@(fq^xp&7>z%SQPbDWkR?~8!!|o^NG`?Pex58g~rI@ zUm_*#^@OL1Cu55@$>3hj&sn;`JcrQSv!Vic8XmGigKp;FaotCt1p-DOtbMLi*D=R5 zUxzdE+#qMtCP6oVrAR5*?xz5|ACbm`!XP1R6lkrOSrXqXgtq>6w0rk+#b16r-u(G> zn|=196sLz842Cbf#Gt{0ICkoBCWpE`OgokEwRu~cbi^$E>AIr|j?0U^Z~gR1`#1*u zqzVvyr|s!kqSIzJr<^5XLDs8}Z(3sbu6_1mq{jF0!>7%E{m0*T^y<6K@BZemH~;?8 z*PDZ}es_3*T(8D1-<8D(o(L5jSIvbU(sif=M9vj`BpK zv6OMpB=8B^Fq3wL;Jlh{)7A)QUj_z`u!el0i96bcGfd#Vo)+589ksdIfIKYdp8Bi% zJxTVifgkM_ifR7Tmxc_;#F`b@8Q01oAItR=sF{5hWc}! zB6ulQo5p@_65{a_JQ;7-?wpNYn*%jot&`~|kmk&6eBV#s)X(ah0_AKtHb6B;Pz-$T zLr*vxo~Zw!!3Z+B3dnwEd2L?dZifEw(X(jL-k21ju-!Q`8BV1!%cDc0oRwr+2!7Kt zKu3W%Zp1{95PkRHX(4j8d@0?JufN}Xc>iM_XXhS0>-?LPB~BQnZlVW|HkJf#5IQnO z=EIHh9<#Tu$(@d}iU%2;S(4Y2=*A$Y=8l4MH(DAqEDL zHNYXNW=A0GOZTdJw2yW@PFc|E)<4P{aDfSq8pyG@joz&4Emt^3JZ-aCLU&%MqTiEt z+Ie?!w)x}hkF8Qb@Rm?^-%k6s?dSdMN5qF6c&{|+U1v?7Elz#y|b$2dd_ zLMlYdlWQ=VWIW`fjTQcOQYyQpNk*vq{;J7?nbf*^Qa-Lz1QUK~q|5kmQ;LSRR{HO%4XuwxkP z8YT4KsZwOCEzPDpCK;4#gKL<%FPm~o=2go!A5dQLJ*_8*6j9_{~4GVv(-$N!}!W)qCy?6Iy@GSjBJ*s+`Y>y5FaBehxEjYw`?8IC;b zgs;9yVRmDmGd_aMWRTZ9Mk6+-Sn>Ge=WBcMQ&Q41Cfx=_L$5sguC0FIjG{E^VClJu zx`r%Z#wnoce>9>W(mfN^1bZqg`Rcp!i%SL38Sc>@*{vvjKU%MkT>74fIQpl7yKqOe zhTW(Lo|h{2czlQ}2XI>lJO~|*J3BzJ0W4yj-sLWNC4Ut>cFn#7;YB? z4awMpLjEUD+cU3c)3xOrQ=(als}#UR?RHDM^Biq{e)D$o<<8S~R{nYOfByczZa&EE zwPp98UVXoL{9Rtu-p%HhyT2)Hf05!|3~JoMboL)JSg+at3x{1`Bt}>xI2;fFUOOeK z-&yY*POZ?Hd=E{H82+Uomm_rVMAL;f7zv^VdP`jDn0ulz||Q9AMlv@I~x^ zU))zyJ$(1Pabe)`v}$Esku?TmcbedEUsVoQG4UXx`;CRs9AT+Fh7+FQP$+qkmUnEe z`}b1KtkjH^;WsvME7y&0IHSK+N4eWsd1o-WT@1UM;yo=28iGeaDXDPwI|LM<7{+w)ne)YCPv07`|IHgxVy*=Cf{_hJx zE*@|8|J{qt&hzJ4$~#4r5E0VrY8F8adIt?%w;F)bTlqJ*SfiXJhT%j}E}7t&OkGt( zA0@~rgtrM18vz>dN=T;LvlZdMJLHNkmJ0FG#HNim;?`$y)Oc5W)n3=UMvX*Bx!WZK z7_s-mSdAmjL@~@aZvOO{apF}ZNMSfIR{rolOkEHnFP7}LXEFDLv^71RQGCWb;pJ<* zZwEdGtbz6$zsy(h2+x9P5B|XF>mrHht_a!d9yexR_sp~sfN9UzyMIQ>KrK&ZW~ZB> z)|cZaE)-AKFk>>MKzUjFo%Xn$7x%f1NqCN1rE7S+3^*By0Q3BK8lyZGEyhEx|H-4H zpTXb1dTi=Z^9F|#Bf;b<0}{E2AAqT%!8-T4J9ox2j9*OxQik%PWM=Yk$^9)-b+;=y()g_+hEk+N;+~?Ipe*E}z?G+|(zep*5)!)3z;051S@`09H z<>FJ1$#VuGewlPY1{+TpRjozCaORnCiLOg+iQcLY?888J$A;Zd8+qzYhM5TCd=0-3 zFZNzRcPWG6soEn$3lD?1VCwsN6o#FeyitrnJ((_@E%i)yG$5dX|R~ut~Xw@y9eNX-*q#9UgHj@U?51RJd|rY z;y`Lv&`EmTH1~EXW?5*%Op_1m$%SArWz_@hJPZByQv^w&>1%~}bv?~2!MIHEI-&r+ zM|%4IqR{n+x96L``|d+|#0DF2?FRG3^T(Ci+?}LWdqSznHBpKQHf65P@*?KHXy$H0 zkNP5PnDXZj$D23hJF&PhWD5oZ`n;OXlXsV!@3-G={`wcE?Zlk*&v={cF@DO=?$KrL zBUS{%5dF0tn8!%ryXR~c^&3a8aBRj~{nXV(>xO3?YNHH?W^WOFX5agM(Ar;ZO<0=e zK+(*sbnW^c%s7=b9ZJ{5cq9Gg)l801x5gSCu+OMYS}<4^b1j@t?W%!CGksoWyqq;` z^}%|1_Wo@`5-lkS`7TCNzYG!18714_DVw-^q+8SD?xWOjrhnmW=!CcKQUTFwl$rB+ zD(9M-K+IR~W@*iWPJW2+MsF{@_H;^*Vv7!y>@e1HlJH6RltBr16>m>y+jV)l{pufW zc1k(!cC5$Xsum|44F8j3%saBX)EN8vk&d850$|Bgf0y^VIF{gYF5qi+n(Fi1pDf{X zqhYWiiW1>AZ8za!U&H>34_#ZUtHz~D!Pu7Ua|dfF+J5%*%W{!l_IRm~HqDqt717wX zYWVu}^kaN>lCkD-DzjrSf~Nk*5QjFN;Z7-{t5(O{q#zk_JTb(J78L$OG|?He4bD)G zb`Os=G-D3wLw|TSkAP!cSo&4$J2 zJ(+I=%Y&_*p)vZ_{^Zirpi^e&S+ES4eTOeXcZ3@mT6!+rOO{8Q8Jsc<@8v7b+Y51* zXGgHHPYf`;2Iz><10gM!v^AdBLP1udfh5+0gkhqH3LO|HYS-=($`wARs3?P!=}zCt zdz_W*{&ey=Zy-e@KT%5*@GJoWd!NrzZfC6}e8e?J=&bnEWico&ijP6-^q+O*DA#!NleT2+eH}b8l6oQ!eG;<}C zxh~|Wg`$AkAO}4-=>|yd?p>jgT!^;fcm(FDUWF$)#lYz^PcmiGuc`pJ3rlsPEU3fB z@|31tto6|wnh}6oL5XR9?_k!4kO7d#ID$60weYmO+wKbwaHz~n#R5*57p4k! zAGPJ#Df@w!Lab40eL174@1L&@eQ1~uJ)y{6==#wYmL<(ehrST;b-Un=LX{D%UywRc!`N*!Vh=_rbgWjJ_ zZ_!b&<9IZxiON6$h7_C1Il1Cd7IWvFvd-vD5i+Ge@+i;V4R|GQA8jVu4(PIjqgCl@b(zTy#+0^2! z8Eq#+N`=gdmBxKtH9|3ZPgC3`MgoN_qHYsIczU}sph;mg9-JgyFnRRQX8rZdN~8H- zUJVE#5ejbmr9Pv~z`5oxxV{hVP7hJ&r~@cHET*)BxCcQIo73SbLvxo1d-&c3GFW1_UGUF^aqd0@ueyJz9Nm zn+9J8WdX$xcv7km5&)CLj*-m-$?KU(bL!t^4A1Jy7p>eE1rYl+>eCqE(^;DY6bd=9 zka+!+SNHpup9+PqH{Y~9*ss5S)Va=-QZw2qLCWre66wWxvXX=wKeJG^fH5C7bzkwr zDE806an`k|YwOwZetJ%*z;h7B{O;Gk+5F9Kf3x}3^RG6){?%{WO1;$ueIKB3J})IW z#dCgh>U%M0L&2xAm z8At&z6skWGfdD%|#J|OBWFllQTI3;768BzRir}^b+AgcXy)A8ZRaiW=iNQ3RL$pDJ zaQpHig?4>BD}OfS(dj!)l7}Z+lDmCsPk?Z$IR2tdZ7(R?vr2x- zGjeK#yQ7$Rt|>qY@YG!2a=-TYe)7_qfU(bmI@X;&^NZ)WG!t`(he!rYFg)7lJ zy46!ncc16+M4xxW^8H=R*cWD$r_fr}Col z#L#44hjzTviDup3_UrQsfAXfvrqn;~#!#N0)hKQN!$MeX2LEsrW}r-#HpXm(QHun2 zdF{c4j#e}H-uqtOn>=&3YvcU{t7g5|qI}Xs2dl**h?4(l4LmxnKS=r6*@RY!}?*8T}BuV3n8DK&;BC0Jil z7c=HD-Myld_@uVr-V5*4S#1EW_Z|-C*a)9e zl7xS)D_%IB=jxAASNY&M#=!U|!~f+X}n$af)1`b;5p8{o+}nH6~W8nF|>H^0b?+PRpdgo{oZHROY$vGvjT< z7j4FI{<-$^EU!|q;3BUS-K>&A*Y1NeDS`V)#p)rB4_fe)*Lg+3?>UOq>^6QFIjIcE za})4L$C0z^r+8x67I(pyZPn@)j*6~#mD-x4)iQxkJFRwg7%z*ECTm;1jqB*aw4`OOG6BW2w zfd;&j*I>r3FXz`zCbk;${nQpp<`Rg>sB?b{Sx}bHq;LVR@X`v4m6OEWF_D2GPTXY` z&Zpe@(u$k!-<3$NHt}h*&HwRV{c`i0)=}G1-D|&gY-pKe)xUg-JYr#WUK;DgV{Aa9 z5RGbtix72jKP5gn(mq2L@9`$@>X%=Ax%r>}$N#kXSHJyTlPiU0!S2XdN~Wfw1f1O| z2YvVSwsIuP)Kbh-W0r6$RWWHXhaRPffqdHxH)fa|EGu5V@u1m6p0iL9Bs9_pd}v1s z^l%8%&;p*iJfZezuB9|5H0t_b65ZSrygZ;`zUVwkrt6a)Bk|zMFnMK5K;Ua6JO@L4?Q+*IlW(HdukVJUH{bGE~yA&etf`G{P`H3;QU)Y`^ zET5?8+KjuEL_8XKyY5Z^-5o7WX;S0~yJ*3SLG~T~5iX`im)BpPx|mneW3yJR%zfJI zi%CmF7`SDGc;hp%NFYZRYIB?jboNB>*29b+0(yo=D}6GA=?=7TryXjh%qN~FgyXpd z+ZYqDQyAzu_-HaRnG%ZI6Y;9$fhV7S3_Vg^6rv4gEBrqAMfmD`l_nGItd7@IU?@qN zsHsTEn0E8<7*FjhTY`s~rA@xhTfp$~%?~**pQ2^=ch{VOT|9LepV}{z@i2xl2B{3j zlj0>*H~6|X=diBmsr-o{xSbhm!_uN(ZG`6(Tp(#ISUlUk{VeJ&tipYw({J0~Wdu!@ z^shF9y*H;G8Ql~e{OuZ}K1=`pn^mv>n$W)7^^B@v@aFJ}e1Mr{ zxh{;%pPfOE>!D!+H_*@EM=Jx48_xX=`8R1^f*`lDP3hH%?Zzf7&kDE)V=97GlSt_e zf_ZU%V>AiZnf+=U1PGFbaML$|(}r;1s+0eYTY=IkfS=Dhs`7U8yKf(De$%gWo^u_> z%3wUmTzb;>{Jw1XxzWfnN2m6g08S3Fe)fXxvd^aN_xdZ8n-Zi9lp4t=kRMX_Q7;~Esw-^Q?e6|8Mph*T=ASXc^Grv%D{(%@DwHL`}iRI^wr%f zNa}aKtaU=f0E}YlzLU?#oA=GO?v@vG=+${%&m5{2ee7*$u$t0GpbSnXgu0{aD4@Qx zUbm6gZZ}Wg3jpvupxk|j|9ipHD9Ug_kO}+TJz-fw+=qsesPzBPK78Pna)rhw#WbPW zVQMdh%|?zc4)-^5-c9J309i}@7>>L;lLKTa#HccuvZ%&d0a;3~@}lj$yvF-e{F60VB-c1VeK?ZZS zYE#v~puinRsc~=)Vv_7nG9nxeJr-C*FgB_Cb4H4Q9>|^dk)d2?$RsN%ao&uo7j3H0 z*k;V;h2Iu$!abR_(r$RS4|;&t6(#k3SKxkm*mu0tm==He>H( z*&Gni*v*=5r*zM6j>A!TnLH&P080z^!bW!sJ%yn6FR70%55Ds&f&r7jeS+4#UW=ad zE=U6i%)!Vj8?gIoRtLD2LZHaNd3^k7^YZnN&Cb8xd}u~f^?`l-rmU-W7=^CHP(1n+ zRnMr)WW~MK8e;FG2L&@|0;6x|zpe%2Ulg_*meRIa^4p((EOcz<|I6oWUIxnYIQWRK zx=+r}zI#dRC}db~XpMW(RUad4U0k?`2A-SV6MOi7v?8)^^IbO=i2T_LaKD6aA`zbYh~(#o4AyTb2n%ACo5|^eN9NdQSnv z(@q1NddPWRBy-Sm*Du3qkzI2SJWmrWGpg=cM;{oCafwvm3opgx)&IpU>4NC9Eow{= zABNdB=Rm7jn;zcPTK4F?Mh?10K^eEy$uP1TblXQtnA{GRqDhzMki8iL#g?Z?;mdQf z%*l#-c%V8Gjk`A_7DbaKy*#-~Qt1=5K%XZ1bp^EL*^Lxq%WGo>H(jNQ`mN z7WY?KSk~}S&#i-Tg0JtVmM&O$&?_{~wGJy3Fyw+k-Q>f=XR|Z(cFOlQ%c;;Cp1?NN zc1Ej7X^G&BAVZuKE6;g(Vc}M|5yQ`+K;h&%9DClmI+19$e&b5SQ3@BVK;Q?`HYSwR|V`GHB znQ_%g?QZ2AZN~@KO%&bkk{KzhzG!SoPIwa)IFO1ArC^)=T@!fmU$^b-S9bICn>Gh- zNh)oLnb@oB&Ckas2&PgUd8zFYZMD^TFZewc#_ilZ0hHZ^~6geD^^E#=E0 zxjEP>Xa0+!rS06VtKqdiowsTA&3>Niq&HHUXh^|~{;3`P$_H*wEh5F=&fam29k=#c zt&>|DpJz7YTr+}`Uyb*bd<`$GhKL8E__z9MRO|4v(P9hU#xx$JG3EfDHpjq8Yrw%k zM^N~|*3WX1R16e>^=r1BF$~2+CsD5rBoxIn(D;Cp*yKydBH(Tws~r<@*=XeP3>#BS zh46BbQFIsw6RjLbgQyoQGw!Mn$YdOKC>P~EmF26;;1} zjO7@2%DI_c)DKH&)kV1K*?DyHq!NU0zka@X{>-MMg(t$i7;z7yL`-|UEn%Z13oC0= zx?nq3|DhED!FGBUta*j{?&A<*EQ}%4&vh`{)y6D)=-9ljF!sAwf7(2I@*g*kOF@K) zVRp;oFuHBqdpAVsLyk3tUtql~)DaLl5A+(NB{aj-Hq=f!HYd(USg1YNl|6oFakUmNF zba7&mpvVo~y`)$4gCT2kSUTj2laYZKL!vJR)UMoC5!J`l^{1(oT;5IljkZaN z)Rbrd?X;b_2rj%7Go~zW;+e?-@*Ly0ZcK@_#ahBAe?7a~^wgB~)Q`<4rYa(j$haEj zYTE#7WABiH?7Ny&%r;z?edf9Tn@>yLg`V?yf`nN3iN2Cp;j zV4^Vh_MU{JN_pDE;*vLs@uD|95SF{G|9ih)(IAElU5#E|v@E6`!C$|<^*mr#*gIO7WvxB-d^^kIV4@oA8V!#zdvZ3q+}eEo ztFJac9Gz`Gm1I8cm_tlzc5fz!>O|Z7wejqU9d6okDVT(4Hz|(^VuB-(`>zMV6Z)1y z2}UrO+?o6-_*iR%<&_Uu+QkU^?3pH=*nHuJ{FzNQ?ag=FCZo>kexR87K}sl#4#^By zw@=^QrL#WNR;{QxG!|4}r>VIkdXWC^m>EAz&|}>Z?J3UrCT5BLF+QbZ_w#p0)xGtd z-Dlu^!m_^eoMTi!S6(GGO3?yn^2U&>r@O!Fga7+J;AaC07TqOd@4Eh{eO5b$J^l1? zbM(4hWkT1}r_WQ4`DO`M3;0rQ%aE8v*ey+>)i6A~q}*msFpPhWKBmy4``6$6RaFY* z;(}3BLaFhZdBBqHoPs&aB%CU%flA)cgmwXD-7tI`Gf%?IDuI1m6vBv#9^4OiW;>^< z1CHRZiNT$YgD5!~EsT`+7QdzB9ams$u`b3Il>TuWq~rnG7=kP^yxqTOi*IYga<%%q z&hxUf&Ml8o?O=arGLbB4U;YtE8jCzOPjxoh)ka^q-#(LpJ>L@*Mh%V>l~0vL&oj^z zt}$yL@9^d+PDXUrtLOCwWcP5I%%Df7@HH#BOtysUF2ujV9PN_4U{7gB&jGMD<#5M< zr>yV2cJe!X9>n+qzf3+D^5L=T^iUVq=LUnea-VCJPBJ#_R+$o=<<6(VqT463n0~>B zzV*>~mCE%=^Eq}khGDmd0SY-gKZVe&V#)jSH29;zLTFD&m>0anMpLKkc1Ng`)^+CZN7PO+%milw)nWQ4Vsust?QMYRm!b&!@t`rS2x0&WHO#-zc)wSH=MO`!EvI0pWV2M$IG`r9KE zgX>chS-*^cdohVUC+E48_#C0x2lb9KTW1X2OGu7B6;8is5&_Fn{234vCnnl>Oep0q zu$RJI6FXSi0)DgpD=do{0j2NSJvsik`Sh{90TZsTe(_Zi!Iya$(h|6dB-da#X5|UH z9g@)DDi2p3>`7bm|NQ#Z=Et92H6iz_%`d+B^*lQ>!qIS235^LpbxXLXJ!6B~FD#LZSFSs1bt?r#AK-pI-}P918FRRv{9X7_suLtB$Frdq<(ObYL-!aLz$m)-HGMNmF*vocVD)~D z!^RVK?-_6SP9MN%1DcVAn!ZO%M9%pMhiZzaO~@7_ zDq*pv1iX7ZJrh?pH{Rjp90_s4eL|*NHGZQ&SSjIBg#7>q+I0- z^EtY?2mC{zKsI|NjGkpk;P1VaF7{!>U<%AXCg{ACKF;{+WS$oiEBUz&=C6M9V)L8d z{#7%i!VTVZiWvUumlYZ%MB#;{pqT4~NcclTApGE|{=8fnYwmsD$Cf?RSTYcyJH?X4 zj`_}a%0-2zwd)bjG9_fM$X*DUxdC+F>l>`y6UXtM?!yy?!p-P4p_zwu;+d}V80=Xn zUv_#@5%tN*DsON-OFAAyx33&;n{MRQoqcQ-MS7*Rb1jKnENA!U@&gGa7?%Q@a-D8t z5T-N&(7FyjOHV2G*f(W)lo#5L)s5}zZ=2|P*cMrPkAumKG;gBamI|}S0OBWJv4$e{ zRXNG?YWLoMc-QaGwN}o#yz}a(GL>#ib!4pKgv#1b}WLwEGCi zUwb=RVRfr+H$=tq>4G?@0WZF(-FO0<2-&67W@mKRO8FT39y&j~Zb@SBwRy|!jS0Fu zSN#V7+M~~noTEwFynnf9ZCUzD5|-wGzh>bnK1wybjP7YXgMSEiGyd`?CBBq;pZjjf zU~o5xF&Y$aibsSptGgQec3$#jvP>DqxW%^(4Cj0iD^sX2%?DsHh*o+46?Am7HK*cn4ONkr9V89WP=C=6Vj zrcl>!2S_CYVL|cj> z1yz3W)A9QtPeE;W$kJ5>y9qda#hpMLx-(+9Yw#3(g+358+yiwF; z-bU%zkYVkJeoqhhPF*teSgT0LAcb=)bX*h~T;3VHNh_5960zVmlXLh)ts!N*Raq5& zn+fS~hJQ_}@Z?q=vwo~vQ9Rw#%HE_;_73VhTw7jywO5#}`k^uM_82`%qJO>MHmMah z?+9O$yFLd>$|U*_Y*dcfDO4f5i9K|iXeL-kX}KrTIEF;kgRSx`CYXonr7mHn|L{%x zR)mF5M`6Y<_AD^L0nd}q)K~qS9<^h5yM*7i!&H(`F0!ftM@nor{w8zq>}^iiZtF?u z4E&3pQy>rTrKzxx>rr0!H5{ySEj^TOqWogYV5OJphs>ovT$jVPW6nDj1{7I z-W9J&?8hLJJG*V0uRNaH5MuDNv1LL>QL=>13PCUmj{y3#)A$CL-8+~1f7&-z5Y-kMV>0)p76#5b zn!oZQ97}jwT;BKEtUdRRS5ZI4uR*OaG2%w#^Z_Eab#$)ZXaea9?%K9#%mm$ zL`?vKS{T-tOb8JqJ+rpUyP8hxCwheUv+!n`%m)Ze}4UD^Qqn3&nmYd-15se;cYvk3~#&mGr+9! z*nPOa`S#206#Mb!QI!OQ;W8y|Ps_{rgApEsV(j&7*g0=K9~1<0aBS>#&m)ZnYsst} znMsA{Zd`d9Bh)>oq{^~7n>MJ;tF*d)QwqU!D5u_M+%c0rwP&I$wKep^e|QW-j(l}p zPV4|5pgkTS(7eLnWa!8u84Lr#$?z=4g||;uH$lR+=s0GpAv`w9h}wL)tNS-f0o@vhc#JrLu{raL3HcLs1jyz@)QEA%?)1D@Hc>wZl({ zB4AmrGLD~IM`MZC#o%`A5+i!k5n~2)6Sl-H-&G86& z(x9x{WyS7Ye<+^Be@dSaH|%cfqR;{EdTv5B*I{sQ*EoE-dzMo08VlD`69U(U1^$>E zUgxkTH0c>cpM0bSdo2$U!w@*XA-nH}2YSX*NSgqwKDcc{fUsZ1$Z9{89LX86xZpnT z`s6=XW1!|3PJ0CR}3rmiF!U_fcV){S>d81vLY?E2K{eJ$c%M%R`1C z^%DswM@!ePZwOujiH8zc{bODVWaS)()4K-i@m7q+qqUUI$_XVacojN%PWbiGVq5Oc+o{0_CPT9Ff=fco1Iy!+RgU~S_Qlq1#%#QD;lpgD9R@09;;M`F-`amJdO2c zTAg;g5ZIn4$K9lM2&(~RlAW~yw_#-z(^AZy3E%b4J%4z3uz8kpd{;OpFSV;Yp+1yM z@JXH()*f~biwUVfV3Ba04Jw9l>!J<@G1ecSnnj&8oi)_p#i}C^KnYG8BD{FT4-;0##cn zS2UTUYlF#zi&^5d$BdB?*lUY^jBKL@p^YT|Sy95D|NL_E4}boybAR5=_;A^H)S+fS zel+u{Tn=W>7Z=wYE}TZ6%!5O2H5jyj5Vd2WTX#)7V648r5=xYGCV?RUls z;LvBkKzLJ2X|@f8|2;X&i7g z-6aFGUj50f>yt4X&p6x|0bJ8nGa(inqR+4rit@PQwV@Lp)SoB9=5AZs7} z$w6DlKfyYyZ^NV7sI~Es=l=TE7tW4z;vc%Y*Lt&zLfzj%PBAUdLYuDlx92Ah3(jbA z|BN$h5DG%!4avq3R z!v(xNNQSHs8Sgqg+T_(T&#RQi6*JwPGn|8L(Uw=0w@kqe){52?SIc+V z;25u)5Maa`U4}jW?0_!*)!iU89?!%FwI4(cE9DSxb7rpVkCD1b9%g5uLe7bV8rQ5n zPf3`GzK?p81ZkJNa|lv-@l1Ll{a{s#%_ztOz5-7e3`h0VQ#Ykl8ld8mdH$9Mofir6 z*^MV$;mZ3Xg6APvnCpELzd-Fx-ZOsXk;&JpLmY3jP67+yUF$vcPmA5%2Rui?ngly* zC!tj)C;dU{`zb|ai@1?cd>LMEEZ8uL8n&-P|9~iJWa)aJ*Qmkq^m;&eIj8e!V1TS+ zhz~g$VP)=I$-eD74fp)iX1UU5$2}T=L1BcjRzHL>LZ)N^YsuIwp8`YQ^8l_cT2;^z zDT?p$lMb+ok0ozm_}On`}ajn@s}+G#d0trD!Un}G})hbt17aPvxd?u&7|x6g1j)|fW!5=AxMn;sh<&wIT5 ztR@S**Ni6IPdhwG;bdbPJ`i5i7u*kv;)0CgR2Ib0o)bo!Ej`bAD}@oVUWIQYoQWvx z*N1p>`ag}CUv@y)VG0bxKYikW_i|PgX#EaSD38GqLzOs%m#L?0ufWTc&vBmo%VxQM z|A&{G*RPN2yCsm^k&=e6Q_M=s5@` zLhGS^;w9;V(hesSNqjaTcQ~d*rA#hUY6H(G8XlN6*>=cTC0MEW;YY-W7Hz0MbY}If zg4FujvYR(W%vbY$N>Az}N_0(X4UgiXJKl`vD3H_RDhk?_$G(uBC5LltFutuhz8{&H zP`YcoO=8aYyIXHogUMyvOM#xa=j};94ZwG}<2K)y`^3Mr!t6cDFm|8P8xvTNc@Rx@ zTVJlvDAFWYOCI;0ZjK&&ZhqpZv3E7R_2tO^sbO_;9x{yK)Bl#O!R0q=0$5uUbM!aZoER8VX3PukHCP|xiGzY7%oCah_186Rz$StVZCUPR zd@@ye;Pwx+(@~36Cj{aF>1W1L8>8q`9>5^F7{oZhGkLRwg|gtGAB2x;=QB>j2q}pa zPaq4C_hVw>;%JB;-o4p8>lC|#Q^m&Nb;cN`YIDN4+IQ)q*;vawui}@}yt1D<-{+rS z{oE>>k8^_KtZ-`lXB}d8-Vqfz=!<8MCyb=T&WZxgt6427nl0!qgo7CaHJ~B`{IQp( z1s@FTc9Su}Uq#gCdAK~St=eG>DKHZjXDMO|>+Gxvw&U`ud8hbK*tS~~;c)tyO@|~D zz(@W=&af|1_D+ECzS;bDMHBmZcHdO%cTq{%_V%X?JihI&l*nwN5;G3IrLoG7ZJI#X zwtZNz&Nh6VIkP>v*Z4MgcN?(K)ivk9+}J5__J@yRoC5O{1B z5|CT@I6Q07g9)Yw$$sW=a+eRvR|}* zwTv7Td`xjp{cjzu9pK~5$pw_F-Dwp}5ype=Q640O ztlFa=;2SxF#A0C0tF0&+-)WzGOpfug zL$@WShiNbuAf`4jG~)%Ig&%+sntB)0(B^>^lP_vYsqhP!) zpY`?EDTX`YG9a^DV_nk#tUh6y+!zn87(xHMI!b44vtGkpo2{u3L`OYK(ZLF6cWNhT z6AiO4p9bnt#^O9>dNIn1H$b>p{uE{1f5<5Q^!{B-1k3lOtV2~^NbR+Cn$X+KwVbUo zlcVFa&HInh_0#3%r;i^upEGLf441Z%N)W+y9voNwGHCnZme35WX`%BJ@mBE3{|;Z8 zP0td2s4dp->C>ek74aYB-3$9w8`%8fIwNZ%hW<+^C~8I^y%fF1sEJ_mW@d&nW44yE zr6l2)2b`gsiImayjWNNR)k00$nc4jK;cWA#AK#XHjkhXc`Xam??q(>IX(cb)m7onB z!_CDR-bxs2YgS3^5)y)%q0jSDyIB>|^k>!{&DBEkc&|kr-3KnIft%dJZ3efngZFA?xujIYrYcb%{=1w<1T;Oz-2=oPQ%M(bQ@68HV7Z4V3uRH}@V9st7AF6{&nSAr?`7qy~U%j9G-mt>gg=;S!w^WSB7iBkZ2TN(@3m-%VmUNnSH(tX*S?S?|3-h$5O(CPZl_ zd^cLU{0-zAK)azIV@_dRT@Ae4@0s4UTf|h)wDnCYJ|KD*tivA zFJnI5>+oHC*fZKR32U5Ql;y0^?i)7YGEecS6-t*KM0v@;Xj(V(LfAh^8N$Koo!&r;|g#wsz!Ar;&^g*4w z&PAr&thiYaQ);4^Zb!%Wm*mVPnYD&_Du#xL)~!?RV?hc`cOo;I^8M=K9y z9J8{+v;E9Ig$++Q3Aa%&#-|J$Zwh2W0^stn#w>Q18GNx>_>eoC7uae?t(KOit@*yU z##~<`yd~5aw|VdWo*P4vLgW})rWpTD!RwremX~qPc;SJVOS`p{qG=Dt6o!q4FY*q| zC=%dvX^mi2?hr8D>3($Vy4+IU-uG_`cU#tXnD-sAH%H~`-hT4inLK%$n8E6ez#oP6 zq_xC>nGwvZi$%{9MqW4tbC^PS8eZ4Yqw!U+QlfYYOm#lIW&N~c0>aqlq)>gY+_0KM zv(abuC8x>Y$`@YdA&MmQiM%Fl7;kOFC@BB}h?kWS9Zcz%j^v+4VaL<6BRk&iWpYoQ zBSX5Mz&(HZq+OrB?8M5C9lzUYe9=U%>*HC9@wx>9@ryjDt-yGjX_TObL*ZjyY-88- zUzYa8yD@F_qj=(ncyIsVvtTaDE9HUu(kEf8XL!HiIKCMVlnnIjL_hHroTVC(W#kzi zYoKQ+OmgI7DyADi#8CU|>Np7$<=vH&RvDdiri=-h1{7;YlW?(49PDqn*ecgdiSM;% z@$l=66-VU90?H!mio+WGPC{uIBws)=+;t9=xlZ)MPK%6&6><9s!S$vL7saKal8 zl0rN^eN&#g{VAgl9kkUBK9oKE2FQusda?RR8s$3WNz({U8%ZH~g*wIa`0 z)s?opP6{PqC0t5#Ff4|Vd%|dbEft%s|3lQh^&Bq|NxNowk0ovLij*P+$1|$u>155a zvOpM&Xvd@ki$xxSVMHXsY)jNEf12e+DM^X_ugWQYQQiQ6|8LZb&MMkOxwKFa2D z+=-=>j9^r|J`;usc~i3c6o)*Gzm#vYn96(9R*fP7KMNA0f`w&QLJ+!eQ+ZR0yP2=N z#ZUGB=eX&s7vGlnFGOgvh;rny@|JiZs{{i-BW@m>9}KBI$nIrZDRhu8^?mkgXrNEm zM`lWJe%-cZmRibl09&wbo(JU<^)7S$MRkSer2DaUzI&XB}+ zt4k%pmDn6?zI@v5(_Q?gJmN`bIG?up@NuE`^XE@z6AwnHE{7(H`7gpf#%2F_5`#10 zr8~m8G|2mwrNNS?=Af$aW-7gk{<|$YfE$Is!vk)EqwZDmF@~hZ_lhor;$d*`h@Zed zb&&OEi?K1LBW&=)s8@X6s-5RgK5t&V`LKESq2mRD^YLS|_8C*PoUP7L%5za3Y|2H# z<8}s$(L-yF1-kcVITo=Y-^e7A8r-$2BV!_`+A>0PL386>&P05i9E%EkH++U4`kk~< z3S0y-Rp!~2QT7qUvy?JCu)_F^G4AS&bMYa5>$_-Rg34BDv&{C6-zc>0it4x8L1(+A zoO`9h9#;5&(C?15|LGYmDv`M=eE#sJP!w12~ z(k(Bn+gZ;(UdL41<rhj$QjP`rxPEpOrxK#Df4Bb6&*k;i+~OUMJK6pfrFNR&9e zYb&zP9T@cN_^Zv6j;-8lYbHXg4$|0d@BT2MKja|>h}`Xj+yJ)-zWYx2-_U}gzuv%q(l+HZwY#g#MRL9S zb~=py;P7;&X!89ioAQW(6t8 zNs-j+c(ipRWPhAue4^y4Cc*zOTHJJ|^MkKyf4p4E4i*>U)u7pFY4T3t1K;xQ3~A~h zN2z$9$6JcyusT?~jvsvtj&jo40UuN;*!B`uYd}ba@73zgOyxo}c1!t2JE3diKW!}N z=U%CwwRV`nBFKqzlP+_*VeK4*Hy+BPl*Hj-`SP#6-aP;67x7QytG$jQpZwT`y%<|E zM>!!4`V#QjT($MsN^}&fn_#qj6=*x+lwLZt{H2MOUVeW!7=OHF_Kmo$dvccGx)dFp{8i%=6TzWTs>&Jw8Q-`QijjN zMfu%HXEb2SxaiD5Zx^TUH}Bv4VgmF%~5}ejFSoYQSVHDQ=80m`E}1w>j+9 zO%oXjfCM;_hmHPO5MCp!v!6kH!aS2BU=L}%m-msMWaltSX0UewGYT{ziJ0}+=LA2P zXXXb0s5{oDyrBF+gp}8!Yz96ivUV-{pHhekH<8azcN;8Z)yxo&*BTT&$f#GJ-m~%v zQbgeiu`)Yt8hT^%P;2QrK@9_`t-CH{xvW%UydTjBftg>PvnFFS*flx=INwpI)jL)e zanXrw@8A8o=HM$h`)f=3N1Y`2q%G)q3F-wQ{z2=qhY?||HeD9pPrlIjcn+Ad91NYv z6JcoV+CyVQ+f5-|N3A#o!GmEPYbrhQYCX5d5Mox_Iv%!oT}CrPS4(Eb+nN z7n@%fvRx&xKfU`o! z7^|_vx3jc;-VI(e%aNq$17Khb%HEo=Vbs?K9?`V69d{r#A^pKShXS}d=aHNoZ4Mso zO*qaLUN$k(<>0E3IxwrslOC)@8^$OQdKp&@!CSD7h{i|wZMGc)Ti=a>?RoOSa9b@k zhfB?vnj&as{9$8Ji}~$uezo}z|MqvAzxpP_$zzMIl-|v4GvI}j`Z5!~HdmFdVVLVE zF1c!%q4pU7=RBKf6>c81KK-KIYHrK1Va3Uz$(JX?s~Ed6=2Z>~5w+$R8%Tn?G;{q& zuYPRMjHir$__FJ8PL|X+jv*MC3L8Bu^gpbeZFBRu{X_Ioc$xy+PdV+kTz0#fPb-op z6mMc|a(^|>`8Yj&yLtcWp9-P>CAK_HnWYqj&3Wibg|;2npx~UNa97f^oE7Atuoth$ zrHjU#?Zns%;}`9;$GK~a;d)XijUg5oqaZ)7BuM&!avhx&F0?h}MaemSZt~G`xEQdS z4B>;E4A1Q%rJ-KUk~*FgObNyOT}m==hvAkYoNJI<2f;Znq24ZlM-aMHr{fLzp6hjY zBgKlLG5Kz@#qvOycBZ%Y`q2P+#eQAFj6RiNGj4n@!N$59GFzR!MG?SQjl7v|II@ zYm=vQ*{YPAHafVu`Krg&YVw#zuhs#l+*CG|&-P^3r&v zD|&-_p>@WD2FB(a|Jii~Bh_X1MB~PQztxP%zbUUePtozN2s$P@&uB=R!oV(1pjhI1 zW~)~&Qcf0L7zw4jhsrLZ!&xUg5OCC1z9rKGYMZj$*47|c}>stWs-nmg=Z5QvqCJsyvRt}N#?RD4+iN; zHL?HU-~D#;*Wdm!FEyd{N)e#bcr?}3otGoCPB~K^uZ9=iY+e#!W&CDoTZUXJh6f!> zPq=t>{;=_snB|9_>#@QI6KFIEV6FEF-xl9MsNv~|h zi)wH^H%hbnhNH2jh$m3t=l=WN4ZUaCYh&bnXRrgVU-;Rl7{addi7E2z8m{sOT9z`> zI$`XJup{CNIU&W15YIq-2+5OEjCm=<$r(&b?xoc=#lJE;n8m~_^Hq#G7MRi{&~a(^ z^EfQ97cR(kj3?#(uFG$Z;;s!dXppCzXqFSPX1za8DR$E;_b8N908Ge@%a|q{F#^np zSptwX9gE%ba0TY=mIKaKTnX7Wf`x2-Q=m4t<&A9R!Q7?*wg`3M(DiN!URE>@OGq)> z{nsrEEp#LBW0}kIg@rNz#!Og)FJYjxM(Oby2d_T=1?_`J_j~npuxo>*Y%D24-#EUO zm+vfh7xO+5 z!bICE$@UsChO_DuuUol9f!XNeEPgsKlCX#5c!rJ17ICwoL-a6{I2_i{jBzBqYr;Mg zBRrQN@1DR3$9PFa!isVU3 z3NPhs6!My^u}bGUqqN)VE_)Lwf*udXc)Q2%9K##mT&C=H+Y42hm0TqQv{$O&wAp_7 z{I%RN#Tsto{C?uGV4j>{?Tqq=Q`*zuf@3xlX&jSd!!IL$eLuJf*U=ko{Fn$-*&tfl zmgmn~7q*%Vjl)I<%qGj-JJCna&W>B@2&`oreq+wZcwZR(L?HD92u?Ka)r zZeqsy*LFynT)b2QK92`11`u`d@X&pB)|$LwediUEkp>ZM8Jvs&V~oUE0EV>|=CeN{ z*2H1-f~p1Xj`AZ<*e#47b_W3no24r*9T1T=3fo~04a(| zL_t)q39DeaR~`q(nl;JYmx#z_@I00nbDcNZj46>o6vdstn+kQfwY^odR}$%S-5seqZ1P^ zg1I{T1TpL;AlCsDXrrKRZ3m@TnNn?a%K;oIvp)t zjPo4AB#ev|&+oKCaEcE@E5k{zr?7XbV|~P5kLT!4>4{m3AGN)-(r@ZE5os!QXIw(1{y@Q3`0T2RZes%y+fZY zCxl1&ph9@p84bBkICBhh4-IW54{tDdjI1UPk!tk8b{l-`*f^#YN^}_|I`{W|WrRV| zOKA)xIXVtXSn6_0h&Yo@D6joEVF zKqCh4V7!DLoDdi%xZl61v?hxXw9mDDAD)?cOK0{hA*{qCFKmScA&r%YF=~7WO&F&J zt%;GX>dJ%wQ&pdbd_lQ)r{w$1?C+Lv=aEA2uKN=>Q7DBdUpa;#W|J$zwO0w6)eJL9 z6C5ngvb^Lw^_aKh?3SxagC0NnDnV(+ESyicO+doI?q!y_92ie?X8&WtQF^t{s!!O* z1I4vL%Trl&3otSsgK7tq^9xzE4ZoR*&8G@*U;glN^MC)B|7-K-pZ`#Sy0dtrmYAx4 ztl?qaA$lp1dD1NEVG|?1<00%e)>-D)lDu<_$y<+J4_*gn z4Lxd$_h=0}19DJq$@xB0w>E8o!+7A?y5nZ`;~h#5t@bPHSp~ZVW_9qmNty`}<3)I$ z(NYGD3p^%cR&x~1GPYm}je#oUtc556CunKLHqi0Pm~x1 zHZsyZ4Cl?je6{)U{oBnyy!BgmnKVFhS_`$Y zyv7=>&*0@z8fOz1>L}+!ZBD6N-%3*~x$N(RwDDpHg>%L4@|Tpayfe7=rC0jQ3mk=B zul8lU%($GXWj(9(>e=ryXoby{@z_|xiY2L-ozhxUa6bGStnrD>WDwA@Mw>o}niwJD zj!ol*qM4L217RV8t;S~8EaMNPse?=pFsQs*@)Gpjm$Wwq8<#j9;;@M4)5jzJZEcG> zd5z7v)WvibOHYq-N4z#}v8r;!A!qV8TwFplhhojuzUlGcDo>?gw&c7;@)3o#5B?Yp z=OysU3;IiA-}l)_y9dxhqZJBor|tCLLk*yN$6T>;F?+Y27g7RYIx+e6FV}v0TCM#khX$cpl*I2-$&3abQ@a{%z`6|SN@Pcc zifOGa?>ywmU3Ld=Cc4ceO}K?Gh)`Ea(BKA*j|&mcnhX=dIro^+6>ShaXOU1GbIx(& zyBaLH*i%;Sl|pVURPyd_*Ifc<94uPe4btC9epqSd}i0>ccSLNy8l4)MgN>N5|T zId{ZD7zoG9dl_ZiLz8n2_m)k*`uKVCzyICuH!t&$UsP`N>O-M?xz;b5z5TaepKQK- z)EavpBmVAzQS3ce9G{2P9-o{MM!9kFh=q0(+HiF8w3Tw4^Q6oFUzKtJkJ)Rpz?bJK z>}b*vYSZq2?~vdTbW|qO6X7_1QQMhbwkywrCR)q9_H0HcdFelt92p}s0oZ-|6F-v0 za?>g~az=PsvoN9(9ii|n>FXNWCyb#qBlhS;zhBpsNtW)p(*>eD28}o^rAG&pgT`C< zJ<_S)oFg{6lR>d5#mv&nr8bs{4x{6g>I65jl=B^!`w-Xz%Q6_rOJkuoI6U~>FMS|2 zVJpL{_*7(*j1!Ci$&(w;Cj<=uT@7Oqt3MP41l7wdyQ?d%s&&AYdxfF8Q-NOC5@DAx z^&Kwn8O!X#cApdSQJ8@@5}*?n9cH?-n#4F^95_VJ(jR@q) z;|`<$Oj)lAh}o~Ou~^{{wd0NOl42C3SWb6XCB|_@=$}6yXCTV4X0%2rBy_X?Uu`p* zb^@2fbL6K94IvsYhtd&(+MdjeE5j^u_~(E5L-m?Rt>8%Dk{4m0Qm{Y%@NV<+uRd;m z_0?C+;xg*-UC%SvTVx}I?K#3?s|lD$E*w?6XH(g-hT*NT)`zvlH_b#}fhBRgNO{{C z$5Lx20EX>o)ain11|M|BGhP{|w7z=Jq(Sw{J(KA!MzqVuFp7iR#i!~K$sL77h}?JY zNt1=ymay?oZ6nM@k8wU~YtA_e>z;?Vts*4jFy>kxaVFz@)|s1VB5I8J82*6MU-&hF zrB)Q(ZkxU5U@_LbRYw$TMcav3!bPnHLwzym@d_TFQwaJ3Kap*C9VcgCNXhrzgZ-R< zavg`wtXsQ3hBbcUyA`>0M?gc|l--0$?mNav>kR_}#`lut+8t%lJ@l`&(@JuLceA`| zo)4q_g3%23RK_e{OQ%^Z6_Ii|I?4HLw=jrl~*W#{Zc$Qf^v~h95U9QTEZnT^_{VsGe{`Vni?ad z&mf&#b-ckCI78b@=y_Op3HB`V+_$ZSDeuVXghxk54Kbdq51s(w#dC{lEsqX~k+OtY0ZHkR0~o^8@3ZFeQyo^#8C>bO%9 zkJ?m1=>GZa`*NC(H(!3yF%EqvMRI)dvG*NI(D$!Do^HN>{Wihe-F*4t`SACLioA{a z?bWBv?_d4%=G#yEn_sppZo8VsTU(V02gAV_9l5)HIRFv8;V1kCUHeuPJuo>l3M?Ll z*DaB3I@*4 zODe@hIm}&_N9}x^42u-7Dgo9J>Dd zUiT&dO^8`tcalN4hkN*^+lR5n10jgxrKCV&Nb5>jezkA2y*lk%qYz>XBwA#Jw~!Lj z19+8Cm~}Jmgdrr#_Xy8S?XwjVT4M<90Js6NM4$z%!AWjkh8QTG5}=S{f%$~gF@7Iw zD@#pzVkU%Yu&muy%4BA?f?Li6ktaV|*Q1zX0C_WgOdXo%l&HiALI%otd8f5a1J(9y zSYcZy4CJT#c$=WuOMv$~Jy7lA=ax$GD2M*tvlJO-$IL030M~~+Mg#5pJl*OBZ`zsU zs9k3En&jB}vc+)~%09}A+_58c-XLW|NiaZ@-bfkEcKUg$Z{EEwRQ~@4EMdgXPDWvk68x z8^QYjdb*S5xQ^t2rYRj^B1%d8@+rC{T5w`qCv?dK`=&HF5510`eGq;{&^-e+8aK^g0ulkJx zxmeo{X|i3BrF$f55`BJDe4;IlC))}gci7Y0H@Qrjk z|0&;}vKk?UuFy>LK%avHV`!)o&;#=NStVWwxFm(oRAMl!V zz^jX3lZ)bGyH@T+sG|$1HvENTgW@tG)!H!MvaQ3r04ik?-}Jp;%yJj>NY9T?ksxf) zp(l8>s;k?`YG7jo&0yLy|NDHrc)?agB@7Sq>#E;+qus3GwgfIghbdO_Yb{xpB}|y7 zdjMo`0rW9)1CncLU8l5IOe^Yj)S&J^lFmzj^6MZL%R;$Hh#>L<*Cqqmy$Q*D0<50+ zXTIVnWeL#gH9^pm-DerFklb;%y{s%6Bfz=eWhT+C4==!p1 z0|^{(XEc6F4$)l}ad#BNc}cdE8EbqZlqj=);1Q&EMY0KrW(U;m9WFt+u}xlILdiHN zBZXn@AwJ4_oPg4xCT363J-6jDnh+QxkWY*7=D1mSjixX$VK0Ei5{)ey%fdbXwU?#T zLtF8T>Dl>{%~eUtDuz1}6~3ftp%XqRR;F{RUAt?HEQ-s5SmV2lw!F}JxA#iG_CL$- z`O~)R1q;?Dy2G#p_K&~cZ2tToKL(?d&A*&}HPP5*!38@lw0u4~DitS-nN{xC(>cj9 zp(rTEwc$7cu8ti6pFf(=v|H&qzZk3kSX7n*u0a-?LMeL%8AOrqvLXpEd;IxN(S)Uh zzWwK8-6ZT2g=i${ms|Aca0V9o2Lp$`$g?LAnQ1PDR;;0?@mStcoCLT-IhhmMZp)F+-Tsy0&jeT zBa`Z5t=gj3kqai+P2D((s=*PeToYu^wo_5>3%bfAc~H?Q8lSV&TT~9AB~5^)KM{U} zesG(F&W!gpCbF~w%yDLigmqz=_4o2%0%~_8sRDKc!xHlBEYreYB6Cb(!eO=vMdX$& z3KC#Qb>R49AQbmtsb*Zx6Ki6PK@XPT@|E~18+h+As_S)KGA*CYBzZQEtBZGOFzq#_ zo->wVR!rNEwx*Amzyv0guO;J8h#-a;cbXb;D}GxTZu)A#5o>McL*Z*2#>gQqf_WRP z_!mXECRrI=#(AX8(3$1gu!Cv$=eMF&F`|N|9Mcf1ZlMHh$d+{dF^lA+>LY>&Ecp*b z(%x0(>!Jdoe^kP2Kjwe>#F3bUZ*2!xWDm9kgrLaE(==1iLd=P(2C~k^Xl{+$xcbw4 zShhgi*cM14zJytf%4(O_#|i{OW1Lll`$@i*TW|=zlXF%-qV13F07Vs)mBa8X0?Sgl zkF@pF%OlY1f5smk&+;KUC<~p>;YPuW(6Q7MthgVGq2neZ1mj@YjPJQwu5*$tQ?o$O zRz59^_w0EmFGfSJEA}7-Nni7?aV~`x1j3bN;0}JmJlXf!FYDuY{Q|pIP}fw zB4Mk&ait)P-gCwO`08TwUr*m|{`4XX?r9Za3d*QnZ#AILq5fdZ5>nh?5YPy$cn*(( zWy@!lqGGTTyT?Dtn(X`AXpiEu4Ka{4-cduryw9Z^398(djliD^W^ljoicY$}G2n@X z(8khy#?@h-=y!Og`)WI+c1BJ2!b9)j+kDac&8`(wpB$C^N0oDKuHKchmTM#dh!(eY z%GAa-i*ODZOIg`ReUg9uV6Wf9=kYHeX)=gF47Lmy1wbO>Xf<3+54~?Le&&maH7PZ3 zeKBc$(E(-1IHP!xrquy=3zi3?yomKz`@wgw?@M58Jo#`R9oAAv00^{3m_bHHxpYB!7Os*He%yVbv0*1_n{0 zbG_|V+Yz0lRX(F#fMl60ziC!+$TZ%EgWL-g10oGZm;teH0oWyj_w21NB;?jS7OthyJnekx zwL4`gL%U_r=~CJWjFv@*obb_Vi{NpklvC8egS5Gj0J(_c`-s2ongwCbOOjqW zxv^EZjc^Dr8Eo<9+S<1(PtX;X=Y0ytyy0zXhZ2(JnozP0(3$9Bw+FdqSmqQ3dgd=h zt1m0a`|{0)&5Lh$n-?w5_fmwDE>nvv$o+nouRV)D*xa>Jn4n$&HGW4(+*iAh8)ZK} zEB~zdP^byWc#5xA<$R?BjXIGT3`}|jQ-F*T%ZDwvycouH5L~wD-#~ST}xnM0}X^(T| zsd}Gb(>TY!YCf}!PZ38?6iy5}d_TL~D8Fcw(3T>%pW(3*?xQ&JC;EQO(lB;D>Dj*A zdloZ81y3>xxYt~(gOLr6Qa-#7>3k*_u}5MU3?$8^wRZIiEhCajt1;ID8cnDXFG7-U zZ4Uho0AM30aCTW_DkDPIm432)2Q|-=eOIFcz!8LfMsS#q(8H|hjOlllL_NhJY{*VOpK}!OX8?2}JYD|n_X~mUir#+@Md@LA( z%iyBE`19b(mNV%Q`PVDhhj@WyLa*LNUq(_8Q+f^lNhzq(1u^d|Xy5alQbd z#>N%#w6j;>Yf6A6Xm&A~poKGc1QG_QMpi?j<>04UjD6a1YAvzOva-yBA_y*$f$gRU zGg-B)jRq1fk?P(yQVtEaLSB8?8G;vqM&pec56Hs|F5E6t0J&>^C@pDs44spVhS|%Ud*ZdP|9{|{{mb#>fL6qi1Or$w~EI1k>6VEOLi`Jpec z6L_sy19w|nx-#M;T02Y13Q}&pT>qgxY=Ag7KNnW0myny+q%DKg8KYIN+z~gF*roIv7HR+1g~0 zO8Qh$857lfmYVYSX_bg*+CV&_@jclWjU~byi!SGNz8cF`hT(P2?MWUdtjJ1p1Obl` zcqs}8el6#BX)jru4qb-;X%K`bE7kZAJ*L{0CIEg@lMiFZ1bZ!k%^9Zb;_!FaG{SI< zz=!->m%+d-*Olh-Vr8FX@d%X7-fGSEJ;4=F`VL_GqQOQ|{e~lEOJ5Qk+ynf(9Pw)j zin$~lf8N~`LqwI`9s4wfiGmWsxlSf~z+k$CkazBPe-c7t@lw*-G}~~ zk|E8#m{O#dwMXzhNjZVLpaANpKBAiE4hxvH39uMr~VNpHgFKtU_hot9L$aR2rC}YB+`}uSa z+Hs=d(LpCGZmun_OCQU3$%14=49yW7(Zr`xgJ2&FDWA#hM5_bM0a7D0x2{DKOr8lO z^({e9adUMYS4?Gl^sJ16=6kA$@li)*R6{Y?on$TYm!{dH2d)MFM9OYZJ+2e^tX5=b zdV;;CYdoVExvo;Kh(~csgx6F*<NwSP2v#=_0W`N5UCA*xqYx);2SYR$Z6IDahbxRSLA~HLg>7dx z233FPi!kr)VZsY6Ce11aCG3U?BJOT(?{iAFaq@HeOyLnGlN{Z1lCW2qyeKe5#RYTM zn5Dx|(0s*?U{#srnf!Gn$T3qe0%V*Tu)2gXrOkb1~EbcT2>5a zNEmNO6Kw^*_>wg>X=nAR@GXnSJ(KGQFLi7hQV5F)RK=spo;9Dn60)T; z?cKC3GYj(51P=KE6OPe$UxW)Zvwo~31n(lW{`Fn_WNzcbjCcbmK@oj2L_qoYS0Ag_+A1mV8#RW6#?;U)W`T1b4n{qb(8QWNC@IC*0v$DOapMl zH0P59+{ZTLMm3lw9)O0JW4M@)=9a)rpf%mv0F3(Y``bxEmNs%1(ozr!wY`vKSI9{f zKZ`DW%bJ0$wep33L_; zn1l0&5{BQsFG?L8W@}ayfBK{dW>#I02o$kS768G)AGB*9bEe{amX=~EXpUWOJdHAC zG11f>CQyd46ROH%S?WqXsnwnfRtPS5zkXNY)wczMx~4$FNxnduoyGb3ZF^x041M#W zBOudCI_>TASL|~NZD0g2xGyHHKqV7Mma87 zTYyE9Jb@`fE_%iw`X}#x{Dj{`JOlrsYo8!Pb z;mZ2z+Zmsg66lSqsB8>MbU%T(pAYxwDXdvTj=FB0XA@smbnR}Q-K9JG5-DNaD0Y9W!~Y^b*2e{x@$`!mxs+8 zV?ouw5$|QC$|F^2obvISoq5p%1L^R2sRJr=*`PkX@94D(6=JyQmJ@}buLRi^r! zP!=MWRak#2!bDW0J)$DO*!#;rW(m9eFp2BFw&V`kJwKbl0}j&~Q}(9s1CBo?*bjfg z04D#|5N`KC1YlSyH0*KMBihN>Co$o$_UW;ray!fW;Ic9YNASfcL58 zH-DCh=abe^Ol-;Df<{L<_?@A=i@$k72p&Jw$}A zh3tt8HsxIw*h&2m#PYO6tp!l#Op3<-t|I~7b_m#~G$zaCiwZ9gf{1FCIPefW$8wK~ z<={D2<({<>F^xcM?E3G%Jg(p62-ju_~ zsejs$wFiEoFxV;FdJ|2&`09)C8R`#1O6if@J=SY@;;XWsc5A5(KaJaVzbRqXz369f zfh?OFWi)AG!Ap=2!Lvdwk&Tg6O}PmMu{cC(`LWTICGS)#>OM=ZiHOsf{RWaO`kq-I z!!?UHIst>)p3MmLDF>5+B0MfCgmINXL2H)06Nqe1V_}59-j1NW4d%PcyUn-%{9^O` zJUY9si1EeiEGcuS12lqmN!6ZH1^YT>{BRzPc3_sXgC?QzX2E9ZA#TmFyz~1;gIN&n#qqz3Z$cis6{0CL*3yd=x(KqOIH8 zRv!tPhYot1^q^>fg(Uzu82~NkM-29v)i@JsUg|%>x7{fGpxd)=+H2n~pR;cZaFu>D z3O{#KKeqxM5zp3vSop72dIK>2udQ5H3GnNT5SvVfuHy$QG4rual5Day6iO)&_ei8? zF^5?kRy&u?^-d0vdJ_0_PFh+jN7&~G})&-+6vYI=vIlAKT-~RnC!_+z@a!F!x zYmL}aF=FyDCZLu0P}XU9ec2E&#|nrbnh3c2=Vu5ZqN(HAf5}=55K~#FiH!r-AnR|e zw`SSf8H>*ZAM0T9-vWS16EqlN1Vg=BhGK35W;tnpEtMgy@mLnn9t3r-SwiZg{&j{Z z{=d&AtkjoN>>yoQjH=I`p)|bAb?zUAFGs6(r%(O6-hAeUZtBjN3`<6CAE!22qA@wd zjg8=dkkcm-=*yqda6RMS2nl3W-ARF#lJp{-1=lh$Ve*4@n0YM_0^t@P;3*JjWHQt_ov;z4> z;g=E>?0}2Mm$^)SWqr;p=UOyT-9T*5JCMq7Ve*HzvfGn^@|q< z2Ota{EHL9r0Ulq}z5vK?3Bh<$8zTPLA#R1ccmr~klY385Ts!i9gPf0hzCfAb!=x$fY2OWx2=1HJR(;Nnmb9UVJu|QVPPe1c0J6RV>?0#h=ssXV zq9+Cz-McJ^xw&uaJH$wXk)!r_9|UMzU_gLGnfVDq1fxNI*GD+b1KLUb{ z(a+?$);fQisPXL(qWQvX4+KPlYF@)w6k3|U@mT?|fQDvfggSOT{5BtRo^jWP{zpmr zzS|}MRbPYaHQesSLkZCa&%btEpn_xls%SKKjzSufvXLdGZ#Wkn5BuRYYqYw&~f((0>(pXVU0hy za3v6WgZup2dj&y#rZ8rOpslP!5$R7hVWeQ~Ayz4$;X?5?A&8AV;Cuaa^DIZLkQgNc zujo0X&TOLW5xU$RyDZ8M<}OqvTAXj){A-gd2G0>rTv>1?FL5hZl)xF9pP8C^14V!2 z2xX227XBCf<_0%4?qv)uW!MCx$x-qln2M{To?X1W#?oWmL&06OuYH#*;UPt$4yV#> zlXsi7EfsP2s(X!`pK}y~v7jG{mL+dM{ob~5;cpshk*c8bjWPfL002ovPDHLkV1oC3 Bv3URh literal 0 HcmV?d00001 diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index a2535b97fd0..cb1f2820155 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -603,6 +603,11 @@ def test_exif(self, test_file): exif = im._getexif() assert exif[274] == 1 + def test_xmp_tags_orientation(self): + with Image.open("Tests/images/xmp_tags_orientation.png") as im: + exif = im.getexif() + assert exif[274] == 3 + def test_exif_save(self, tmp_path): with Image.open("Tests/images/exif.png") as im: test_file = str(tmp_path / "temp.png") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 19ddcbba506..f85e5ed2e1f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -35,6 +35,7 @@ import sys import tempfile import warnings +import xml.etree.ElementTree from collections.abc import Callable, MutableMapping from pathlib import Path @@ -1297,10 +1298,30 @@ def getextrema(self): return tuple(extrema) return self.im.getextrema() + def _parse_xmp_tags(self): + if 0x0112 in self._exif: + return + + xmp_tags = self.info.get("XML:com.adobe.xmp") + if not xmp_tags: + return + + root = xml.etree.ElementTree.fromstring(xmp_tags) + for elem in root.iter(): + if elem.tag.endswith("}Description"): + break + else: + return + + orientation = elem.attrib.get("{http://ns.adobe.com/tiff/1.0/}Orientation") + if orientation: + self._exif[0x0112] = int(orientation) + def getexif(self): if self._exif is None: self._exif = Exif() self._exif.load(self.info.get("exif")) + self._parse_xmp_tags() return self._exif def getim(self): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index ee9d52b4cc6..698462523cc 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -940,6 +940,7 @@ def getexif(self): "".join(self.info["Raw profile type exif"].split("\n")[3:]) ) self._exif.load(exif_info) + self._parse_xmp_tags() return self._exif def _close__fp(self): From f21816918e23b582b4ad3c65c6bf27ce952488c2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 16 Apr 2020 21:14:19 +1000 Subject: [PATCH 061/262] Allow ImageMagick zTXt chunks to be extracted after copy() --- Tests/test_file_png.py | 24 +++++++++++++--------- src/PIL/Image.py | 43 ++++++++++++++++++++------------------- src/PIL/PngImagePlugin.py | 12 +---------- 3 files changed, 37 insertions(+), 42 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index cb1f2820155..a44bdecf87e 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -591,19 +591,23 @@ def test_textual_chunks_after_idat(self): with Image.open("Tests/images/hopper_idat_after_image_end.png") as im: assert im.text == {"TXT": "VALUE", "ZIP": "VALUE"} - @pytest.mark.parametrize( - "test_file", - [ - "Tests/images/exif.png", # With an EXIF chunk - "Tests/images/exif_imagemagick.png", # With an ImageMagick zTXt chunk - ], - ) - def test_exif(self, test_file): - with Image.open(test_file) as im: + def test_exif(self): + # With an EXIF chunk + with Image.open("Tests/images/exif.png") as im: exif = im._getexif() assert exif[274] == 1 - def test_xmp_tags_orientation(self): + # With an ImageMagick zTXt chunk + with Image.open("Tests/images/exif_imagemagick.png") as im: + exif = im._getexif() + assert exif[274] == 1 + + # Assert that info still can be extracted + # when the image is no longer a PngImageFile instance + exif = im.copy().getexif() + assert exif[274] == 1 + + # With XMP tags with Image.open("Tests/images/xmp_tags_orientation.png") as im: exif = im.getexif() assert exif[274] == 3 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index f85e5ed2e1f..d3c7cf0b1ff 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1298,30 +1298,31 @@ def getextrema(self): return tuple(extrema) return self.im.getextrema() - def _parse_xmp_tags(self): - if 0x0112 in self._exif: - return - - xmp_tags = self.info.get("XML:com.adobe.xmp") - if not xmp_tags: - return - - root = xml.etree.ElementTree.fromstring(xmp_tags) - for elem in root.iter(): - if elem.tag.endswith("}Description"): - break - else: - return - - orientation = elem.attrib.get("{http://ns.adobe.com/tiff/1.0/}Orientation") - if orientation: - self._exif[0x0112] = int(orientation) - def getexif(self): if self._exif is None: self._exif = Exif() - self._exif.load(self.info.get("exif")) - self._parse_xmp_tags() + + exif_info = self.info.get("exif") + if exif_info is None and "Raw profile type exif" in self.info: + exif_info = bytes.fromhex( + "".join(self.info["Raw profile type exif"].split("\n")[3:]) + ) + self._exif.load(exif_info) + + # XMP tags + if 0x0112 not in self._exif: + xmp_tags = self.info.get("XML:com.adobe.xmp") + if xmp_tags: + root = xml.etree.ElementTree.fromstring(xmp_tags) + for elem in root.iter(): + if elem.tag.endswith("}Description"): + orientation = elem.attrib.get( + "{http://ns.adobe.com/tiff/1.0/}Orientation" + ) + if orientation: + self._exif[0x0112] = int(orientation) + break + return self._exif def getim(self): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 698462523cc..f62bf8542c8 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -931,17 +931,7 @@ def getexif(self): if "exif" not in self.info: self.load() - if self._exif is None: - self._exif = Image.Exif() - - exif_info = self.info.get("exif") - if exif_info is None and "Raw profile type exif" in self.info: - exif_info = bytes.fromhex( - "".join(self.info["Raw profile type exif"].split("\n")[3:]) - ) - self._exif.load(exif_info) - self._parse_xmp_tags() - return self._exif + return super().getexif() def _close__fp(self): try: From 913e79f0102971f5d9f9bd1fcffbd4045933a24f Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 15:52:10 +0300 Subject: [PATCH 062/262] Test the modes --- Tests/test_pickle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 02212c7d880..aa06e57ae53 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -62,8 +62,8 @@ def helper_pickle_string( def test_pickle_image(tmp_path, test_file, test_mode): # Act / Assert for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): - helper_pickle_string(pickle, protocol, test_file) - helper_pickle_file(tmp_path, pickle, protocol, test_file) + helper_pickle_string(pickle, protocol, test_file, test_mode) + helper_pickle_file(tmp_path, pickle, protocol, test_file, test_mode) def test_pickle_la_mode_with_palette(tmp_path): From 654229d1b94983c500444c268483119363ee5270 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 16 Apr 2020 20:42:29 +0300 Subject: [PATCH 063/262] GitHub Actions now sets CI=true --- MANIFEST.in | 2 -- Tests/helper.py | 8 ++------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index a5f726b040a..f5d367fdd03 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -21,10 +21,8 @@ exclude .appveyor.yml exclude .coveragerc exclude .editorconfig exclude .readthedocs.yml -exclude azure-pipelines.yml exclude codecov.yml global-exclude .git* global-exclude *.pyc global-exclude *.so -prune .azure-pipelines prune .ci diff --git a/Tests/helper.py b/Tests/helper.py index 15a51ccd1e9..a8ca85dc44f 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -272,12 +272,8 @@ def on_github_actions(): def on_ci(): - # Travis and AppVeyor have "CI" - # Azure Pipelines has "TF_BUILD" - # GitHub Actions has "GITHUB_ACTIONS" - return ( - "CI" in os.environ or "TF_BUILD" in os.environ or "GITHUB_ACTIONS" in os.environ - ) + # GitHub Actions, Travis and AppVeyor have "CI" + return "CI" in os.environ def is_big_endian(): From f589f8689fd5906b9c120b9f6fee1b61f3ee621b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 17 Apr 2020 11:20:38 +0300 Subject: [PATCH 064/262] Initialise __logical_frame = 0 so tell() == 0 when unpickled Co-Authored-By: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/WebPImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index a24ff682cd3..000e02b43b0 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -39,7 +39,7 @@ class WebPImageFile(ImageFile.ImageFile): format = "WEBP" format_description = "WebP image" __loaded = -1 - __logical_frame = -1 + __logical_frame = 0 def _open(self): if not _webp.HAVE_WEBPANIM: From 94751da23e3876a395f2b44033c76842a5cb6158 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 17 Apr 2020 11:21:57 +0300 Subject: [PATCH 065/262] Initialise __physical_frame = 0 and add pickle roundtrip tell test --- Tests/test_pickle.py | 11 +++++++++++ src/PIL/WebPImagePlugin.py | 1 + 2 files changed, 12 insertions(+) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index aa06e57ae53..a6652562cdc 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -82,3 +82,14 @@ def test_pickle_la_mode_with_palette(tmp_path): im.mode = "PA" assert im == loaded_im + + +def test_pickle_tell(): + # Arrange + image = Image.open("Tests/images/hopper.webp") + + # Act: roundtrip + unpickled_image = pickle.loads(pickle.dumps(image)) + + # Assert + assert unpickled_image.tell() == 0 diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 000e02b43b0..011e10716dc 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -40,6 +40,7 @@ class WebPImageFile(ImageFile.ImageFile): format_description = "WebP image" __loaded = -1 __logical_frame = 0 + __physical_frame = 0 def _open(self): if not _webp.HAVE_WEBPANIM: From fe8225732a38153d5b5671cfd567256c3fcf0c1c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 18:29:45 +1000 Subject: [PATCH 066/262] Throw image loading error faster --- src/PIL/ImageFile.py | 4 ++-- src/PIL/TiffImagePlugin.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 8637a47f953..9a780b4e04f 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -150,10 +150,10 @@ def verify(self): def load(self): """Load image data based on tile list""" - pixel = Image.Image.load(self) - if self.tile is None: raise OSError("cannot load this image") + + pixel = Image.Image.load(self) if not self.tile: return pixel diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 4b04752edae..e8d82ae46ac 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1094,10 +1094,10 @@ def _load_libtiff(self): """ Overload method triggered when we detect a compressed tiff Calls out to libtiff """ - pixel = Image.Image.load(self) - if self.tile is None: raise OSError("cannot load this image") + + pixel = Image.Image.load(self) if not self.tile: return pixel From 986c55ff646852573b95986b628e5ab133a2ed4a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 19:15:05 +1000 Subject: [PATCH 067/262] Initialise __loaded = 0, removed initialisation of __physical_frame --- src/PIL/WebPImagePlugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 011e10716dc..77a02e4b9b3 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -38,9 +38,8 @@ class WebPImageFile(ImageFile.ImageFile): format = "WEBP" format_description = "WebP image" - __loaded = -1 + __loaded = 0 __logical_frame = 0 - __physical_frame = 0 def _open(self): if not _webp.HAVE_WEBPANIM: From e52b8cefe16ce247696da943790dda005d8a85e6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 20:08:10 +1000 Subject: [PATCH 068/262] Skip test if webp is not available --- Tests/test_pickle.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index a6652562cdc..4bf6d586a15 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -84,6 +84,7 @@ def test_pickle_la_mode_with_palette(tmp_path): assert im == loaded_im +@skip_unless_feature("webp") def test_pickle_tell(): # Arrange image = Image.open("Tests/images/hopper.webp") From 76d96b6383d0e8b64d7d695e4377d9b81c1e7f2c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 21:13:50 +1000 Subject: [PATCH 069/262] Removed default arguments from helper functions --- Tests/test_pickle.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 4bf6d586a15..7dd29e216f3 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -6,9 +6,7 @@ from .helper import skip_unless_feature -def helper_pickle_file( - tmp_path, pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None -): +def helper_pickle_file(tmp_path, pickle, protocol, test_file, mode): # Arrange with Image.open(test_file) as im: filename = str(tmp_path / "temp.pkl") @@ -25,9 +23,7 @@ def helper_pickle_file( assert im == loaded_im -def helper_pickle_string( - pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None -): +def helper_pickle_string(pickle, protocol, test_file, mode): with Image.open(test_file) as im: if mode: im = im.convert(mode) From 0b33a87b0d15fcf280db2078895a99da9c38441d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Apr 2020 14:37:49 +1000 Subject: [PATCH 070/262] Replaced property methods for n_frames and is_animated with normal properties --- src/PIL/DcxImagePlugin.py | 10 ++-------- src/PIL/FliImagePlugin.py | 11 ++--------- src/PIL/MicImagePlugin.py | 10 ++-------- src/PIL/MpoImagePlugin.py | 13 +++---------- src/PIL/PsdImagePlugin.py | 10 ++-------- src/PIL/TiffImagePlugin.py | 10 +++------- src/PIL/WebPImagePlugin.py | 14 ++++---------- 7 files changed, 18 insertions(+), 60 deletions(-) diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index 7d2aff325aa..a12d9195b7a 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -59,16 +59,10 @@ def _open(self): self.__fp = self.fp self.frame = None + self.n_frames = len(self._offset) + self.is_animated = self.n_frames > 1 self.seek(0) - @property - def n_frames(self): - return len(self._offset) - - @property - def is_animated(self): - return len(self._offset) > 1 - def seek(self, frame): if not self._seek_check(frame): return diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 9bf7d74d6e4..98909456990 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -51,7 +51,8 @@ def _open(self): raise SyntaxError("not an FLI/FLC file") # frames - self.__framecount = i16(s[6:8]) + self.n_frames = i16(s[6:8]) + self.is_animated = self.n_frames > 1 # image characteristics self.mode = "P" @@ -110,14 +111,6 @@ def _palette(self, palette, shift): palette[i] = (r, g, b) i += 1 - @property - def n_frames(self): - return self.__framecount - - @property - def is_animated(self): - return self.__framecount > 1 - def seek(self, frame): if not self._seek_check(frame): return diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 8610988fcd2..1d7af7c7ac7 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -64,20 +64,14 @@ def _open(self): self.__fp = self.fp self.frame = None + self._n_frames = len(self.images) + self.is_animated = self._n_frames > 1 if len(self.images) > 1: self.category = Image.CONTAINER self.seek(0) - @property - def n_frames(self): - return len(self.images) - - @property - def is_animated(self): - return len(self.images) > 1 - def seek(self, frame): if not self._seek_check(frame): return diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index e97176d572b..575cc9c8ec6 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -48,15 +48,16 @@ def _open(self): def _after_jpeg_open(self, mpheader=None): self.mpinfo = mpheader if mpheader is not None else self._getmp() - self.__framecount = self.mpinfo[0xB001] + self.n_frames = self.mpinfo[0xB001] self.__mpoffsets = [ mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002] ] self.__mpoffsets[0] = 0 # Note that the following assertion will only be invalid if something # gets broken within JpegImagePlugin. - assert self.__framecount == len(self.__mpoffsets) + assert self.n_frames == len(self.__mpoffsets) del self.info["mpoffset"] # no longer needed + self.is_animated = self.n_frames > 1 self.__fp = self.fp # FIXME: hack self.__fp.seek(self.__mpoffsets[0]) # get ready to read first frame self.__frame = 0 @@ -67,14 +68,6 @@ def _after_jpeg_open(self, mpheader=None): def load_seek(self, pos): self.__fp.seek(pos) - @property - def n_frames(self): - return self.__framecount - - @property - def is_animated(self): - return self.__framecount > 1 - def seek(self, frame): if not self._seek_check(frame): return diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index cceb85c5b35..044df443d75 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -119,6 +119,8 @@ def _open(self): if size: self.layers = _layerinfo(self.fp) self.fp.seek(end) + self.n_frames = len(self.layers) + self.is_animated = self.n_frames > 1 # # image descriptor @@ -130,14 +132,6 @@ def _open(self): self.frame = 1 self._min_frame = 1 - @property - def n_frames(self): - return len(self.layers) - - @property - def is_animated(self): - return len(self.layers) > 1 - def seek(self, layer): if not self._seek_check(layer): return diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e8d82ae46ac..ee2d57743db 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1015,10 +1015,6 @@ def n_frames(self): self.seek(current) return self._n_frames - @property - def is_animated(self): - return self._is_animated - def seek(self, frame): """Select a given frame as current image""" if not self._seek_check(frame): @@ -1052,7 +1048,7 @@ def _seek(self, frame): if self.__next == 0: self._n_frames = frame + 1 if len(self._frame_pos) == 1: - self._is_animated = self.__next != 0 + self.is_animated = self.__next != 0 self.__frame += 1 self.fp.seek(self._frame_pos[frame]) self.tag_v2.load(self.fp) @@ -1087,7 +1083,7 @@ def load_end(self): # allow closing if we're on the first frame, there's no next # This is the ImageFile.load path only, libtiff specific below. - if not self._is_animated: + if not self.is_animated: self._close_exclusive_fp_after_loading = True def _load_libtiff(self): @@ -1138,7 +1134,7 @@ def _load_libtiff(self): except ValueError: raise OSError("Couldn't set the image") - close_self_fp = self._exclusive_fp and not self._is_animated + close_self_fp = self._exclusive_fp and not self.is_animated if hasattr(self.fp, "getvalue"): # We've got a stringio like thing passed in. Yay for all in memory. # The decoder needs the entire file in one shot, so there's not diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 77a02e4b9b3..fd8c02a2d38 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -54,7 +54,8 @@ def _open(self): self._size = width, height self.fp = BytesIO(data) self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] - self._n_frames = 1 + self.n_frames = 1 + self.is_animated = False return # Use the newer AnimDecoder API to parse the (possibly) animated file, @@ -72,7 +73,8 @@ def _open(self): bgcolor & 0xFF, ) self.info["background"] = (bg_r, bg_g, bg_b, bg_a) - self._n_frames = frame_count + self.n_frames = frame_count + self.is_animated = self.n_frames > 1 self.mode = "RGB" if mode == "RGBX" else mode self.rawmode = mode self.tile = [] @@ -97,14 +99,6 @@ def _getexif(self): return None return dict(self.getexif()) - @property - def n_frames(self): - return self._n_frames - - @property - def is_animated(self): - return self._n_frames > 1 - def seek(self, frame): if not _webp.HAVE_WEBPANIM: return super().seek(frame) From 03b5ffbd238e932380e029d98690219db4214bba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 22:05:25 +1000 Subject: [PATCH 071/262] Use common _seek_check method --- src/PIL/WebPImagePlugin.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index fd8c02a2d38..f9d824cf427 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -100,14 +100,8 @@ def _getexif(self): return dict(self.getexif()) def seek(self, frame): - if not _webp.HAVE_WEBPANIM: - return super().seek(frame) - - # Perform some simple checks first - if frame >= self._n_frames: - raise EOFError("attempted to seek beyond end of sequence") - if frame < 0: - raise EOFError("negative frame index is not valid") + if not self._seek_check(frame): + return # Set logical frame to requested position self.__logical_frame = frame From 574b0ee315202676d6ede6a8a8a0d2b7c54d5dea Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 22:05:38 +1000 Subject: [PATCH 072/262] Removed initial seek --- src/PIL/WebPImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index f9d824cf427..3b9fe8f75f0 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -92,7 +92,6 @@ def _open(self): # Initialize seek state self._reset(reset=False) - self.seek(0) def _getexif(self): if "exif" not in self.info: From 9956a6c777d55b5ee0e17208bea6a138bbd8baf8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 17 Apr 2020 22:13:14 +1000 Subject: [PATCH 073/262] Fixed bug when unpickling TIFF images --- Tests/test_pickle.py | 1 + src/PIL/TiffImagePlugin.py | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index 4bf6d586a15..3e44e99ef59 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -49,6 +49,7 @@ def helper_pickle_string( pytest.param( "Tests/images/hopper.webp", None, marks=skip_unless_feature("webp") ), + ("Tests/images/hopper.tif", None), ("Tests/images/test-card.png", None), ("Tests/images/zero_bb.png", None), ("Tests/images/zero_bb_scale2.png", None), diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e8d82ae46ac..f9e0ad07025 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1066,7 +1066,7 @@ def tell(self): return self.__frame def load(self): - if self.use_load_libtiff: + if self.tile and self.use_load_libtiff: return self._load_libtiff() return super().load() @@ -1094,12 +1094,7 @@ def _load_libtiff(self): """ Overload method triggered when we detect a compressed tiff Calls out to libtiff """ - if self.tile is None: - raise OSError("cannot load this image") - - pixel = Image.Image.load(self) - if not self.tile: - return pixel + Image.Image.load(self) self.load_prepare() From a0d8e550ec34729d4b67774069ccc2b1319f44b1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 18 Apr 2020 08:50:52 +1000 Subject: [PATCH 074/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4ece866f2d4..29a3fb69c06 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,18 @@ Changelog (Pillow) ================== +7.2.0 (unreleased) +------------------ + +- Fixed bug when unpickling TIFF images #4565 + [radarhere] + +- Fix pickling WebP #4561 + [hugovk, radarhere] + +- Raise an EOFError when seeking too far in PNG #4528 + [radarhere] + 7.1.1 (2020-04-02) ------------------ From e10cab42f1112645e1cf90b0e460e441e2682f1f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 19 Apr 2020 20:56:17 +1000 Subject: [PATCH 075/262] Consider transparency when drawing text on an RGBA image --- Tests/images/transparent_background_text.png | Bin 0 -> 1271 bytes Tests/test_image_paste.py | 2 +- Tests/test_imagefont.py | 12 ++++++++++++ src/libImaging/Paste.c | 8 ++++++-- 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 Tests/images/transparent_background_text.png diff --git a/Tests/images/transparent_background_text.png b/Tests/images/transparent_background_text.png new file mode 100644 index 0000000000000000000000000000000000000000..40acd92b62243953ebc0910629922eef112424e1 GIT binary patch literal 1271 zcmeAS@N?(olHy`uVBq!ia0y~yVAKJ!Q#jawqzxnQ0R{$^eV#6kAr*7p-j44O4V5|Y zaeb1ailSHjRfP~GB~2xzo}kkUbedi$F5RZ1(b?3*rNp7svT4Dmh4vy-_g?W1V_7;e zY(_>vOH{xk=T}*_SK8jW<>mdWvPi%A(d_%Zz5m^|yl3ZoWBdHfnf{&D&*y!fEjdBO za}p+6^4QZQB=niTwv<-&MP2Ou{8{s5{nOWKf8*HW zGCxZM1y7E8^Iy$?`3CEr=oRrSG0b-uY;MoJ&}Y56q4mJt1M_BWD=(VKx8~syTlfDA z?tP^KR)t%w4hJRwJ+OxD?Q@1|?XIU07Bjl92#4}U)E?M=ApQWu2RnoHi;}1B zTkga;NLBcqEKN0D6ZUwfzm!cG<8CN^u zp8GfV-G2EU8vkoqH?)@Yi9by~qjWZAm*Dx72dN31nPM!On)h@J@10nBVCs_h*(%rH zs@)B!klC^LM3zpYCY*4>VDmLH4X zdYnGXR=47^na|@0McY@K_O2H6WPPj0c|6-b)ACDYf4_0u?*>cWThA~2D-yW$CP}M6 z?}N5xrtiJyKbNH5DQNw;nn>+15746fXt6pX_wk>Vey(Vz1ESm%I2qEVmk!GpC1%cYTT9E4RUn zG5$%B?MtOOPP>A4G)+0YYr%0tk*|~GOmDbIIV^ipB>Xb(_T`d)bFSDukPG^kb*1Xk z+)KA+dW(MClBAota;Mx5o#RI4$19}W_umc3meO?GKl>A7#45i!^*6H*S!dqndf%0+ zbLG8W<@6lScg7Ne^=?P{Ix5tZo?ZDeTT0M5@P5L(%X$AVZqiUPdy?D!={~pg9_8y^ zmC8zWIxFwX3$A{hV|=eduW#{vZk=DcN@`cXze{AhWHL|?~z<)l;>pT3T@ wmAj{^ZuIT%u-)miK6g51E~c|cU;cllW|6Y9@p)IxfCUmdKI;Vst0LA-JqyPW_ literal 0 HcmV?d00001 diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index 1d3ca813550..3740fbcdccd 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -236,7 +236,7 @@ def test_color_mask_L(self): [ (127, 191, 254, 191), (111, 207, 206, 110), - (127, 254, 127, 0), + (255, 255, 255, 0) if mode == "RGBA" else (127, 254, 127, 0), (207, 207, 239, 239), (191, 191, 190, 191), (207, 206, 111, 112), diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0e642cde28e..b9dec9530d9 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -150,6 +150,18 @@ def test_render_equal(self): assert_image_equal(img_path, img_filelike) + def test_transparent_background(self): + im = Image.new(mode="RGBA", size=(300, 100)) + draw = ImageDraw.Draw(im) + ttf = self.get_font() + + txt = "Hello World!" + draw.text((10, 10), txt, font=ttf) + + target = "Tests/images/transparent_background_text.png" + with Image.open(target) as target_img: + assert_image_similar(im, target_img, 4.09) + def test_textsize_equal(self): im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c index 0bda25739ae..69353ce4633 100644 --- a/src/libImaging/Paste.c +++ b/src/libImaging/Paste.c @@ -379,9 +379,13 @@ fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask, UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; for (x = 0; x < xsize; x++) { for (i = 0; i < pixelsize; i++) { - *out = BLEND(*mask, *out, ink[i], tmp1); - out++; + UINT8 channel_mask = *mask; + if (strcmp(imOut->mode, "RGBA") == 0 && i != 3) { + channel_mask = 255 - (255 - channel_mask) * (1 - (255 - out[3]) / 255); + } + out[i] = BLEND(channel_mask, out[i], ink[i], tmp1); } + out += pixelsize; mask++; } } From 270bc4fc6031d9ab67c62b55de3292167b041f57 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 19 Apr 2020 13:59:40 +0300 Subject: [PATCH 076/262] Don't show own deprecation warning in test logs --- Tests/test_imagefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 649b0ac9b1d..7719c7c7166 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -95,7 +95,7 @@ def test_safeblock(self): def test_raise_ioerror(self): with pytest.raises(IOError): - with pytest.raises(DeprecationWarning): + with pytest.warns(DeprecationWarning): ImageFile.raise_ioerror(1) def test_raise_oserror(self): From 9390e5636a21ef6dbd68f787eb975bf40b255fa9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 20 Apr 2020 18:53:37 +1000 Subject: [PATCH 077/262] Also consider other alpha modes --- src/libImaging/Paste.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c index 69353ce4633..d0610cfc57c 100644 --- a/src/libImaging/Paste.c +++ b/src/libImaging/Paste.c @@ -380,7 +380,13 @@ fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask, for (x = 0; x < xsize; x++) { for (i = 0; i < pixelsize; i++) { UINT8 channel_mask = *mask; - if (strcmp(imOut->mode, "RGBA") == 0 && i != 3) { + if (( + strcmp(imOut->mode, "RGBa") == 0 || + strcmp(imOut->mode, "RGBA") == 0 || + strcmp(imOut->mode, "La") == 0 || + strcmp(imOut->mode, "LA") == 0 || + strcmp(imOut->mode, "PA") == 0 + ) && i != 3) { channel_mask = 255 - (255 - channel_mask) * (1 - (255 - out[3]) / 255); } out[i] = BLEND(channel_mask, out[i], ink[i], tmp1); From a0641b87c27bf3b24b531e393b8d2621f351b26d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 20 Apr 2020 19:27:09 +1000 Subject: [PATCH 078/262] Assert that warning is raised --- Tests/test_imagefile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 7719c7c7166..6964d3e004e 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -95,8 +95,9 @@ def test_safeblock(self): def test_raise_ioerror(self): with pytest.raises(IOError): - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning) as record: ImageFile.raise_ioerror(1) + assert len(record) == 1 def test_raise_oserror(self): with pytest.raises(OSError): From d21e45536a998a75aeee67f41dcd77f41d6ced7c Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 23 Apr 2020 11:18:24 +0300 Subject: [PATCH 079/262] Link to Filters concept from methods' parameters [CI skip] --- src/PIL/Image.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 19ddcbba506..e8effbe49c8 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2247,6 +2247,7 @@ def thumbnail(self, size, resample=BICUBIC, reducing_gap=2.0): :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.LANCZOS`. If omitted, it defaults to :py:attr:`PIL.Image.BICUBIC`. (was :py:attr:`PIL.Image.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 using :py:meth:`~PIL.Image.Image.reduce` or @@ -2336,6 +2337,7 @@ def getdata(self): environment), or :py:attr:`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:attr:`PIL.Image.NEAREST`. + See: :ref:`concept-filters`. :param fill: If **method** is an :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of the arguments passed to it. Otherwise, it is unused. From b65e72b2fcf313c4235e27d0be5be5c82b3237a8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Apr 2020 20:05:30 +1000 Subject: [PATCH 080/262] Updated example code to new-style class [ci skip] --- 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 e8effbe49c8..2b5e3b6f08f 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2325,7 +2325,7 @@ def transform(size, method, data, resample, fill=1): It may also be an object with a :py:meth:`~method.getdata` method that returns a tuple supplying new **method** and **data** values:: - class Example(object): + class Example: def getdata(self): method = Image.EXTENT data = (0, 0, 100, 100) From f5e9252b12890502f3c9b24d32941ab09e297523 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2020 18:48:10 +1000 Subject: [PATCH 081/262] Fixed drawing a jointed line with a sequence of numeric values --- Tests/test_imagedraw.py | 112 +++++++++++++++++++++++++++++++--------- src/PIL/ImageDraw.py | 2 + 2 files changed, 89 insertions(+), 25 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index f6eabb21a66..5aa9599ec93 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -874,31 +874,93 @@ def test_wide_line_dot(): def test_line_joint(): - im = Image.new("RGB", (500, 325)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_line_joint_curve.png" - - # Act - xy = [ - (400, 280), - (380, 280), - (450, 280), - (440, 120), - (350, 200), - (310, 280), - (300, 280), - (250, 280), - (250, 200), - (150, 200), - (150, 260), - (50, 200), - (150, 50), - (250, 100), - ] - draw.line(xy, GRAY, 50, "curve") - - # Assert - assert_image_similar(im, Image.open(expected), 3) + for xy in [ + [ + (400, 280), + (380, 280), + (450, 280), + (440, 120), + (350, 200), + (310, 280), + (300, 280), + (250, 280), + (250, 200), + (150, 200), + (150, 260), + (50, 200), + (150, 50), + (250, 100), + ], + ( + 400, + 280, + 380, + 280, + 450, + 280, + 440, + 120, + 350, + 200, + 310, + 280, + 300, + 280, + 250, + 280, + 250, + 200, + 150, + 200, + 150, + 260, + 50, + 200, + 150, + 50, + 250, + 100, + ), + [ + 400, + 280, + 380, + 280, + 450, + 280, + 440, + 120, + 350, + 200, + 310, + 280, + 300, + 280, + 250, + 280, + 250, + 200, + 150, + 200, + 150, + 260, + 50, + 200, + 150, + 50, + 250, + 100, + ], + ]: + im = Image.new("RGB", (500, 325)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_line_joint_curve.png" + + # Act + draw.line(xy, GRAY, 50, "curve") + + # Assert + assert_image_similar(im, Image.open(expected), 3) def test_textsize_empty_string(): diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 7abd459f97d..cbecf652d4f 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -156,6 +156,8 @@ def line(self, xy, fill=None, width=0, joint=None): if ink is not None: self.draw.draw_lines(xy, ink, width) if joint == "curve" and width > 4: + if not isinstance(xy[0], (list, tuple)): + xy = [tuple(xy[i : i + 2]) for i in range(0, len(xy), 2)] for i in range(1, len(xy) - 1): point = xy[i] angles = [ From e42676d940008ea60977d0b8cef3afb2f03ef273 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 24 Apr 2020 22:35:42 +1000 Subject: [PATCH 082/262] Added Ubuntu 20.04 --- .github/workflows/test-docker.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index b6e703fd9a3..db7ab5f10c5 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -14,6 +14,7 @@ jobs: arch, ubuntu-16.04-xenial-amd64, ubuntu-18.04-bionic-amd64, + ubuntu-20.04-focal-amd64, debian-9-stretch-x86, debian-10-buster-x86, centos-6-amd64, From 8149d9eabf26a6e429615db8658e0452fc1954b6 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 25 Apr 2020 08:46:40 +0300 Subject: [PATCH 083/262] Release notes for 7.1.2 --- CHANGES.rst | 3 +++ docs/releasenotes/7.1.2.rst | 16 ++++++++++++++++ docs/releasenotes/index.rst | 1 + 3 files changed, 20 insertions(+) create mode 100644 docs/releasenotes/7.1.2.rst diff --git a/CHANGES.rst b/CHANGES.rst index 29a3fb69c06..2b3141ff367 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,9 @@ Changelog (Pillow) - Fix pickling WebP #4561 [hugovk, radarhere] +7.1.2 (2020-04-25) +------------------ + - Raise an EOFError when seeking too far in PNG #4528 [radarhere] diff --git a/docs/releasenotes/7.1.2.rst b/docs/releasenotes/7.1.2.rst new file mode 100644 index 00000000000..c9d0d54eb06 --- /dev/null +++ b/docs/releasenotes/7.1.2.rst @@ -0,0 +1,16 @@ +7.1.2 +----- + +Fix another regression seeking PNG files +======================================== + +This fixes a regression introduced in 7.1.0 when adding support for APNG files. + +When calling ``seek(n)`` on a regular PNG where ``n > 0``, it failed to raise an +``EOFError`` as it should have done, resulting in: + +.. code-block:: python + + AttributeError: 'NoneType' object has no attribute 'read' + +Pillow 7.1.2 now raises the correct exception. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 7cdb5849448..575931c4d93 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 7.1.2 7.1.1 7.1.0 7.0.0 From b171a6450f351be42d46687346bf364906c2f703 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 25 Apr 2020 22:22:33 +0300 Subject: [PATCH 084/262] Remove soon-EOL Fedora 30 --- .github/workflows/test-docker.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index db7ab5f10c5..211f0d5f5b9 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -22,7 +22,6 @@ jobs: centos-8-amd64, amazon-1-amd64, amazon-2-amd64, - fedora-30-amd64, fedora-31-amd64, ] dockerTag: [master] From 9581108cf8b2be94bf44b15eaa8adc3e4fd4bf81 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Apr 2020 07:48:49 +1000 Subject: [PATCH 085/262] Removed Fedora 30 from CI targets [ci skip] --- docs/installation.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index a83bf5748d1..42618002fec 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -396,8 +396,6 @@ These platforms are built and tested for every change. +----------------------------------+--------------------------+-----------------------+ | Debian 10 Buster | 3.7 |x86 | +----------------------------------+--------------------------+-----------------------+ -| Fedora 30 | 3.7 |x86-64 | -+----------------------------------+--------------------------+-----------------------+ | Fedora 31 | 3.7 |x86-64 | +----------------------------------+--------------------------+-----------------------+ | macOS 10.15 Catalina | 3.5, 3.6, 3.7, 3.8, PyPy3|x86-64 | From 727868d5d7118ea3dff923d0df54d673792b2d5d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Apr 2020 08:03:16 +1000 Subject: [PATCH 086/262] Increased epsilon to pass on Windows --- Tests/test_file_wmf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index 9db4f63583f..3339cbfd349 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -66,7 +66,7 @@ def test_load_set_dpi(): assert im.size == (164, 164) with Image.open("Tests/images/drawing_wmf_ref_144.png") as expected: - assert_image_similar(im, expected, 2.0) + assert_image_similar(im, expected, 2.1) def test_save(tmp_path): From 661760a88374aacff07d7264da2c8a1debb46629 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Apr 2020 14:01:38 +1000 Subject: [PATCH 087/262] Updated Ubuntu CI targets [ci skip] --- docs/installation.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 42618002fec..ca7e25f9adc 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -402,6 +402,10 @@ These platforms are built and tested for every change. +----------------------------------+--------------------------+-----------------------+ | Ubuntu Linux 16.04 LTS | 3.5, 3.6, 3.7, 3.8, PyPy3|x86-64 | +----------------------------------+--------------------------+-----------------------+ +| Ubuntu Linux 18.04 LTS | 3.6 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ +| Ubuntu Linux 20.04 LTS | 3.8 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ | Windows Server 2012 R2 | 3.5, 3.8 |x86, x86-64 | | +--------------------------+-----------------------+ | | PyPy3, 3.7/MinGW |x86 | From 6fb74c9b8b55e6aa15a21958ac0a0bc82328e884 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 9 Feb 2020 16:00:36 +1100 Subject: [PATCH 088/262] Added Python 3.9 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index e77102e44a2..980506368dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,9 @@ matrix: - python: "pypy3" name: "PyPy3 Xenial" + - python: "3.9-dev" + name: "3.9-dev Xenial" + services: xvfb - python: "3.8" name: "3.8 Xenial" services: xvfb From 83125b5ae48f9782b960f1dc2856887d92aa3c5d Mon Sep 17 00:00:00 2001 From: Rodrigo Benenson Date: Tue, 28 Apr 2020 18:58:26 +0200 Subject: [PATCH 089/262] Checks over exif instead of reloeaded_exif test_imagefile.py checks exif instead of reloaded_exif; making the tests pass when they should not. --- Tests/test_imagefile.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 6964d3e004e..805625a24ad 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -261,9 +261,9 @@ def test_exif_jpeg(self, tmp_path): with Image.open(out) as reloaded: reloaded_exif = reloaded.getexif() assert reloaded_exif[258] == 8 - assert 40960 not in exif + assert 40960 not in reloaded_exif assert reloaded_exif[40963] == 455 - assert exif[11] == "Pillow test" + assert reloaded_exif[11] == "Pillow test" with Image.open("Tests/images/no-dpi-in-exif.jpg") as im: # Big endian exif = im.getexif() @@ -281,9 +281,9 @@ def test_exif_jpeg(self, tmp_path): with Image.open(out) as reloaded: reloaded_exif = reloaded.getexif() assert reloaded_exif[258] == 8 - assert 40960 not in exif + assert 34665 not in reloaded_exif assert reloaded_exif[40963] == 455 - assert exif[305] == "Pillow test" + assert reloaded_exif[305] == "Pillow test" @skip_unless_feature("webp") @skip_unless_feature("webp_anim") @@ -302,7 +302,7 @@ def check_exif(): reloaded_exif = reloaded.getexif() assert reloaded_exif[258] == 8 assert reloaded_exif[40963] == 455 - assert exif[305] == "Pillow test" + assert reloaded_exif[305] == "Pillow test" im.save(out, exif=exif) check_exif() From 456b36308a958de45235d689ad01cf792c8589d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20N=C3=BC=C3=9Flein?= Date: Wed, 29 Apr 2020 11:49:33 +0200 Subject: [PATCH 090/262] Mention Ubuntu versions younger than 16.04 I suppose people will just try it either way but it's good to know that it still works with the same libs all the way through to 20.04 --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index ca7e25f9adc..1da7fe77277 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -328,7 +328,7 @@ In Fedora, the command is:: .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. -Prerequisites are installed on **Ubuntu 16.04 LTS** with:: +Prerequisites for **Ubuntu 16.04 LTS or later** are installed with:: sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ From e8987491d44e8f58c8503e6df5bcdab64e504e7f Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 29 Apr 2020 16:03:42 +0300 Subject: [PATCH 091/262] Add Fedora 32 --- .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 211f0d5f5b9..8d1c2e24c08 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -23,6 +23,7 @@ jobs: amazon-1-amd64, amazon-2-amd64, fedora-31-amd64, + fedora-32-amd64, ] dockerTag: [master] diff --git a/docs/installation.rst b/docs/installation.rst index ca7e25f9adc..62549bd264e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -398,6 +398,8 @@ These platforms are built and tested for every change. +----------------------------------+--------------------------+-----------------------+ | Fedora 31 | 3.7 |x86-64 | +----------------------------------+--------------------------+-----------------------+ +| Fedora 32 | 3.8 |x86-64 | ++----------------------------------+--------------------------+-----------------------+ | macOS 10.15 Catalina | 3.5, 3.6, 3.7, 3.8, PyPy3|x86-64 | +----------------------------------+--------------------------+-----------------------+ | Ubuntu Linux 16.04 LTS | 3.5, 3.6, 3.7, 3.8, PyPy3|x86-64 | From cc39dbab0e219c4784fd8d03901cc8e6fda745e2 Mon Sep 17 00:00:00 2001 From: David Walker Date: Thu, 30 Apr 2020 23:25:45 -0700 Subject: [PATCH 092/262] Fix ImageChops documentation. Many methods were incorrectly documented as requriring mode "1". The remaining ones require *both* images to be mode "1". Documentation only, [ci skip] --- src/PIL/ImageChops.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/PIL/ImageChops.py b/src/PIL/ImageChops.py index 2d13b529fef..904df484e86 100644 --- a/src/PIL/ImageChops.py +++ b/src/PIL/ImageChops.py @@ -54,7 +54,7 @@ def invert(image): def lighter(image1, image2): """ Compares the two images, pixel by pixel, and returns a new image containing - the lighter values. At least one of the images must have mode "1". + the lighter values. .. code-block:: python @@ -71,7 +71,7 @@ def lighter(image1, image2): def darker(image1, image2): """ Compares the two images, pixel by pixel, and returns a new image containing - the darker values. At least one of the images must have mode "1". + the darker values. .. code-block:: python @@ -88,7 +88,7 @@ def darker(image1, image2): def difference(image1, image2): """ Returns the absolute value of the pixel-by-pixel difference between the two - images. At least one of the images must have mode "1". + images. .. code-block:: python @@ -107,8 +107,7 @@ def multiply(image1, image2): Superimposes two images on top of each other. If you multiply an image with a solid black image, the result is black. If - you multiply with a solid white image, the image is unaffected. At least - one of the images must have mode "1". + you multiply with a solid white image, the image is unaffected. .. code-block:: python @@ -124,8 +123,7 @@ def multiply(image1, image2): def screen(image1, image2): """ - Superimposes two inverted images on top of each other. At least one of the - images must have mode "1". + Superimposes two inverted images on top of each other. .. code-block:: python @@ -179,7 +177,6 @@ def add(image1, image2, scale=1.0, offset=0): """ Adds two images, dividing the result by scale and adding the offset. If omitted, scale defaults to 1.0, and offset to 0.0. - At least one of the images must have mode "1". .. code-block:: python @@ -196,8 +193,7 @@ def add(image1, image2, scale=1.0, offset=0): def subtract(image1, image2, scale=1.0, offset=0): """ Subtracts two images, dividing the result by scale and adding the offset. - If omitted, scale defaults to 1.0, and offset to 0.0. At least one of the - images must have mode "1". + If omitted, scale defaults to 1.0, and offset to 0.0. .. code-block:: python @@ -212,8 +208,7 @@ def subtract(image1, image2, scale=1.0, offset=0): def add_modulo(image1, image2): - """Add two images, without clipping the result. At least one of the images - must have mode "1". + """Add two images, without clipping the result. .. code-block:: python @@ -228,8 +223,7 @@ def add_modulo(image1, image2): def subtract_modulo(image1, image2): - """Subtract two images, without clipping the result. At least one of the - images must have mode "1". + """Subtract two images, without clipping the result. .. code-block:: python @@ -244,8 +238,10 @@ def subtract_modulo(image1, image2): def logical_and(image1, image2): - """Logical AND between two images. At least one of the images must have - mode "1". + """Logical AND between two images. + + Both of the images must have mode "1". For an AND in RGB mode, use a + multiply() by a black-and-white mask. .. code-block:: python @@ -260,8 +256,9 @@ def logical_and(image1, image2): def logical_or(image1, image2): - """Logical OR between two images. At least one of the images must have - mode "1". + """Logical OR between two images. + + Both of the images must have mode "1". .. code-block:: python @@ -276,8 +273,9 @@ def logical_or(image1, image2): def logical_xor(image1, image2): - """Logical XOR between two images. At least one of the images must have - mode "1". + """Logical XOR between two images. + + Both of the images must have mode "1". .. code-block:: python From d5c38146916703b21d922f02469e19b3621c467b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 1 May 2020 19:41:51 +1000 Subject: [PATCH 093/262] Changed default offset for Exif --- 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 2b5e3b6f08f..0c8b42a0909 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3281,7 +3281,7 @@ def load(self, data): self._data.update(ifd) self._ifds[0x8769] = ifd - def tobytes(self, offset=0): + def tobytes(self, offset=8): from . import TiffImagePlugin if self.endian == "<": From ae43af61daf17c0fd9e027afa6b80d7248e23a64 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 1 May 2020 22:08:57 +1000 Subject: [PATCH 094/262] Replaced tabs with spaces --- src/_imagingtk.c | 8 +- src/_webp.c | 2 +- src/display.c | 54 +- src/encode.c | 2 +- src/libImaging/BcnDecode.c | 1381 +++++++++++++++++---------------- src/libImaging/BitDecode.c | 28 +- src/libImaging/Blend.c | 70 +- src/libImaging/ConvertYCbCr.c | 2 +- src/libImaging/Copy.c | 4 +- src/libImaging/Crop.c | 12 +- src/libImaging/EpsEncode.c | 76 +- src/libImaging/Except.c | 2 +- src/libImaging/Fill.c | 6 +- src/libImaging/FliDecode.c | 352 ++++----- src/libImaging/GetBBox.c | 102 +-- src/libImaging/Gif.h | 6 +- src/libImaging/GifDecode.c | 362 ++++----- src/libImaging/GifEncode.c | 254 +++--- src/libImaging/HexDecode.c | 52 +- src/libImaging/Histo.c | 178 ++--- src/libImaging/ImPlatform.h | 30 +- src/libImaging/Jpeg.h | 8 +- src/libImaging/Jpeg2K.h | 4 +- src/libImaging/JpegDecode.c | 12 +- src/libImaging/JpegEncode.c | 428 +++++----- src/libImaging/Matrix.c | 66 +- src/libImaging/Negative.c | 10 +- src/libImaging/Offset.c | 26 +- src/libImaging/PackDecode.c | 92 +-- src/libImaging/PcdDecode.c | 82 +- src/libImaging/PcxDecode.c | 86 +- src/libImaging/Point.c | 10 +- src/libImaging/RawDecode.c | 88 +-- src/libImaging/RawEncode.c | 74 +- src/libImaging/SgiRleDecode.c | 4 +- src/libImaging/TgaRleDecode.c | 86 +- src/libImaging/TiffDecode.h | 16 +- src/libImaging/Unpack.c | 32 +- src/libImaging/UnpackYCC.c | 36 +- src/libImaging/XbmDecode.c | 66 +- src/libImaging/XbmEncode.c | 96 +-- src/libImaging/Zip.h | 24 +- src/outline.c | 76 +- 43 files changed, 2203 insertions(+), 2202 deletions(-) diff --git a/src/_imagingtk.c b/src/_imagingtk.c index bdf5e68d192..95d97764593 100644 --- a/src/_imagingtk.c +++ b/src/_imagingtk.c @@ -4,8 +4,8 @@ * tkinter hooks * * history: - * 99-07-26 fl created - * 99-08-15 fl moved to its own support module + * 99-07-26 fl created + * 99-08-15 fl moved to its own support module * * Copyright (c) Secret Labs AB 1999. * @@ -45,8 +45,8 @@ _tkinit(PyObject* self, PyObject* args) interp = (Tcl_Interp*)PyLong_AsVoidPtr(arg); else { TkappObject* app; - /* Do it the hard way. This will break if the TkappObject - layout changes */ + /* Do it the hard way. This will break if the TkappObject + layout changes */ app = (TkappObject*)PyLong_AsVoidPtr(arg); interp = app->interp; } diff --git a/src/_webp.c b/src/_webp.c index babea60f3ea..8979118a248 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -836,7 +836,7 @@ void addAnimFlagToModule(PyObject* m) { void addTransparencyFlagToModule(PyObject* m) { PyModule_AddObject(m, "HAVE_TRANSPARENCY", - PyBool_FromLong(!WebPDecoderBuggyAlpha())); + PyBool_FromLong(!WebPDecoderBuggyAlpha())); } static int setup_module(PyObject* m) { diff --git a/src/display.c b/src/display.c index eff9de5c40a..9a337d1c041 100644 --- a/src/display.c +++ b/src/display.c @@ -28,7 +28,7 @@ #include "Imaging.h" /* -------------------------------------------------------------------- */ -/* Windows DIB support */ +/* Windows DIB support */ #ifdef _WIN32 @@ -57,12 +57,12 @@ _new(const char* mode, int xsize, int ysize) display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType); if (display == NULL) - return NULL; + return NULL; display->dib = ImagingNewDIB(mode, xsize, ysize); if (!display->dib) { - Py_DECREF(display); - return NULL; + Py_DECREF(display); + return NULL; } return display; @@ -72,7 +72,7 @@ static void _delete(ImagingDisplayObject* display) { if (display->dib) - ImagingDeleteDIB(display->dib); + ImagingDeleteDIB(display->dib); PyObject_Del(display); } @@ -81,7 +81,7 @@ _expose(ImagingDisplayObject* display, PyObject* args) { HDC hdc; if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) - return NULL; + return NULL; ImagingExposeDIB(display->dib, hdc); @@ -98,7 +98,7 @@ _draw(ImagingDisplayObject* display, PyObject* args) if (!PyArg_ParseTuple(args, F_HANDLE "(iiii)(iiii)", &hdc, dst+0, dst+1, dst+2, dst+3, src+0, src+1, src+2, src+3)) - return NULL; + return NULL; ImagingDrawDIB(display->dib, hdc, dst, src); @@ -117,15 +117,15 @@ _paste(ImagingDisplayObject* display, PyObject* args) int xy[4]; xy[0] = xy[1] = xy[2] = xy[3] = 0; if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy+0, xy+1, xy+2, xy+3)) - return NULL; + return NULL; im = PyImaging_AsImaging(op); if (!im) - return NULL; + return NULL; if (xy[2] <= xy[0]) - xy[2] = xy[0] + im->xsize; + xy[2] = xy[0] + im->xsize; if (xy[3] <= xy[1]) - xy[3] = xy[1] + im->ysize; + xy[3] = xy[1] + im->ysize; ImagingPasteDIB(display->dib, im, xy); @@ -140,7 +140,7 @@ _query_palette(ImagingDisplayObject* display, PyObject* args) int status; if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) - return NULL; + return NULL; status = ImagingQueryPaletteDIB(display->dib, hdc); @@ -154,7 +154,7 @@ _getdc(ImagingDisplayObject* display, PyObject* args) HDC dc; if (!PyArg_ParseTuple(args, F_HANDLE, &window)) - return NULL; + return NULL; dc = GetDC(window); if (!dc) { @@ -172,7 +172,7 @@ _releasedc(ImagingDisplayObject* display, PyObject* args) HDC dc; if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, &window, &dc)) - return NULL; + return NULL; ReleaseDC(window, dc); @@ -228,13 +228,13 @@ static struct PyMethodDef methods[] = { static PyObject* _getattr_mode(ImagingDisplayObject* self, void* closure) { - return Py_BuildValue("s", self->dib->mode); + return Py_BuildValue("s", self->dib->mode); } static PyObject* _getattr_size(ImagingDisplayObject* self, void* closure) { - return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); + return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); } static struct PyGetSetDef getsetters[] = { @@ -244,13 +244,13 @@ static struct PyGetSetDef getsetters[] = { }; static PyTypeObject ImagingDisplayType = { - PyVarObject_HEAD_INIT(NULL, 0) - "ImagingDisplay", /*tp_name*/ - sizeof(ImagingDisplayObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)_delete, /*tp_dealloc*/ - 0, /*tp_print*/ + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingDisplay", /*tp_name*/ + sizeof(ImagingDisplayObject),/*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_delete, /*tp_dealloc*/ + 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ @@ -285,11 +285,11 @@ PyImaging_DisplayWin32(PyObject* self, PyObject* args) int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; display = _new(mode, xsize, ysize); if (display == NULL) - return NULL; + return NULL; return (PyObject*) display; } @@ -647,7 +647,7 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) PyObject* callback; int width = 0, height = 0; if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height)) - return NULL; + return NULL; if (width <= 0) width = CW_USEDEFAULT; @@ -817,7 +817,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) #endif /* _WIN32 */ /* -------------------------------------------------------------------- */ -/* X11 support */ +/* X11 support */ #ifdef HAVE_XCB #include diff --git a/src/encode.c b/src/encode.c index cbf1da3a9bd..e5649891348 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1095,7 +1095,7 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* JPEG 2000 */ +/* JPEG 2000 */ /* -------------------------------------------------------------------- */ #ifdef HAVE_OPENJPEG diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index c2c4f21e7c3..3e62aa6c998 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -16,841 +16,842 @@ typedef struct { - UINT8 r, g, b, a; + UINT8 r, g, b, a; } rgba; typedef struct { - UINT8 l; + UINT8 l; } lum; typedef struct { - FLOAT32 r, g, b; + FLOAT32 r, g, b; } rgb32f; typedef struct { - UINT16 c0, c1; - UINT32 lut; + UINT16 c0, c1; + UINT32 lut; } bc1_color; typedef struct { - UINT8 a0, a1; - UINT8 lut[6]; + UINT8 a0, a1; + UINT8 lut[6]; } bc3_alpha; #define LOAD16(p) \ - (p)[0] | ((p)[1] << 8) + (p)[0] | ((p)[1] << 8) #define LOAD32(p) \ - (p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24) + (p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24) static void bc1_color_load(bc1_color *dst, const UINT8 *src) { - dst->c0 = LOAD16(src); - dst->c1 = LOAD16(src + 2); - dst->lut = LOAD32(src + 4); + dst->c0 = LOAD16(src); + dst->c1 = LOAD16(src + 2); + dst->lut = LOAD32(src + 4); } static void bc3_alpha_load(bc3_alpha *dst, const UINT8 *src) { - memcpy(dst, src, sizeof(bc3_alpha)); + memcpy(dst, src, sizeof(bc3_alpha)); } static rgba decode_565(UINT16 x) { - rgba c; - int r, g, b; - r = (x & 0xf800) >> 8; - r |= r >> 5; - c.r = r; - g = (x & 0x7e0) >> 3; - g |= g >> 6; - c.g = g; - b = (x & 0x1f) << 3; - b |= b >> 5; - c.b = b; - c.a = 0xff; - return c; + rgba c; + int r, g, b; + r = (x & 0xf800) >> 8; + r |= r >> 5; + c.r = r; + g = (x & 0x7e0) >> 3; + g |= g >> 6; + c.g = g; + b = (x & 0x1f) << 3; + b |= b >> 5; + c.b = b; + c.a = 0xff; + return c; } static void decode_bc1_color(rgba *dst, const UINT8 *src) { - bc1_color col; - rgba p[4]; - int n, cw; - UINT16 r0, g0, b0, r1, g1, b1; - bc1_color_load(&col, src); - - p[0] = decode_565(col.c0); - r0 = p[0].r; - g0 = p[0].g; - b0 = p[0].b; - p[1] = decode_565(col.c1); - r1 = p[1].r; - g1 = p[1].g; - b1 = p[1].b; - if (col.c0 > col.c1) { - p[2].r = (2*r0 + 1*r1) / 3; - p[2].g = (2*g0 + 1*g1) / 3; - p[2].b = (2*b0 + 1*b1) / 3; - p[2].a = 0xff; - p[3].r = (1*r0 + 2*r1) / 3; - p[3].g = (1*g0 + 2*g1) / 3; - p[3].b = (1*b0 + 2*b1) / 3; - p[3].a = 0xff; - } else { - p[2].r = (r0 + r1) / 2; - p[2].g = (g0 + g1) / 2; - p[2].b = (b0 + b1) / 2; - p[2].a = 0xff; - p[3].r = 0; - p[3].g = 0; - p[3].b = 0; - p[3].a = 0; - } - for (n = 0; n < 16; n++) { - cw = 3 & (col.lut >> (2 * n)); - dst[n] = p[cw]; - } + bc1_color col; + rgba p[4]; + int n, cw; + UINT16 r0, g0, b0, r1, g1, b1; + bc1_color_load(&col, src); + + p[0] = decode_565(col.c0); + r0 = p[0].r; + g0 = p[0].g; + b0 = p[0].b; + p[1] = decode_565(col.c1); + r1 = p[1].r; + g1 = p[1].g; + b1 = p[1].b; + if (col.c0 > col.c1) { + p[2].r = (2*r0 + 1*r1) / 3; + p[2].g = (2*g0 + 1*g1) / 3; + p[2].b = (2*b0 + 1*b1) / 3; + p[2].a = 0xff; + p[3].r = (1*r0 + 2*r1) / 3; + p[3].g = (1*g0 + 2*g1) / 3; + p[3].b = (1*b0 + 2*b1) / 3; + p[3].a = 0xff; + } else { + p[2].r = (r0 + r1) / 2; + p[2].g = (g0 + g1) / 2; + p[2].b = (b0 + b1) / 2; + p[2].a = 0xff; + p[3].r = 0; + p[3].g = 0; + p[3].b = 0; + p[3].a = 0; + } + for (n = 0; n < 16; n++) { + cw = 3 & (col.lut >> (2 * n)); + dst[n] = p[cw]; + } } static void decode_bc3_alpha(char *dst, const UINT8 *src, int stride, int o) { - bc3_alpha b; - UINT16 a0, a1; - UINT8 a[8]; - int n, lut, aw; - bc3_alpha_load(&b, src); - - a0 = b.a0; - a1 = b.a1; - a[0] = (UINT8)a0; - a[1] = (UINT8)a1; - if (a0 > a1) { - a[2] = (6*a0 + 1*a1) / 7; - a[3] = (5*a0 + 2*a1) / 7; - a[4] = (4*a0 + 3*a1) / 7; - a[5] = (3*a0 + 4*a1) / 7; - a[6] = (2*a0 + 5*a1) / 7; - a[7] = (1*a0 + 6*a1) / 7; - } else { - a[2] = (4*a0 + 1*a1) / 5; - a[3] = (3*a0 + 2*a1) / 5; - a[4] = (2*a0 + 3*a1) / 5; - a[5] = (1*a0 + 4*a1) / 5; - a[6] = 0; - a[7] = 0xff; - } - lut = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16); - for (n = 0; n < 8; n++) { - aw = 7 & (lut >> (3 * n)); - dst[stride * n + o] = a[aw]; - } - lut = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16); - for (n = 0; n < 8; n++) { - aw = 7 & (lut >> (3 * n)); - dst[stride * (8+n) + o] = a[aw]; - } + bc3_alpha b; + UINT16 a0, a1; + UINT8 a[8]; + int n, lut, aw; + bc3_alpha_load(&b, src); + + a0 = b.a0; + a1 = b.a1; + a[0] = (UINT8)a0; + a[1] = (UINT8)a1; + if (a0 > a1) { + a[2] = (6*a0 + 1*a1) / 7; + a[3] = (5*a0 + 2*a1) / 7; + a[4] = (4*a0 + 3*a1) / 7; + a[5] = (3*a0 + 4*a1) / 7; + a[6] = (2*a0 + 5*a1) / 7; + a[7] = (1*a0 + 6*a1) / 7; + } else { + a[2] = (4*a0 + 1*a1) / 5; + a[3] = (3*a0 + 2*a1) / 5; + a[4] = (2*a0 + 3*a1) / 5; + a[5] = (1*a0 + 4*a1) / 5; + a[6] = 0; + a[7] = 0xff; + } + lut = b.lut[0] | (b.lut[1] << 8) | (b.lut[2] << 16); + for (n = 0; n < 8; n++) { + aw = 7 & (lut >> (3 * n)); + dst[stride * n + o] = a[aw]; + } + lut = b.lut[3] | (b.lut[4] << 8) | (b.lut[5] << 16); + for (n = 0; n < 8; n++) { + aw = 7 & (lut >> (3 * n)); + dst[stride * (8+n) + o] = a[aw]; + } } static void decode_bc1_block(rgba *col, const UINT8* src) { - decode_bc1_color(col, src); + decode_bc1_color(col, src); } static void decode_bc2_block(rgba *col, const UINT8* src) { - int n, bitI, byI, av; - decode_bc1_color(col, src + 8); - for (n = 0; n < 16; n++) { - bitI = n * 4; - byI = bitI >> 3; - av = 0xf & (src[byI] >> (bitI & 7)); - av = (av << 4) | av; - col[n].a = av; - } + int n, bitI, byI, av; + decode_bc1_color(col, src + 8); + for (n = 0; n < 16; n++) { + bitI = n * 4; + byI = bitI >> 3; + av = 0xf & (src[byI] >> (bitI & 7)); + av = (av << 4) | av; + col[n].a = av; + } } static void decode_bc3_block(rgba *col, const UINT8* src) { - decode_bc1_color(col, src + 8); - decode_bc3_alpha((char *)col, src, sizeof(col[0]), 3); + decode_bc1_color(col, src + 8); + decode_bc3_alpha((char *)col, src, sizeof(col[0]), 3); } static void decode_bc4_block(lum *col, const UINT8* src) { - decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0); + decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0); } static void decode_bc5_block(rgba *col, const UINT8* src) { - decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0); - decode_bc3_alpha((char *)col, src + 8, sizeof(col[0]), 1); + decode_bc3_alpha((char *)col, src, sizeof(col[0]), 0); + decode_bc3_alpha((char *)col, src + 8, sizeof(col[0]), 1); } /* BC6 and BC7 are described here: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt */ static UINT8 get_bit(const UINT8* src, int bit) { - int by = bit >> 3; - bit &= 7; - return (src[by] >> bit) & 1; + int by = bit >> 3; + bit &= 7; + return (src[by] >> bit) & 1; } static UINT8 get_bits(const UINT8* src, int bit, int count) { - UINT8 v; - int x; - int by = bit >> 3; - bit &= 7; - if (!count) { - return 0; - } - if (bit + count <= 8) { - v = (src[by] >> bit) & ((1 << count) - 1); - } else { - x = src[by] | (src[by+1] << 8); - v = (x >> bit) & ((1 << count) - 1); - } - return v; + UINT8 v; + int x; + int by = bit >> 3; + bit &= 7; + if (!count) { + return 0; + } + if (bit + count <= 8) { + v = (src[by] >> bit) & ((1 << count) - 1); + } else { + x = src[by] | (src[by+1] << 8); + v = (x >> bit) & ((1 << count) - 1); + } + return v; } /* BC7 */ typedef struct { - char ns; - char pb; - char rb; - char isb; - char cb; - char ab; - char epb; - char spb; - char ib; - char ib2; + char ns; + char pb; + char rb; + char isb; + char cb; + char ab; + char epb; + char spb; + char ib; + char ib2; } bc7_mode_info; static const bc7_mode_info bc7_modes[] = { - {3, 4, 0, 0, 4, 0, 1, 0, 3, 0}, - {2, 6, 0, 0, 6, 0, 0, 1, 3, 0}, - {3, 6, 0, 0, 5, 0, 0, 0, 2, 0}, - {2, 6, 0, 0, 7, 0, 1, 0, 2, 0}, - {1, 0, 2, 1, 5, 6, 0, 0, 2, 3}, - {1, 0, 2, 0, 7, 8, 0, 0, 2, 2}, - {1, 0, 0, 0, 7, 7, 1, 0, 4, 0}, - {2, 6, 0, 0, 5, 5, 1, 0, 2, 0} + {3, 4, 0, 0, 4, 0, 1, 0, 3, 0}, + {2, 6, 0, 0, 6, 0, 0, 1, 3, 0}, + {3, 6, 0, 0, 5, 0, 0, 0, 2, 0}, + {2, 6, 0, 0, 7, 0, 1, 0, 2, 0}, + {1, 0, 2, 1, 5, 6, 0, 0, 2, 3}, + {1, 0, 2, 0, 7, 8, 0, 0, 2, 2}, + {1, 0, 0, 0, 7, 7, 1, 0, 4, 0}, + {2, 6, 0, 0, 5, 5, 1, 0, 2, 0} }; /* Subset indices: Table.P2, 1 bit per index */ static const UINT16 bc7_si2[] = { - 0xcccc, 0x8888, 0xeeee, 0xecc8, 0xc880, 0xfeec, 0xfec8, 0xec80, - 0xc800, 0xffec, 0xfe80, 0xe800, 0xffe8, 0xff00, 0xfff0, 0xf000, - 0xf710, 0x008e, 0x7100, 0x08ce, 0x008c, 0x7310, 0x3100, 0x8cce, - 0x088c, 0x3110, 0x6666, 0x366c, 0x17e8, 0x0ff0, 0x718e, 0x399c, - 0xaaaa, 0xf0f0, 0x5a5a, 0x33cc, 0x3c3c, 0x55aa, 0x9696, 0xa55a, - 0x73ce, 0x13c8, 0x324c, 0x3bdc, 0x6996, 0xc33c, 0x9966, 0x0660, - 0x0272, 0x04e4, 0x4e40, 0x2720, 0xc936, 0x936c, 0x39c6, 0x639c, - 0x9336, 0x9cc6, 0x817e, 0xe718, 0xccf0, 0x0fcc, 0x7744, 0xee22}; + 0xcccc, 0x8888, 0xeeee, 0xecc8, 0xc880, 0xfeec, 0xfec8, 0xec80, + 0xc800, 0xffec, 0xfe80, 0xe800, 0xffe8, 0xff00, 0xfff0, 0xf000, + 0xf710, 0x008e, 0x7100, 0x08ce, 0x008c, 0x7310, 0x3100, 0x8cce, + 0x088c, 0x3110, 0x6666, 0x366c, 0x17e8, 0x0ff0, 0x718e, 0x399c, + 0xaaaa, 0xf0f0, 0x5a5a, 0x33cc, 0x3c3c, 0x55aa, 0x9696, 0xa55a, + 0x73ce, 0x13c8, 0x324c, 0x3bdc, 0x6996, 0xc33c, 0x9966, 0x0660, + 0x0272, 0x04e4, 0x4e40, 0x2720, 0xc936, 0x936c, 0x39c6, 0x639c, + 0x9336, 0x9cc6, 0x817e, 0xe718, 0xccf0, 0x0fcc, 0x7744, 0xee22}; /* Table.P3, 2 bits per index */ static const UINT32 bc7_si3[] = { - 0xaa685050, 0x6a5a5040, 0x5a5a4200, 0x5450a0a8, - 0xa5a50000, 0xa0a05050, 0x5555a0a0, 0x5a5a5050, - 0xaa550000, 0xaa555500, 0xaaaa5500, 0x90909090, - 0x94949494, 0xa4a4a4a4, 0xa9a59450, 0x2a0a4250, - 0xa5945040, 0x0a425054, 0xa5a5a500, 0x55a0a0a0, - 0xa8a85454, 0x6a6a4040, 0xa4a45000, 0x1a1a0500, - 0x0050a4a4, 0xaaa59090, 0x14696914, 0x69691400, - 0xa08585a0, 0xaa821414, 0x50a4a450, 0x6a5a0200, - 0xa9a58000, 0x5090a0a8, 0xa8a09050, 0x24242424, - 0x00aa5500, 0x24924924, 0x24499224, 0x50a50a50, - 0x500aa550, 0xaaaa4444, 0x66660000, 0xa5a0a5a0, - 0x50a050a0, 0x69286928, 0x44aaaa44, 0x66666600, - 0xaa444444, 0x54a854a8, 0x95809580, 0x96969600, - 0xa85454a8, 0x80959580, 0xaa141414, 0x96960000, - 0xaaaa1414, 0xa05050a0, 0xa0a5a5a0, 0x96000000, - 0x40804080, 0xa9a8a9a8, 0xaaaaaa44, 0x2a4a5254}; + 0xaa685050, 0x6a5a5040, 0x5a5a4200, 0x5450a0a8, + 0xa5a50000, 0xa0a05050, 0x5555a0a0, 0x5a5a5050, + 0xaa550000, 0xaa555500, 0xaaaa5500, 0x90909090, + 0x94949494, 0xa4a4a4a4, 0xa9a59450, 0x2a0a4250, + 0xa5945040, 0x0a425054, 0xa5a5a500, 0x55a0a0a0, + 0xa8a85454, 0x6a6a4040, 0xa4a45000, 0x1a1a0500, + 0x0050a4a4, 0xaaa59090, 0x14696914, 0x69691400, + 0xa08585a0, 0xaa821414, 0x50a4a450, 0x6a5a0200, + 0xa9a58000, 0x5090a0a8, 0xa8a09050, 0x24242424, + 0x00aa5500, 0x24924924, 0x24499224, 0x50a50a50, + 0x500aa550, 0xaaaa4444, 0x66660000, 0xa5a0a5a0, + 0x50a050a0, 0x69286928, 0x44aaaa44, 0x66666600, + 0xaa444444, 0x54a854a8, 0x95809580, 0x96969600, + 0xa85454a8, 0x80959580, 0xaa141414, 0x96960000, + 0xaaaa1414, 0xa05050a0, 0xa0a5a5a0, 0x96000000, + 0x40804080, 0xa9a8a9a8, 0xaaaaaa44, 0x2a4a5254}; /* Anchor indices: Table.A2 */ static const char bc7_ai0[] = { - 15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15, - 15, 2, 8, 2, 2, 8, 8,15, - 2, 8, 2, 2, 8, 8, 2, 2, - 15,15, 6, 8, 2, 8,15,15, - 2, 8, 2, 2, 2,15,15, 6, - 6, 2, 6, 8,15,15, 2, 2, - 15,15,15,15,15, 2, 2,15}; + 15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15, + 15, 2, 8, 2, 2, 8, 8,15, + 2, 8, 2, 2, 8, 8, 2, 2, + 15,15, 6, 8, 2, 8,15,15, + 2, 8, 2, 2, 2,15,15, 6, + 6, 2, 6, 8,15,15, 2, 2, + 15,15,15,15,15, 2, 2,15}; /* Table.A3a */ static const char bc7_ai1[] = { - 3, 3,15,15, 8, 3,15,15, - 8, 8, 6, 6, 6, 5, 3, 3, - 3, 3, 8,15, 3, 3, 6,10, - 5, 8, 8, 6, 8, 5,15,15, - 8,15, 3, 5, 6,10, 8,15, - 15, 3,15, 5,15,15,15,15, - 3,15, 5, 5, 5, 8, 5,10, - 5,10, 8,13,15,12, 3, 3}; + 3, 3,15,15, 8, 3,15,15, + 8, 8, 6, 6, 6, 5, 3, 3, + 3, 3, 8,15, 3, 3, 6,10, + 5, 8, 8, 6, 8, 5,15,15, + 8,15, 3, 5, 6,10, 8,15, + 15, 3,15, 5,15,15,15,15, + 3,15, 5, 5, 5, 8, 5,10, + 5,10, 8,13,15,12, 3, 3}; /* Table.A3b */ static const char bc7_ai2[] = { - 15, 8, 8, 3,15,15, 3, 8, - 15,15,15,15,15,15,15, 8, - 15, 8,15, 3,15, 8,15, 8, - 3,15, 6,10,15,15,10, 8, - 15, 3,15,10,10, 8, 9,10, - 6,15, 8,15, 3, 6, 6, 8, - 15, 3,15,15,15,15,15,15, - 15,15,15,15, 3,15,15, 8}; + 15, 8, 8, 3,15,15, 3, 8, + 15,15,15,15,15,15,15, 8, + 15, 8,15, 3,15, 8,15, 8, + 3,15, 6,10,15,15,10, 8, + 15, 3,15,10,10, 8, 9,10, + 6,15, 8,15, 3, 6, 6, 8, + 15, 3,15,15,15,15,15,15, + 15,15,15,15, 3,15,15, 8}; /* Interpolation weights */ static const char bc7_weights2[] = {0, 21, 43, 64}; static const char bc7_weights3[] = {0, 9, 18, 27, 37, 46, 55, 64}; static const char bc7_weights4[] = { - 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64}; + 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64}; static const char *bc7_get_weights(int n) { - if (n == 2) { - return bc7_weights2; - } - if (n == 3) { - return bc7_weights3; - } - return bc7_weights4; + if (n == 2) { + return bc7_weights2; + } + if (n == 3) { + return bc7_weights3; + } + return bc7_weights4; } static int bc7_get_subset(int ns, int partition, int n) { - if (ns == 2) { - return 1 & (bc7_si2[partition] >> n); - } - if (ns == 3) { - return 3 & (bc7_si3[partition] >> (2 * n)); - } - return 0; + if (ns == 2) { + return 1 & (bc7_si2[partition] >> n); + } + if (ns == 3) { + return 3 & (bc7_si3[partition] >> (2 * n)); + } + return 0; } static UINT8 expand_quantized(UINT8 v, int bits) { - v = v << (8 - bits); - return v | (v >> bits); + v = v << (8 - bits); + return v | (v >> bits); } static void bc7_lerp(rgba *dst, const rgba *e, int s0, int s1) { - int t0 = 64 - s0; - int t1 = 64 - s1; - dst->r = (UINT8)((t0 * e[0].r + s0 * e[1].r + 32) >> 6); - dst->g = (UINT8)((t0 * e[0].g + s0 * e[1].g + 32) >> 6); - dst->b = (UINT8)((t0 * e[0].b + s0 * e[1].b + 32) >> 6); - dst->a = (UINT8)((t1 * e[0].a + s1 * e[1].a + 32) >> 6); + int t0 = 64 - s0; + int t1 = 64 - s1; + dst->r = (UINT8)((t0 * e[0].r + s0 * e[1].r + 32) >> 6); + dst->g = (UINT8)((t0 * e[0].g + s0 * e[1].g + 32) >> 6); + dst->b = (UINT8)((t0 * e[0].b + s0 * e[1].b + 32) >> 6); + dst->a = (UINT8)((t1 * e[0].a + s1 * e[1].a + 32) >> 6); } static void decode_bc7_block(rgba *col, const UINT8* src) { - rgba endpoints[6]; - int bit = 0, cibit, aibit; - int mode = src[0]; - int i, j; - int numep, cb, ab, ib, ib2, i0, i1, s; - UINT8 index_sel, partition, rotation, val; - const char *cw, *aw; - const bc7_mode_info *info; - - /* mode is the number of unset bits before the first set bit: */ - if (!mode) { - /* degenerate case when no bits set */ - for (i = 0; i < 16; i++) { - col[i].r = col[i].g = col[i].b = 0; - col[i].a = 255; - } - return; - } - while (!(mode & (1 << bit++))) ; - mode = bit - 1; - info = &bc7_modes[mode]; - /* color selection bits: {subset}{endpoint} */ - cb = info->cb; - ab = info->ab; - cw = bc7_get_weights(info->ib); - aw = bc7_get_weights((ab && info->ib2) ? info->ib2 : info->ib); + rgba endpoints[6]; + int bit = 0, cibit, aibit; + int mode = src[0]; + int i, j; + int numep, cb, ab, ib, ib2, i0, i1, s; + UINT8 index_sel, partition, rotation, val; + const char *cw, *aw; + const bc7_mode_info *info; + + /* mode is the number of unset bits before the first set bit: */ + if (!mode) { + /* degenerate case when no bits set */ + for (i = 0; i < 16; i++) { + col[i].r = col[i].g = col[i].b = 0; + col[i].a = 255; + } + return; + } + while (!(mode & (1 << bit++))) ; + mode = bit - 1; + info = &bc7_modes[mode]; + /* color selection bits: {subset}{endpoint} */ + cb = info->cb; + ab = info->ab; + cw = bc7_get_weights(info->ib); + aw = bc7_get_weights((ab && info->ib2) ? info->ib2 : info->ib); #define LOAD(DST, N) \ - DST = get_bits(src, bit, N); \ - bit += N; - LOAD(partition, info->pb); - LOAD(rotation, info->rb); - LOAD(index_sel, info->isb); - numep = info->ns << 1; - - /* red */ - for (i = 0; i < numep; i++) { - LOAD(val, cb); - endpoints[i].r = val; - } - - /* green */ - for (i = 0; i < numep; i++) { - LOAD(val, cb); - endpoints[i].g = val; - } - - /* blue */ - for (i = 0; i < numep; i++) { - LOAD(val, cb); - endpoints[i].b = val; - } - - /* alpha */ - for (i = 0; i < numep; i++) { - if (ab) { - LOAD(val, ab); - } else { - val = 255; - } - endpoints[i].a = val; - } - - /* p-bits */ + DST = get_bits(src, bit, N); \ + bit += N; + LOAD(partition, info->pb); + LOAD(rotation, info->rb); + LOAD(index_sel, info->isb); + numep = info->ns << 1; + + /* red */ + for (i = 0; i < numep; i++) { + LOAD(val, cb); + endpoints[i].r = val; + } + + /* green */ + for (i = 0; i < numep; i++) { + LOAD(val, cb); + endpoints[i].g = val; + } + + /* blue */ + for (i = 0; i < numep; i++) { + LOAD(val, cb); + endpoints[i].b = val; + } + + /* alpha */ + for (i = 0; i < numep; i++) { + if (ab) { + LOAD(val, ab); + } else { + val = 255; + } + endpoints[i].a = val; + } + + /* p-bits */ #define ASSIGN_P(x) x = (x << 1) | val - if (info->epb) { - /* per endpoint */ - cb++; - if (ab) { - ab++; - } - for (i = 0; i < numep; i++) { - LOAD(val, 1); - ASSIGN_P(endpoints[i].r); - ASSIGN_P(endpoints[i].g); - ASSIGN_P(endpoints[i].b); - if (ab) { - ASSIGN_P(endpoints[i].a); - } - } - } - if (info->spb) { - /* per subset */ - cb++; - if (ab) { - ab++; - } - for (i = 0; i < numep; i+=2) { - LOAD(val, 1); - for (j = 0; j < 2; j++) { - ASSIGN_P(endpoints[i+j].r); - ASSIGN_P(endpoints[i+j].g); - ASSIGN_P(endpoints[i+j].b); - if (ab) { - ASSIGN_P(endpoints[i+j].a); - } - } - } - } + if (info->epb) { + /* per endpoint */ + cb++; + if (ab) { + ab++; + } + for (i = 0; i < numep; i++) { + LOAD(val, 1); + ASSIGN_P(endpoints[i].r); + ASSIGN_P(endpoints[i].g); + ASSIGN_P(endpoints[i].b); + if (ab) { + ASSIGN_P(endpoints[i].a); + } + } + } + if (info->spb) { + /* per subset */ + cb++; + if (ab) { + ab++; + } + for (i = 0; i < numep; i+=2) { + LOAD(val, 1); + for (j = 0; j < 2; j++) { + ASSIGN_P(endpoints[i+j].r); + ASSIGN_P(endpoints[i+j].g); + ASSIGN_P(endpoints[i+j].b); + if (ab) { + ASSIGN_P(endpoints[i+j].a); + } + } + } + } #undef ASSIGN_P #define EXPAND(x, b) x = expand_quantized(x, b) - for (i = 0; i < numep; i++) { - EXPAND(endpoints[i].r, cb); - EXPAND(endpoints[i].g, cb); - EXPAND(endpoints[i].b, cb); - if (ab) { - EXPAND(endpoints[i].a, ab); - } - } + for (i = 0; i < numep; i++) { + EXPAND(endpoints[i].r, cb); + EXPAND(endpoints[i].g, cb); + EXPAND(endpoints[i].b, cb); + if (ab) { + EXPAND(endpoints[i].a, ab); + } + } #undef EXPAND #undef LOAD - cibit = bit; - aibit = cibit + 16 * info->ib - info->ns; - for (i = 0; i < 16; i++) { - s = bc7_get_subset(info->ns, partition, i) << 1; - ib = info->ib; - if (i == 0) { - ib--; - } else if (info->ns == 2) { - if (i == bc7_ai0[partition]) { - ib--; - } - } else if (info->ns == 3) { - if (i == bc7_ai1[partition]) { - ib--; - } else if (i == bc7_ai2[partition]) { - ib--; - } - } - i0 = get_bits(src, cibit, ib); - cibit += ib; - - if (ab && info->ib2) { - ib2 = info->ib2; - if (ib2 && i == 0) { - ib2--; - } - i1 = get_bits(src, aibit, ib2); - aibit += ib2; - if (index_sel) { - bc7_lerp(&col[i], &endpoints[s], aw[i1], cw[i0]); - } else { - bc7_lerp(&col[i], &endpoints[s], cw[i0], aw[i1]); - } - } else { - bc7_lerp(&col[i], &endpoints[s], cw[i0], cw[i0]); - } + cibit = bit; + aibit = cibit + 16 * info->ib - info->ns; + for (i = 0; i < 16; i++) { + s = bc7_get_subset(info->ns, partition, i) << 1; + ib = info->ib; + if (i == 0) { + ib--; + } else if (info->ns == 2) { + if (i == bc7_ai0[partition]) { + ib--; + } + } else if (info->ns == 3) { + if (i == bc7_ai1[partition]) { + ib--; + } else if (i == bc7_ai2[partition]) { + ib--; + } + } + i0 = get_bits(src, cibit, ib); + cibit += ib; + + if (ab && info->ib2) { + ib2 = info->ib2; + if (ib2 && i == 0) { + ib2--; + } + i1 = get_bits(src, aibit, ib2); + aibit += ib2; + if (index_sel) { + bc7_lerp(&col[i], &endpoints[s], aw[i1], cw[i0]); + } else { + bc7_lerp(&col[i], &endpoints[s], cw[i0], aw[i1]); + } + } else { + bc7_lerp(&col[i], &endpoints[s], cw[i0], cw[i0]); + } #define ROTATE(x, y) \ - val = x; \ - x = y; \ - y = val - if (rotation == 1) { - ROTATE(col[i].r, col[i].a); - } else if (rotation == 2) { - ROTATE(col[i].g, col[i].a); - } else if (rotation == 3) { - ROTATE(col[i].b, col[i].a); - } + val = x; \ + x = y; \ + y = val + if (rotation == 1) { + ROTATE(col[i].r, col[i].a); + } else if (rotation == 2) { + ROTATE(col[i].g, col[i].a); + } else if (rotation == 3) { + ROTATE(col[i].b, col[i].a); + } #undef ROTATE - } + } } /* BC6 */ typedef struct { - char ns; /* number of subsets (also called regions) */ - char tr; /* whether endpoints are delta-compressed */ - char pb; /* partition bits */ - char epb; /* endpoint bits */ - char rb; /* red bits (delta) */ - char gb; /* green bits (delta) */ - char bb; /* blue bits (delta) */ + char ns; /* number of subsets (also called regions) */ + char tr; /* whether endpoints are delta-compressed */ + char pb; /* partition bits */ + char epb; /* endpoint bits */ + char rb; /* red bits (delta) */ + char gb; /* green bits (delta) */ + char bb; /* blue bits (delta) */ } bc6_mode_info; static const bc6_mode_info bc6_modes[] = { - // 00 - {2, 1, 5, 10, 5, 5, 5}, - // 01 - {2, 1, 5, 7, 6, 6, 6}, - // 10 - {2, 1, 5, 11, 5, 4, 4}, - {2, 1, 5, 11, 4, 5, 4}, - {2, 1, 5, 11, 4, 4, 5}, - {2, 1, 5, 9, 5, 5, 5}, - {2, 1, 5, 8, 6, 5, 5}, - {2, 1, 5, 8, 5, 6, 5}, - {2, 1, 5, 8, 5, 5, 6}, - {2, 0, 5, 6, 6, 6, 6}, - // 11 - {1, 0, 0, 10, 10, 10, 10}, - {1, 1, 0, 11, 9, 9, 9}, - {1, 1, 0, 12, 8, 8, 8}, - {1, 1, 0, 16, 4, 4, 4} + // 00 + {2, 1, 5, 10, 5, 5, 5}, + // 01 + {2, 1, 5, 7, 6, 6, 6}, + // 10 + {2, 1, 5, 11, 5, 4, 4}, + {2, 1, 5, 11, 4, 5, 4}, + {2, 1, 5, 11, 4, 4, 5}, + {2, 1, 5, 9, 5, 5, 5}, + {2, 1, 5, 8, 6, 5, 5}, + {2, 1, 5, 8, 5, 6, 5}, + {2, 1, 5, 8, 5, 5, 6}, + {2, 0, 5, 6, 6, 6, 6}, + // 11 + {1, 0, 0, 10, 10, 10, 10}, + {1, 1, 0, 11, 9, 9, 9}, + {1, 1, 0, 12, 8, 8, 8}, + {1, 1, 0, 16, 4, 4, 4} }; /* Table.F, encoded as a sequence of bit indices */ static const UINT8 bc6_bit_packings[][75] = { - {116, 132, 176, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, - 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, - 81, 82, 83, 84, 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, - 145, 146, 147, 148, 175}, - {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 172, 173, 132, 16, 17, 18, 19, 20, 21, - 22, 133, 174, 116, 32, 33, 34, 35, 36, 37, 38, 175, 177, 176, 48, 49, 50, - 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, - 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, - 145, 146, 147, 148, 149}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 10, 112, 113, 114, - 115, 64, 65, 66, 67, 26, 172, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, - 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, - 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 164, 112, 113, 114, - 115, 64, 65, 66, 67, 68, 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, - 128, 129, 130, 131, 96, 97, 98, 99, 172, 174, 144, 145, 146, 147, 116, - 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 132, 112, 113, 114, - 115, 64, 65, 66, 67, 26, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, - 128, 129, 130, 131, 96, 97, 98, 99, 173, 174, 144, 145, 146, 147, 176, - 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 132, 16, 17, 18, 19, 20, 21, 22, 23, 24, 116, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 176, 48, 49, 50, 51, 52, 164, 112, 113, - 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, - 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, - 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 164, 132, 16, 17, 18, 19, 20, 21, 22, 23, 174, 116, - 32, 33, 34, 35, 36, 37, 38, 39, 175, 176, 48, 49, 50, 51, 52, 53, 112, 113, - 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, - 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, - 149}, - {0, 1, 2, 3, 4, 5, 6, 7, 172, 132, 16, 17, 18, 19, 20, 21, 22, 23, 117, 116, - 32, 33, 34, 35, 36, 37, 38, 39, 165, 176, 48, 49, 50, 51, 52, 164, 112, - 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, - 84, 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, - 148, 175}, - {0, 1, 2, 3, 4, 5, 6, 7, 173, 132, 16, 17, 18, 19, 20, 21, 22, 23, 133, 116, - 32, 33, 34, 35, 36, 37, 38, 39, 177, 176, 48, 49, 50, 51, 52, 164, 112, - 113, 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, - 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, - 148, 175}, - {0, 1, 2, 3, 4, 5, 164, 172, 173, 132, 16, 17, 18, 19, 20, 21, 117, 133, - 174, 116, 32, 33, 34, 35, 36, 37, 165, 175, 177, 176, 48, 49, 50, 51, 52, - 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, - 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, - 146, 147, 148, 149}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 80, 81, 82, 83, 84, 85, 86, 87, 88, - 89}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 10, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 26, 80, 81, 82, 83, 84, 85, 86, 87, 88, - 42}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 11, 10, - 64, 65, 66, 67, 68, 69, 70, 71, 27, 26, 80, 81, 82, 83, 84, 85, 86, 87, 43, - 42}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, - 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 15, 14, 13, 12, 11, 10, - 64, 65, 66, 67, 31, 30, 29, 28, 27, 26, 80, 81, 82, 83, 47, 46, 45, 44, 43, - 42}}; + {116, 132, 176, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, + 164, 112, 113, 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, + 81, 82, 83, 84, 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, + 145, 146, 147, 148, 175}, + {117, 164, 165, 0, 1, 2, 3, 4, 5, 6, 172, 173, 132, 16, 17, 18, 19, 20, 21, + 22, 133, 174, 116, 32, 33, 34, 35, 36, 37, 38, 175, 177, 176, 48, 49, 50, + 51, 52, 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, + 80, 81, 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, + 145, 146, 147, 148, 149}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 10, 112, 113, 114, + 115, 64, 65, 66, 67, 26, 172, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, + 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, + 175}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 164, 112, 113, 114, + 115, 64, 65, 66, 67, 68, 26, 160, 161, 162, 163, 80, 81, 82, 83, 42, 173, + 128, 129, 130, 131, 96, 97, 98, 99, 172, 174, 144, 145, 146, 147, 116, + 175}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 10, 132, 112, 113, 114, + 115, 64, 65, 66, 67, 26, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, 42, + 128, 129, 130, 131, 96, 97, 98, 99, 173, 174, 144, 145, 146, 147, 176, + 175}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 132, 16, 17, 18, 19, 20, 21, 22, 23, 24, 116, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 176, 48, 49, 50, 51, 52, 164, 112, 113, + 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, + 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, 148, + 175}, + {0, 1, 2, 3, 4, 5, 6, 7, 164, 132, 16, 17, 18, 19, 20, 21, 22, 23, 174, 116, + 32, 33, 34, 35, 36, 37, 38, 39, 175, 176, 48, 49, 50, 51, 52, 53, 112, 113, + 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, 84, + 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, 146, 147, 148, + 149}, + {0, 1, 2, 3, 4, 5, 6, 7, 172, 132, 16, 17, 18, 19, 20, 21, 22, 23, 117, 116, + 32, 33, 34, 35, 36, 37, 38, 39, 165, 176, 48, 49, 50, 51, 52, 164, 112, + 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, 82, 83, + 84, 173, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, + 148, 175}, + {0, 1, 2, 3, 4, 5, 6, 7, 173, 132, 16, 17, 18, 19, 20, 21, 22, 23, 133, 116, + 32, 33, 34, 35, 36, 37, 38, 39, 177, 176, 48, 49, 50, 51, 52, 164, 112, + 113, 114, 115, 64, 65, 66, 67, 68, 172, 160, 161, 162, 163, 80, 81, 82, 83, + 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 174, 144, 145, 146, 147, + 148, 175}, + {0, 1, 2, 3, 4, 5, 164, 172, 173, 132, 16, 17, 18, 19, 20, 21, 117, 133, + 174, 116, 32, 33, 34, 35, 36, 37, 165, 175, 177, 176, 48, 49, 50, 51, 52, + 53, 112, 113, 114, 115, 64, 65, 66, 67, 68, 69, 160, 161, 162, 163, 80, 81, + 82, 83, 84, 85, 128, 129, 130, 131, 96, 97, 98, 99, 100, 101, 144, 145, + 146, 147, 148, 149}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 56, 10, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 26, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 42}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 52, 53, 54, 55, 11, 10, + 64, 65, 66, 67, 68, 69, 70, 71, 27, 26, 80, 81, 82, 83, 84, 85, 86, 87, 43, + 42}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, 15, 14, 13, 12, 11, 10, + 64, 65, 66, 67, 31, 30, 29, 28, 27, 26, 80, 81, 82, 83, 47, 46, 45, 44, 43, + 42}}; static void bc6_sign_extend(UINT16 *v, int prec) { - int x = *v; - if (x & (1 << (prec - 1))) { - x |= -1 << prec; - } - *v = (UINT16)x; + int x = *v; + if (x & (1 << (prec - 1))) { + x |= -1 << prec; + } + *v = (UINT16)x; } static int bc6_unquantize(UINT16 v, int prec, int sign) { - int s = 0; - int x; - if (!sign) { - x = v; - if (prec >= 15) return x; - if (x == 0) return 0; - if (x == ((1 << prec) - 1)) { - return 0xffff; - } - return ((x << 15) + 0x4000) >> (prec - 1); - } else { - x = (INT16)v; - if (prec >= 16) return x; - if (x < 0) { - s = 1; - x = -x; - } - - if (x != 0) { - if (x >= ((1 << (prec - 1)) - 1)) { - x = 0x7fff; - } else { - x = ((x << 15) + 0x4000) >> (prec - 1); - } - } - - if (s) { - return -x; - } - return x; - } + int s = 0; + int x; + if (!sign) { + x = v; + if (prec >= 15) return x; + if (x == 0) return 0; + if (x == ((1 << prec) - 1)) { + return 0xffff; + } + return ((x << 15) + 0x4000) >> (prec - 1); + } else { + x = (INT16)v; + if (prec >= 16) return x; + if (x < 0) { + s = 1; + x = -x; + } + + if (x != 0) { + if (x >= ((1 << (prec - 1)) - 1)) { + x = 0x7fff; + } else { + x = ((x << 15) + 0x4000) >> (prec - 1); + } + } + + if (s) { + return -x; + } + return x; + } } static float half_to_float(UINT16 h) { - /* https://gist.github.com/rygorous/2144712 */ - union { - UINT32 u; - float f; - } o, m; - m.u = 0x77800000; - o.u = (h & 0x7fff) << 13; - o.f *= m.f; - m.u = 0x47800000; - if (o.f >= m.f) { - o.u |= 255 << 23; - } - o.u |= (h & 0x8000) << 16; - return o.f; + /* https://gist.github.com/rygorous/2144712 */ + union { + UINT32 u; + float f; + } o, m; + m.u = 0x77800000; + o.u = (h & 0x7fff) << 13; + o.f *= m.f; + m.u = 0x47800000; + if (o.f >= m.f) { + o.u |= 255 << 23; + } + o.u |= (h & 0x8000) << 16; + return o.f; } static float bc6_finalize(int v, int sign) { - if (sign) { - if (v < 0) { - v = ((-v) * 31) / 32; - return half_to_float((UINT16)(0x8000 | v)); - } else { - return half_to_float((UINT16)((v * 31) / 32)); - } - } else { - return half_to_float((UINT16)((v * 31) / 64)); - } + if (sign) { + if (v < 0) { + v = ((-v) * 31) / 32; + return half_to_float((UINT16)(0x8000 | v)); + } else { + return half_to_float((UINT16)((v * 31) / 32)); + } + } else { + return half_to_float((UINT16)((v * 31) / 64)); + } } static void bc6_lerp(rgb32f *col, int *e0, int *e1, int s, int sign) { - int r, g, b; - int t = 64 - s; - r = (e0[0] * t + e1[0] * s) >> 6; - g = (e0[1] * t + e1[1] * s) >> 6; - b = (e0[2] * t + e1[2] * s) >> 6; - col->r = bc6_finalize(r, sign); - col->g = bc6_finalize(g, sign); - col->b = bc6_finalize(b, sign); + int r, g, b; + int t = 64 - s; + r = (e0[0] * t + e1[0] * s) >> 6; + g = (e0[1] * t + e1[1] * s) >> 6; + b = (e0[2] * t + e1[2] * s) >> 6; + col->r = bc6_finalize(r, sign); + col->g = bc6_finalize(g, sign); + col->b = bc6_finalize(b, sign); } static void decode_bc6_block(rgb32f *col, const UINT8* src, int sign) { - UINT16 endpoints[12]; /* storage for r0, g0, b0, r1, ... */ - int ueps[12]; - int i, i0, ib2, di, dw, mask, numep, s; - UINT8 partition; - const bc6_mode_info *info; - const char *cw; - int bit = 5; - int epbits = 75; - int ib = 3; - int mode = src[0] & 0x1f; - if ((mode & 3) == 0 || (mode & 3) == 1) { - mode &= 3; - bit = 2; - } else if ((mode & 3) == 2) { - mode = 2 + (mode >> 2); - epbits = 72; - } else { - mode = 10 + (mode >> 2); - epbits = 60; - ib = 4; - } - if (mode >= 14) { - /* invalid block */ - memset(col, 0, 16 * sizeof(col[0])); - return; - } - info = &bc6_modes[mode]; - cw = bc7_get_weights(ib); - numep = info->ns == 2 ? 12 : 6; - for (i = 0; i < 12; i++) { - endpoints[i] = 0; - } - for (i = 0; i < epbits; i++) { - di = bc6_bit_packings[mode][i]; - dw = di >> 4; - di &= 15; - endpoints[dw] |= (UINT16)get_bit(src, bit + i) << di; - } - bit += epbits; - partition = get_bits(src, bit, info->pb); - bit += info->pb; - mask = (1 << info->epb) - 1; - if (sign) { /* sign-extend e0 if signed */ - bc6_sign_extend(&endpoints[0], info->epb); - bc6_sign_extend(&endpoints[1], info->epb); - bc6_sign_extend(&endpoints[2], info->epb); - } - if (sign || info->tr) { /* sign-extend e1,2,3 if signed or deltas */ - for (i = 3; i < numep; i += 3) { - bc6_sign_extend(&endpoints[i+0], info->rb); - bc6_sign_extend(&endpoints[i+1], info->gb); - bc6_sign_extend(&endpoints[i+2], info->bb); - } - } - if (info->tr) { /* apply deltas */ - for (i = 3; i < numep; i++) { - endpoints[i] = (endpoints[i] + endpoints[0]) & mask; - } - if (sign) { - for (i = 3; i < numep; i += 3) { - bc6_sign_extend(&endpoints[i+0], info->rb); - bc6_sign_extend(&endpoints[i+1], info->gb); - bc6_sign_extend(&endpoints[i+2], info->bb); - } - } - } - for (i = 0; i < numep; i++) { - ueps[i] = bc6_unquantize(endpoints[i], info->epb, sign); - } - for (i = 0; i < 16; i++) { - s = bc7_get_subset(info->ns, partition, i) * 6; - ib2 = ib; - if (i == 0) { - ib2--; - } else if (info->ns == 2) { - if (i == bc7_ai0[partition]) { - ib2--; - } - } - i0 = get_bits(src, bit, ib2); - bit += ib2; - - bc6_lerp(&col[i], &ueps[s], &ueps[s+3], cw[i0], sign); - } + UINT16 endpoints[12]; /* storage for r0, g0, b0, r1, ... */ + int ueps[12]; + int i, i0, ib2, di, dw, mask, numep, s; + UINT8 partition; + const bc6_mode_info *info; + const char *cw; + int bit = 5; + int epbits = 75; + int ib = 3; + int mode = src[0] & 0x1f; + if ((mode & 3) == 0 || (mode & 3) == 1) { + mode &= 3; + bit = 2; + } else if ((mode & 3) == 2) { + mode = 2 + (mode >> 2); + epbits = 72; + } else { + mode = 10 + (mode >> 2); + epbits = 60; + ib = 4; + } + if (mode >= 14) { + /* invalid block */ + memset(col, 0, 16 * sizeof(col[0])); + return; + } + info = &bc6_modes[mode]; + cw = bc7_get_weights(ib); + numep = info->ns == 2 ? 12 : 6; + for (i = 0; i < 12; i++) { + endpoints[i] = 0; + } + for (i = 0; i < epbits; i++) { + di = bc6_bit_packings[mode][i]; + dw = di >> 4; + di &= 15; + endpoints[dw] |= (UINT16)get_bit(src, bit + i) << di; + } + bit += epbits; + partition = get_bits(src, bit, info->pb); + bit += info->pb; + mask = (1 << info->epb) - 1; + if (sign) { /* sign-extend e0 if signed */ + bc6_sign_extend(&endpoints[0], info->epb); + bc6_sign_extend(&endpoints[1], info->epb); + bc6_sign_extend(&endpoints[2], info->epb); + } + if (sign || info->tr) { /* sign-extend e1,2,3 if signed or deltas */ + for (i = 3; i < numep; i += 3) { + bc6_sign_extend(&endpoints[i+0], info->rb); + bc6_sign_extend(&endpoints[i+1], info->gb); + bc6_sign_extend(&endpoints[i+2], info->bb); + } + } + if (info->tr) { /* apply deltas */ + for (i = 3; i < numep; i++) { + endpoints[i] = (endpoints[i] + endpoints[0]) & mask; + } + if (sign) { + for (i = 3; i < numep; i += 3) { + bc6_sign_extend(&endpoints[i+0], info->rb); + bc6_sign_extend(&endpoints[i+1], info->gb); + bc6_sign_extend(&endpoints[i+2], info->bb); + } + } + } + for (i = 0; i < numep; i++) { + ueps[i] = bc6_unquantize(endpoints[i], info->epb, sign); + } + for (i = 0; i < 16; i++) { + s = bc7_get_subset(info->ns, partition, i) * 6; + ib2 = ib; + if (i == 0) { + ib2--; + } else if (info->ns == 2) { + if (i == bc7_ai0[partition]) { + ib2--; + } + } + i0 = get_bits(src, bit, ib2); + bit += ib2; + + bc6_lerp(&col[i], &ueps[s], &ueps[s+3], cw[i0], sign); + } } static void put_block(Imaging im, ImagingCodecState state, const char *col, int sz, int C) { - int width = state->xsize; - int height = state->ysize; - int xmax = width + state->xoff; - int ymax = height + state->yoff; - int j, i, y, x; - char *dst; - for (j = 0; j < 4; j++) { - y = state->y + j; - if (C) { - if (y >= height) { - continue; - } - if (state->ystep < 0) { - y = state->yoff + ymax - y - 1; - } - dst = im->image[y]; - for (i = 0; i < 4; i++) { - x = state->x + i; - if (x >= width) { - continue; - } - memcpy(dst + sz*x, col + sz*(j*4 + i), sz); - } - } else { - if (state->ystep < 0) { - y = state->yoff + ymax - y - 1; - } - x = state->x; - dst = im->image[y] + sz*x; - memcpy(dst, col + sz*(j*4), 4 * sz); - } - } - state->x += 4; - if (state->x >= xmax) { - state->y += 4; - state->x = state->xoff; - } + int width = state->xsize; + int height = state->ysize; + int xmax = width + state->xoff; + int ymax = height + state->yoff; + int j, i, y, x; + char *dst; + for (j = 0; j < 4; j++) { + y = state->y + j; + if (C) { + if (y >= height) { + continue; + } + if (state->ystep < 0) { + y = state->yoff + ymax - y - 1; + } + dst = im->image[y]; + for (i = 0; i < 4; i++) { + x = state->x + i; + if (x >= width) { + continue; + } + memcpy(dst + sz*x, col + sz*(j*4 + i), sz); + } + } else { + if (state->ystep < 0) { + y = state->yoff + ymax - y - 1; + } + x = state->x; + dst = im->image[y] + sz*x; + memcpy(dst, col + sz*(j*4), 4 * sz); + } + } + state->x += 4; + if (state->x >= xmax) { + state->y += 4; + state->x = state->xoff; + } } static int decode_bcn(Imaging im, ImagingCodecState state, const UINT8* src, int bytes, int N, int C) { - int ymax = state->ysize + state->yoff; - const UINT8 *ptr = src; - switch (N) { + int ymax = state->ysize + state->yoff; + const UINT8 *ptr = src; + switch (N) { #define DECODE_LOOP(NN, SZ, TY, ...) \ - case NN: \ - while (bytes >= SZ) { \ - TY col[16]; \ - memset(col, 0, 16 * sizeof(col[0])); \ - decode_bc##NN##_block(col, ptr); \ - put_block(im, state, (const char *)col, sizeof(col[0]), C); \ - ptr += SZ; \ - bytes -= SZ; \ - if (state->y >= ymax) return -1; \ - } \ - break - DECODE_LOOP(1, 8, rgba); - DECODE_LOOP(2, 16, rgba); - DECODE_LOOP(3, 16, rgba); - DECODE_LOOP(4, 8, lum); - DECODE_LOOP(5, 16, rgba); - case 6: - while (bytes >= 16) { - rgb32f col[16]; - decode_bc6_block(col, ptr, (state->state >> 4) & 1); - put_block(im, state, (const char *)col, sizeof(col[0]), C); - ptr += 16; - bytes -= 16; - if (state->y >= ymax) return -1; \ - } - break; - DECODE_LOOP(7, 16, rgba); + case NN: \ + while (bytes >= SZ) { \ + TY col[16]; \ + memset(col, 0, 16 * sizeof(col[0])); \ + decode_bc##NN##_block(col, ptr); \ + put_block(im, state, (const char *)col, sizeof(col[0]), C); \ + ptr += SZ; \ + bytes -= SZ; \ + if (state->y >= ymax) return -1; \ + } \ + break + + DECODE_LOOP(1, 8, rgba); + DECODE_LOOP(2, 16, rgba); + DECODE_LOOP(3, 16, rgba); + DECODE_LOOP(4, 8, lum); + DECODE_LOOP(5, 16, rgba); + case 6: + while (bytes >= 16) { + rgb32f col[16]; + decode_bc6_block(col, ptr, (state->state >> 4) & 1); + put_block(im, state, (const char *)col, sizeof(col[0]), C); + ptr += 16; + bytes -= 16; + if (state->y >= ymax) return -1; \ + } + break; + DECODE_LOOP(7, 16, rgba); #undef DECODE_LOOP - } - return (int)(ptr - src); + } + return (int)(ptr - src); } int ImagingBcnDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t bytes) { - int N = state->state & 0xf; - int width = state->xsize; - int height = state->ysize; - if ((width & 3) | (height & 3)) { - return decode_bcn(im, state, buf, bytes, N, 1); - } else { - return decode_bcn(im, state, buf, bytes, N, 0); - } + int N = state->state & 0xf; + int width = state->xsize; + int height = state->ysize; + if ((width & 3) | (height & 3)) { + return decode_bcn(im, state, buf, bytes, N, 1); + } else { + return decode_bcn(im, state, buf, bytes, N, 0); + } } diff --git a/src/libImaging/BitDecode.c b/src/libImaging/BitDecode.c index 7120b332138..97d263c0b28 100644 --- a/src/libImaging/BitDecode.c +++ b/src/libImaging/BitDecode.c @@ -5,7 +5,7 @@ * decoder for packed bitfields (converts to floating point) * * history: - * 97-05-31 fl created (much more than originally intended) + * 97-05-31 fl created (much more than originally intended) * * Copyright (c) Fredrik Lundh 1997. * Copyright (c) Secret Labs AB 1997. @@ -27,18 +27,18 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (state->state == 0) { - /* Initialize context variables */ + /* Initialize context variables */ /* this decoder only works for float32 image buffers */ if (im->type != IMAGING_TYPE_FLOAT32) { - state->errcode = IMAGING_CODEC_CONFIG; - return -1; + state->errcode = IMAGING_CODEC_CONFIG; + return -1; } /* sanity check */ if (bitstate->bits < 1 || bitstate->bits >= 32) { - state->errcode = IMAGING_CODEC_CONFIG; - return -1; + state->errcode = IMAGING_CODEC_CONFIG; + return -1; } bitstate->mask = (1<bits)-1; @@ -46,14 +46,14 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (bitstate->sign) bitstate->signmask = (1<<(bitstate->bits-1)); - /* check image orientation */ - if (state->ystep < 0) { - state->y = state->ysize-1; - state->ystep = -1; - } else - state->ystep = 1; + /* check image orientation */ + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; - state->state = 1; + state->state = 1; } @@ -119,7 +119,7 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt *(FLOAT32*)(&im->image32[state->y][state->x]) = pixel; /* step forward */ - if (++state->x >= state->xsize) { + if (++state->x >= state->xsize) { /* new line */ state->y += state->ystep; if (state->y < 0 || state->y >= state->ysize) { diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c index 19a080d6d5b..caaf2ba8bcc 100644 --- a/src/libImaging/Blend.c +++ b/src/libImaging/Blend.c @@ -5,9 +5,9 @@ * interpolate between two existing images * * history: - * 96-03-20 fl Created - * 96-05-18 fl Simplified blend expression - * 96-10-05 fl Fixed expression bug, special case for interpolation + * 96-03-20 fl Created + * 96-05-18 fl Simplified blend expression + * 96-10-05 fl Fixed expression bug, special case for interpolation * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -32,48 +32,48 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) return ImagingError_ModeError(); if (imIn1->type != imIn2->type || - imIn1->bands != imIn2->bands || - imIn1->xsize != imIn2->xsize || - imIn1->ysize != imIn2->ysize) - return ImagingError_Mismatch(); + imIn1->bands != imIn2->bands || + imIn1->xsize != imIn2->xsize || + imIn1->ysize != imIn2->ysize) + return ImagingError_Mismatch(); /* Shortcuts */ if (alpha == 0.0) - return ImagingCopy(imIn1); + return ImagingCopy(imIn1); else if (alpha == 1.0) - return ImagingCopy(imIn2); + return ImagingCopy(imIn2); imOut = ImagingNewDirty(imIn1->mode, imIn1->xsize, imIn1->ysize); if (!imOut) - return NULL; + return NULL; if (alpha >= 0 && alpha <= 1.0) { - /* Interpolate between bands */ - for (y = 0; y < imIn1->ysize; y++) { - UINT8* in1 = (UINT8*) imIn1->image[y]; - UINT8* in2 = (UINT8*) imIn2->image[y]; - UINT8* out = (UINT8*) imOut->image[y]; - for (x = 0; x < imIn1->linesize; x++) - out[x] = (UINT8) - ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); - } + /* Interpolate between bands */ + for (y = 0; y < imIn1->ysize; y++) { + UINT8* in1 = (UINT8*) imIn1->image[y]; + UINT8* in2 = (UINT8*) imIn2->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn1->linesize; x++) + out[x] = (UINT8) + ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); + } } else { - /* Extrapolation; must make sure to clip resulting values */ - for (y = 0; y < imIn1->ysize; y++) { - UINT8* in1 = (UINT8*) imIn1->image[y]; - UINT8* in2 = (UINT8*) imIn2->image[y]; - UINT8* out = (UINT8*) imOut->image[y]; - for (x = 0; x < imIn1->linesize; x++) { - float temp = (float) - ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); - if (temp <= 0.0) - out[x] = 0; - else if (temp >= 255.0) - out[x] = 255; - else - out[x] = (UINT8) temp; - } - } + /* Extrapolation; must make sure to clip resulting values */ + for (y = 0; y < imIn1->ysize; y++) { + UINT8* in1 = (UINT8*) imIn1->image[y]; + UINT8* in2 = (UINT8*) imIn2->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn1->linesize; x++) { + float temp = (float) + ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); + if (temp <= 0.0) + out[x] = 0; + else if (temp >= 255.0) + out[x] = 255; + else + out[x] = (UINT8) temp; + } + } } return imOut; diff --git a/src/libImaging/ConvertYCbCr.c b/src/libImaging/ConvertYCbCr.c index 6ce549111e4..47de4ff7112 100644 --- a/src/libImaging/ConvertYCbCr.c +++ b/src/libImaging/ConvertYCbCr.c @@ -5,7 +5,7 @@ * code to convert YCbCr data * * history: - * 98-07-01 hk Created + * 98-07-01 hk Created * * Copyright (c) Secret Labs AB 1998 * diff --git a/src/libImaging/Copy.c b/src/libImaging/Copy.c index 1bc9b1a709a..e0bdaf63ea5 100644 --- a/src/libImaging/Copy.c +++ b/src/libImaging/Copy.c @@ -26,7 +26,7 @@ _copy(Imaging imOut, Imaging imIn) int y; if (!imIn) - return (Imaging) ImagingError_ValueError(NULL); + return (Imaging) ImagingError_ValueError(NULL); imOut = ImagingNew2Dirty(imIn->mode, imOut, imIn); if (!imOut) @@ -36,7 +36,7 @@ _copy(Imaging imOut, Imaging imIn) ImagingSectionEnter(&cookie); if (imIn->block != NULL && imOut->block != NULL) - memcpy(imOut->block, imIn->block, imIn->ysize * imIn->linesize); + memcpy(imOut->block, imIn->block, imIn->ysize * imIn->linesize); else for (y = 0; y < imIn->ysize; y++) memcpy(imOut->image[y], imIn->image[y], imIn->linesize); diff --git a/src/libImaging/Crop.c b/src/libImaging/Crop.c index 4407c1b1d2d..a2b6796819b 100644 --- a/src/libImaging/Crop.c +++ b/src/libImaging/Crop.c @@ -5,9 +5,9 @@ * cut region from image * * history: - * 95-11-27 fl Created - * 98-07-10 fl Fixed "null result" error - * 99-02-05 fl Rewritten to use Paste primitive + * 95-11-27 fl Created + * 98-07-10 fl Fixed "null result" error + * 99-02-05 fl Rewritten to use Paste primitive * * Copyright (c) Secret Labs AB 1997-99. * Copyright (c) Fredrik Lundh 1995. @@ -28,7 +28,7 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) INT32 zero = 0; if (!imIn) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); xsize = sx1 - sx0; if (xsize < 0) @@ -39,12 +39,12 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) imOut = ImagingNewDirty(imIn->mode, xsize, ysize); if (!imOut) - return NULL; + return NULL; ImagingCopyPalette(imOut, imIn); if (sx0 < 0 || sy0 < 0 || sx1 > imIn->xsize || sy1 > imIn->ysize) - (void) ImagingFill(imOut, &zero); + (void) ImagingFill(imOut, &zero); dx0 = -sx0; dy0 = -sy0; diff --git a/src/libImaging/EpsEncode.c b/src/libImaging/EpsEncode.c index 45fab0a6ed4..2a6aad4a3eb 100644 --- a/src/libImaging/EpsEncode.c +++ b/src/libImaging/EpsEncode.c @@ -5,11 +5,11 @@ * encoder for EPS hex data * * history: - * 96-04-19 fl created - * 96-06-27 fl don't drop last block of encoded data + * 96-04-19 fl created + * 96-06-27 fl don't drop last block of encoded data * * notes: - * FIXME: rename to HexEncode.c ?? + * FIXME: rename to HexEncode.c ?? * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -31,47 +31,47 @@ ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) UINT8* in, i; if (!state->state) { - state->state = HEXBYTE; - state->xsize *= im->pixelsize; /* Hack! */ + state->state = HEXBYTE; + state->xsize *= im->pixelsize; /* Hack! */ } in = (UINT8*) im->image[state->y]; for (;;) { - if (state->state == NEWLINE) { - if (bytes < 1) - break; - *ptr++ = '\n'; - bytes--; - state->state = HEXBYTE; - } - - if (bytes < 2) - break; - - i = in[state->x++]; - *ptr++ = hex[(i>>4)&15]; - *ptr++ = hex[i&15]; - bytes -= 2; - - /* Skip junk bytes */ - if (im->bands == 3 && (state->x & 3) == 3) - state->x++; - - if (++state->count >= 79/2) { - state->state = NEWLINE; - state->count = 0; - } - - if (state->x >= state->xsize) { - state->x = 0; - if (++state->y >= state->ysize) { - state->errcode = IMAGING_CODEC_END; - break; - } - in = (UINT8*) im->image[state->y]; - } + if (state->state == NEWLINE) { + if (bytes < 1) + break; + *ptr++ = '\n'; + bytes--; + state->state = HEXBYTE; + } + + if (bytes < 2) + break; + + i = in[state->x++]; + *ptr++ = hex[(i>>4)&15]; + *ptr++ = hex[i&15]; + bytes -= 2; + + /* Skip junk bytes */ + if (im->bands == 3 && (state->x & 3) == 3) + state->x++; + + if (++state->count >= 79/2) { + state->state = NEWLINE; + state->count = 0; + } + + if (state->x >= state->xsize) { + state->x = 0; + if (++state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_END; + break; + } + in = (UINT8*) im->image[state->y]; + } } diff --git a/src/libImaging/Except.c b/src/libImaging/Except.c index d6d2b218674..4850b4f8747 100644 --- a/src/libImaging/Except.c +++ b/src/libImaging/Except.c @@ -57,7 +57,7 @@ void * ImagingError_ValueError(const char *message) { if (!message) - message = "exception: bad argument to function"; + message = "exception: bad argument to function"; fprintf(stderr, "*** %s\n", message); return NULL; } diff --git a/src/libImaging/Fill.c b/src/libImaging/Fill.c index d641a59962a..27022bc48d5 100644 --- a/src/libImaging/Fill.c +++ b/src/libImaging/Fill.c @@ -5,9 +5,9 @@ * fill image with constant pixel value * * history: - * 95-11-26 fl moved from Imaging.c - * 96-05-17 fl added radial fill, renamed wedge to linear - * 98-06-23 fl changed ImageFill signature + * 95-11-26 fl moved from Imaging.c + * 96-05-17 fl added radial fill, renamed wedge to linear + * 98-06-23 fl changed ImageFill signature * * Copyright (c) Secret Labs AB 1997-98. All rights reserved. * Copyright (c) Fredrik Lundh 1995-96. diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c index 108e1edf93a..5351d5664ac 100644 --- a/src/libImaging/FliDecode.c +++ b/src/libImaging/FliDecode.c @@ -5,8 +5,8 @@ * decoder for Autodesk Animator FLI/FLC animations * * history: - * 97-01-03 fl Created - * 97-01-17 fl Added SS2 support (FLC) + * 97-01-03 fl Created + * 97-01-17 fl Added SS2 support (FLC) * * Copyright (c) Fredrik Lundh 1997. * Copyright (c) Secret Labs AB 1997. @@ -18,10 +18,10 @@ #include "Imaging.h" -#define I16(ptr)\ +#define I16(ptr)\ ((ptr)[0] + ((ptr)[1] << 8)) -#define I32(ptr)\ +#define I32(ptr)\ ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24)) #define ERR_IF_DATA_OOB(offset) \ @@ -29,7 +29,7 @@ state->errcode = IMAGING_CODEC_OVERRUN; \ return -1; \ } - + int ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t bytes) { @@ -42,7 +42,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt /* If not even the chunk size is present, we'd better leave */ if (bytes < 4) - return 0; + return 0; /* We don't decode anything unless we have a full chunk in the input buffer */ @@ -51,7 +51,7 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt framesize = I32(ptr); if (framesize < I32(ptr)) - return 0; + return 0; /* Make sure this is a frame chunk. The Python driver takes case of other chunk types. */ @@ -61,8 +61,8 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt return -1; } if (I16(ptr+4) != 0xF1FA) { - state->errcode = IMAGING_CODEC_UNKNOWN; - return -1; + state->errcode = IMAGING_CODEC_UNKNOWN; + return -1; } chunks = I16(ptr+6); @@ -71,173 +71,173 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt /* Process subchunks */ for (c = 0; c < chunks; c++) { - UINT8* data; - if (bytes < 10) { - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } - data = ptr + 6; - switch (I16(ptr+4)) { - case 4: case 11: - /* FLI COLOR chunk */ - break; /* ignored; handled by Python code */ - case 7: - /* FLI SS2 chunk (word delta) */ - /* OOB ok, we've got 4 bytes min on entry */ - lines = I16(data); data += 2; - for (l = y = 0; l < lines && y < state->ysize; l++, y++) { - UINT8* local_buf = (UINT8*) im->image[y]; - int p, packets; - ERR_IF_DATA_OOB(2) - packets = I16(data); data += 2; - while (packets & 0x8000) { - /* flag word */ - if (packets & 0x4000) { - y += 65536 - packets; /* skip lines */ - if (y >= state->ysize) { - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } - local_buf = (UINT8*) im->image[y]; - } else { - /* store last byte (used if line width is odd) */ - local_buf[state->xsize-1] = (UINT8) packets; - } - ERR_IF_DATA_OOB(2) - packets = I16(data); data += 2; - } - for (p = x = 0; p < packets; p++) { - ERR_IF_DATA_OOB(2) - x += data[0]; /* pixel skip */ - if (data[1] >= 128) { - ERR_IF_DATA_OOB(4) - i = 256-data[1]; /* run */ - if (x + i + i > state->xsize) - break; - for (j = 0; j < i; j++) { - local_buf[x++] = data[2]; - local_buf[x++] = data[3]; - } - data += 2 + 2; - } else { - i = 2 * (int) data[1]; /* chunk */ - if (x + i > state->xsize) - break; - ERR_IF_DATA_OOB(2+i) - memcpy(local_buf + x, data + 2, i); - data += 2 + i; - x += i; - } - } - if (p < packets) - break; /* didn't process all packets */ - } - if (l < lines) { - /* didn't process all lines */ - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } - break; - case 12: - /* FLI LC chunk (byte delta) */ - /* OOB Check ok, we have 4 bytes min here */ - y = I16(data); ymax = y + I16(data+2); data += 4; - for (; y < ymax && y < state->ysize; y++) { - UINT8* out = (UINT8*) im->image[y]; - ERR_IF_DATA_OOB(1) - int p, packets = *data++; - for (p = x = 0; p < packets; p++, x += i) { - ERR_IF_DATA_OOB(2) - x += data[0]; /* skip pixels */ - if (data[1] & 0x80) { - i = 256-data[1]; /* run */ - if (x + i > state->xsize) - break; - ERR_IF_DATA_OOB(3) - memset(out + x, data[2], i); - data += 3; - } else { - i = data[1]; /* chunk */ - if (x + i > state->xsize) - break; - ERR_IF_DATA_OOB(2+i) - memcpy(out + x, data + 2, i); - data += i + 2; - } - } - if (p < packets) - break; /* didn't process all packets */ - } - if (y < ymax) { - /* didn't process all lines */ - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } - break; - case 13: - /* FLI BLACK chunk */ - for (y = 0; y < state->ysize; y++) - memset(im->image[y], 0, state->xsize); - break; - case 15: - /* FLI BRUN chunk */ - /* OOB, ok, we've got 4 bytes min on entry */ - for (y = 0; y < state->ysize; y++) { - UINT8* out = (UINT8*) im->image[y]; - data += 1; /* ignore packetcount byte */ - for (x = 0; x < state->xsize; x += i) { - ERR_IF_DATA_OOB(2) - if (data[0] & 0x80) { - i = 256 - data[0]; - if (x + i > state->xsize) { - break; /* safety first */ - } - ERR_IF_DATA_OOB(i+1) - memcpy(out + x, data + 1, i); - data += i + 1; - } else { - i = data[0]; - if (x + i > state->xsize) - break; /* safety first */ - memset(out + x, data[1], i); - data += 2; - } - } - if (x != state->xsize) { - /* didn't unpack whole line */ - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } - } - break; - case 16: - /* COPY chunk */ - if (state->xsize > bytes/state->ysize) { - /* not enough data for frame */ - return ptr - buf; /* bytes consumed */ - } - for (y = 0; y < state->ysize; y++) { - UINT8* local_buf = (UINT8*) im->image[y]; - memcpy(local_buf, data, state->xsize); - data += state->xsize; - } - break; - case 18: - /* PSTAMP chunk */ - break; /* ignored */ - default: - /* unknown chunk */ - /* printf("unknown FLI/FLC chunk: %d\n", I16(ptr+4)); */ - state->errcode = IMAGING_CODEC_UNKNOWN; - return -1; - } - advance = I32(ptr); - if (advance < 0 || advance > bytes) { - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } - ptr += advance; - bytes -= advance; + UINT8* data; + if (bytes < 10) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + data = ptr + 6; + switch (I16(ptr+4)) { + case 4: case 11: + /* FLI COLOR chunk */ + break; /* ignored; handled by Python code */ + case 7: + /* FLI SS2 chunk (word delta) */ + /* OOB ok, we've got 4 bytes min on entry */ + lines = I16(data); data += 2; + for (l = y = 0; l < lines && y < state->ysize; l++, y++) { + UINT8* local_buf = (UINT8*) im->image[y]; + int p, packets; + ERR_IF_DATA_OOB(2) + packets = I16(data); data += 2; + while (packets & 0x8000) { + /* flag word */ + if (packets & 0x4000) { + y += 65536 - packets; /* skip lines */ + if (y >= state->ysize) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + local_buf = (UINT8*) im->image[y]; + } else { + /* store last byte (used if line width is odd) */ + local_buf[state->xsize-1] = (UINT8) packets; + } + ERR_IF_DATA_OOB(2) + packets = I16(data); data += 2; + } + for (p = x = 0; p < packets; p++) { + ERR_IF_DATA_OOB(2) + x += data[0]; /* pixel skip */ + if (data[1] >= 128) { + ERR_IF_DATA_OOB(4) + i = 256-data[1]; /* run */ + if (x + i + i > state->xsize) + break; + for (j = 0; j < i; j++) { + local_buf[x++] = data[2]; + local_buf[x++] = data[3]; + } + data += 2 + 2; + } else { + i = 2 * (int) data[1]; /* chunk */ + if (x + i > state->xsize) + break; + ERR_IF_DATA_OOB(2+i) + memcpy(local_buf + x, data + 2, i); + data += 2 + i; + x += i; + } + } + if (p < packets) + break; /* didn't process all packets */ + } + if (l < lines) { + /* didn't process all lines */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + break; + case 12: + /* FLI LC chunk (byte delta) */ + /* OOB Check ok, we have 4 bytes min here */ + y = I16(data); ymax = y + I16(data+2); data += 4; + for (; y < ymax && y < state->ysize; y++) { + UINT8* out = (UINT8*) im->image[y]; + ERR_IF_DATA_OOB(1) + int p, packets = *data++; + for (p = x = 0; p < packets; p++, x += i) { + ERR_IF_DATA_OOB(2) + x += data[0]; /* skip pixels */ + if (data[1] & 0x80) { + i = 256-data[1]; /* run */ + if (x + i > state->xsize) + break; + ERR_IF_DATA_OOB(3) + memset(out + x, data[2], i); + data += 3; + } else { + i = data[1]; /* chunk */ + if (x + i > state->xsize) + break; + ERR_IF_DATA_OOB(2+i) + memcpy(out + x, data + 2, i); + data += i + 2; + } + } + if (p < packets) + break; /* didn't process all packets */ + } + if (y < ymax) { + /* didn't process all lines */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + break; + case 13: + /* FLI BLACK chunk */ + for (y = 0; y < state->ysize; y++) + memset(im->image[y], 0, state->xsize); + break; + case 15: + /* FLI BRUN chunk */ + /* OOB, ok, we've got 4 bytes min on entry */ + for (y = 0; y < state->ysize; y++) { + UINT8* out = (UINT8*) im->image[y]; + data += 1; /* ignore packetcount byte */ + for (x = 0; x < state->xsize; x += i) { + ERR_IF_DATA_OOB(2) + if (data[0] & 0x80) { + i = 256 - data[0]; + if (x + i > state->xsize) { + break; /* safety first */ + } + ERR_IF_DATA_OOB(i+1) + memcpy(out + x, data + 1, i); + data += i + 1; + } else { + i = data[0]; + if (x + i > state->xsize) + break; /* safety first */ + memset(out + x, data[1], i); + data += 2; + } + } + if (x != state->xsize) { + /* didn't unpack whole line */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + } + break; + case 16: + /* COPY chunk */ + if (state->xsize > bytes/state->ysize) { + /* not enough data for frame */ + return ptr - buf; /* bytes consumed */ + } + for (y = 0; y < state->ysize; y++) { + UINT8* local_buf = (UINT8*) im->image[y]; + memcpy(local_buf, data, state->xsize); + data += state->xsize; + } + break; + case 18: + /* PSTAMP chunk */ + break; /* ignored */ + default: + /* unknown chunk */ + /* printf("unknown FLI/FLC chunk: %d\n", I16(ptr+4)); */ + state->errcode = IMAGING_CODEC_UNKNOWN; + return -1; + } + advance = I32(ptr); + if (advance < 0 || advance > bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + ptr += advance; + bytes -= advance; } return -1; /* end of frame */ diff --git a/src/libImaging/GetBBox.c b/src/libImaging/GetBBox.c index ea7a35e4870..acb50fd95e7 100644 --- a/src/libImaging/GetBBox.c +++ b/src/libImaging/GetBBox.c @@ -33,47 +33,47 @@ ImagingGetBBox(Imaging im, int bbox[4]) bbox[1] = -1; bbox[2] = bbox[3] = 0; -#define GETBBOX(image, mask)\ +#define GETBBOX(image, mask)\ for (y = 0; y < im->ysize; y++) {\ - has_data = 0;\ - for (x = 0; x < im->xsize; x++)\ - if (im->image[y][x] & mask) {\ - has_data = 1;\ - if (x < bbox[0])\ - bbox[0] = x;\ - if (x >= bbox[2])\ - bbox[2] = x+1;\ - }\ - if (has_data) {\ - if (bbox[1] < 0)\ - bbox[1] = y;\ - bbox[3] = y+1;\ - }\ + has_data = 0;\ + for (x = 0; x < im->xsize; x++)\ + if (im->image[y][x] & mask) {\ + has_data = 1;\ + if (x < bbox[0])\ + bbox[0] = x;\ + if (x >= bbox[2])\ + bbox[2] = x+1;\ + }\ + if (has_data) {\ + if (bbox[1] < 0)\ + bbox[1] = y;\ + bbox[3] = y+1;\ + }\ } if (im->image8) { - GETBBOX(image8, 0xff); + GETBBOX(image8, 0xff); } else { - INT32 mask = 0xffffffff; - if (im->bands == 3) { - ((UINT8*) &mask)[3] = 0; - } else if (strcmp(im->mode, "RGBa") == 0 || - strcmp(im->mode, "RGBA") == 0 || - strcmp(im->mode, "La") == 0 || - strcmp(im->mode, "LA") == 0 || - strcmp(im->mode, "PA") == 0) { + INT32 mask = 0xffffffff; + if (im->bands == 3) { + ((UINT8*) &mask)[3] = 0; + } else if (strcmp(im->mode, "RGBa") == 0 || + strcmp(im->mode, "RGBA") == 0 || + strcmp(im->mode, "La") == 0 || + strcmp(im->mode, "LA") == 0 || + strcmp(im->mode, "PA") == 0) { #ifdef WORDS_BIGENDIAN - mask = 0x000000ff; + mask = 0x000000ff; #else - mask = 0xff000000; + mask = 0xff000000; #endif - } - GETBBOX(image32, mask); + } + GETBBOX(image32, mask); } /* Check that we got a box */ if (bbox[1] < 0) - return 0; /* no data */ + return 0; /* no data */ return 1; /* ok */ } @@ -91,25 +91,25 @@ ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj) memset(xproj, 0, im->xsize); memset(yproj, 0, im->ysize); -#define GETPROJ(image, mask)\ - for (y = 0; y < im->ysize; y++) {\ - has_data = 0;\ - for (x = 0; x < im->xsize; x++)\ - if (im->image[y][x] & mask) {\ - has_data = 1;\ - xproj[x] = 1;\ - }\ - if (has_data)\ - yproj[y] = 1;\ - } + #define GETPROJ(image, mask)\ + for (y = 0; y < im->ysize; y++) {\ + has_data = 0;\ + for (x = 0; x < im->xsize; x++)\ + if (im->image[y][x] & mask) {\ + has_data = 1;\ + xproj[x] = 1;\ + }\ + if (has_data)\ + yproj[y] = 1;\ + } if (im->image8) { - GETPROJ(image8, 0xff); + GETPROJ(image8, 0xff); } else { - INT32 mask = 0xffffffff; - if (im->bands == 3) - ((UINT8*) &mask)[3] = 0; - GETPROJ(image32, mask); + INT32 mask = 0xffffffff; + if (im->bands == 3) + ((UINT8*) &mask)[3] = 0; + GETPROJ(image32, mask); } return 1; /* ok */ @@ -124,7 +124,7 @@ ImagingGetExtrema(Imaging im, void *extrema) FLOAT32 fmin, fmax; if (im->bands != 1) { - (void) ImagingError_ModeError(); + (void) ImagingError_ModeError(); return -1; /* mismatch */ } @@ -202,11 +202,11 @@ ImagingGetExtrema(Imaging im, void *extrema) memcpy(extrema, &v, sizeof(v)); v = (UINT16) imax; memcpy(((char*)extrema) + sizeof(v), &v, sizeof(v)); - break; + break; } /* FALL THROUGH */ default: - (void) ImagingError_ModeError(); + (void) ImagingError_ModeError(); return -1; } return 1; /* ok */ @@ -265,14 +265,14 @@ getcolors32(Imaging im, int maxcolors, int* size) /* printf("code_poly=%d\n", code_poly); */ if (!code_size) - return ImagingError_MemoryError(); /* just give up */ + return ImagingError_MemoryError(); /* just give up */ if (!im->image32) - return ImagingError_ModeError(); + return ImagingError_ModeError(); table = calloc(code_size + 1, sizeof(ImagingColorItem)); if (!table) - return ImagingError_MemoryError(); + return ImagingError_MemoryError(); pixel_mask = 0xffffffff; if (im->bands == 3) diff --git a/src/libImaging/Gif.h b/src/libImaging/Gif.h index 2cb95efd289..bb118396cdf 100644 --- a/src/libImaging/Gif.h +++ b/src/libImaging/Gif.h @@ -10,10 +10,10 @@ /* Max size for a LZW code word. */ -#define GIFBITS 12 +#define GIFBITS 12 -#define GIFTABLE (1< -#include /* memcpy() */ +#include /* memcpy() */ #include "Gif.h" @@ -34,23 +34,23 @@ state->x = 0;\ state->y += context->step;\ while (state->y >= state->ysize)\ - switch (context->interlace) {\ - case 1:\ - context->repeat = state->y = 4;\ - context->interlace = 2;\ - break;\ - case 2:\ - context->step = 4;\ - context->repeat = state->y = 2;\ - context->interlace = 3;\ - break;\ - case 3:\ - context->step = 2;\ - context->repeat = state->y = 1;\ - context->interlace = 0;\ - break;\ - default:\ - return -1;\ + switch (context->interlace) {\ + case 1:\ + context->repeat = state->y = 4;\ + context->interlace = 2;\ + break;\ + case 2:\ + context->step = 4;\ + context->repeat = state->y = 2;\ + context->interlace = 3;\ + break;\ + case 3:\ + context->step = 2;\ + context->repeat = state->y = 1;\ + context->interlace = 0;\ + break;\ + default:\ + return -1;\ }\ if (state->y < state->ysize)\ out = im->image8[state->y + state->yoff] + state->xoff;\ @@ -70,227 +70,227 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ssize_t if (!state->state) { - /* Initialise state */ - if (context->bits < 0 || context->bits > 12) { - state->errcode = IMAGING_CODEC_CONFIG; - return -1; - } + /* Initialise state */ + if (context->bits < 0 || context->bits > 12) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } - /* Clear code */ - context->clear = 1 << context->bits; + /* Clear code */ + context->clear = 1 << context->bits; - /* End code */ - context->end = context->clear + 1; + /* End code */ + context->end = context->clear + 1; - /* Interlace */ - if (context->interlace) { - context->interlace = 1; - context->step = context->repeat = 8; - } else - context->step = 1; + /* Interlace */ + if (context->interlace) { + context->interlace = 1; + context->step = context->repeat = 8; + } else + context->step = 1; - state->state = 1; + state->state = 1; } out = im->image8[state->y + state->yoff] + state->xoff + state->x; for (;;) { - if (state->state == 1) { + if (state->state == 1) { - /* First free entry in table */ - context->next = context->clear + 2; + /* First free entry in table */ + context->next = context->clear + 2; - /* Initial code size */ - context->codesize = context->bits + 1; - context->codemask = (1 << context->codesize) - 1; + /* Initial code size */ + context->codesize = context->bits + 1; + context->codemask = (1 << context->codesize) - 1; - /* Buffer pointer. We fill the buffer from right, which - allows us to return all of it in one operation. */ - context->bufferindex = GIFBUFFER; + /* Buffer pointer. We fill the buffer from right, which + allows us to return all of it in one operation. */ + context->bufferindex = GIFBUFFER; - state->state = 2; - } + state->state = 2; + } - if (context->bufferindex < GIFBUFFER) { + if (context->bufferindex < GIFBUFFER) { - /* Return whole buffer in one chunk */ - i = GIFBUFFER - context->bufferindex; - p = &context->buffer[context->bufferindex]; + /* Return whole buffer in one chunk */ + i = GIFBUFFER - context->bufferindex; + p = &context->buffer[context->bufferindex]; - context->bufferindex = GIFBUFFER; + context->bufferindex = GIFBUFFER; - } else { + } else { - /* Get current symbol */ + /* Get current symbol */ - while (context->bitcount < context->codesize) { + while (context->bitcount < context->codesize) { - if (context->blocksize > 0) { + if (context->blocksize > 0) { - /* Read next byte */ - c = *ptr++; bytes--; + /* Read next byte */ + c = *ptr++; bytes--; - context->blocksize--; + context->blocksize--; - /* New bits are shifted in from from the left. */ - context->bitbuffer |= (INT32) c << context->bitcount; - context->bitcount += 8; + /* New bits are shifted in from from the left. */ + context->bitbuffer |= (INT32) c << context->bitcount; + context->bitcount += 8; - } else { + } else { - /* New GIF block */ + /* New GIF block */ - /* We don't start decoding unless we have a full block */ - if (bytes < 1) - return ptr - buffer; - c = *ptr; - if (bytes < c+1) - return ptr - buffer; + /* We don't start decoding unless we have a full block */ + if (bytes < 1) + return ptr - buffer; + c = *ptr; + if (bytes < c+1) + return ptr - buffer; - context->blocksize = c; + context->blocksize = c; - ptr++; bytes--; + ptr++; bytes--; - } - } + } + } - /* Extract current symbol from bit buffer. */ - c = (int) context->bitbuffer & context->codemask; + /* Extract current symbol from bit buffer. */ + c = (int) context->bitbuffer & context->codemask; - /* Adjust buffer */ - context->bitbuffer >>= context->codesize; - context->bitcount -= context->codesize; + /* Adjust buffer */ + context->bitbuffer >>= context->codesize; + context->bitcount -= context->codesize; - /* If c is less than "clear", it's a data byte. Otherwise, - it's either clear/end or a code symbol which should be - expanded. */ + /* If c is less than "clear", it's a data byte. Otherwise, + it's either clear/end or a code symbol which should be + expanded. */ - if (c == context->clear) { - if (state->state != 2) - state->state = 1; - continue; - } + if (c == context->clear) { + if (state->state != 2) + state->state = 1; + continue; + } - if (c == context->end) - break; + if (c == context->end) + break; - i = 1; - p = &context->lastdata; + i = 1; + p = &context->lastdata; - if (state->state == 2) { + if (state->state == 2) { - /* First valid symbol after clear; use as is */ - if (c > context->clear) { - state->errcode = IMAGING_CODEC_BROKEN; - return -1; - } + /* First valid symbol after clear; use as is */ + if (c > context->clear) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } - context->lastdata = context->lastcode = c; - state->state = 3; + context->lastdata = context->lastcode = c; + state->state = 3; - } else { + } else { - thiscode = c; + thiscode = c; - if (c > context->next) { - state->errcode = IMAGING_CODEC_BROKEN; - return -1; - } + if (c > context->next) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } - if (c == context->next) { + if (c == context->next) { - /* c == next is allowed. not sure why. */ + /* c == next is allowed. not sure why. */ - if (context->bufferindex <= 0) { - state->errcode = IMAGING_CODEC_BROKEN; - return -1; - } + if (context->bufferindex <= 0) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } - context->buffer[--context->bufferindex] = - context->lastdata; + context->buffer[--context->bufferindex] = + context->lastdata; - c = context->lastcode; + c = context->lastcode; - } + } - while (c >= context->clear) { + while (c >= context->clear) { - /* Copy data string to buffer (beginning from right) */ + /* Copy data string to buffer (beginning from right) */ - if (context->bufferindex <= 0 || c >= GIFTABLE) { - state->errcode = IMAGING_CODEC_BROKEN; - return -1; - } + if (context->bufferindex <= 0 || c >= GIFTABLE) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } - context->buffer[--context->bufferindex] = - context->data[c]; + context->buffer[--context->bufferindex] = + context->data[c]; - c = context->link[c]; - } + c = context->link[c]; + } - context->lastdata = c; + context->lastdata = c; - if (context->next < GIFTABLE) { + if (context->next < GIFTABLE) { - /* We'll only add this symbol if we have room - for it (take advise, Netscape!) */ - context->data[context->next] = c; - context->link[context->next] = context->lastcode; + /* We'll only add this symbol if we have room + for it (take advise, Netscape!) */ + context->data[context->next] = c; + context->link[context->next] = context->lastcode; - if (context->next == context->codemask && - context->codesize < GIFBITS) { + if (context->next == context->codemask && + context->codesize < GIFBITS) { - /* Expand code size */ - context->codesize++; - context->codemask = (1 << context->codesize) - 1; - } + /* Expand code size */ + context->codesize++; + context->codemask = (1 << context->codesize) - 1; + } - context->next++; + context->next++; - } + } - context->lastcode = thiscode; + context->lastcode = thiscode; - } - } + } + } - /* Copy the bytes into the image */ - if (state->y >= state->ysize) { - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } + /* Copy the bytes into the image */ + if (state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } - /* To squeeze some extra pixels out of this loop, we test for - some common cases and handle them separately. */ + /* To squeeze some extra pixels out of this loop, we test for + some common cases and handle them separately. */ - /* FIXME: should we handle the transparency index in here??? */ + /* FIXME: should we handle the transparency index in here??? */ - if (i == 1) { - if (state->x < state->xsize-1) { - /* Single pixel, not at the end of the line. */ - *out++ = p[0]; - state->x++; - continue; - } - } else if (state->x + i <= state->xsize) { - /* This string fits into current line. */ - memcpy(out, p, i); - out += i; - state->x += i; - if (state->x == state->xsize) { - NEWLINE(state, context); - } - continue; - } + if (i == 1) { + if (state->x < state->xsize-1) { + /* Single pixel, not at the end of the line. */ + *out++ = p[0]; + state->x++; + continue; + } + } else if (state->x + i <= state->xsize) { + /* This string fits into current line. */ + memcpy(out, p, i); + out += i; + state->x += i; + if (state->x == state->xsize) { + NEWLINE(state, context); + } + continue; + } - /* No shortcut, copy pixel by pixel */ - for (c = 0; c < i; c++) { - *out++ = p[c]; - if (++state->x >= state->xsize) { - NEWLINE(state, context); - } - } + /* No shortcut, copy pixel by pixel */ + for (c = 0; c < i; c++) { + *out++ = p[c]; + if (++state->x >= state->xsize) { + NEWLINE(state, context); + } + } } return ptr - buffer; diff --git a/src/libImaging/GifEncode.c b/src/libImaging/GifEncode.c index f211814ed03..90441d184e2 100644 --- a/src/libImaging/GifEncode.c +++ b/src/libImaging/GifEncode.c @@ -5,11 +5,11 @@ * encoder for uncompressed GIF data * * history: - * 97-01-05 fl created (writes uncompressed data) - * 97-08-27 fl fixed off-by-one error in buffer size test - * 98-07-09 fl added interlace write support - * 99-02-07 fl rewritten, now uses a run-length encoding strategy - * 99-02-08 fl improved run-length encoding for long runs + * 97-01-05 fl created (writes uncompressed data) + * 97-08-27 fl fixed off-by-one error in buffer size test + * 98-07-09 fl added interlace write support + * 99-02-07 fl rewritten, now uses a run-length encoding strategy + * 99-02-08 fl improved run-length encoding for long runs * * Copyright (c) Secret Labs AB 1997-99. * Copyright (c) Fredrik Lundh 1997. @@ -145,17 +145,17 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (!state->state) { - /* place a clear code in the output buffer */ - context->bitbuffer = CLEAR_CODE; - context->bitcount = 9; + /* place a clear code in the output buffer */ + context->bitbuffer = CLEAR_CODE; + context->bitcount = 9; - state->count = FIRST_CODE; + state->count = FIRST_CODE; - if (context->interlace) { - context->interlace = 1; - context->step = 8; - } else - context->step = 1; + if (context->interlace) { + context->interlace = 1; + context->step = 8; + } else + context->step = 1; context->last = -1; @@ -169,152 +169,152 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) for (;;) - switch (state->state) { + switch (state->state) { - case INIT: - case ENCODE: + case INIT: + case ENCODE: - /* identify and store a run of pixels */ + /* identify and store a run of pixels */ - if (state->x == 0 || state->x >= state->xsize) { + if (state->x == 0 || state->x >= state->xsize) { - if (!context->interlace && state->y >= state->ysize) { - state->state = ENCODE_EOF; - break; - } + if (!context->interlace && state->y >= state->ysize) { + state->state = ENCODE_EOF; + break; + } - if (context->flush) { - state->state = FLUSH; - break; - } + if (context->flush) { + state->state = FLUSH; + break; + } - /* get another line of data */ - state->shuffle( - state->buffer, - (UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->xsize + /* get another line of data */ + state->shuffle( + state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize ); - state->x = 0; - - if (state->state == INIT) { - /* preload the run-length buffer and get going */ - context->last = state->buffer[0]; - context->count = state->x = 1; - state->state = ENCODE; - } + state->x = 0; - /* step forward, according to the interlace settings */ - state->y += context->step; - while (context->interlace && state->y >= state->ysize) - switch (context->interlace) { - case 1: - state->y = 4; - context->interlace = 2; - break; - case 2: - context->step = 4; - state->y = 2; - context->interlace = 3; - break; - case 3: - context->step = 2; - state->y = 1; - context->interlace = 0; - break; - default: - /* just make sure we don't loop forever */ - context->interlace = 0; + if (state->state == INIT) { + /* preload the run-length buffer and get going */ + context->last = state->buffer[0]; + context->count = state->x = 1; + state->state = ENCODE; } - } + /* step forward, according to the interlace settings */ + state->y += context->step; + while (context->interlace && state->y >= state->ysize) + switch (context->interlace) { + case 1: + state->y = 4; + context->interlace = 2; + break; + case 2: + context->step = 4; + state->y = 2; + context->interlace = 3; + break; + case 3: + context->step = 2; + state->y = 1; + context->interlace = 0; + break; + default: + /* just make sure we don't loop forever */ + context->interlace = 0; + } - this = state->buffer[state->x++]; + } + + this = state->buffer[state->x++]; - if (this == context->last) - context->count++; - else { - EMIT_RUN(label1); - context->last = this; - context->count = 1; - } - break; + if (this == context->last) + context->count++; + else { + EMIT_RUN(label1); + context->last = this; + context->count = 1; + } + break; - case ENCODE_EOF: + case ENCODE_EOF: - /* write the final run */ - EMIT_RUN(label2); + /* write the final run */ + EMIT_RUN(label2); - /* write an end of image marker */ - EMIT(EOF_CODE); + /* write an end of image marker */ + EMIT(EOF_CODE); - /* empty the bit buffer */ - while (context->bitcount > 0) { - if (!emit(context, (UINT8) context->bitbuffer)) { - state->errcode = IMAGING_CODEC_MEMORY; - return 0; + /* empty the bit buffer */ + while (context->bitcount > 0) { + if (!emit(context, (UINT8) context->bitbuffer)) { + state->errcode = IMAGING_CODEC_MEMORY; + return 0; + } + context->bitbuffer >>= 8; + context->bitcount -= 8; } - context->bitbuffer >>= 8; - context->bitcount -= 8; - } - /* flush the last block, and exit */ - if (context->block) { - GIFENCODERBLOCK* block; - block = context->flush; - while (block && block->next) - block = block->next; - if (block) - block->next = context->block; - else - context->flush = context->block; - context->block = NULL; - } + /* flush the last block, and exit */ + if (context->block) { + GIFENCODERBLOCK* block; + block = context->flush; + while (block && block->next) + block = block->next; + if (block) + block->next = context->block; + else + context->flush = context->block; + context->block = NULL; + } - state->state = EXIT; + state->state = EXIT; - /* fall through... */ + /* fall through... */ - case EXIT: - case FLUSH: + case EXIT: + case FLUSH: - while (context->flush) { + while (context->flush) { - /* get a block from the flush queue */ - block = context->flush; + /* get a block from the flush queue */ + block = context->flush; - if (block->size > 0) { + if (block->size > 0) { - /* make sure it fits into the output buffer */ - if (bytes < block->size+1) - return ptr - buf; + /* make sure it fits into the output buffer */ + if (bytes < block->size+1) + return ptr - buf; - ptr[0] = block->size; - memcpy(ptr+1, block->data, block->size); + ptr[0] = block->size; + memcpy(ptr+1, block->data, block->size); - ptr += block->size+1; - bytes -= block->size+1; + ptr += block->size+1; + bytes -= block->size+1; - } + } - context->flush = block->next; + context->flush = block->next; - if (context->free) - free(context->free); - context->free = block; + if (context->free) + free(context->free); + context->free = block; - } + } - if (state->state == EXIT) { - /* this was the last block! */ - if (context->free) - free(context->free); - state->errcode = IMAGING_CODEC_END; - return ptr - buf; - } + if (state->state == EXIT) { + /* this was the last block! */ + if (context->free) + free(context->free); + state->errcode = IMAGING_CODEC_END; + return ptr - buf; + } - state->state = ENCODE; - break; - } + state->state = ENCODE; + break; + } } diff --git a/src/libImaging/HexDecode.c b/src/libImaging/HexDecode.c index 8bd9bf67fa7..9b20bc2accc 100644 --- a/src/libImaging/HexDecode.c +++ b/src/libImaging/HexDecode.c @@ -5,7 +5,7 @@ * decoder for hex encoded image data * * history: - * 96-05-16 fl Created + * 96-05-16 fl Created * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -16,9 +16,9 @@ #include "Imaging.h" -#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\ - (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\ - (v >= 'A' && v <= 'F') ? v - 'A' + 10 : -1) +#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\ + (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\ + (v >= 'A' && v <= 'F') ? v - 'A' + 10 : -1) int ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t bytes) @@ -30,38 +30,38 @@ ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { - if (bytes < 2) - return ptr - buf; + if (bytes < 2) + return ptr - buf; - a = HEX(ptr[0]); - b = HEX(ptr[1]); + a = HEX(ptr[0]); + b = HEX(ptr[1]); - if (a < 0 || b < 0) { + if (a < 0 || b < 0) { - ptr++; - bytes--; + ptr++; + bytes--; - } else { + } else { - ptr += 2; - bytes -= 2; + ptr += 2; + bytes -= 2; - state->buffer[state->x] = (a<<4) + b; + state->buffer[state->x] = (a<<4) + b; - if (++state->x >= state->bytes) { + if (++state->x >= state->bytes) { - /* Got a full line, unpack it */ - state->shuffle((UINT8*) im->image[state->y], state->buffer, - state->xsize); + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y], state->buffer, + state->xsize); - state->x = 0; + state->x = 0; - if (++state->y >= state->ysize) { - /* End of file (errcode = 0) */ - return -1; - } - } + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } - } + } } } diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index 5c2824ab03e..0d08e9f086a 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -30,7 +30,7 @@ void ImagingHistogramDelete(ImagingHistogram h) { if (h->histogram) - free(h->histogram); + free(h->histogram); free(h); } @@ -60,115 +60,115 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) FLOAT32 fmin, fmax, scale; if (!im) - return ImagingError_ModeError(); + return ImagingError_ModeError(); if (imMask) { - /* Validate mask */ - if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) - return ImagingError_Mismatch(); - if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) - return ImagingError_ValueError("bad transparency mask"); + /* Validate mask */ + if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) + return ImagingError_Mismatch(); + if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) + return ImagingError_ValueError("bad transparency mask"); } h = ImagingHistogramNew(im); if (imMask) { - /* mask */ - if (im->image8) { - ImagingSectionEnter(&cookie); - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) - if (imMask->image8[y][x] != 0) - h->histogram[im->image8[y][x]]++; - ImagingSectionLeave(&cookie); - } else { /* yes, we need the braces. C isn't Python! */ + /* mask */ + if (im->image8) { + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + if (imMask->image8[y][x] != 0) + h->histogram[im->image8[y][x]]++; + ImagingSectionLeave(&cookie); + } else { /* yes, we need the braces. C isn't Python! */ if (im->type != IMAGING_TYPE_UINT8) { ImagingHistogramDelete(h); return ImagingError_ModeError(); } ImagingSectionEnter(&cookie); - for (y = 0; y < im->ysize; y++) { - UINT8* in = (UINT8*) im->image32[y]; - for (x = 0; x < im->xsize; x++) - if (imMask->image8[y][x] != 0) { - h->histogram[(*in++)]++; - h->histogram[(*in++)+256]++; - h->histogram[(*in++)+512]++; - h->histogram[(*in++)+768]++; - } else - in += 4; - } - ImagingSectionLeave(&cookie); - } - } else { - /* mask not given; process pixels in image */ - if (im->image8) { - ImagingSectionEnter(&cookie); - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) - h->histogram[im->image8[y][x]]++; - ImagingSectionLeave(&cookie); - } else { - switch (im->type) { - case IMAGING_TYPE_UINT8: - ImagingSectionEnter(&cookie); - for (y = 0; y < im->ysize; y++) { - UINT8* in = (UINT8*) im->image[y]; - for (x = 0; x < im->xsize; x++) { + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image32[y]; + for (x = 0; x < im->xsize; x++) + if (imMask->image8[y][x] != 0) { h->histogram[(*in++)]++; h->histogram[(*in++)+256]++; h->histogram[(*in++)+512]++; h->histogram[(*in++)+768]++; - } - } - ImagingSectionLeave(&cookie); - break; - case IMAGING_TYPE_INT32: - if (!minmax) { - ImagingHistogramDelete(h); - return ImagingError_ValueError("min/max not given"); - } - if (!im->xsize || !im->ysize) - break; - memcpy(&imin, minmax, sizeof(imin)); - memcpy(&imax, ((char*)minmax) + sizeof(imin), sizeof(imax)); - if (imin >= imax) - break; + } else + in += 4; + } + ImagingSectionLeave(&cookie); + } + } else { + /* mask not given; process pixels in image */ + if (im->image8) { ImagingSectionEnter(&cookie); - scale = 255.0F / (imax - imin); - for (y = 0; y < im->ysize; y++) { - INT32* in = im->image32[y]; - for (x = 0; x < im->xsize; x++) { - i = (int) (((*in++)-imin)*scale); - if (i >= 0 && i < 256) - h->histogram[i]++; + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + h->histogram[im->image8[y][x]]++; + ImagingSectionLeave(&cookie); + } else { + switch (im->type) { + case IMAGING_TYPE_UINT8: + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image[y]; + for (x = 0; x < im->xsize; x++) { + h->histogram[(*in++)]++; + h->histogram[(*in++)+256]++; + h->histogram[(*in++)+512]++; + h->histogram[(*in++)+768]++; + } } - } - ImagingSectionLeave(&cookie); - break; - case IMAGING_TYPE_FLOAT32: - if (!minmax) { - ImagingHistogramDelete(h); - return ImagingError_ValueError("min/max not given"); - } - if (!im->xsize || !im->ysize) + ImagingSectionLeave(&cookie); break; - memcpy(&fmin, minmax, sizeof(fmin)); - memcpy(&fmax, ((char*)minmax) + sizeof(fmin), sizeof(fmax)); - if (fmin >= fmax) + case IMAGING_TYPE_INT32: + if (!minmax) { + ImagingHistogramDelete(h); + return ImagingError_ValueError("min/max not given"); + } + if (!im->xsize || !im->ysize) + break; + memcpy(&imin, minmax, sizeof(imin)); + memcpy(&imax, ((char*)minmax) + sizeof(imin), sizeof(imax)); + if (imin >= imax) + break; + ImagingSectionEnter(&cookie); + scale = 255.0F / (imax - imin); + for (y = 0; y < im->ysize; y++) { + INT32* in = im->image32[y]; + for (x = 0; x < im->xsize; x++) { + i = (int) (((*in++)-imin)*scale); + if (i >= 0 && i < 256) + h->histogram[i]++; + } + } + ImagingSectionLeave(&cookie); break; - ImagingSectionEnter(&cookie); - scale = 255.0F / (fmax - fmin); - for (y = 0; y < im->ysize; y++) { - FLOAT32* in = (FLOAT32*) im->image32[y]; - for (x = 0; x < im->xsize; x++) { - i = (int) (((*in++)-fmin)*scale); - if (i >= 0 && i < 256) - h->histogram[i]++; + case IMAGING_TYPE_FLOAT32: + if (!minmax) { + ImagingHistogramDelete(h); + return ImagingError_ValueError("min/max not given"); } - } - ImagingSectionLeave(&cookie); - break; + if (!im->xsize || !im->ysize) + break; + memcpy(&fmin, minmax, sizeof(fmin)); + memcpy(&fmax, ((char*)minmax) + sizeof(fmin), sizeof(fmax)); + if (fmin >= fmax) + break; + ImagingSectionEnter(&cookie); + scale = 255.0F / (fmax - fmin); + for (y = 0; y < im->ysize; y++) { + FLOAT32* in = (FLOAT32*) im->image32[y]; + for (x = 0; x < im->xsize; x++) { + i = (int) (((*in++)-fmin)*scale); + if (i >= 0 && i < 256) + h->histogram[i]++; + } + } + ImagingSectionLeave(&cookie); + break; } } } diff --git a/src/libImaging/ImPlatform.h b/src/libImaging/ImPlatform.h index b2d4db78594..576ceaa58b3 100644 --- a/src/libImaging/ImPlatform.h +++ b/src/libImaging/ImPlatform.h @@ -39,41 +39,41 @@ /* For System that are not Windows, we'll need to define these. */ #if SIZEOF_SHORT == 2 -#define INT16 short +#define INT16 short #elif SIZEOF_INT == 2 -#define INT16 int +#define INT16 int #else -#define INT16 short /* most things works just fine anyway... */ +#define INT16 short /* most things works just fine anyway... */ #endif #if SIZEOF_SHORT == 4 -#define INT32 short +#define INT32 short #elif SIZEOF_INT == 4 -#define INT32 int +#define INT32 int #elif SIZEOF_LONG == 4 -#define INT32 long +#define INT32 long #else #error Cannot find required 32-bit integer type #endif #if SIZEOF_LONG == 8 -#define INT64 long +#define INT64 long #elif SIZEOF_LONG_LONG == 8 -#define INT64 long +#define INT64 long #endif -#define INT8 signed char -#define UINT8 unsigned char +#define INT8 signed char +#define UINT8 unsigned char -#define UINT16 unsigned INT16 -#define UINT32 unsigned INT32 +#define UINT16 unsigned INT16 +#define UINT32 unsigned INT32 #endif /* assume IEEE; tweak if necessary (patches are welcome) */ -#define FLOAT16 UINT16 -#define FLOAT32 float -#define FLOAT64 double +#define FLOAT16 UINT16 +#define FLOAT32 float +#define FLOAT64 double #ifdef _MSC_VER typedef signed __int64 int64_t; diff --git a/src/libImaging/Jpeg.h b/src/libImaging/Jpeg.h index 7ff658c3231..df6d8a90319 100644 --- a/src/libImaging/Jpeg.h +++ b/src/libImaging/Jpeg.h @@ -14,13 +14,13 @@ typedef struct { - struct jpeg_error_mgr pub; /* "public" fields */ - jmp_buf setjmp_buffer; /* for return to caller */ + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ } JPEGERROR; /* -------------------------------------------------------------------- */ -/* Decoder */ +/* Decoder */ typedef struct { struct jpeg_source_mgr pub; @@ -56,7 +56,7 @@ typedef struct { /* -------------------------------------------------------------------- */ -/* Encoder */ +/* Encoder */ typedef struct { struct jpeg_destination_mgr pub; diff --git a/src/libImaging/Jpeg2K.h b/src/libImaging/Jpeg2K.h index 7bb14eb2c0d..7645b932641 100644 --- a/src/libImaging/Jpeg2K.h +++ b/src/libImaging/Jpeg2K.h @@ -14,7 +14,7 @@ #define BUFFER_SIZE OPJ_J2K_STREAM_CHUNK_SIZE /* -------------------------------------------------------------------- */ -/* Decoder */ +/* Decoder */ /* -------------------------------------------------------------------- */ typedef struct { @@ -44,7 +44,7 @@ typedef struct { } JPEG2KDECODESTATE; /* -------------------------------------------------------------------- */ -/* Encoder */ +/* Encoder */ /* -------------------------------------------------------------------- */ typedef struct { diff --git a/src/libImaging/JpegDecode.c b/src/libImaging/JpegDecode.c index 39d96de531b..ce0c2ca7ce5 100644 --- a/src/libImaging/JpegDecode.c +++ b/src/libImaging/JpegDecode.c @@ -301,14 +301,14 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t by /* -------------------------------------------------------------------- */ int ImagingJpegDecodeCleanup(ImagingCodecState state){ - /* called to free the decompression engine when the decode terminates - due to a corrupt or truncated image - */ + /* called to free the decompression engine when the decode terminates + due to a corrupt or truncated image + */ JPEGSTATE* context = (JPEGSTATE*) state->context; - /* Clean up */ - jpeg_destroy_decompress(&context->cinfo); - return -1; + /* Clean up */ + jpeg_destroy_decompress(&context->cinfo); + return -1; } #endif diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 9d838279162..2eb35d6bf5b 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -78,7 +78,7 @@ error(j_common_ptr cinfo) /* -------------------------------------------------------------------- */ -/* Encoder */ +/* Encoder */ /* -------------------------------------------------------------------- */ int @@ -88,24 +88,24 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) int ok; if (setjmp(context->error.setjmp_buffer)) { - /* JPEG error handler */ - jpeg_destroy_compress(&context->cinfo); - state->errcode = IMAGING_CODEC_BROKEN; - return -1; + /* JPEG error handler */ + jpeg_destroy_compress(&context->cinfo); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; } if (!state->state) { - /* Setup compression context (very similar to the decoder) */ - context->cinfo.err = jpeg_std_error(&context->error.pub); - context->error.pub.error_exit = error; - jpeg_create_compress(&context->cinfo); - jpeg_buffer_dest(&context->cinfo, &context->destination); + /* Setup compression context (very similar to the decoder) */ + context->cinfo.err = jpeg_std_error(&context->error.pub); + context->error.pub.error_exit = error; + jpeg_create_compress(&context->cinfo); + jpeg_buffer_dest(&context->cinfo, &context->destination); context->extra_offset = 0; - /* Ready to encode */ - state->state = 1; + /* Ready to encode */ + state->state = 1; } @@ -115,212 +115,212 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) switch (state->state) { - case 1: - - context->cinfo.image_width = state->xsize; - context->cinfo.image_height = state->ysize; - - switch (state->bits) { - case 8: - context->cinfo.input_components = 1; - context->cinfo.in_color_space = JCS_GRAYSCALE; - break; - case 24: - context->cinfo.input_components = 3; - if (strcmp(im->mode, "YCbCr") == 0) - context->cinfo.in_color_space = JCS_YCbCr; - else - context->cinfo.in_color_space = JCS_RGB; - break; - case 32: - context->cinfo.input_components = 4; - context->cinfo.in_color_space = JCS_CMYK; - #ifdef JCS_EXTENSIONS - if (strcmp(context->rawmode, "RGBX") == 0) - context->cinfo.in_color_space = JCS_EXT_RGBX; - #endif - break; - default: - state->errcode = IMAGING_CODEC_CONFIG; - return -1; - } - - /* Compressor configuration */ - jpeg_set_defaults(&context->cinfo); - - /* Use custom quantization tables */ - if (context->qtables) { - int i; - int quality = 100; - int last_q = 0; - if (context->quality != -1) { - quality = context->quality; - } - for (i = 0; i < context->qtablesLen; i++) { - // TODO: Should add support for none baseline - jpeg_add_quant_table(&context->cinfo, i, &context->qtables[i * DCTSIZE2], - quality, TRUE); - context->cinfo.comp_info[i].quant_tbl_no = i; - last_q = i; - } - if (context->qtablesLen == 1) { - // jpeg_set_defaults created two qtables internally, but we only wanted one. - jpeg_add_quant_table(&context->cinfo, 1, &context->qtables[0], - quality, TRUE); - } - for (i = last_q; i < context->cinfo.num_components; i++) { - context->cinfo.comp_info[i].quant_tbl_no = last_q; - } - } else if (context->quality != -1) { - jpeg_set_quality(&context->cinfo, context->quality, 1); - } - - /* Set subsampling options */ - switch (context->subsampling) - { - case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ - { - context->cinfo.comp_info[0].h_samp_factor = 1; - context->cinfo.comp_info[0].v_samp_factor = 1; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; - } - case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ - { - context->cinfo.comp_info[0].h_samp_factor = 2; - context->cinfo.comp_info[0].v_samp_factor = 1; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; - } - case 2: /* 2x2, 1x1, 1x1 (4:2:0) : High */ - { - context->cinfo.comp_info[0].h_samp_factor = 2; - context->cinfo.comp_info[0].v_samp_factor = 2; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; - } - default: - { - /* Use the lib's default */ - break; - } - } - if (context->progressive) - jpeg_simple_progression(&context->cinfo); - context->cinfo.smoothing_factor = context->smooth; - context->cinfo.optimize_coding = (boolean) context->optimize; - if (context->xdpi > 0 && context->ydpi > 0) { - context->cinfo.density_unit = 1; /* dots per inch */ - context->cinfo.X_density = context->xdpi; - context->cinfo.Y_density = context->ydpi; - } - switch (context->streamtype) { - case 1: - /* tables only -- not yet implemented */ - state->errcode = IMAGING_CODEC_CONFIG; - return -1; - case 2: - /* image only */ - jpeg_suppress_tables(&context->cinfo, TRUE); - jpeg_start_compress(&context->cinfo, FALSE); - /* suppress extra section */ - context->extra_offset = context->extra_size; - break; - default: - /* interchange stream */ - jpeg_start_compress(&context->cinfo, TRUE); - break; - } - state->state++; - /* fall through */ - - case 2: - // check for exif len + 'APP1' header bytes - if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer){ - break; - } - //add exif header - if (context->rawExifLen > 0){ - jpeg_write_marker(&context->cinfo, JPEG_APP0+1, - (unsigned char*)context->rawExif, context->rawExifLen); - } - - state->state++; - /* fall through */ - case 3: - - if (context->extra) { - /* copy extra buffer to output buffer */ - unsigned int n = context->extra_size - context->extra_offset; - if (n > context->destination.pub.free_in_buffer) - n = context->destination.pub.free_in_buffer; - memcpy(context->destination.pub.next_output_byte, - context->extra + context->extra_offset, n); - context->destination.pub.next_output_byte += n; - context->destination.pub.free_in_buffer -= n; - context->extra_offset += n; - if (context->extra_offset >= context->extra_size) + case 1: + + context->cinfo.image_width = state->xsize; + context->cinfo.image_height = state->ysize; + + switch (state->bits) { + case 8: + context->cinfo.input_components = 1; + context->cinfo.in_color_space = JCS_GRAYSCALE; + break; + case 24: + context->cinfo.input_components = 3; + if (strcmp(im->mode, "YCbCr") == 0) + context->cinfo.in_color_space = JCS_YCbCr; + else + context->cinfo.in_color_space = JCS_RGB; + break; + case 32: + context->cinfo.input_components = 4; + context->cinfo.in_color_space = JCS_CMYK; + #ifdef JCS_EXTENSIONS + if (strcmp(context->rawmode, "RGBX") == 0) + context->cinfo.in_color_space = JCS_EXT_RGBX; + #endif + break; + default: + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + /* Compressor configuration */ + jpeg_set_defaults(&context->cinfo); + + /* Use custom quantization tables */ + if (context->qtables) { + int i; + int quality = 100; + int last_q = 0; + if (context->quality != -1) { + quality = context->quality; + } + for (i = 0; i < context->qtablesLen; i++) { + // TODO: Should add support for none baseline + jpeg_add_quant_table(&context->cinfo, i, &context->qtables[i * DCTSIZE2], + quality, TRUE); + context->cinfo.comp_info[i].quant_tbl_no = i; + last_q = i; + } + if (context->qtablesLen == 1) { + // jpeg_set_defaults created two qtables internally, but we only wanted one. + jpeg_add_quant_table(&context->cinfo, 1, &context->qtables[0], + quality, TRUE); + } + for (i = last_q; i < context->cinfo.num_components; i++) { + context->cinfo.comp_info[i].quant_tbl_no = last_q; + } + } else if (context->quality != -1) { + jpeg_set_quality(&context->cinfo, context->quality, 1); + } + + /* Set subsampling options */ + switch (context->subsampling) + { + case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ + { + context->cinfo.comp_info[0].h_samp_factor = 1; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; + } + case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ + { + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; + } + case 2: /* 2x2, 1x1, 1x1 (4:2:0) : High */ + { + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 2; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; + } + default: + { + /* Use the lib's default */ + break; + } + } + if (context->progressive) + jpeg_simple_progression(&context->cinfo); + context->cinfo.smoothing_factor = context->smooth; + context->cinfo.optimize_coding = (boolean) context->optimize; + if (context->xdpi > 0 && context->ydpi > 0) { + context->cinfo.density_unit = 1; /* dots per inch */ + context->cinfo.X_density = context->xdpi; + context->cinfo.Y_density = context->ydpi; + } + switch (context->streamtype) { + case 1: + /* tables only -- not yet implemented */ + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + case 2: + /* image only */ + jpeg_suppress_tables(&context->cinfo, TRUE); + jpeg_start_compress(&context->cinfo, FALSE); + /* suppress extra section */ + context->extra_offset = context->extra_size; + break; + default: + /* interchange stream */ + jpeg_start_compress(&context->cinfo, TRUE); + break; + } + state->state++; + /* fall through */ + + case 2: + // check for exif len + 'APP1' header bytes + if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer){ + break; + } + //add exif header + if (context->rawExifLen > 0){ + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, + (unsigned char*)context->rawExif, context->rawExifLen); + } + + state->state++; + /* fall through */ + case 3: + + if (context->extra) { + /* copy extra buffer to output buffer */ + unsigned int n = context->extra_size - context->extra_offset; + if (n > context->destination.pub.free_in_buffer) + n = context->destination.pub.free_in_buffer; + memcpy(context->destination.pub.next_output_byte, + context->extra + context->extra_offset, n); + context->destination.pub.next_output_byte += n; + context->destination.pub.free_in_buffer -= n; + context->extra_offset += n; + if (context->extra_offset >= context->extra_size) + state->state++; + else + break; + } else state->state++; - else + + case 4: + if (1024 > context->destination.pub.free_in_buffer){ + break; + } + + ok = 1; + while (state->y < state->ysize) { + state->shuffle(state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); + ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1); + if (ok != 1) + break; + state->y++; + } + + if (ok != 1) break; - } else - state->state++; + state->state++; + /* fall through */ - case 4: - if (1024 > context->destination.pub.free_in_buffer){ + case 5: + + /* Finish compression */ + if (context->destination.pub.free_in_buffer < 100) + break; + jpeg_finish_compress(&context->cinfo); + + /* Clean up */ + if (context->extra) { + free(context->extra); + context->extra = NULL; + } + if (context->rawExif) { + free(context->rawExif); + context->rawExif = NULL; + } + if (context->qtables) { + free(context->qtables); + context->qtables = NULL; + } + + jpeg_destroy_compress(&context->cinfo); + /* if (jerr.pub.num_warnings) return BROKEN; */ + state->errcode = IMAGING_CODEC_END; break; - } - - ok = 1; - while (state->y < state->ysize) { - state->shuffle(state->buffer, - (UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->xsize); - ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1); - if (ok != 1) - break; - state->y++; - } - - if (ok != 1) - break; - state->state++; - /* fall through */ - - case 5: - - /* Finish compression */ - if (context->destination.pub.free_in_buffer < 100) - break; - jpeg_finish_compress(&context->cinfo); - - /* Clean up */ - if (context->extra) { - free(context->extra); - context->extra = NULL; - } - if (context->rawExif) { - free(context->rawExif); - context->rawExif = NULL; - } - if (context->qtables) { - free(context->qtables); - context->qtables = NULL; - } - - jpeg_destroy_compress(&context->cinfo); - /* if (jerr.pub.num_warnings) return BROKEN; */ - state->errcode = IMAGING_CODEC_END; - break; } diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c index 5cc7795a4be..22d5d642cfa 100644 --- a/src/libImaging/Matrix.c +++ b/src/libImaging/Matrix.c @@ -17,7 +17,7 @@ #include "Imaging.h" -#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8) v) +#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8) v) Imaging @@ -28,47 +28,47 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) /* Assume there's enough data in the buffer */ if (!im) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (strcmp(mode, "L") == 0 && im->bands == 3) { - imOut = ImagingNewDirty("L", im->xsize, im->ysize); - if (!imOut) - return NULL; + imOut = ImagingNewDirty("L", im->xsize, im->ysize); + if (!imOut) + return NULL; - for (y = 0; y < im->ysize; y++) { - UINT8* in = (UINT8*) im->image[y]; - UINT8* out = (UINT8*) imOut->image[y]; + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; - for (x = 0; x < im->xsize; x++) { - float v = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5; - out[x] = CLIPF(v); - in += 4; - } - } + for (x = 0; x < im->xsize; x++) { + float v = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5; + out[x] = CLIPF(v); + in += 4; + } + } } else if (strlen(mode) == 3 && im->bands == 3) { - imOut = ImagingNewDirty(mode, im->xsize, im->ysize); - if (!imOut) - return NULL; - - for (y = 0; y < im->ysize; y++) { - UINT8* in = (UINT8*) im->image[y]; - UINT8* out = (UINT8*) imOut->image[y]; - - for (x = 0; x < im->xsize; x++) { - float v0 = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5; - float v1 = m[4]*in[0] + m[5]*in[1] + m[6]*in[2] + m[7] + 0.5; - float v2 = m[8]*in[0] + m[9]*in[1] + m[10]*in[2] + m[11] + 0.5; - out[0] = CLIPF(v0); - out[1] = CLIPF(v1); - out[2] = CLIPF(v2); - in += 4; out += 4; - } - } + imOut = ImagingNewDirty(mode, im->xsize, im->ysize); + if (!imOut) + return NULL; + + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + + for (x = 0; x < im->xsize; x++) { + float v0 = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5; + float v1 = m[4]*in[0] + m[5]*in[1] + m[6]*in[2] + m[7] + 0.5; + float v2 = m[8]*in[0] + m[9]*in[1] + m[10]*in[2] + m[11] + 0.5; + out[0] = CLIPF(v0); + out[1] = CLIPF(v1); + out[2] = CLIPF(v2); + in += 4; out += 4; + } + } } else - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); return imOut; } diff --git a/src/libImaging/Negative.c b/src/libImaging/Negative.c index 4dedcb24517..fe08e4753d9 100644 --- a/src/libImaging/Negative.c +++ b/src/libImaging/Negative.c @@ -8,7 +8,7 @@ * FIXME: Maybe this should be implemented using ImagingPoint() * * history: - * 95-11-27 fl: Created + * 95-11-27 fl: Created * * Copyright (c) Fredrik Lundh 1995. * Copyright (c) Secret Labs AB 1997. @@ -27,15 +27,15 @@ ImagingNegative(Imaging im) int x, y; if (!im) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); if (!imOut) - return NULL; + return NULL; for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->linesize; x++) - imOut->image[y][x] = ~im->image[y][x]; + for (x = 0; x < im->linesize; x++) + imOut->image[y][x] = ~im->image[y][x]; return imOut; } diff --git a/src/libImaging/Offset.c b/src/libImaging/Offset.c index b3d9425fb65..0f9793c3c21 100644 --- a/src/libImaging/Offset.c +++ b/src/libImaging/Offset.c @@ -5,7 +5,7 @@ * offset an image in x and y directions * * history: - * 96-07-22 fl: Created + * 96-07-22 fl: Created * 98-11-01 cgw@pgt.com: Fixed negative-array index bug * * Copyright (c) Fredrik Lundh 1996. @@ -25,11 +25,11 @@ ImagingOffset(Imaging im, int xoffset, int yoffset) Imaging imOut; if (!im) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); if (!imOut) - return NULL; + return NULL; ImagingCopyPalette(imOut, im); @@ -37,25 +37,25 @@ ImagingOffset(Imaging im, int xoffset, int yoffset) xoffset %= im->xsize; xoffset = im->xsize - xoffset; if (xoffset < 0) - xoffset += im->xsize; + xoffset += im->xsize; yoffset %= im->ysize; yoffset = im->ysize - yoffset; if (yoffset < 0) - yoffset += im->ysize; + yoffset += im->ysize; -#define OFFSET(image)\ +#define OFFSET(image)\ for (y = 0; y < im->ysize; y++)\ - for (x = 0; x < im->xsize; x++) {\ - int yi = (y + yoffset) % im->ysize;\ - int xi = (x + xoffset) % im->xsize;\ - imOut->image[y][x] = im->image[yi][xi];\ - } + for (x = 0; x < im->xsize; x++) {\ + int yi = (y + yoffset) % im->ysize;\ + int xi = (x + xoffset) % im->xsize;\ + imOut->image[y][x] = im->image[yi][xi];\ + } if (im->image8) - OFFSET(image8) + OFFSET(image8) else - OFFSET(image32) + OFFSET(image32) return imOut; } diff --git a/src/libImaging/PackDecode.c b/src/libImaging/PackDecode.c index ef54f3c9a4f..b9c846f4b04 100644 --- a/src/libImaging/PackDecode.c +++ b/src/libImaging/PackDecode.c @@ -5,7 +5,7 @@ * decoder for PackBits image data. * * history: - * 96-04-19 fl Created + * 96-04-19 fl Created * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -18,7 +18,7 @@ int ImagingPackbitsDecode(Imaging im, ImagingCodecState state, - UINT8* buf, Py_ssize_t bytes) + UINT8* buf, Py_ssize_t bytes) { UINT8 n; UINT8* ptr; @@ -28,65 +28,65 @@ ImagingPackbitsDecode(Imaging im, ImagingCodecState state, for (;;) { - if (bytes < 1) - return ptr - buf; + if (bytes < 1) + return ptr - buf; - if (ptr[0] & 0x80) { + if (ptr[0] & 0x80) { - if (ptr[0] == 0x80) { - /* Nop */ - ptr++; bytes--; - continue; - } + if (ptr[0] == 0x80) { + /* Nop */ + ptr++; bytes--; + continue; + } - /* Run */ - if (bytes < 2) - return ptr - buf; + /* Run */ + if (bytes < 2) + return ptr - buf; - for (n = 257 - ptr[0]; n > 0; n--) { - if (state->x >= state->bytes) { - /* state->errcode = IMAGING_CODEC_OVERRUN; */ - break; - } - state->buffer[state->x++] = ptr[1]; - } + for (n = 257 - ptr[0]; n > 0; n--) { + if (state->x >= state->bytes) { + /* state->errcode = IMAGING_CODEC_OVERRUN; */ + break; + } + state->buffer[state->x++] = ptr[1]; + } - ptr += 2; bytes -= 2; + ptr += 2; bytes -= 2; - } else { + } else { - /* Literal */ - n = ptr[0]+2; + /* Literal */ + n = ptr[0]+2; - if (bytes < n) - return ptr - buf; + if (bytes < n) + return ptr - buf; - for (i = 1; i < n; i++) { - if (state->x >= state->bytes) { - /* state->errcode = IMAGING_CODEC_OVERRUN; */ - break; - } - state->buffer[state->x++] = ptr[i]; - } + for (i = 1; i < n; i++) { + if (state->x >= state->bytes) { + /* state->errcode = IMAGING_CODEC_OVERRUN; */ + break; + } + state->buffer[state->x++] = ptr[i]; + } - ptr += n; bytes -= n; + ptr += n; bytes -= n; - } + } - if (state->x >= state->bytes) { + if (state->x >= state->bytes) { - /* Got a full line, unpack it */ - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->buffer, - state->xsize); + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); - state->x = 0; + state->x = 0; - if (++state->y >= state->ysize) { - /* End of file (errcode = 0) */ - return -1; - } - } + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } } } diff --git a/src/libImaging/PcdDecode.c b/src/libImaging/PcdDecode.c index 8ff264edfc8..6f86dc96b35 100644 --- a/src/libImaging/PcdDecode.c +++ b/src/libImaging/PcdDecode.c @@ -5,13 +5,13 @@ * decoder for uncompressed PCD image data. * * history: - * 96-05-10 fl Created - * 96-05-18 fl New tables - * 97-01-25 fl Use PhotoYCC unpacker + * 96-05-10 fl Created + * 96-05-18 fl New tables + * 97-01-25 fl Use PhotoYCC unpacker * * notes: - * This driver supports uncompressed PCD modes only - * (resolutions up to 768x512). + * This driver supports uncompressed PCD modes only + * (resolutions up to 768x512). * * Copyright (c) Fredrik Lundh 1996-97. * Copyright (c) Secret Labs AB 1997. @@ -37,42 +37,42 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { - /* We need data for two full lines before we can do anything */ - if (bytes < chunk) - return ptr - buf; - - /* Unpack first line */ - out = state->buffer; - for (x = 0; x < state->xsize; x++) { - out[0] = ptr[x]; - out[1] = ptr[(x+4*state->xsize)/2]; - out[2] = ptr[(x+5*state->xsize)/2]; - out += 3; - } - - state->shuffle((UINT8*) im->image[state->y], - state->buffer, state->xsize); - - if (++state->y >= state->ysize) - return -1; /* This can hardly happen */ - - /* Unpack second line */ - out = state->buffer; - for (x = 0; x < state->xsize; x++) { - out[0] = ptr[x+state->xsize]; - out[1] = ptr[(x+4*state->xsize)/2]; - out[2] = ptr[(x+5*state->xsize)/2]; - out += 3; - } - - state->shuffle((UINT8*) im->image[state->y], - state->buffer, state->xsize); - - if (++state->y >= state->ysize) - return -1; - - ptr += chunk; - bytes -= chunk; + /* We need data for two full lines before we can do anything */ + if (bytes < chunk) + return ptr - buf; + + /* Unpack first line */ + out = state->buffer; + for (x = 0; x < state->xsize; x++) { + out[0] = ptr[x]; + out[1] = ptr[(x+4*state->xsize)/2]; + out[2] = ptr[(x+5*state->xsize)/2]; + out += 3; + } + + state->shuffle((UINT8*) im->image[state->y], + state->buffer, state->xsize); + + if (++state->y >= state->ysize) + return -1; /* This can hardly happen */ + + /* Unpack second line */ + out = state->buffer; + for (x = 0; x < state->xsize; x++) { + out[0] = ptr[x+state->xsize]; + out[1] = ptr[(x+4*state->xsize)/2]; + out[2] = ptr[(x+5*state->xsize)/2]; + out += 3; + } + + state->shuffle((UINT8*) im->image[state->y], + state->buffer, state->xsize); + + if (++state->y >= state->ysize) + return -1; + + ptr += chunk; + bytes -= chunk; } } diff --git a/src/libImaging/PcxDecode.c b/src/libImaging/PcxDecode.c index e5a38f4bec1..a4e40864b4d 100644 --- a/src/libImaging/PcxDecode.c +++ b/src/libImaging/PcxDecode.c @@ -5,7 +5,7 @@ * decoder for PCX image data. * * history: - * 95-09-14 fl Created + * 95-09-14 fl Created * * Copyright (c) Fredrik Lundh 1995. * Copyright (c) Secret Labs AB 1997. @@ -31,59 +31,59 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { - if (bytes < 1) - return ptr - buf; + if (bytes < 1) + return ptr - buf; - if ((*ptr & 0xC0) == 0xC0) { + if ((*ptr & 0xC0) == 0xC0) { - /* Run */ - if (bytes < 2) - return ptr - buf; + /* Run */ + if (bytes < 2) + return ptr - buf; - n = ptr[0] & 0x3F; + n = ptr[0] & 0x3F; - while (n > 0) { - if (state->x >= state->bytes) { - state->errcode = IMAGING_CODEC_OVERRUN; - break; - } - state->buffer[state->x++] = ptr[1]; - n--; - } + while (n > 0) { + if (state->x >= state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + break; + } + state->buffer[state->x++] = ptr[1]; + n--; + } + + ptr += 2; bytes -= 2; - ptr += 2; bytes -= 2; + } else { - } else { + /* Literal */ + state->buffer[state->x++] = ptr[0]; + ptr++; bytes--; - /* Literal */ - state->buffer[state->x++] = ptr[0]; - ptr++; bytes--; + } + + if (state->x >= state->bytes) { + if (state->bytes % state->xsize && state->bytes > state->xsize) { + int bands = state->bytes / state->xsize; + int stride = state->bytes / bands; + int i; + for (i=1; i< bands; i++) { // note -- skipping first band + memmove(&state->buffer[i*state->xsize], + &state->buffer[i*stride], + state->xsize); + } + } + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); - } + state->x = 0; - if (state->x >= state->bytes) { - if (state->bytes % state->xsize && state->bytes > state->xsize) { - int bands = state->bytes / state->xsize; - int stride = state->bytes / bands; - int i; - for (i=1; i< bands; i++) { // note -- skipping first band - memmove(&state->buffer[i*state->xsize], - &state->buffer[i*stride], - state->xsize); + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; } } - /* Got a full line, unpack it */ - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->buffer, - state->xsize); - - state->x = 0; - - if (++state->y >= state->ysize) { - /* End of file (errcode = 0) */ - return -1; - } - } } } diff --git a/src/libImaging/Point.c b/src/libImaging/Point.c index 9b4bf6b754b..a80b9e7d2fc 100644 --- a/src/libImaging/Point.c +++ b/src/libImaging/Point.c @@ -139,7 +139,7 @@ ImagingPoint(Imaging imIn, const char* mode, const void* table) void (*point)(Imaging imIn, Imaging imOut, im_point_context* context); if (!imIn) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (!mode) mode = imIn->mode; @@ -152,7 +152,7 @@ ImagingPoint(Imaging imIn, const char* mode, const void* table) imOut = ImagingNew(mode, imIn->xsize, imIn->ysize); if (!imOut) - return NULL; + return NULL; /* find appropriate handler */ if (imIn->type == IMAGING_TYPE_UINT8) { @@ -210,11 +210,11 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) if (!imIn || (strcmp(imIn->mode, "I") != 0 && strcmp(imIn->mode, "I;16") != 0 && strcmp(imIn->mode, "F") != 0)) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) - return NULL; + return NULL; switch (imIn->type) { case IMAGING_TYPE_INT32: @@ -254,7 +254,7 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) } ImagingSectionLeave(&cookie); break; - } + } /* FALL THROUGH */ default: ImagingDelete(imOut); diff --git a/src/libImaging/RawDecode.c b/src/libImaging/RawDecode.c index c069bdb8864..5e95905cc0b 100644 --- a/src/libImaging/RawDecode.c +++ b/src/libImaging/RawDecode.c @@ -5,7 +5,7 @@ * decoder for raw (uncompressed) image data * * history: - * 96-03-07 fl rewritten + * 96-03-07 fl rewritten * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -29,28 +29,28 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (state->state == 0) { - /* Initialize context variables */ - - /* get size of image data and padding */ - state->bytes = (state->xsize * state->bits + 7) / 8; - if (rawstate->stride) { - rawstate->skip = rawstate->stride - state->bytes; - if (rawstate->skip < 0) { - state->errcode = IMAGING_CODEC_CONFIG; - return -1; - } - } else { - rawstate->skip = 0; - } - - /* check image orientation */ - if (state->ystep < 0) { - state->y = state->ysize-1; - state->ystep = -1; - } else - state->ystep = 1; - - state->state = LINE; + /* Initialize context variables */ + + /* get size of image data and padding */ + state->bytes = (state->xsize * state->bits + 7) / 8; + if (rawstate->stride) { + rawstate->skip = rawstate->stride - state->bytes; + if (rawstate->skip < 0) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + } else { + rawstate->skip = 0; + } + + /* check image orientation */ + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; + + state->state = LINE; } @@ -58,38 +58,38 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { - if (state->state == SKIP) { + if (state->state == SKIP) { - /* Skip padding between lines */ + /* Skip padding between lines */ - if (bytes < rawstate->skip) - return ptr - buf; + if (bytes < rawstate->skip) + return ptr - buf; - ptr += rawstate->skip; - bytes -= rawstate->skip; + ptr += rawstate->skip; + bytes -= rawstate->skip; - state->state = LINE; + state->state = LINE; - } + } - if (bytes < state->bytes) - return ptr - buf; + if (bytes < state->bytes) + return ptr - buf; - /* Unpack data */ - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, ptr, state->xsize); + /* Unpack data */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, ptr, state->xsize); - ptr += state->bytes; - bytes -= state->bytes; + ptr += state->bytes; + bytes -= state->bytes; - state->y += state->ystep; + state->y += state->ystep; - if (state->y < 0 || state->y >= state->ysize) { - /* End of file (errcode = 0) */ - return -1; - } + if (state->y < 0 || state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } - state->state = SKIP; + state->state = SKIP; } diff --git a/src/libImaging/RawEncode.c b/src/libImaging/RawEncode.c index a3b74b8cffc..da614a24b62 100644 --- a/src/libImaging/RawEncode.c +++ b/src/libImaging/RawEncode.c @@ -9,8 +9,8 @@ * in ImageFile.py, but it should be solved here instead. * * history: - * 96-04-30 fl created - * 97-01-03 fl fixed padding + * 96-04-30 fl created + * 97-01-03 fl fixed padding * * Copyright (c) Fredrik Lundh 1996-97. * Copyright (c) Secret Labs AB 1997. @@ -27,60 +27,60 @@ ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (!state->state) { - /* The "count" field holds the stride, if specified. Fix - things up so "bytes" is the full size, and "count" the - packed size */ + /* The "count" field holds the stride, if specified. Fix + things up so "bytes" is the full size, and "count" the + packed size */ - if (state->count > 0) { - int bytes = state->count; + if (state->count > 0) { + int bytes = state->count; - /* stride must not be less than real size */ - if (state->count < state->bytes) { - state->errcode = IMAGING_CODEC_CONFIG; - return -1; - } - state->count = state->bytes; - state->bytes = bytes; - } else - state->count = state->bytes; + /* stride must not be less than real size */ + if (state->count < state->bytes) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + state->count = state->bytes; + state->bytes = bytes; + } else + state->count = state->bytes; - /* The "ystep" field specifies the orientation */ + /* The "ystep" field specifies the orientation */ - if (state->ystep < 0) { - state->y = state->ysize-1; - state->ystep = -1; - } else - state->ystep = 1; + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; - state->state = 1; + state->state = 1; } if (bytes < state->bytes) { - state->errcode = IMAGING_CODEC_CONFIG; - return 0; + state->errcode = IMAGING_CODEC_CONFIG; + return 0; } ptr = buf; while (bytes >= state->bytes) { - state->shuffle(ptr, (UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->xsize); + state->shuffle(ptr, (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); - if (state->bytes > state->count) - /* zero-pad the buffer, if necessary */ - memset(ptr + state->count, 0, state->bytes - state->count); + if (state->bytes > state->count) + /* zero-pad the buffer, if necessary */ + memset(ptr + state->count, 0, state->bytes - state->count); - ptr += state->bytes; - bytes -= state->bytes; + ptr += state->bytes; + bytes -= state->bytes; - state->y += state->ystep; + state->y += state->ystep; - if (state->y < 0 || state->y >= state->ysize) { - state->errcode = IMAGING_CODEC_END; - break; - } + if (state->y < 0 || state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_END; + break; + } } diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c index 3f9400a5bf9..2d903cc047b 100644 --- a/src/libImaging/SgiRleDecode.c +++ b/src/libImaging/SgiRleDecode.c @@ -6,7 +6,7 @@ * * history: * 2017-07-28 mb fixed for images larger than 64KB - * 2017-07-20 mb created + * 2017-07-20 mb created * * Copyright (c) Mickael Bonfill 2017. * @@ -101,7 +101,7 @@ static int expandrow2(UINT8* dest, const UINT8* src, int n, int z, int xsize) int ImagingSgiRleDecode(Imaging im, ImagingCodecState state, - UINT8* buf, Py_ssize_t bytes) + UINT8* buf, Py_ssize_t bytes) { UINT8 *ptr; SGISTATE *c; diff --git a/src/libImaging/TgaRleDecode.c b/src/libImaging/TgaRleDecode.c index d1971e54623..bdbf27a14bc 100644 --- a/src/libImaging/TgaRleDecode.c +++ b/src/libImaging/TgaRleDecode.c @@ -5,8 +5,8 @@ * decoder for Targa RLE data. * * history: - * 97-01-04 fl created - * 98-09-11 fl don't one byte per pixel; take orientation into account + * 97-01-04 fl created + * 98-09-11 fl don't one byte per pixel; take orientation into account * * Copyright (c) Fredrik Lundh 1997. * Copyright (c) Secret Labs AB 1997-98. @@ -20,7 +20,7 @@ int ImagingTgaRleDecode(Imaging im, ImagingCodecState state, - UINT8* buf, Py_ssize_t bytes) + UINT8* buf, Py_ssize_t bytes) { int n, depth; UINT8* ptr; @@ -29,14 +29,14 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, if (state->state == 0) { - /* check image orientation */ - if (state->ystep < 0) { - state->y = state->ysize-1; - state->ystep = -1; - } else - state->ystep = 1; + /* check image orientation */ + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; - state->state = 1; + state->state = 1; } @@ -44,22 +44,22 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, for (;;) { - if (bytes < 1) - return ptr - buf; + if (bytes < 1) + return ptr - buf; - if (ptr[0] & 0x80) { + if (ptr[0] & 0x80) { - /* Run (1 + pixelsize bytes) */ + /* Run (1 + pixelsize bytes) */ - if (bytes < 1 + depth) - break; + if (bytes < 1 + depth) + break; - n = depth * ((ptr[0] & 0x7f) + 1); + n = depth * ((ptr[0] & 0x7f) + 1); - if (state->x + n > state->bytes) { - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } + if (state->x + n > state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } if (depth == 1) memset(state->buffer + state->x, ptr[1], n); @@ -70,38 +70,38 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, } ptr += 1 + depth; - bytes -= 1 + depth; + bytes -= 1 + depth; - } else { + } else { - /* Literal (1+n+1 bytes block) */ - n = depth * (ptr[0] + 1); + /* Literal (1+n+1 bytes block) */ + n = depth * (ptr[0] + 1); - if (bytes < 1 + n) - break; + if (bytes < 1 + n) + break; - if (state->x + n > state->bytes) { - state->errcode = IMAGING_CODEC_OVERRUN; - return -1; - } + if (state->x + n > state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } - memcpy(state->buffer + state->x, ptr + 1, n); + memcpy(state->buffer + state->x, ptr + 1, n); - ptr += 1 + n; - bytes -= 1 + n; + ptr += 1 + n; + bytes -= 1 + n; - } + } - state->x += n; + state->x += n; - if (state->x >= state->bytes) { + if (state->x >= state->bytes) { - /* Got a full line, unpack it */ - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->buffer, - state->xsize); + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); - state->x = 0; + state->x = 0; state->y += state->ystep; @@ -110,7 +110,7 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, return -1; } - } + } } diff --git a/src/libImaging/TiffDecode.h b/src/libImaging/TiffDecode.h index 08ef35cfd3c..3d27e65b676 100644 --- a/src/libImaging/TiffDecode.h +++ b/src/libImaging/TiffDecode.h @@ -28,17 +28,17 @@ #define _PIL_LIBTIFF_ typedef struct { - tdata_t data; /* tdata_t == void* */ - toff_t loc; /* toff_t == uint32 */ - tsize_t size; /* tsize_t == int32 */ - int fp; + tdata_t data; /* tdata_t == void* */ + toff_t loc; /* toff_t == uint32 */ + tsize_t size; /* tsize_t == int32 */ + int fp; uint32 ifd; /* offset of the ifd, used for multipage * Should be uint32 for libtiff 3.9.x * uint64 for libtiff 4.0.x */ - TIFF *tiff; /* Used in write */ - toff_t eof; - int flrealloc;/* may we realloc */ + TIFF *tiff; /* Used in write */ + toff_t eof; + int flrealloc;/* may we realloc */ } TIFFSTATE; @@ -55,7 +55,7 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); */ /* -#define VA_ARGS(...) __VA_ARGS__ +#define VA_ARGS(...) __VA_ARGS__ #define TRACE(args) fprintf(stderr, VA_ARGS args) */ diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index adf2dd27700..6235937d17b 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -998,7 +998,7 @@ unpackI16N_I16B(UINT8* out, const UINT8* in, int pixels){ UINT8* tmp = (UINT8*) out; for (i = 0; i < pixels; i++) { C16B; - in += 2; tmp += 2; + in += 2; tmp += 2; } } @@ -1008,7 +1008,7 @@ unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){ UINT8* tmp = (UINT8*) out; for (i = 0; i < pixels; i++) { C16L; - in += 2; tmp += 2; + in += 2; tmp += 2; } } @@ -1053,7 +1053,7 @@ unpackI12_I16(UINT8* out, const UINT8* in, int pixels){ memcpy(out, &pixel, sizeof(pixel)); #endif - in += 3; out+=2; + in += 3; out+=2; } if (i == pixels-1) { pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4); @@ -1306,7 +1306,7 @@ static struct { /* greyscale w. alpha */ {"LA", "LA", 16, unpackLA}, {"LA", "LA;L", 16, unpackLAL}, - + /* greyscale w. alpha premultiplied */ {"La", "La", 16, unpackLA}, @@ -1447,16 +1447,16 @@ static struct { {"YCbCr", "YCbCrK", 32, copy4}, /* LAB Color */ - {"LAB", "LAB", 24, ImagingUnpackLAB}, - {"LAB", "L", 8, band0}, - {"LAB", "A", 8, band1}, - {"LAB", "B", 8, band2}, + {"LAB", "LAB", 24, ImagingUnpackLAB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, + {"LAB", "B", 8, band2}, /* HSV Color */ - {"HSV", "HSV", 24, ImagingUnpackRGB}, - {"HSV", "H", 8, band0}, - {"HSV", "S", 8, band1}, - {"HSV", "V", 8, band2}, + {"HSV", "HSV", 24, ImagingUnpackRGB}, + {"HSV", "H", 8, band0}, + {"HSV", "S", 8, band1}, + {"HSV", "V", 8, band2}, /* integer variations */ {"I", "I", 32, copy4}, @@ -1505,11 +1505,11 @@ static struct { {"I;16B", "I;16B", 16, copy2}, {"I;16L", "I;16L", 16, copy2}, - {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. - {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. - {"I;16B", "I;16N", 16, unpackI16N_I16B}, + {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16B", "I;16N", 16, unpackI16N_I16B}, - {"I;16", "I;12", 12, unpackI12_I16}, // 12 bit Tiffs stored in 16bits. + {"I;16", "I;12", 12, unpackI12_I16}, // 12 bit Tiffs stored in 16bits. {NULL} /* sentinel */ }; diff --git a/src/libImaging/UnpackYCC.c b/src/libImaging/UnpackYCC.c index 19da1f65443..d6bce17ad02 100644 --- a/src/libImaging/UnpackYCC.c +++ b/src/libImaging/UnpackYCC.c @@ -5,7 +5,7 @@ * code to convert and unpack PhotoYCC data * * history: - * 97-01-25 fl Moved from PcdDecode.c + * 97-01-25 fl Moved from PcdDecode.c * * Copyright (c) Fredrik Lundh 1996-97. * Copyright (c) Secret Labs AB 1997. @@ -116,12 +116,12 @@ static INT16 GR[] = { 127, 126, 125, 124, 123, 122, 121, 121, 120, 119, -85, -86, -87, -88, -89, -90, -91, -92, -93, -94, -94, -95, -96, -97, -98, -99, -100, -101, -102, -103, -104, -105, -106, -107, -107, -108 }; -#define R 0 -#define G 1 -#define B 2 -#define A 3 +#define R 0 +#define G 1 +#define B 2 +#define A 3 -#define YCC2RGB(rgb, y, cb, cr) {\ +#define YCC2RGB(rgb, y, cb, cr) {\ int l = L[y];\ int r = l + CR[cr];\ int g = l + GR[cr] + GB[cb];\ @@ -137,9 +137,9 @@ ImagingUnpackYCC(UINT8* out, const UINT8* in, int pixels) int i; /* PhotoYCC triplets */ for (i = 0; i < pixels; i++) { - YCC2RGB(out, in[0], in[1], in[2]); - out[A] = 255; - out += 4; in += 3; + YCC2RGB(out, in[0], in[1], in[2]); + out[A] = 255; + out += 4; in += 3; } } @@ -149,14 +149,14 @@ ImagingUnpackYCCA(UINT8* out, const UINT8* in, int pixels) int i; /* PhotoYCC triplets plus premultiplied alpha */ for (i = 0; i < pixels; i++) { - /* Divide by alpha */ - UINT8 rgb[3]; - rgb[0] = (in[3] == 0) ? 0 : (((int) in[0] * 255) / in[3]); - rgb[1] = (in[3] == 0) ? 0 : (((int) in[1] * 255) / in[3]); - rgb[2] = (in[3] == 0) ? 0 : (((int) in[2] * 255) / in[3]); - /* Convert non-multiplied data to RGB */ - YCC2RGB(out, rgb[0], rgb[1], rgb[2]); - out[A] = in[3]; - out += 4; in += 4; + /* Divide by alpha */ + UINT8 rgb[3]; + rgb[0] = (in[3] == 0) ? 0 : (((int) in[0] * 255) / in[3]); + rgb[1] = (in[3] == 0) ? 0 : (((int) in[1] * 255) / in[3]); + rgb[2] = (in[3] == 0) ? 0 : (((int) in[2] * 255) / in[3]); + /* Convert non-multiplied data to RGB */ + YCC2RGB(out, rgb[0], rgb[1], rgb[2]); + out[A] = in[3]; + out += 4; in += 4; } } diff --git a/src/libImaging/XbmDecode.c b/src/libImaging/XbmDecode.c index 75b4961abaa..088c71df3d1 100644 --- a/src/libImaging/XbmDecode.c +++ b/src/libImaging/XbmDecode.c @@ -5,7 +5,7 @@ * decoder for XBM hex image data * * history: - * 96-04-13 fl Created + * 96-04-13 fl Created * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -16,9 +16,9 @@ #include "Imaging.h" -#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\ - (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\ - (v >= 'A' && v <= 'F') ? v - 'A' + 10 : 0) +#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\ + (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\ + (v >= 'A' && v <= 'F') ? v - 'A' + 10 : 0) int ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t bytes) @@ -28,53 +28,53 @@ ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt UINT8* ptr; if (!state->state) - state->state = SKIP; + state->state = SKIP; ptr = buf; for (;;) { - if (state->state == SKIP) { + if (state->state == SKIP) { - /* Skip forward until next 'x' */ + /* Skip forward until next 'x' */ - while (bytes > 0) { - if (*ptr == 'x') - break; - ptr++; - bytes--; - } + while (bytes > 0) { + if (*ptr == 'x') + break; + ptr++; + bytes--; + } - if (bytes == 0) - return ptr - buf; + if (bytes == 0) + return ptr - buf; - state->state = BYTE; + state->state = BYTE; - } + } - if (bytes < 3) - return ptr - buf; + if (bytes < 3) + return ptr - buf; - state->buffer[state->x] = (HEX(ptr[1])<<4) + HEX(ptr[2]); + state->buffer[state->x] = (HEX(ptr[1])<<4) + HEX(ptr[2]); - if (++state->x >= state->bytes) { + if (++state->x >= state->bytes) { - /* Got a full line, unpack it */ - state->shuffle((UINT8*) im->image[state->y], state->buffer, - state->xsize); + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y], state->buffer, + state->xsize); - state->x = 0; + state->x = 0; - if (++state->y >= state->ysize) { - /* End of file (errcode = 0) */ - return -1; - } - } + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } - ptr += 3; - bytes -= 3; + ptr += 3; + bytes -= 3; - state->state = SKIP; + state->state = SKIP; } diff --git a/src/libImaging/XbmEncode.c b/src/libImaging/XbmEncode.c index e066fd6b583..cf72da61958 100644 --- a/src/libImaging/XbmEncode.c +++ b/src/libImaging/XbmEncode.c @@ -5,7 +5,7 @@ * encoder for Xbm data * * history: - * 96-11-01 fl created + * 96-11-01 fl created * * Copyright (c) Fredrik Lundh 1996. * Copyright (c) Secret Labs AB 1997. @@ -27,79 +27,79 @@ ImagingXbmEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (!state->state) { - /* 8 pixels are stored in no more than 6 bytes */ - state->bytes = 6*(state->xsize+7)/8; + /* 8 pixels are stored in no more than 6 bytes */ + state->bytes = 6*(state->xsize+7)/8; - state->state = 1; + state->state = 1; } if (bytes < state->bytes) { - state->errcode = IMAGING_CODEC_MEMORY; - return 0; + state->errcode = IMAGING_CODEC_MEMORY; + return 0; } ptr = buf; while (bytes >= state->bytes) { - state->shuffle(state->buffer, - (UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->xsize); + state->shuffle(state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); - if (state->y < state->ysize-1) { + if (state->y < state->ysize-1) { - /* any line but the last */ - for (n = 0; n < state->xsize; n += 8) { + /* any line but the last */ + for (n = 0; n < state->xsize; n += 8) { - i = state->buffer[n/8]; + i = state->buffer[n/8]; - *ptr++ = '0'; - *ptr++ = 'x'; - *ptr++ = hex[(i>>4)&15]; - *ptr++ = hex[i&15]; - *ptr++ = ','; - bytes -= 5; + *ptr++ = '0'; + *ptr++ = 'x'; + *ptr++ = hex[(i>>4)&15]; + *ptr++ = hex[i&15]; + *ptr++ = ','; + bytes -= 5; - if (++state->count >= 79/5) { - *ptr++ = '\n'; - bytes--; - state->count = 0; - } + if (++state->count >= 79/5) { + *ptr++ = '\n'; + bytes--; + state->count = 0; + } - } + } - state->y++; + state->y++; - } else { + } else { - /* last line */ - for (n = 0; n < state->xsize; n += 8) { + /* last line */ + for (n = 0; n < state->xsize; n += 8) { - i = state->buffer[n/8]; + i = state->buffer[n/8]; - *ptr++ = '0'; - *ptr++ = 'x'; - *ptr++ = hex[(i>>4)&15]; - *ptr++ = hex[i&15]; + *ptr++ = '0'; + *ptr++ = 'x'; + *ptr++ = hex[(i>>4)&15]; + *ptr++ = hex[i&15]; - if (n < state->xsize-8) { - *ptr++ = ','; - if (++state->count >= 79/5) { - *ptr++ = '\n'; - bytes--; - state->count = 0; - } - } else - *ptr++ = '\n'; + if (n < state->xsize-8) { + *ptr++ = ','; + if (++state->count >= 79/5) { + *ptr++ = '\n'; + bytes--; + state->count = 0; + } + } else + *ptr++ = '\n'; - bytes -= 5; + bytes -= 5; - } + } - state->errcode = IMAGING_CODEC_END; - break; - } + state->errcode = IMAGING_CODEC_END; + break; + } } return ptr - buf; diff --git a/src/libImaging/Zip.h b/src/libImaging/Zip.h index 21a336f908a..b05c93bb402 100644 --- a/src/libImaging/Zip.h +++ b/src/libImaging/Zip.h @@ -12,10 +12,10 @@ /* modes */ -#define ZIP_PNG 0 /* continuous, filtered image data */ -#define ZIP_PNG_PALETTE 1 /* non-continuous data, disable filtering */ -#define ZIP_TIFF_PREDICTOR 2 /* TIFF, with predictor */ -#define ZIP_TIFF 3 /* TIFF, without predictor */ +#define ZIP_PNG 0 /* continuous, filtered image data */ +#define ZIP_PNG_PALETTE 1 /* non-continuous data, disable filtering */ +#define ZIP_TIFF_PREDICTOR 2 /* TIFF, with predictor */ +#define ZIP_TIFF 3 /* TIFF, without predictor */ typedef struct { @@ -39,24 +39,24 @@ typedef struct { /* PRIVATE CONTEXT (set by decoder/encoder) */ - z_stream z_stream; /* (de)compression stream */ + z_stream z_stream; /* (de)compression stream */ - UINT8* previous; /* previous line (allocated) */ + UINT8* previous; /* previous line (allocated) */ - int last_output; /* # bytes last output by inflate */ + int last_output; /* # bytes last output by inflate */ /* Compressor specific stuff */ - UINT8* prior; /* filter storage (allocated) */ + UINT8* prior; /* filter storage (allocated) */ UINT8* up; UINT8* average; UINT8* paeth; - UINT8* output; /* output data */ + UINT8* output; /* output data */ - int prefix; /* size of filter prefix (0 for TIFF data) */ + int prefix; /* size of filter prefix (0 for TIFF data) */ - int interlaced; /* is the image interlaced? (PNG) */ + int interlaced; /* is the image interlaced? (PNG) */ - int pass; /* current pass of the interlaced image (PNG) */ + int pass; /* current pass of the interlaced image (PNG) */ } ZIPSTATE; diff --git a/src/outline.c b/src/outline.c index 25e63aeaf56..8a7cc32adec 100644 --- a/src/outline.c +++ b/src/outline.c @@ -23,7 +23,7 @@ /* -------------------------------------------------------------------- */ -/* Class */ +/* Class */ typedef struct { PyObject_HEAD @@ -44,7 +44,7 @@ _outline_new(void) self = PyObject_New(OutlineObject, &OutlineType); if (self == NULL) - return NULL; + return NULL; self->outline = ImagingOutlineNew(); @@ -69,7 +69,7 @@ PyOutline_AsOutline(PyObject* outline) /* -------------------------------------------------------------------- */ -/* Factories */ +/* Factories */ PyObject* PyOutline_Create(PyObject* self, PyObject* args) @@ -82,14 +82,14 @@ PyOutline_Create(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* Methods */ +/* Methods */ static PyObject* _outline_move(OutlineObject* self, PyObject* args) { float x0, y0; if (!PyArg_ParseTuple(args, "ff", &x0, &y0)) - return NULL; + return NULL; ImagingOutlineMove(self->outline, x0, y0); @@ -102,7 +102,7 @@ _outline_line(OutlineObject* self, PyObject* args) { float x1, y1; if (!PyArg_ParseTuple(args, "ff", &x1, &y1)) - return NULL; + return NULL; ImagingOutlineLine(self->outline, x1, y1); @@ -115,7 +115,7 @@ _outline_curve(OutlineObject* self, PyObject* args) { float x1, y1, x2, y2, x3, y3; if (!PyArg_ParseTuple(args, "ffffff", &x1, &y1, &x2, &y2, &x3, &y3)) - return NULL; + return NULL; ImagingOutlineCurve(self->outline, x1, y1, x2, y2, x3, y3); @@ -158,35 +158,35 @@ static struct PyMethodDef _outline_methods[] = { }; static PyTypeObject OutlineType = { - PyVarObject_HEAD_INIT(NULL, 0) - "Outline", /*tp_name*/ - sizeof(OutlineObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)_outline_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - _outline_methods, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ + PyVarObject_HEAD_INIT(NULL, 0) + "Outline", /*tp_name*/ + sizeof(OutlineObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_outline_dealloc,/*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _outline_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; From 94c7af7596e984a40a9bb7ea373ac336e55a9d36 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 1 May 2020 21:23:39 +0300 Subject: [PATCH 095/262] Replace spaces with tabs and add to pre-commit linting --- .pre-commit-config.yaml | 6 ++ CHANGES.rst | 58 +++++++++---------- depends/install_extra_test_images.sh | 12 ++-- .../writing-your-own-file-decoder.rst | 4 +- docs/reference/Image.rst | 4 +- docs/reference/ImageCms.rst | 10 ++-- 6 files changed, 50 insertions(+), 44 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e30453c3ed2..c3939a0035f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,3 +30,9 @@ repos: hooks: - id: check-merge-conflict - id: check-yaml + + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.7 + hooks: + - id: remove-tabs + exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) diff --git a/CHANGES.rst b/CHANGES.rst index 2b3141ff367..6ac9af43971 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5310,23 +5310,23 @@ Pre-fork + Added keyword options to the "save" method. The following options are currently supported: - format option description + Format Option Description -------------------------------------------------------- - JPEG optimize minimize output file at the - expense of compression speed. + JPEG optimize Minimize output file at the + expense of compression speed. - JPEG progressive enable progressive output. the - option value is ignored. + JPEG progressive Enable progressive output. + The option value is ignored. - JPEG quality set compression quality (1-100). - the default value is 75. + JPEG quality Set compression quality (1-100). + The default value is 75. - JPEG smooth smooth dithered images. value - is strength (1-100). default is - off (0). + JPEG smooth Smooth dithered images. + Value is strength (1-100). + Default is off (0). - PNG optimize minimize output file at the - expense of compression speed. + PNG optimize Minimize output file at the + expense of compression speed. Expect more options in future releases. Also note that file writers silently ignore unknown options. @@ -5347,31 +5347,31 @@ Pre-fork + Various improvements to the sample scripts: "pilconvert" Carries out some extra tricks in order to make - the resulting file as small as possible. + the resulting file as small as possible. - "explode" (NEW) Split an image sequence into individual frames. + "explode" (NEW) Split an image sequence into individual frames. - "gifmaker" (NEW) Convert a sequence file into a GIF animation. - Note that the GIF encoder create "uncompressed" GIF - files, so animations created by this script are - rather large (typically 2-5 times the compressed - sizes). + "gifmaker" (NEW) Convert a sequence file into a GIF animation. + Note that the GIF encoder create "uncompressed" GIF + files, so animations created by this script are + rather large (typically 2-5 times the compressed + sizes). - "image2py" (NEW) Convert a single image to a python module. See - comments in this script for details. + "image2py" (NEW) Convert a single image to a python module. See + comments in this script for details. - "player" If multiple images are given on the command line, - they are interpreted as frames in a sequence. The - script assumes that they all have the same size. - Also note that this script now can play FLI/FLC - and GIF animations. + "player" If multiple images are given on the command line, + they are interpreted as frames in a sequence. The + script assumes that they all have the same size. + Also note that this script now can play FLI/FLC + and GIF animations. This player can also execute embedded Python animation applets (ARG format only). - "viewer" Transparent images ("P" with transparency property, - and "RGBA") are superimposed on the standard Tk back- - ground. + "viewer" Transparent images ("P" with transparency property, + and "RGBA") are superimposed on the standard Tk back- + ground. + Fixed colour argument to "new". For multilayer images, pass a tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, diff --git a/depends/install_extra_test_images.sh b/depends/install_extra_test_images.sh index 36af34b54c3..02da12d61a4 100755 --- a/depends/install_extra_test_images.sh +++ b/depends/install_extra_test_images.sh @@ -4,12 +4,12 @@ # Use SVN to just fetch a single Git subdirectory svn_export() { - if [ ! -z $1 ]; then - echo "" - echo "Retrying svn export..." - echo "" - fi + if [ ! -z $1 ]; then + echo "" + echo "Retrying svn export..." + echo "" + fi - svn export --force https://github.com/python-pillow/pillow-depends/trunk/test_images ../Tests/images + svn export --force https://github.com/python-pillow/pillow-depends/trunk/test_images ../Tests/images } svn_export || svn_export retry || svn_export retry || svn_export retry diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 58e2bccc51c..24e46ff0072 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -179,7 +179,7 @@ complete list, see the table in the :py:mod:`Unpack.c` module. The following table describes some commonly used **raw modes**: +-----------+-----------------------------------------------------------------+ -| mode | description | +| mode | description | +===========+=================================================================+ | ``1`` | 1-bit bilevel, stored with the leftmost pixel in the most | | | significant bit. 0 means black, 1 means white. | @@ -223,7 +223,7 @@ You can use the ``raw`` decoder to read images where data is packed in any standard machine data type, using one of the following raw modes: ============ ======================================= -mode description +mode description ============ ======================================= ``F`` 32-bit native floating point. ``F;8`` 8-bit unsigned integer. diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index c824ff1763f..216fa1196e4 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -58,8 +58,8 @@ Functions ``warnings.simplefilter('ignore', Image.DecompressionBombWarning)``. See also `the logging documentation`_ to have warnings output to the logging facility instead of stderr. - .. _decompression bombs: https://en.wikipedia.org/wiki/Zip_bomb - .. _the logging documentation: https://docs.python.org/3/library/logging.html#integration-with-the-warnings-module + .. _decompression bombs: https://en.wikipedia.org/wiki/Zip_bomb + .. _the logging documentation: https://docs.python.org/3/library/logging.html#integration-with-the-warnings-module Image processing ^^^^^^^^^^^^^^^^ diff --git a/docs/reference/ImageCms.rst b/docs/reference/ImageCms.rst index 922e1685a13..67c58176565 100644 --- a/docs/reference/ImageCms.rst +++ b/docs/reference/ImageCms.rst @@ -413,10 +413,10 @@ can be easily displayed in a chromaticity diagram, for example). with :py:attr:`.intent_supported`. :param intent: One of ``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC``, - ``ImageCms.INTENT_PERCEPTUAL``, - ``ImageCms.INTENT_RELATIVE_COLORIMETRIC`` - and ``ImageCms.INTENT_SATURATION``. + ``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`` + ``ImageCms.DIRECTION_OUTPUT`` + and ``ImageCms.DIRECTION_PROOF`` :return: Boolean if the intent and direction is supported. From d23df7227c419a010c4a82599856f83abd2633b5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 May 2020 10:24:26 +1000 Subject: [PATCH 096/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6ac9af43971..03f9cf28f31 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Fixes default offset for Exif #4594 + [rodrigob, radarhere] + - Fixed bug when unpickling TIFF images #4565 [radarhere] From 6669ffd053464110055cb31e66bd3373ae3b30f7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 3 May 2020 13:06:25 +1000 Subject: [PATCH 097/262] Updated documentation now that Linux is supported --- docs/reference/ImageGrab.rst | 10 ++++------ src/PIL/ImageGrab.py | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index ddd5bbbb516..5cc8e55e04d 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -1,20 +1,18 @@ .. py:module:: PIL.ImageGrab .. py:currentmodule:: PIL.ImageGrab -:py:mod:`ImageGrab` Module (macOS and Windows only) -=================================================== +:py:mod:`ImageGrab` Module +========================== The :py:mod:`ImageGrab` module can be used to copy the contents of the screen or the clipboard to a PIL image memory. -.. note:: The current version works on macOS and Windows only. - .. versionadded:: 1.1.3 .. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None) Take a snapshot of the screen. The pixels inside the bounding box are - returned as an "RGB" image on Windows or "RGBA" on macOS. + returned as an "RGBA" on macOS, or an "RGB" image otherwise. If the bounding box is omitted, the entire screen is copied. .. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS), 7.1.0 (Linux (X11)) @@ -36,7 +34,7 @@ or the clipboard to a PIL image memory. .. py:function:: PIL.ImageGrab.grabclipboard() - Take a snapshot of the clipboard image, if any. + Take a snapshot of the clipboard image, if any. Only macOS and Windows are currently supported. .. versionadded:: 1.1.4 (Windows), 3.3.0 (macOS) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 39d5e23c72d..8b38df323a5 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -2,7 +2,7 @@ # The Python Imaging Library # $Id$ # -# screen grabber (macOS and Windows only) +# screen grabber # # History: # 2001-04-26 fl created From 59957fb8d8593bf2f87663b8c8c357ea060c8dd5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 4 May 2020 20:07:23 +1000 Subject: [PATCH 098/262] Added support for 1-D NumPy arrays --- Tests/test_numpy.py | 5 +++++ src/PIL/Image.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 30ab5132a31..56addca1baa 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -89,6 +89,11 @@ def test_3d_array(): assert_image(Image.fromarray(a[:, :, 1]), "L", TEST_IMAGE_SIZE) +def test_1d_array(): + a = numpy.ones(5, dtype=numpy.uint8) + assert_image(Image.fromarray(a), "L", (1, 5)) + + def _test_img_equals_nparray(img, np): assert len(np.shape) >= 2 np_size = np.shape[1], np.shape[0] diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0c8b42a0909..f500a49d554 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2727,7 +2727,7 @@ def fromarray(obj, mode=None): if ndim > ndmax: raise ValueError("Too many dimensions: %d > %d." % (ndim, ndmax)) - size = shape[1], shape[0] + size = 1 if ndim == 1 else shape[1], shape[0] if strides is not None: if hasattr(obj, "tobytes"): obj = obj.tobytes() From f15e4a8e0614c05b63b9e1b84103ed75101695ba Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 8 May 2020 19:24:38 +0300 Subject: [PATCH 099/262] truncate icclist instead of changing to None --- Tests/images/icc-after-SOF.jpg | Bin 0 -> 212 bytes Tests/test_file_jpeg.py | 4 ++++ src/PIL/JpegImagePlugin.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Tests/images/icc-after-SOF.jpg diff --git a/Tests/images/icc-after-SOF.jpg b/Tests/images/icc-after-SOF.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a284a2298dc67db0094155b7680c24c96eadf7a9 GIT binary patch literal 212 zcmex=vcDq>wPbh^&;noUD|LjDm`ux`L99vW$#|xu%YRp^1r! zyt<{Wg^`V(v5C?D0}R|83|tIcjEsT||Bo<;f-Po11{}CuaD+ib0O&+i@c$MA2gvpI L4D%1v|Gx Date: Sat, 9 May 2020 03:16:04 +0200 Subject: [PATCH 100/262] build PyPy wheels on GHA; update installation docs --- .github/workflows/test-windows.yml | 8 ++++---- docs/installation.rst | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index a5761a0e051..24f8406bcfa 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -88,7 +88,7 @@ jobs: # GPL licensed; skip if building wheels - name: Build dependencies / libimagequant - if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')" + if: "github.event_name != 'push'" run: "& winbuild\\build\\build_dep_libimagequant.cmd" # Raqm dependencies @@ -143,14 +143,14 @@ jobs: - name: Build wheel id: wheel - if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" + if: "github.event_name == 'push'" run: | for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a winbuild\\build\\build_pillow.cmd bdist_wheel" shell: cmd - - uses: actions/upload-artifact@v2-preview - if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')" + - uses: actions/upload-artifact@v2 + if: "github.event_name == 'push'" with: name: ${{ steps.wheel.outputs.dist }} path: dist\*.whl diff --git a/docs/installation.rst b/docs/installation.rst index 7d6307b6983..58d9856914b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -292,7 +292,10 @@ or from within the uncompressed source directory:: Building on Windows ^^^^^^^^^^^^^^^^^^^ -There are build scripts and notes for the Windows build in the ``winbuild`` directory. +We recommend you use prebuilt wheels from PyPI. +If you wish to compile Pillow manually, you can use the build scripts +in the ``winbuild`` directory used for CI testing and development. +These scripts require Visual Studio 2017 or newer and NASM. Building on FreeBSD ^^^^^^^^^^^^^^^^^^^ From 5728662c7f93997f9d0e4d588e44437d45d880da Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 9 May 2020 09:29:40 +0200 Subject: [PATCH 101/262] add support for CF_DIBV5, CF_HDROP, and 'PNG' in ImageGrab.grabclipboard() on win32 --- Tests/test_imagegrab.py | 25 +++++++++++++++++- src/PIL/ImageGrab.py | 22 ++++++++++++---- src/display.c | 56 +++++++++++++++++++++++++++++++++-------- 3 files changed, 86 insertions(+), 17 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 82e746fda97..9b5084acf97 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -4,7 +4,7 @@ import pytest from PIL import Image, ImageGrab -from .helper import assert_image +from .helper import assert_image, assert_image_equal_tofile class TestImageGrab: @@ -71,3 +71,26 @@ def test_grabclipboard(self): im = ImageGrab.grabclipboard() assert_image(im, im.mode, im.size) + + @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") + def test_grabclipboard_file(self): + p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE) + p.stdin.write(rb'Set-Clipboard -Path "Tests\images\hopper.gif"') + p.communicate() + + im = ImageGrab.grabclipboard() + assert_image_equal_tofile(im, "Tests/images/hopper.gif") + + @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") + def test_grabclipboard_png(self): + p = subprocess.Popen(["powershell", "-command", "-"], stdin=subprocess.PIPE) + p.stdin.write( + rb"""$bytes = [System.IO.File]::ReadAllBytes("Tests\images\hopper.png") +$ms = new-object System.IO.MemoryStream(, $bytes) +[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") +[Windows.Forms.Clipboard]::SetData("PNG", $ms)""" + ) + p.communicate() + + im = ImageGrab.grabclipboard() + assert_image_equal_tofile(im, "Tests/images/hopper.png") diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 39d5e23c72d..9fd8b161075 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -93,12 +93,24 @@ def grabclipboard(): os.unlink(filepath) return im elif sys.platform == "win32": - data = Image.core.grabclipboard_win32() + import io + + fmt, data = Image.core.grabclipboard_win32() + if isinstance(data, str): + if fmt == "file": + with open(data, "rb") as f: + im = Image.open(io.BytesIO(f.read())) + return im if isinstance(data, bytes): - from . import BmpImagePlugin - import io + data = io.BytesIO(data) + if fmt == "png": + from . import PngImagePlugin + + return PngImagePlugin.PngImageFile(data) + elif fmt == "DIB": + from . import BmpImagePlugin - return BmpImagePlugin.DibImageFile(io.BytesIO(data)) - return data + return BmpImagePlugin.DibImageFile(data) + return None else: raise NotImplementedError("ImageGrab.grabclipboard() is macOS and Windows only") diff --git a/src/display.c b/src/display.c index 9a337d1c041..0d52ca2cf5b 100644 --- a/src/display.c +++ b/src/display.c @@ -32,6 +32,7 @@ #ifdef _WIN32 +#include #include "ImDib.h" #if SIZEOF_VOID_P == 8 @@ -473,33 +474,66 @@ PyObject* PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args) { int clip; - HANDLE handle; + HANDLE handle = NULL; int size; void* data; PyObject* result; + UINT format; + UINT formats[] = { CF_DIB, CF_DIBV5, CF_HDROP, RegisterClipboardFormatA("PNG"), 0 }; + LPCSTR format_names[] = { "DIB", "DIB", "file", "png", NULL }; - clip = OpenClipboard(NULL); - /* FIXME: check error status */ + if (!OpenClipboard(NULL)) { + PyErr_SetString(PyExc_OSError, "failed to open clipboard"); + return NULL; + } + + // find best format as set by clipboard owner + format = 0; + while (!handle && (format = EnumClipboardFormats(format))) { + for (UINT i = 0; formats[i] != 0; i++) { + if (format == formats[i]) { + handle = GetClipboardData(format); + format = i; + break; + } + } + } - handle = GetClipboardData(CF_DIB); if (!handle) { - /* FIXME: add CF_HDROP support to allow cut-and-paste from - the explorer */ CloseClipboard(); - Py_INCREF(Py_None); - return Py_None; + return Py_BuildValue("zO", NULL, Py_None); + } + + if (formats[format] == CF_HDROP) { + LPDROPFILES files = (LPDROPFILES)GlobalLock(handle); + size = GlobalSize(handle); + + if (files->fWide) { + LPCWSTR filename = (LPCWSTR)(((char*)files) + files->pFiles); + size = wcsnlen(filename, (size - files->pFiles) / 2); + result = Py_BuildValue("zu#", "file", filename, size); + } + else { + LPCSTR filename = (LPCSTR)(((char*)files) + files->pFiles); + size = strnlen(filename, size - files->pFiles); + result = Py_BuildValue("zs#", "file", filename, size); + } + + GlobalUnlock(handle); + CloseClipboard(); + + return result; } - size = GlobalSize(handle); data = GlobalLock(handle); + size = GlobalSize(handle); result = PyBytes_FromStringAndSize(data, size); GlobalUnlock(handle); - CloseClipboard(); - return result; + return Py_BuildValue("zN", format_names[format], result); } /* -------------------------------------------------------------------- */ From 1656edaf4176eea36b3acac0f8410b2a204493a3 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 9 May 2020 10:40:10 +0200 Subject: [PATCH 102/262] fix docs compliance for CF_HDROP --- Tests/test_imagegrab.py | 4 +++- src/PIL/ImageGrab.py | 19 ++++++++++++------- src/display.c | 22 ---------------------- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 9b5084acf97..23eee2445b3 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -1,3 +1,4 @@ +import os import subprocess import sys @@ -79,7 +80,8 @@ def test_grabclipboard_file(self): p.communicate() im = ImageGrab.grabclipboard() - assert_image_equal_tofile(im, "Tests/images/hopper.gif") + assert len(im) == 1 + assert os.path.samefile(im[0], "Tests/images/hopper.gif") @pytest.mark.skipif(sys.platform != "win32", reason="Windows only") def test_grabclipboard_png(self): diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 9fd8b161075..cb685b1ede9 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -93,15 +93,20 @@ def grabclipboard(): os.unlink(filepath) return im elif sys.platform == "win32": - import io - fmt, data = Image.core.grabclipboard_win32() - if isinstance(data, str): - if fmt == "file": - with open(data, "rb") as f: - im = Image.open(io.BytesIO(f.read())) - return im + if fmt == "file": # CF_HDROP + import struct + + o = struct.unpack_from("I", data)[0] + if data[16] != 0: + files = data[o:].decode("utf-16le").split("\0") + return files[: files.index("")] + else: + files = data[o:].decode("mbcs").split("\0") + return files[: files.index("")] if isinstance(data, bytes): + import io + data = io.BytesIO(data) if fmt == "png": from . import PngImagePlugin diff --git a/src/display.c b/src/display.c index 0d52ca2cf5b..c8d3086e3e2 100644 --- a/src/display.c +++ b/src/display.c @@ -32,7 +32,6 @@ #ifdef _WIN32 -#include #include "ImDib.h" #if SIZEOF_VOID_P == 8 @@ -504,27 +503,6 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args) return Py_BuildValue("zO", NULL, Py_None); } - if (formats[format] == CF_HDROP) { - LPDROPFILES files = (LPDROPFILES)GlobalLock(handle); - size = GlobalSize(handle); - - if (files->fWide) { - LPCWSTR filename = (LPCWSTR)(((char*)files) + files->pFiles); - size = wcsnlen(filename, (size - files->pFiles) / 2); - result = Py_BuildValue("zu#", "file", filename, size); - } - else { - LPCSTR filename = (LPCSTR)(((char*)files) + files->pFiles); - size = strnlen(filename, size - files->pFiles); - result = Py_BuildValue("zs#", "file", filename, size); - } - - GlobalUnlock(handle); - CloseClipboard(); - - return result; - } - data = GlobalLock(handle); size = GlobalSize(handle); From a230d237c98b73fcfad826a40c709e9719994e13 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 9 May 2020 20:17:14 +1000 Subject: [PATCH 103/262] Updated Freetype to 2.10.2 --- .github/workflows/test-windows.yml | 2 +- winbuild/config.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index adbe979686e..d45dc946769 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -170,7 +170,7 @@ jobs: set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32 set BUILD=%GITHUB_WORKSPACE%\winbuild\build - cd /D %BUILD%\freetype-2.10.1 + cd /D %BUILD%\freetype-2.10.2 call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }} echo on rmdir /S /Q objs diff --git a/winbuild/config.py b/winbuild/config.py index 93413d1e573..6d512945cd8 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -40,9 +40,9 @@ "dir": "tiff-4.1.0", }, "freetype": { - "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501 - "filename": "freetype-2.10.1.tar.gz", - "dir": "freetype-2.10.1", + "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.2.tar.gz", # noqa: E501 + "filename": "freetype-2.10.2.tar.gz", + "dir": "freetype-2.10.2", }, "lcms-2.7": { "url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip", From f1c66a0ba5cdf94c324dde0a14912023f988f7e8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2020 10:50:20 +1000 Subject: [PATCH 104/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 03f9cf28f31..f12a321c925 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- JPEG: Truncate icclist instead of setting to None #4613 + [homm] + - Fixes default offset for Exif #4594 [rodrigob, radarhere] From 9d701a0026199ff7819d54b7a9bdf6b0a443324c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2020 19:55:33 +1000 Subject: [PATCH 105/262] Removed duplicate return statements --- src/libImaging/Except.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libImaging/Except.c b/src/libImaging/Except.c index 4850b4f8747..cce93cdc252 100644 --- a/src/libImaging/Except.c +++ b/src/libImaging/Except.c @@ -43,14 +43,12 @@ void * ImagingError_ModeError(void) { return ImagingError_ValueError("bad image mode"); - return NULL; } void * ImagingError_Mismatch(void) { return ImagingError_ValueError("images don't match"); - return NULL; } void * From c1d9931adccd0962eeeffb41bcdedf76e501d854 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 10 May 2020 19:56:36 +1000 Subject: [PATCH 106/262] Added braces --- src/Tk/tkImaging.c | 11 +- src/_imaging.c | 567 +++++++++++++++++++++----------- src/_imagingcms.c | 106 +++--- src/_imagingft.c | 117 ++++--- src/_imagingmath.c | 20 +- src/_imagingmorph.c | 6 +- src/_imagingtk.c | 7 +- src/_webp.c | 21 +- src/decode.c | 168 ++++++---- src/display.c | 134 +++++--- src/encode.c | 156 +++++---- src/libImaging/Access.c | 11 +- src/libImaging/AlphaComposite.c | 9 +- src/libImaging/Bands.c | 45 ++- src/libImaging/BcnDecode.c | 20 +- src/libImaging/BitDecode.c | 34 +- src/libImaging/Blend.c | 24 +- src/libImaging/BoxBlur.c | 15 +- src/libImaging/Chops.c | 19 +- src/libImaging/Convert.c | 181 ++++++---- src/libImaging/Copy.c | 14 +- src/libImaging/Crop.c | 9 +- src/libImaging/Dib.c | 43 ++- src/libImaging/Draw.c | 193 ++++++----- src/libImaging/Effects.c | 20 +- src/libImaging/EpsEncode.c | 9 +- src/libImaging/Except.c | 3 +- src/libImaging/File.c | 9 +- src/libImaging/Fill.c | 18 +- src/libImaging/Filter.c | 46 ++- src/libImaging/FliDecode.c | 30 +- src/libImaging/Geometry.c | 207 +++++++----- src/libImaging/GetBBox.c | 69 ++-- src/libImaging/GifDecode.c | 12 +- src/libImaging/GifEncode.c | 38 ++- src/libImaging/HexDecode.c | 3 +- src/libImaging/Histo.c | 58 ++-- src/libImaging/Jpeg2KDecode.c | 109 +++--- src/libImaging/Jpeg2KEncode.c | 51 ++- src/libImaging/JpegDecode.c | 44 ++- src/libImaging/JpegEncode.c | 30 +- src/libImaging/Matrix.c | 12 +- src/libImaging/ModeFilter.c | 23 +- src/libImaging/Negative.c | 12 +- src/libImaging/Offset.c | 20 +- src/libImaging/Pack.c | 58 ++-- src/libImaging/PackDecode.c | 9 +- src/libImaging/Palette.c | 45 ++- src/libImaging/Paste.c | 51 ++- src/libImaging/PcdDecode.c | 9 +- src/libImaging/PcxDecode.c | 6 +- src/libImaging/Point.c | 41 ++- src/libImaging/Quant.c | 193 ++++++++--- src/libImaging/QuantHash.c | 20 +- src/libImaging/QuantHeap.c | 16 +- src/libImaging/QuantOctree.c | 59 +++- src/libImaging/QuantPngQuant.c | 12 +- src/libImaging/RankFilter.c | 45 ++- src/libImaging/RawDecode.c | 9 +- src/libImaging/RawEncode.c | 6 +- src/libImaging/Reduce.c | 6 +- src/libImaging/Resample.c | 63 ++-- src/libImaging/SgiRleDecode.c | 18 +- src/libImaging/Storage.c | 30 +- src/libImaging/SunRleDecode.c | 9 +- src/libImaging/TgaRleDecode.c | 19 +- src/libImaging/TgaRleEncode.c | 33 +- src/libImaging/Unpack.c | 12 +- src/libImaging/UnsharpMask.c | 9 +- src/libImaging/XbmDecode.c | 12 +- src/libImaging/ZipDecode.c | 28 +- src/libImaging/ZipEncode.c | 17 +- src/map.c | 81 +++-- src/outline.c | 27 +- src/path.c | 123 ++++--- 75 files changed, 2505 insertions(+), 1314 deletions(-) diff --git a/src/Tk/tkImaging.c b/src/Tk/tkImaging.c index 59801f58eb9..161a835fbd2 100644 --- a/src/Tk/tkImaging.c +++ b/src/Tk/tkImaging.c @@ -68,8 +68,9 @@ ImagingFind(const char* name) #else id = atol(name); #endif - if (!id) + if (!id) { return NULL; + } return (Imaging) id; } @@ -119,10 +120,11 @@ PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp, block.offset[0] = 0; block.offset[1] = 1; block.offset[2] = 2; - if (strcmp(im->mode, "RGBA") == 0) + if (strcmp(im->mode, "RGBA") == 0) { block.offset[3] = 3; /* alpha (or reserved, under 8.2) */ - else + } else { block.offset[3] = 0; /* no alpha */ + } } else { TCL_APPEND_RESULT(interp, "Bad mode", (char*) NULL); return TCL_ERROR; @@ -136,10 +138,11 @@ PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp, if (TK_LT_85) { /* Tk 8.4 */ TK_PHOTO_PUT_BLOCK_84(photo, &block, 0, 0, block.width, block.height, TK_PHOTO_COMPOSITE_SET); - if (strcmp(im->mode, "RGBA") == 0) + if (strcmp(im->mode, "RGBA") == 0) { /* Tk workaround: we need apply ToggleComplexAlphaIfNeeded */ /* (fixed in Tk 8.5a3) */ TK_PHOTO_SET_SIZE_84(photo, block.width, block.height); + } } else { /* Tk >=8.5 */ TK_PHOTO_PUT_BLOCK_85(interp, photo, &block, 0, 0, block.width, diff --git a/src/_imaging.c b/src/_imaging.c index 9a9a51f719f..f0ba220408c 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -170,8 +170,9 @@ PyImagingNew(Imaging imOut) { ImagingObject* imagep; - if (!imOut) + if (!imOut) { return NULL; + } imagep = PyObject_New(ImagingObject, &Imaging_Type); if (imagep == NULL) { @@ -197,8 +198,9 @@ _dealloc(ImagingObject* imagep) printf("imaging %p deleted\n", imagep); #endif - if (imagep->access) + if (imagep->access) { ImagingAccessDelete(imagep->image, imagep->access); + } ImagingDelete(imagep->image); PyObject_Del(imagep); } @@ -322,8 +324,9 @@ getbands(const char* mode) /* FIXME: add primitive to libImaging to avoid extra allocation */ im = ImagingNew(mode, 0, 0); - if (!im) + if (!im) { return -1; + } bands = im->bands; @@ -373,8 +376,9 @@ getlist(PyObject* arg, Py_ssize_t* length, const char* wrong_length, int type) /* malloc check ok, type & ff is just a sizeof(something) calloc checks for overflow */ list = calloc(n, type & 0xff); - if ( ! list) + if ( ! list) { return PyErr_NoMemory(); + } seq = PySequence_Fast(arg, must_be_sequence); if ( ! seq) { @@ -413,8 +417,9 @@ getlist(PyObject* arg, Py_ssize_t* length, const char* wrong_length, int type) return NULL; } - if (length) + if (length) { *length = n; + } return list; } @@ -546,12 +551,14 @@ getink(PyObject* color, Imaging im, char* ink) r = (UINT8) r; } else { if (im->bands == 2) { - if (!PyArg_ParseTuple(color, "L|i", &r, &a)) + if (!PyArg_ParseTuple(color, "L|i", &r, &a)) { return NULL; + } g = b = r; } else { - if (!PyArg_ParseTuple(color, "Lii|i", &r, &g, &b, &a)) + if (!PyArg_ParseTuple(color, "Lii|i", &r, &g, &b, &a)) { return NULL; + } } } ink[0] = (char) CLIP8(r); @@ -562,23 +569,26 @@ getink(PyObject* color, Imaging im, char* ink) return ink; case IMAGING_TYPE_INT32: /* signed integer */ - if (rIsInt != 1) + if (rIsInt != 1) { return NULL; + } itmp = r; memcpy(ink, &itmp, sizeof(itmp)); return ink; case IMAGING_TYPE_FLOAT32: /* floating point */ f = PyFloat_AsDouble(color); - if (f == -1.0 && PyErr_Occurred()) + if (f == -1.0 && PyErr_Occurred()) { return NULL; + } ftmp = f; memcpy(ink, &ftmp, sizeof(ftmp)); return ink; case IMAGING_TYPE_SPECIAL: if (strncmp(im->mode, "I;16", 4) == 0) { - if (rIsInt != 1) + if (rIsInt != 1) { return NULL; + } ink[0] = (UINT8) r; ink[1] = (UINT8) (r >> 8); ink[2] = ink[3] = 0; @@ -606,12 +616,14 @@ _fill(PyObject* self, PyObject* args) xsize = ysize = 256; color = NULL; - if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) + if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) { return NULL; + } im = ImagingNewDirty(mode, xsize, ysize); - if (!im) + if (!im) { return NULL; + } buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; if (color) { @@ -633,8 +645,9 @@ _new(PyObject* self, PyObject* args) char* mode; int xsize, ysize; - if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) { return NULL; + } return PyImagingNew(ImagingNew(mode, xsize, ysize)); } @@ -645,8 +658,9 @@ _new_block(PyObject* self, PyObject* args) char* mode; int xsize, ysize; - if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) { return NULL; + } return PyImagingNew(ImagingNewBlock(mode, xsize, ysize)); } @@ -656,8 +670,9 @@ _linear_gradient(PyObject* self, PyObject* args) { char* mode; - if (!PyArg_ParseTuple(args, "s", &mode)) + if (!PyArg_ParseTuple(args, "s", &mode)) { return NULL; + } return PyImagingNew(ImagingFillLinearGradient(mode)); } @@ -667,8 +682,9 @@ _radial_gradient(PyObject* self, PyObject* args) { char* mode; - if (!PyArg_ParseTuple(args, "s", &mode)) + if (!PyArg_ParseTuple(args, "s", &mode)) { return NULL; + } return PyImagingNew(ImagingFillRadialGradient(mode)); } @@ -681,8 +697,9 @@ _alpha_composite(ImagingObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O!O!", &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) + &Imaging_Type, &imagep2)) { return NULL; + } return PyImagingNew(ImagingAlphaComposite(imagep1->image, imagep2->image)); } @@ -698,8 +715,9 @@ _blend(ImagingObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O!O!|d", &Imaging_Type, &imagep1, &Imaging_Type, &imagep2, - &alpha)) + &alpha)) { return NULL; + } return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image, (float) alpha)); @@ -762,8 +780,9 @@ _prepare_lut_table(PyObject* table, Py_ssize_t table_size) /* malloc check ok, max is 2 * 4 * 65**3 = 2197000 */ prepared = (INT16*) malloc(sizeof(INT16) * table_size); if ( ! prepared) { - if (free_table_data) + if (free_table_data) { free(table_data); + } return (INT16*) PyErr_NoMemory(); } @@ -880,8 +899,9 @@ _convert(ImagingObject* self, PyObject* args) int dither = 0; ImagingObject *paletteimage = NULL; - if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) + if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) { return NULL; + } if (paletteimage != NULL) { if (!PyImaging_Check(paletteimage)) { PyObject_Print((PyObject *)paletteimage, stderr, 0); @@ -904,11 +924,13 @@ _convert2(ImagingObject* self, PyObject* args) ImagingObject* imagep2; if (!PyArg_ParseTuple(args, "O!O!", &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) + &Imaging_Type, &imagep2)) { return NULL; + } - if (!ImagingConvert2(imagep1->image, imagep2->image)) + if (!ImagingConvert2(imagep1->image, imagep2->image)) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -950,8 +972,9 @@ _convert_transparent(ImagingObject* self, PyObject* args) static PyObject* _copy(ImagingObject* self, PyObject* args) { - if (!PyArg_ParseTuple(args, "")) + if (!PyArg_ParseTuple(args, "")) { return NULL; + } return PyImagingNew(ImagingCopy(self->image)); } @@ -960,8 +983,9 @@ static PyObject* _crop(ImagingObject* self, PyObject* args) { int x0, y0, x1, y1; - if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1)) + if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1)) { return NULL; + } return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1)); } @@ -971,8 +995,9 @@ _expand_image(ImagingObject* self, PyObject* args) { int x, y; int mode = 0; - if (!PyArg_ParseTuple(args, "ii|i", &x, &y, &mode)) + if (!PyArg_ParseTuple(args, "ii|i", &x, &y, &mode)) { return NULL; + } return PyImagingNew(ImagingExpand(self->image, x, y, mode)); } @@ -988,13 +1013,15 @@ _filter(ImagingObject* self, PyObject* args) float divisor, offset; PyObject* kernel = NULL; if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize, - &divisor, &offset, &kernel)) + &divisor, &offset, &kernel)) { return NULL; + } /* get user-defined kernel */ kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32); - if (!kerneldata) + if (!kerneldata) { return NULL; + } if (kernelsize != (Py_ssize_t) xsize * (Py_ssize_t) ysize) { free(kerneldata); return ImagingError_ValueError("bad kernel size"); @@ -1022,13 +1049,15 @@ _gaussian_blur(ImagingObject* self, PyObject* args) float radius = 0; int passes = 3; - if (!PyArg_ParseTuple(args, "f|i", &radius, &passes)) + if (!PyArg_ParseTuple(args, "f|i", &radius, &passes)) { return NULL; + } imIn = self->image; imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } if (!ImagingGaussianBlur(imOut, imIn, radius, passes)) { ImagingDelete(imOut); @@ -1049,8 +1078,9 @@ _getpalette(ImagingObject* self, PyObject* args) char* mode = "RGB"; char* rawmode = "RGB"; - if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) + if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) { return NULL; + } if (!self->image->palette) { PyErr_SetString(PyExc_ValueError, no_palette); @@ -1064,8 +1094,9 @@ _getpalette(ImagingObject* self, PyObject* args) } palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); - if (!palette) + if (!palette) { return NULL; + } pack((UINT8*) PyBytes_AsString(palette), self->image->palette->palette, palettesize); @@ -1089,24 +1120,27 @@ _getxy(PyObject* xy, int* x, int *y) { PyObject* value; - if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2) + if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2) { goto badarg; + } value = PyTuple_GET_ITEM(xy, 0); - if (PyLong_Check(value)) + if (PyLong_Check(value)) { *x = PyLong_AS_LONG(value); - else if (PyFloat_Check(value)) + } else if (PyFloat_Check(value)) { *x = (int) PyFloat_AS_DOUBLE(value); - else + } else { goto badval; + } value = PyTuple_GET_ITEM(xy, 1); - if (PyLong_Check(value)) + if (PyLong_Check(value)) { *y = PyLong_AS_LONG(value); - else if (PyFloat_Check(value)) + } else if (PyFloat_Check(value)) { *y = (int) PyFloat_AS_DOUBLE(value); - else + } else { goto badval; + } return 0; @@ -1141,8 +1175,9 @@ _getpixel(ImagingObject* self, PyObject* args) xy = PyTuple_GET_ITEM(args, 0); - if (_getxy(xy, &x, &y)) + if (_getxy(xy, &x, &y)) { return NULL; + } if (self->access == NULL) { Py_INCREF(Py_None); @@ -1168,20 +1203,23 @@ parse_histogram_extremap(ImagingObject* self, PyObject* extremap, if (extremap) { switch (self->image->type) { case IMAGING_TYPE_UINT8: - if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) + if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) { return NULL; + } ep->u[0] = CLIP8(i0); ep->u[1] = CLIP8(i1); break; case IMAGING_TYPE_INT32: - if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) + if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) { return NULL; + } ep->i[0] = i0; ep->i[1] = i1; break; case IMAGING_TYPE_FLOAT32: - if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1)) + if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1)) { return NULL; + } ep->f[0] = (FLOAT32) f0; ep->f[1] = (FLOAT32) f1; break; @@ -1205,15 +1243,17 @@ _histogram(ImagingObject* self, PyObject* args) PyObject* extremap = NULL; ImagingObject* maskp = NULL; - if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) + if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) { return NULL; + } /* Using a var to avoid allocations. */ ep = parse_histogram_extremap(self, extremap, &extrema); h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep); - if (!h) + if (!h) { return NULL; + } /* Build an integer list containing the histogram */ list = PyList_New(h->bands * 256); @@ -1246,15 +1286,17 @@ _entropy(ImagingObject* self, PyObject* args) PyObject* extremap = NULL; ImagingObject* maskp = NULL; - if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) + if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) { return NULL; + } /* Using a local var to avoid allocations. */ ep = parse_histogram_extremap(self, extremap, &extrema); h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep); - if (!h) + if (!h) { return NULL; + } /* Calculate the histogram entropy */ /* First, sum the histogram data */ @@ -1286,8 +1328,9 @@ static PyObject* _modefilter(ImagingObject* self, PyObject* args) { int size; - if (!PyArg_ParseTuple(args, "i", &size)) + if (!PyArg_ParseTuple(args, "i", &size)) { return NULL; + } return PyImagingNew(ImagingModeFilter(self->image, size)); } @@ -1297,8 +1340,9 @@ static PyObject* _offset(ImagingObject* self, PyObject* args) { int xoffset, yoffset; - if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset)) + if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset)) { return NULL; + } return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset)); } @@ -1315,19 +1359,21 @@ _paste(ImagingObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O(iiii)|O!", &source, &x0, &y0, &x1, &y1, - &Imaging_Type, &maskp)) - return NULL; + &Imaging_Type, &maskp)) { + return NULL; + } - if (PyImaging_Check(source)) + if (PyImaging_Check(source)) { status = ImagingPaste( self->image, PyImaging_AsImaging(source), (maskp) ? maskp->image : NULL, x0, y0, x1, y1 ); - else { - if (!getink(source, self->image, ink)) + } else { + if (!getink(source, self->image, ink)) { return NULL; + } status = ImagingFill2( self->image, ink, (maskp) ? maskp->image : NULL, @@ -1335,8 +1381,9 @@ _paste(ImagingObject* self, PyObject* args) ); } - if (status < 0) + if (status < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -1353,8 +1400,9 @@ _point(ImagingObject* self, PyObject* args) PyObject* list; char* mode; - if (!PyArg_ParseTuple(args, "Oz", &list, &mode)) - return NULL; + if (!PyArg_ParseTuple(args, "Oz", &list, &mode)) { + return NULL; + } if (mode && !strcmp(mode, "F")) { FLOAT32* data; @@ -1362,8 +1410,9 @@ _point(ImagingObject* self, PyObject* args) /* map from 8-bit data to floating point */ n = 256; data = getlist(list, &n, wrong_number, TYPE_FLOAT32); - if (!data) + if (!data) { return NULL; + } im = ImagingPoint(self->image, mode, (void*) data); free(data); @@ -1374,8 +1423,9 @@ _point(ImagingObject* self, PyObject* args) /* FIXME: support arbitrary number of entries (requires API change) */ n = 65536; data = getlist(list, &n, wrong_number, TYPE_UINT8); - if (!data) + if (!data) { return NULL; + } im = ImagingPoint(self->image, mode, (void*) data); free(data); @@ -1385,20 +1435,23 @@ _point(ImagingObject* self, PyObject* args) if (mode) { bands = getbands(mode); - if (bands < 0) + if (bands < 0) { return NULL; - } else + } + } else { bands = self->image->bands; + } /* map to integer data */ n = 256 * bands; data = getlist(list, &n, wrong_number, TYPE_INT32); - if (!data) + if (!data) { return NULL; + } - if (mode && !strcmp(mode, "I")) + if (mode && !strcmp(mode, "I")) { im = ImagingPoint(self->image, mode, (void*) data); - else if (mode && bands > 1) { + } else if (mode && bands > 1) { for (i = 0; i < 256; i++) { lut[i*4] = CLIP8(data[i]); lut[i*4+1] = CLIP8(data[i+256]); @@ -1409,8 +1462,9 @@ _point(ImagingObject* self, PyObject* args) im = ImagingPoint(self->image, mode, (void*) lut); } else { /* map individual bands */ - for (i = 0; i < n; i++) + for (i = 0; i < n; i++) { lut[i] = CLIP8(data[i]); + } im = ImagingPoint(self->image, mode, (void*) lut); } free(data); @@ -1424,8 +1478,9 @@ _point_transform(ImagingObject* self, PyObject* args) { double scale = 1.0; double offset = 0.0; - if (!PyArg_ParseTuple(args, "|dd", &scale, &offset)) - return NULL; + if (!PyArg_ParseTuple(args, "|dd", &scale, &offset)) { + return NULL; + } return PyImagingNew(ImagingPointTransform(self->image, scale, offset)); } @@ -1443,8 +1498,9 @@ _putdata(ImagingObject* self, PyObject* args) double scale = 1.0; double offset = 0.0; - if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) + if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) { return NULL; + } if (!PySequence_Check(data)) { PyErr_SetString(PyExc_TypeError, must_be_sequence); @@ -1463,7 +1519,7 @@ _putdata(ImagingObject* self, PyObject* args) if (PyBytes_Check(data)) { unsigned char* p; p = (unsigned char*) PyBytes_AS_STRING(data); - if (scale == 1.0 && offset == 0.0) + if (scale == 1.0 && offset == 0.0) { /* Plain string data */ for (i = y = 0; i < n; i += image->xsize, y++) { x = n - i; @@ -1471,13 +1527,14 @@ _putdata(ImagingObject* self, PyObject* args) x = image->xsize; memcpy(image->image8[y], p+i, x); } - else + } else { /* Scaled and clipped string data */ for (i = x = y = 0; i < n; i++) { image->image8[y][x] = CLIP8((int) (p[i] * scale + offset)); if (++x >= (int) image->xsize) x = 0, y++; } + } } else { seq = PySequence_Fast(data, must_be_sequence); if (!seq) { @@ -1576,8 +1633,9 @@ _quantize(ImagingObject* self, PyObject* args) int colours = 256; int method = 0; int kmeans = 0; - if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans)) + if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans)) { return NULL; + } if (!self->image->xsize || !self->image->ysize) { /* no content; return an empty image */ @@ -1599,8 +1657,9 @@ _putpalette(ImagingObject* self, PyObject* args) char* rawmode; UINT8* palette; Py_ssize_t palettesize; - if (!PyArg_ParseTuple(args, "sy#", &rawmode, &palette, &palettesize)) + if (!PyArg_ParseTuple(args, "sy#", &rawmode, &palette, &palettesize)) { return NULL; + } if (strcmp(self->image->mode, "L") && strcmp(self->image->mode, "LA") && strcmp(self->image->mode, "P") && strcmp(self->image->mode, "PA")) { @@ -1636,8 +1695,9 @@ _putpalettealpha(ImagingObject* self, PyObject* args) { int index; int alpha = 0; - if (!PyArg_ParseTuple(args, "i|i", &index, &alpha)) + if (!PyArg_ParseTuple(args, "i|i", &index, &alpha)) { return NULL; + } if (!self->image->palette) { PyErr_SetString(PyExc_ValueError, no_palette); @@ -1662,8 +1722,9 @@ _putpalettealphas(ImagingObject* self, PyObject* args) int i; UINT8 *values; Py_ssize_t length; - if (!PyArg_ParseTuple(args, "y#", &values, &length)) + if (!PyArg_ParseTuple(args, "y#", &values, &length)) { return NULL; + } if (!self->image->palette) { PyErr_SetString(PyExc_ValueError, no_palette); @@ -1692,8 +1753,9 @@ _putpixel(ImagingObject* self, PyObject* args) int x, y; PyObject* color; - if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color)) + if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color)) { return NULL; + } im = self->image; @@ -1709,11 +1771,13 @@ _putpixel(ImagingObject* self, PyObject* args) return NULL; } - if (!getink(color, im, ink)) + if (!getink(color, im, ink)) { return NULL; + } - if (self->access) + if (self->access) { self->access->put_pixel(im, x, y, ink); + } Py_INCREF(Py_None); return Py_None; @@ -1724,8 +1788,9 @@ static PyObject* _rankfilter(ImagingObject* self, PyObject* args) { int size, rank; - if (!PyArg_ParseTuple(args, "ii", &size, &rank)) + if (!PyArg_ParseTuple(args, "ii", &size, &rank)) { return NULL; + } return PyImagingNew(ImagingRankFilter(self->image, size, rank)); } @@ -1746,8 +1811,9 @@ _resize(ImagingObject* self, PyObject* args) box[3] = imIn->ysize; if (!PyArg_ParseTuple(args, "(ii)|i(ffff)", &xsize, &ysize, &filter, - &box[0], &box[1], &box[2], &box[3])) + &box[0], &box[1], &box[2], &box[3])) { return NULL; + } if (xsize < 1 || ysize < 1) { return ImagingError_ValueError("height and width must be > 0"); @@ -1807,8 +1873,9 @@ _reduce(ImagingObject* self, PyObject* args) box[3] = imIn->ysize; if (!PyArg_ParseTuple(args, "(ii)|(iiii)", &xscale, &yscale, - &box[0], &box[1], &box[2], &box[3])) + &box[0], &box[1], &box[2], &box[3])) { return NULL; + } if (xscale < 1 || yscale < 1) { return ImagingError_ValueError("scale must be > 0"); @@ -1851,8 +1918,9 @@ im_setmode(ImagingObject* self, PyObject* args) char* mode; Py_ssize_t modelen; - if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen)) + if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen)) { return NULL; + } im = self->image; @@ -1872,8 +1940,9 @@ im_setmode(ImagingObject* self, PyObject* args) return NULL; } - if (self->access) + if (self->access) { ImagingAccessDelete(im, self->access); + } self->access = ImagingAccessNew(im); Py_INCREF(Py_None); @@ -1900,8 +1969,9 @@ _transform2(ImagingObject* self, PyObject* args) &x0, &y0, &x1, &y1, &Imaging_Type, &imagep, &method, &data, - &filter, &fill)) - return NULL; + &filter, &fill)) { + return NULL; + } switch (method) { case IMAGING_TRANSFORM_AFFINE: @@ -1918,8 +1988,9 @@ _transform2(ImagingObject* self, PyObject* args) } a = getlist(data, &n, wrong_number, TYPE_DOUBLE); - if (!a) + if (!a) { return NULL; + } imOut = ImagingTransform( self->image, imagep->image, method, @@ -1927,8 +1998,9 @@ _transform2(ImagingObject* self, PyObject* args) free(a); - if (!imOut) + if (!imOut) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -1941,8 +2013,9 @@ _transpose(ImagingObject* self, PyObject* args) Imaging imOut; int op; - if (!PyArg_ParseTuple(args, "i", &op)) - return NULL; + if (!PyArg_ParseTuple(args, "i", &op)) { + return NULL; + } imIn = self->image; @@ -1963,7 +2036,7 @@ _transpose(ImagingObject* self, PyObject* args) return NULL; } - if (imOut) + if (imOut) { switch (op) { case 0: (void) ImagingFlipLeftRight(imOut, imIn); @@ -1987,6 +2060,7 @@ _transpose(ImagingObject* self, PyObject* args) (void) ImagingTransverse(imOut, imIn); break; } + } return PyImagingNew(imOut); } @@ -2000,16 +2074,19 @@ _unsharp_mask(ImagingObject* self, PyObject* args) float radius; int percent, threshold; - if (!PyArg_ParseTuple(args, "fii", &radius, &percent, &threshold)) + if (!PyArg_ParseTuple(args, "fii", &radius, &percent, &threshold)) { return NULL; + } imIn = self->image; imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } - if (!ImagingUnsharpMask(imOut, imIn, radius, percent, threshold)) + if (!ImagingUnsharpMask(imOut, imIn, radius, percent, threshold)) { return NULL; + } return PyImagingNew(imOut); } @@ -2023,13 +2100,15 @@ _box_blur(ImagingObject* self, PyObject* args) float radius; int n = 1; - if (!PyArg_ParseTuple(args, "f|i", &radius, &n)) + if (!PyArg_ParseTuple(args, "f|i", &radius, &n)) { return NULL; + } imIn = self->image; imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } if (!ImagingBoxBlur(imOut, imIn, radius, n)) { ImagingDelete(imOut); @@ -2067,12 +2146,14 @@ _getcolors(ImagingObject* self, PyObject* args) PyObject* out; int maxcolors = 256; - if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors)) + if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors)) { return NULL; + } items = ImagingGetColors(self->image, maxcolors, &colors); - if (!items) + if (!items) { return NULL; + } if (colors > maxcolors) { out = Py_None; @@ -2105,10 +2186,11 @@ _getextrema(ImagingObject* self, PyObject* args) int status; status = ImagingGetExtrema(self->image, &extrema); - if (status < 0) + if (status < 0) { return NULL; + } - if (status) + if (status) { switch (self->image->type) { case IMAGING_TYPE_UINT8: return Py_BuildValue("BB", extrema.u[0], extrema.u[1]); @@ -2121,6 +2203,7 @@ _getextrema(ImagingObject* self, PyObject* args) return Py_BuildValue("HH", extrema.s[0], extrema.s[1]); } } + } Py_INCREF(Py_None); return Py_None; @@ -2162,8 +2245,9 @@ _getband(ImagingObject* self, PyObject* args) { int band; - if (!PyArg_ParseTuple(args, "i", &band)) + if (!PyArg_ParseTuple(args, "i", &band)) { return NULL; + } return PyImagingNew(ImagingGetBand(self->image, band)); } @@ -2174,11 +2258,13 @@ _fillband(ImagingObject* self, PyObject* args) int band; int color; - if (!PyArg_ParseTuple(args, "ii", &band, &color)) + if (!PyArg_ParseTuple(args, "ii", &band, &color)) { return NULL; + } - if (!ImagingFillBand(self->image, band, color)) + if (!ImagingFillBand(self->image, band, color)) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2191,11 +2277,13 @@ _putband(ImagingObject* self, PyObject* args) int band; if (!PyArg_ParseTuple(args, "O!i", &Imaging_Type, &imagep, - &band)) + &band)) { return NULL; + } - if (!ImagingPutBand(self->image, imagep->image, band)) + if (!ImagingPutBand(self->image, imagep->image, band)) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2213,13 +2301,22 @@ _merge(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "sO!|O!O!O!", &mode, &Imaging_Type, &band0, &Imaging_Type, &band1, - &Imaging_Type, &band2, &Imaging_Type, &band3)) + &Imaging_Type, &band2, &Imaging_Type, &band3)) { return NULL; + } - if (band0) bands[0] = band0->image; - if (band1) bands[1] = band1->image; - if (band2) bands[2] = band2->image; - if (band3) bands[3] = band3->image; + if (band0) { + bands[0] = band0->image; + } + if (band1) { + bands[1] = band1->image; + } + if (band2) { + bands[2] = band2->image; + } + if (band3) { + bands[3] = band3->image; + } return PyImagingNew(ImagingMerge(mode, bands)); } @@ -2233,14 +2330,16 @@ _split(ImagingObject* self, PyObject* args) PyObject* imaging_object; Imaging bands[4] = {NULL, NULL, NULL, NULL}; - if ( ! ImagingSplit(self->image, bands)) + if ( ! ImagingSplit(self->image, bands)) { return NULL; + } list = PyTuple_New(self->image->bands); for (i = 0; i < self->image->bands; i++) { imaging_object = PyImagingNew(bands[i]); - if ( ! imaging_object) + if ( ! imaging_object) { fails += 1; + } PyTuple_SET_ITEM(list, i, imaging_object); } if (fails) { @@ -2265,8 +2364,9 @@ _chop_lighter(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopLighter(self->image, imagep->image)); } @@ -2276,8 +2376,9 @@ _chop_darker(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopDarker(self->image, imagep->image)); } @@ -2287,8 +2388,9 @@ _chop_difference(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopDifference(self->image, imagep->image)); } @@ -2298,8 +2400,9 @@ _chop_multiply(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopMultiply(self->image, imagep->image)); } @@ -2309,8 +2412,9 @@ _chop_screen(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopScreen(self->image, imagep->image)); } @@ -2326,8 +2430,9 @@ _chop_add(ImagingObject* self, PyObject* args) offset = 0; if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, - &scale, &offset)) + &scale, &offset)) { return NULL; + } return PyImagingNew(ImagingChopAdd(self->image, imagep->image, scale, offset)); @@ -2344,8 +2449,9 @@ _chop_subtract(ImagingObject* self, PyObject* args) offset = 0; if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, - &scale, &offset)) + &scale, &offset)) { return NULL; + } return PyImagingNew(ImagingChopSubtract(self->image, imagep->image, scale, offset)); @@ -2356,8 +2462,9 @@ _chop_and(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopAnd(self->image, imagep->image)); } @@ -2367,8 +2474,9 @@ _chop_or(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopOr(self->image, imagep->image)); } @@ -2378,8 +2486,9 @@ _chop_xor(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopXor(self->image, imagep->image)); } @@ -2389,8 +2498,9 @@ _chop_add_modulo(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image)); } @@ -2400,8 +2510,9 @@ _chop_subtract_modulo(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image)); } @@ -2411,8 +2522,9 @@ _chop_soft_light(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopSoftLight(self->image, imagep->image)); } @@ -2422,8 +2534,9 @@ _chop_hard_light(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingChopHardLight(self->image, imagep->image)); } @@ -2433,8 +2546,9 @@ _chop_overlay(ImagingObject* self, PyObject* args) { ImagingObject* imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { return NULL; + } return PyImagingNew(ImagingOverlay(self->image, imagep->image)); } @@ -2457,8 +2571,9 @@ _font_new(PyObject* self_, PyObject* args) Py_ssize_t glyphdata_length; if (!PyArg_ParseTuple(args, "O!y#", &Imaging_Type, &imagep, - &glyphdata, &glyphdata_length)) + &glyphdata, &glyphdata_length)) { return NULL; + } if (glyphdata_length != 256 * 20) { PyErr_SetString(PyExc_ValueError, wrong_length); @@ -2466,8 +2581,9 @@ _font_new(PyObject* self_, PyObject* args) } self = PyObject_New(ImagingFontObject, &ImagingFont_Type); - if (self == NULL) + if (self == NULL) { return NULL; + } /* glyph bitmap */ self->bitmap = imagep->image; @@ -2486,10 +2602,12 @@ _font_new(PyObject* self_, PyObject* args) self->glyphs[i].sy0 = S16(B16(glyphdata, 14)); self->glyphs[i].sx1 = S16(B16(glyphdata, 16)); self->glyphs[i].sy1 = S16(B16(glyphdata, 18)); - if (self->glyphs[i].dy0 < y0) + if (self->glyphs[i].dy0 < y0) { y0 = self->glyphs[i].dy0; - if (self->glyphs[i].dy1 > y1) + } + if (self->glyphs[i].dy1 > y1) { y1 = self->glyphs[i].dy1; + } glyphdata += 20; } @@ -2515,8 +2633,9 @@ textwidth(ImagingFontObject* self, const unsigned char* text) { int xsize; - for (xsize = 0; *text; text++) + for (xsize = 0; *text; text++) { xsize += self->glyphs[*text].dx; + } return xsize; } @@ -2595,15 +2714,17 @@ _font_getmask(ImagingFontObject* self, PyObject* args) self->bitmap, glyph->sx0, glyph->sy0, glyph->sx1, glyph->sy1 ); - if (!bitmap) + if (!bitmap) { goto failed; + } status = ImagingPaste( im, bitmap, NULL, glyph->dx0+x, glyph->dy0+b, glyph->dx1+x, glyph->dy1+b ); ImagingDelete(bitmap); - if (status < 0) + if (status < 0) { goto failed; + } x = x + glyph->dx; b = b + glyph->dy; } @@ -2623,8 +2744,9 @@ _font_getsize(ImagingFontObject* self, PyObject* args) PyObject* encoded_string; PyObject* val; - if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string)) + if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string)) { return NULL; + } _font_text_asBytes(encoded_string, &text); if (!text) { @@ -2651,12 +2773,14 @@ _draw_new(PyObject* self_, PyObject* args) ImagingObject* imagep; int blend = 0; - if (!PyArg_ParseTuple(args, "O!|i", &Imaging_Type, &imagep, &blend)) + if (!PyArg_ParseTuple(args, "O!|i", &Imaging_Type, &imagep, &blend)) { return NULL; + } self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type); - if (self == NULL) + if (self == NULL) { return NULL; + } /* keep a reference to the image object */ Py_INCREF(imagep); @@ -2683,11 +2807,13 @@ _draw_ink(ImagingDrawObject* self, PyObject* args) { INT32 ink = 0; PyObject* color; - if (!PyArg_ParseTuple(args, "O", &color)) + if (!PyArg_ParseTuple(args, "O", &color)) { return NULL; + } - if (!getink(color, self->image->image, (char*) &ink)) + if (!getink(color, self->image->image, (char*) &ink)) { return NULL; + } return PyLong_FromLong((int) ink); } @@ -2703,12 +2829,14 @@ _draw_arc(ImagingDrawObject* self, PyObject* args) int width = 0; float start, end; int op = 0; - if (!PyArg_ParseTuple(args, "Offi|ii", &data, &start, &end, &ink, &width)) + if (!PyArg_ParseTuple(args, "Offi|ii", &data, &start, &end, &ink, &width)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n != 2) { PyErr_SetString(PyExc_TypeError, must_be_two_coordinates); free(xy); @@ -2723,8 +2851,9 @@ _draw_arc(ImagingDrawObject* self, PyObject* args) free(xy); - if (n < 0) + if (n < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2739,12 +2868,14 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args) PyObject *data; ImagingObject* bitmap; int ink; - if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink)) + if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n != 1) { PyErr_SetString(PyExc_TypeError, "coordinate list must contain exactly 1 coordinate" @@ -2760,8 +2891,9 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args) free(xy); - if (n < 0) + if (n < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2778,12 +2910,14 @@ _draw_chord(ImagingDrawObject* self, PyObject* args) int width = 0; float start, end; if (!PyArg_ParseTuple(args, "Offii|i", - &data, &start, &end, &ink, &fill, &width)) + &data, &start, &end, &ink, &fill, &width)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n != 2) { PyErr_SetString(PyExc_TypeError, must_be_two_coordinates); free(xy); @@ -2798,8 +2932,9 @@ _draw_chord(ImagingDrawObject* self, PyObject* args) free(xy); - if (n < 0) + if (n < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2815,12 +2950,14 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; int width = 0; - if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) + if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n != 2) { PyErr_SetString(PyExc_TypeError, must_be_two_coordinates); free(xy); @@ -2835,8 +2972,9 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) free(xy); - if (n < 0) + if (n < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2851,12 +2989,14 @@ _draw_lines(ImagingDrawObject* self, PyObject* args) PyObject *data; int ink; int width = 0; - if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width)) + if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (width <= 1) { double *p = NULL; @@ -2870,12 +3010,13 @@ _draw_lines(ImagingDrawObject* self, PyObject* args) return NULL; } } - if (p) /* draw last point */ + if (p) {/* draw last point */ ImagingDrawPoint( self->image->image, (int) p[2], (int) p[3], &ink, self->blend ); + } } else { for (i = 0; i < n-1; i++) { double *p = &xy[i+i]; @@ -2903,12 +3044,14 @@ _draw_points(ImagingDrawObject* self, PyObject* args) PyObject *data; int ink; - if (!PyArg_ParseTuple(args, "Oi", &data, &ink)) + if (!PyArg_ParseTuple(args, "Oi", &data, &ink)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } for (i = 0; i < n; i++) { double *p = &xy[i+i]; @@ -2938,8 +3081,9 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) PyObject* outline_; int ink; int fill = 0; - if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill)) + if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill)) { return NULL; + } outline = PyOutline_AsOutline(outline_); if (!outline) { @@ -2948,8 +3092,9 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) } if (ImagingDrawOutline(self->image->image, outline, - &ink, fill, self->blend) < 0) - return NULL; + &ink, fill, self->blend) < 0) { + return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -2967,12 +3112,14 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args) int ink, fill; int width = 0; float start, end; - if (!PyArg_ParseTuple(args, "Offii|i", &data, &start, &end, &ink, &fill, &width)) + if (!PyArg_ParseTuple(args, "Offii|i", &data, &start, &end, &ink, &fill, &width)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n != 2) { PyErr_SetString(PyExc_TypeError, must_be_two_coordinates); free(xy); @@ -2987,8 +3134,9 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args) free(xy); - if (n < 0) + if (n < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -3004,12 +3152,14 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args) PyObject* data; int ink; int fill = 0; - if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) + if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n < 2) { PyErr_SetString(PyExc_TypeError, "coordinate list must contain at least 2 coordinates" @@ -3050,12 +3200,14 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; int width = 0; - if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) + if (!PyArg_ParseTuple(args, "Oi|ii", &data, &ink, &fill, &width)) { return NULL; + } n = PyPath_Flatten(data, &xy); - if (n < 0) + if (n < 0) { return NULL; + } if (n != 2) { PyErr_SetString(PyExc_TypeError, must_be_two_coordinates); free(xy); @@ -3070,8 +3222,9 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args) free(xy); - if (n < 0) + if (n < 0) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -3106,12 +3259,14 @@ pixel_access_new(ImagingObject* imagep, PyObject* args) PixelAccessObject *self; int readonly = 0; - if (!PyArg_ParseTuple(args, "|i", &readonly)) + if (!PyArg_ParseTuple(args, "|i", &readonly)) { return NULL; + } self = PyObject_New(PixelAccessObject, &PixelAccess_Type); - if (self == NULL) + if (self == NULL) { return NULL; + } /* keep a reference to the image object */ Py_INCREF(imagep); @@ -3133,8 +3288,9 @@ static PyObject * pixel_access_getitem(PixelAccessObject *self, PyObject *xy) { int x, y; - if (_getxy(xy, &x, &y)) + if (_getxy(xy, &x, &y)) { return NULL; + } return getpixel(self->image->image, self->image->access, x, y); } @@ -3151,8 +3307,9 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) return -1; } - if (_getxy(xy, &x, &y)) + if (_getxy(xy, &x, &y)) { return -1; + } if (x < 0) { x = im->xsize + x; @@ -3166,11 +3323,13 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) return -1; } - if (!color) /* FIXME: raise exception? */ + if (!color) {/* FIXME: raise exception? */ return 0; + } - if (!getink(color, im, ink)) + if (!getink(color, im, ink)) { return -1; + } self->image->access->put_pixel(im, x, y, ink); @@ -3196,8 +3355,9 @@ _effect_mandelbrot(ImagingObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "|(ii)(dddd)i", &xsize, &ysize, &extent[0], &extent[1], &extent[2], &extent[3], - &quality)) - return NULL; + &quality)) { + return NULL; + } return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality)); } @@ -3207,8 +3367,9 @@ _effect_noise(ImagingObject* self, PyObject* args) { int xsize, ysize; float sigma = 128; - if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma)) + if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma)) { return NULL; + } return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma)); } @@ -3218,8 +3379,9 @@ _effect_spread(ImagingObject* self, PyObject* args) { int dist; - if (!PyArg_ParseTuple(args, "i", &dist)) + if (!PyArg_ParseTuple(args, "i", &dist)) { return NULL; + } return PyImagingNew(ImagingEffectSpread(self->image, dist)); } @@ -3237,8 +3399,9 @@ _getcodecstatus(PyObject* self, PyObject* args) int status; char* msg; - if (!PyArg_ParseTuple(args, "i", &status)) + if (!PyArg_ParseTuple(args, "i", &status)) { return NULL; + } switch (status) { case IMAGING_CODEC_OVERRUN: @@ -3268,11 +3431,13 @@ _save_ppm(ImagingObject* self, PyObject* args) { char* filename; - if (!PyArg_ParseTuple(args, "s", &filename)) + if (!PyArg_ParseTuple(args, "s", &filename)) { return NULL; + } - if (!ImagingSavePPM(self->image, filename)) + if (!ImagingSavePPM(self->image, filename)) { return NULL; + } Py_INCREF(Py_None); return Py_None; @@ -3457,8 +3622,9 @@ image_item(ImagingObject *self, Py_ssize_t i) if (im->xsize > 0) { x = i % im->xsize; y = i / im->xsize; - } else + } else { x = y = 0; /* leave it to getpixel to raise an exception */ + } return getpixel(im, self->access, x, y); } @@ -3614,12 +3780,14 @@ _get_stats(PyObject* self, PyObject* args) PyObject* d; ImagingMemoryArena arena = &ImagingDefaultArena; - if (!PyArg_ParseTuple(args, ":get_stats")) + if (!PyArg_ParseTuple(args, ":get_stats")) { return NULL; + } d = PyDict_New(); - if ( ! d) + if ( ! d) { return NULL; + } PyDict_SetItemString(d, "new_count", PyLong_FromLong(arena->stats_new_count)); PyDict_SetItemString(d, "allocated_blocks", @@ -3640,8 +3808,9 @@ _reset_stats(PyObject* self, PyObject* args) { ImagingMemoryArena arena = &ImagingDefaultArena; - if (!PyArg_ParseTuple(args, ":reset_stats")) + if (!PyArg_ParseTuple(args, ":reset_stats")) { return NULL; + } arena->stats_new_count = 0; arena->stats_allocated_blocks = 0; @@ -3656,8 +3825,9 @@ _reset_stats(PyObject* self, PyObject* args) static PyObject* _get_alignment(PyObject* self, PyObject* args) { - if (!PyArg_ParseTuple(args, ":get_alignment")) + if (!PyArg_ParseTuple(args, ":get_alignment")) { return NULL; + } return PyLong_FromLong(ImagingDefaultArena.alignment); } @@ -3665,8 +3835,9 @@ _get_alignment(PyObject* self, PyObject* args) static PyObject* _get_block_size(PyObject* self, PyObject* args) { - if (!PyArg_ParseTuple(args, ":get_block_size")) + if (!PyArg_ParseTuple(args, ":get_block_size")) { return NULL; + } return PyLong_FromLong(ImagingDefaultArena.block_size); } @@ -3674,8 +3845,9 @@ _get_block_size(PyObject* self, PyObject* args) static PyObject* _get_blocks_max(PyObject* self, PyObject* args) { - if (!PyArg_ParseTuple(args, ":get_blocks_max")) + if (!PyArg_ParseTuple(args, ":get_blocks_max")) { return NULL; + } return PyLong_FromLong(ImagingDefaultArena.blocks_max); } @@ -3684,8 +3856,9 @@ static PyObject* _set_alignment(PyObject* self, PyObject* args) { int alignment; - if (!PyArg_ParseTuple(args, "i:set_alignment", &alignment)) + if (!PyArg_ParseTuple(args, "i:set_alignment", &alignment)) { return NULL; + } if (alignment < 1 || alignment > 128) { PyErr_SetString(PyExc_ValueError, "alignment should be from 1 to 128"); @@ -3707,8 +3880,9 @@ static PyObject* _set_block_size(PyObject* self, PyObject* args) { int block_size; - if (!PyArg_ParseTuple(args, "i:set_block_size", &block_size)) + if (!PyArg_ParseTuple(args, "i:set_block_size", &block_size)) { return NULL; + } if (block_size <= 0) { PyErr_SetString(PyExc_ValueError, @@ -3732,8 +3906,9 @@ static PyObject* _set_blocks_max(PyObject* self, PyObject* args) { int blocks_max; - if (!PyArg_ParseTuple(args, "i:set_blocks_max", &blocks_max)) + if (!PyArg_ParseTuple(args, "i:set_blocks_max", &blocks_max)) { return NULL; + } if (blocks_max < 0) { PyErr_SetString(PyExc_ValueError, @@ -3761,8 +3936,9 @@ _clear_cache(PyObject* self, PyObject* args) { int i = 0; - if (!PyArg_ParseTuple(args, "|i:clear_cache", &i)) + if (!PyArg_ParseTuple(args, "|i:clear_cache", &i)) { return NULL; + } ImagingMemoryClearCache(&ImagingDefaultArena, i); @@ -3951,18 +4127,22 @@ setup_module(PyObject* m) { const char* version = (char*)PILLOW_VERSION; /* Ready object types */ - if (PyType_Ready(&Imaging_Type) < 0) + if (PyType_Ready(&Imaging_Type) < 0) { return -1; + } #ifdef WITH_IMAGEDRAW - if (PyType_Ready(&ImagingFont_Type) < 0) + if (PyType_Ready(&ImagingFont_Type) < 0) { return -1; + } - if (PyType_Ready(&ImagingDraw_Type) < 0) + if (PyType_Ready(&ImagingDraw_Type) < 0) { return -1; + } #endif - if (PyType_Ready(&PixelAccess_Type) < 0) + if (PyType_Ready(&PixelAccess_Type) < 0) { return -1; + } ImagingAccessInit(); @@ -4046,8 +4226,9 @@ PyInit__imaging(void) { m = PyModule_Create(&module_def); - if (setup_module(m) < 0) + if (setup_module(m) < 0) { return NULL; + } return m; } diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 0f287013b27..60b6b722884 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -88,8 +88,9 @@ cms_profile_new(cmsHPROFILE profile) CmsProfileObject* self; self = PyObject_New(CmsProfileObject, &CmsProfile_Type); - if (!self) + if (!self) { return NULL; + } self->profile = profile; @@ -102,8 +103,9 @@ cms_profile_open(PyObject* self, PyObject* args) cmsHPROFILE hProfile; char* sProfile; - if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) + if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) { return NULL; + } hProfile = cmsOpenProfileFromFile(sProfile, "r"); if (!hProfile) { @@ -121,8 +123,9 @@ cms_profile_fromstring(PyObject* self, PyObject* args) char* pProfile; Py_ssize_t nProfile; - if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) + if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) { return NULL; + } hProfile = cmsOpenProfileFromMem(pProfile, nProfile); if (!hProfile) { @@ -198,8 +201,9 @@ cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out) CmsTransformObject* self; self = PyObject_New(CmsTransformObject, &CmsTransform_Type); - if (!self) + if (!self) { return NULL; + } self->transform = transform; @@ -292,17 +296,19 @@ pyCMSgetAuxChannelChannel (cmsUInt32Number format, int auxChannelNdx) if (T_SWAPFIRST(format) && T_DOSWAP(format)) { // reverse order, before anything but last extra is shifted last - if (auxChannelNdx == numExtras - 1) + if (auxChannelNdx == numExtras - 1) { return numColors + numExtras - 1; - else + } else { return numExtras - 2 - auxChannelNdx; + } } else if (T_SWAPFIRST(format)) { // in order, after color channels, but last extra is shifted to first - if (auxChannelNdx == numExtras - 1) + if (auxChannelNdx == numExtras - 1) { return 0; - else + } else { return numColors + 1 + auxChannelNdx; + } } else if (T_DOSWAP(format)) { // reverse order, before anything @@ -330,23 +336,26 @@ pyCMScopyAux (cmsHTRANSFORM hTransform, Imaging imDst, const Imaging imSrc) int e; // trivially copied - if (imDst == imSrc) + if (imDst == imSrc) { return; + } dstLCMSFormat = cmsGetTransformOutputFormat(hTransform); srcLCMSFormat = cmsGetTransformInputFormat(hTransform); // currently, all Pillow formats are chunky formats, but check it anyway - if (T_PLANAR(dstLCMSFormat) || T_PLANAR(srcLCMSFormat)) + if (T_PLANAR(dstLCMSFormat) || T_PLANAR(srcLCMSFormat)) { return; + } // copy only if channel format is identical, except OPTIMIZED is ignored as it // does not affect the aux channel if (T_FLOAT(dstLCMSFormat) != T_FLOAT(srcLCMSFormat) || T_FLAVOR(dstLCMSFormat) != T_FLAVOR(srcLCMSFormat) || T_ENDIAN16(dstLCMSFormat) != T_ENDIAN16(srcLCMSFormat) - || T_BYTES(dstLCMSFormat) != T_BYTES(srcLCMSFormat)) + || T_BYTES(dstLCMSFormat) != T_BYTES(srcLCMSFormat)) { return; + } numSrcExtras = T_EXTRA(srcLCMSFormat); numDstExtras = T_EXTRA(dstLCMSFormat); @@ -367,8 +376,9 @@ pyCMScopyAux (cmsHTRANSFORM hTransform, Imaging imDst, const Imaging imSrc) char* pDstExtras = imDst->image[y] + dstChannel * channelSize; const char* pSrcExtras = imSrc->image[y] + srcChannel * channelSize; - for (x = 0; x < xSize; x++) + for (x = 0; x < xSize; x++) { memcpy(pDstExtras + x * dstChunkSize, pSrcExtras + x * srcChunkSize, channelSize); + } } } } @@ -378,14 +388,16 @@ pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) { int i; - if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) + if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) { return -1; + } Py_BEGIN_ALLOW_THREADS // transform color channels only - for (i = 0; i < im->ysize; i++) + for (i = 0; i < im->ysize; i++) { cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize); + } // lcms by default does nothing to the auxiliary channels leaving those // unchanged. To do "the right thing" here, i.e. maintain identical results @@ -417,8 +429,9 @@ _buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sIn Py_END_ALLOW_THREADS - if (!hTransform) + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build transform"); + } return hTransform; /* if NULL, an exception is set */ } @@ -442,8 +455,9 @@ _buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsH Py_END_ALLOW_THREADS - if (!hTransform) + if (!hTransform) { PyErr_SetString(PyExc_ValueError, "cannot build proof transform"); + } return hTransform; /* if NULL, an exception is set */ } @@ -462,13 +476,15 @@ buildTransform(PyObject *self, PyObject *args) { cmsHTRANSFORM transform = NULL; - if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS)) + if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS)) { return NULL; + } transform = _buildTransform(pInputProfile->profile, pOutputProfile->profile, sInMode, sOutMode, iRenderingIntent, cmsFLAGS); - if (!transform) + if (!transform) { return NULL; + } return cms_transform_new(transform, sInMode, sOutMode); } @@ -487,13 +503,15 @@ buildProofTransform(PyObject *self, PyObject *args) cmsHTRANSFORM transform = NULL; - if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS)) + if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS)) { return NULL; + } transform = _buildProofTransform(pInputProfile->profile, pOutputProfile->profile, pProofProfile->profile, sInMode, sOutMode, iRenderingIntent, iProofIntent, cmsFLAGS); - if (!transform) + if (!transform) { return NULL; + } return cms_transform_new(transform, sInMode, sOutMode); @@ -509,8 +527,9 @@ cms_transform_apply(CmsTransformObject *self, PyObject *args) int result; - if (!PyArg_ParseTuple(args, "nn:apply", &idIn, &idOut)) + if (!PyArg_ParseTuple(args, "nn:apply", &idIn, &idOut)) { return NULL; + } im = (Imaging) idIn; imOut = (Imaging) idOut; @@ -532,8 +551,9 @@ createProfile(PyObject *self, PyObject *args) cmsCIExyY whitePoint; cmsBool result; - if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) + if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) { return NULL; + } if (strcmp(sColorSpace, "LAB") == 0) { if (dColorTemp > 0.0) { @@ -575,8 +595,9 @@ cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) int intent; int direction; - if (!PyArg_ParseTuple(args, "ii:is_intent_supported", &intent, &direction)) + if (!PyArg_ParseTuple(args, "ii:is_intent_supported", &intent, &direction)) { return NULL; + } result = cmsIsIntentSupported(self->profile, intent, direction); @@ -602,8 +623,9 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) HANDLE handle = 0; int is_dc = 0; - if (!PyArg_ParseTuple(args, "|" F_HANDLE "i:get_display_profile", &handle, &is_dc)) + if (!PyArg_ParseTuple(args, "|" F_HANDLE "i:get_display_profile", &handle, &is_dc)) { return NULL; + } filename_size = sizeof(filename); @@ -615,8 +637,9 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) ReleaseDC((HWND) handle, dc); } - if (ok) + if (ok) { return PyUnicode_FromStringAndSize(filename, filename_size-1); + } Py_INCREF(Py_None); return Py_None; @@ -745,10 +768,11 @@ _profile_read_ciexyz(CmsProfileObject* self, cmsTagSignature info, int multi) Py_INCREF(Py_None); return Py_None; } - if (multi) + if (multi) { return _xyz3_py(XYZ); - else + } else { return _xyz_py(XYZ); + } } static PyObject* @@ -826,8 +850,9 @@ static cmsBool _calculate_rgb_primaries(CmsProfileObject* self, cmsCIEXYZTRIPLE* // double array of RGB values with max on each identity hXYZ = cmsCreateXYZProfile(); - if (hXYZ == NULL) + if (hXYZ == NULL) { return 0; + } // transform from our profile to XYZ using doubles for highest precision hTransform = cmsCreateTransform(self->profile, TYPE_RGB_DBL, @@ -835,8 +860,9 @@ static cmsBool _calculate_rgb_primaries(CmsProfileObject* self, cmsCIEXYZTRIPLE* INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE); cmsCloseProfile(hXYZ); - if (hTransform == NULL) + if (hTransform == NULL) { return 0; + } cmsDoTransform(hTransform, (void*) input, result, 3); cmsDeleteTransform(hTransform); @@ -881,8 +907,9 @@ _is_intent_supported(CmsProfileObject* self, int clut) /* Only valid for ICC Intents (otherwise we read invalid memory in lcms cmsio1.c). */ if (!(intent == INTENT_PERCEPTUAL || intent == INTENT_RELATIVE_COLORIMETRIC - || intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) + || intent == INTENT_SATURATION || intent == INTENT_ABSOLUTE_COLORIMETRIC)) { continue; + } id = PyLong_FromLong((long) intent); entry = Py_BuildValue("(OOO)", @@ -1276,8 +1303,9 @@ cms_profile_getattr_red_primary(CmsProfileObject* self, void* closure) cmsBool result = 0; cmsCIEXYZTRIPLE primaries; - if (cmsIsMatrixShaper(self->profile)) + if (cmsIsMatrixShaper(self->profile)) { result = _calculate_rgb_primaries(self, &primaries); + } if (! result) { Py_INCREF(Py_None); return Py_None; @@ -1292,8 +1320,9 @@ cms_profile_getattr_green_primary(CmsProfileObject* self, void* closure) cmsBool result = 0; cmsCIEXYZTRIPLE primaries; - if (cmsIsMatrixShaper(self->profile)) + if (cmsIsMatrixShaper(self->profile)) { result = _calculate_rgb_primaries(self, &primaries); + } if (! result) { Py_INCREF(Py_None); return Py_None; @@ -1308,8 +1337,9 @@ cms_profile_getattr_blue_primary(CmsProfileObject* self, void* closure) cmsBool result = 0; cmsCIEXYZTRIPLE primaries; - if (cmsIsMatrixShaper(self->profile)) + if (cmsIsMatrixShaper(self->profile)) { result = _calculate_rgb_primaries(self, &primaries); + } if (! result) { Py_INCREF(Py_None); return Py_None; @@ -1387,12 +1417,13 @@ cms_profile_getattr_icc_measurement_condition (CmsProfileObject* self, void* clo return Py_None; } - if (mc->Geometry == 1) + if (mc->Geometry == 1) { geo = "45/0, 0/45"; - else if (mc->Geometry == 2) + } else if (mc->Geometry == 2) { geo = "0d, d/0"; - else + } else { geo = "unknown"; + } return Py_BuildValue("{s:i,s:(ddd),s:s,s:d,s:s}", "observer", mc->Observer, @@ -1611,8 +1642,9 @@ PyInit__imagingcms(void) { m = PyModule_Create(&module_def); - if (setup_module(m) < 0) + if (setup_module(m) < 0) { return NULL; + } PyDateTime_IMPORT; diff --git a/src/_imagingft.c b/src/_imagingft.c index f1b299d9136..795ab4d2013 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -137,11 +137,12 @@ geterror(int code) { int i; - for (i = 0; ft_errors[i].message; i++) + for (i = 0; ft_errors[i].message; i++) { if (ft_errors[i].code == code) { PyErr_SetString(PyExc_OSError, ft_errors[i].message); return NULL; } + } PyErr_SetString(PyExc_OSError, "unknown freetype error"); return NULL; @@ -274,8 +275,9 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) self = PyObject_New(FontObject, &Font_Type); if (!self) { - if (filename) + if (filename) { PyMem_Free(filename); + } return NULL; } @@ -299,8 +301,9 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) } } - if (!error) + if (!error) { error = FT_Set_Pixel_Sizes(self->face, 0, size); + } if (!error && encoding && strlen((char*) encoding) == 4) { FT_Encoding encoding_tag = FT_MAKE_TAG( @@ -308,8 +311,9 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) ); error = FT_Select_Charmap(self->face, encoding_tag); } - if (filename) + if (filename) { PyMem_Free(filename); + } if (error) { if (self->font_bytes) { @@ -327,8 +331,9 @@ static int font_getchar(PyObject* string, int index, FT_ULong* char_out) { if (PyUnicode_Check(string)) { - if (index >= PyUnicode_GET_LENGTH(string)) + if (index >= PyUnicode_GET_LENGTH(string)) { return 0; + } *char_out = PyUnicode_READ_CHAR(string, index); return 1; } @@ -443,8 +448,9 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * if (PyUnicode_Check(item)) { bytes = PyUnicode_AsUTF8String(item); - if (bytes == NULL) + if (bytes == NULL) { goto failed; + } feature = PyBytes_AS_STRING(bytes); size = PyBytes_GET_SIZE(bytes); } @@ -608,8 +614,9 @@ font_getsize(FontObject* self, PyObject* args) /* calculate size and bearing for a given string */ PyObject* string; - if (!PyArg_ParseTuple(args, "O|zOz:getsize", &string, &dir, &features, &lang)) + if (!PyArg_ParseTuple(args, "O|zOz:getsize", &string, &dir, &features, &lang)) { return NULL; + } count = text_layout(string, self, dir, features, lang, &glyph_info, 0); if (PyErr_Occurred()) { @@ -631,8 +638,9 @@ font_getsize(FontObject* self, PyObject* args) * Yifu Yu, 2014-10-15 */ error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); - if (error) + if (error) { return geterror(error); + } if (i == 0) { if (horizontal_dir) { @@ -657,21 +665,26 @@ font_getsize(FontObject* self, PyObject* args) offset = glyph_info[i].x_advance - face->glyph->metrics.width - face->glyph->metrics.horiBearingX; - if (offset < 0) + if (offset < 0) { x_advanced -= offset; - if (x_advanced > x_max) + } + if (x_advanced > x_max) { x_max = x_advanced; + } bbox.yMax += glyph_info[i].y_offset; bbox.yMin += glyph_info[i].y_offset; - if (bbox.yMax > y_max) + if (bbox.yMax > y_max) { y_max = bbox.yMax; - if (bbox.yMin < y_min) + } + if (bbox.yMin < y_min) { y_min = bbox.yMin; + } // find max distance of baseline from top - if (face->glyph->metrics.horiBearingY > yoffset) + if (face->glyph->metrics.horiBearingY > yoffset) { yoffset = face->glyph->metrics.horiBearingY; + } } else { y_max -= glyph_info[i].y_advance; @@ -685,10 +698,12 @@ font_getsize(FontObject* self, PyObject* args) y_max -= offset; } - if (bbox.xMax > x_max) + if (bbox.xMax > x_max) { x_max = bbox.xMax; - if (i == 0 || bbox.xMin < x_min) + } + if (i == 0 || bbox.xMin < x_min) { x_min = bbox.xMin; + } } FT_Done_Glyph(glyph); @@ -702,20 +717,22 @@ font_getsize(FontObject* self, PyObject* args) if (face) { if (horizontal_dir) { // left bearing - if (xoffset < 0) + if (xoffset < 0) { x_max -= xoffset; - else + } else { xoffset = 0; + } /* difference between the font ascender and the distance of * the baseline from the top */ yoffset = PIXEL(self->face->size->metrics.ascender - yoffset); } else { // top bearing - if (yoffset < 0) + if (yoffset < 0) { y_max -= yoffset; - else + } else { yoffset = 0; + } } } @@ -800,8 +817,9 @@ font_render(FontObject* self, PyObject* args) temp = bitmap.rows - glyph_slot->bitmap_top; temp -= PIXEL(glyph_info[i].y_offset); - if (temp > ascender) + if (temp > ascender) { ascender = temp; + } } if (stroker == NULL) { @@ -855,10 +873,12 @@ font_render(FontObject* self, PyObject* args) x0 = 0; x1 = bitmap.width; - if (xx < 0) + if (xx < 0) { x0 = -xx; - if (xx + x1 > im->xsize) + } + if (xx + x1 > im->xsize) { x1 = im->xsize - xx; + } source = (unsigned char*) bitmap.buffer; for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++) { @@ -876,8 +896,9 @@ font_render(FontObject* self, PyObject* args) // use monochrome mask (on palette images, etc) int j, k, m = 128; for (j = k = 0; j < x1; j++) { - if (j >= x0 && (source[k] & m)) + if (j >= x0 && (source[k] & m)) { target[j] = 255; + } if (!(m >>= 1)) { m = 128; k++; @@ -887,8 +908,9 @@ font_render(FontObject* self, PyObject* args) // use antialiased rendering int k; for (k = x0; k < x1; k++) { - if (target[k] < source[k]) + if (target[k] < source[k]) { target[k] = source[k]; + } } } } @@ -919,8 +941,9 @@ font_render(FontObject* self, PyObject* args) PyObject *list_names, *list_name; error = FT_Get_MM_Var(self->face, &master); - if (error) + if (error) { return geterror(error); + } num_namedstyles = master->num_namedstyles; list_names = PyList_New(num_namedstyles); @@ -928,12 +951,14 @@ font_render(FontObject* self, PyObject* args) name_count = FT_Get_Sfnt_Name_Count(self->face); for (i = 0; i < name_count; i++) { error = FT_Get_Sfnt_Name(self->face, i, &name); - if (error) + if (error) { return geterror(error); + } for (j = 0; j < num_namedstyles; j++) { - if (PyList_GetItem(list_names, j) != NULL) + if (PyList_GetItem(list_names, j) != NULL) { continue; + } if (master->namedstyle[j].strid == name.name_id) { list_name = Py_BuildValue("y#", name.string, name.string_len); @@ -958,8 +983,9 @@ font_render(FontObject* self, PyObject* args) FT_SfntName name; PyObject *list_axes, *list_axis, *axis_name; error = FT_Get_MM_Var(self->face, &master); - if (error) + if (error) { return geterror(error); + } num_axis = master->num_axis; name_count = FT_Get_Sfnt_Name_Count(self->face); @@ -978,8 +1004,9 @@ font_render(FontObject* self, PyObject* args) for (j = 0; j < name_count; j++) { error = FT_Get_Sfnt_Name(self->face, j, &name); - if (error) + if (error) { return geterror(error); + } if (name.name_id == axis.strid) { axis_name = Py_BuildValue("y#", name.string, name.string_len); @@ -1002,12 +1029,14 @@ font_render(FontObject* self, PyObject* args) int error; int instance_index; - if (!PyArg_ParseTuple(args, "i", &instance_index)) + if (!PyArg_ParseTuple(args, "i", &instance_index)) { return NULL; + } error = FT_Set_Named_Instance(self->face, instance_index); - if (error) + if (error) { return geterror(error); + } Py_INCREF(Py_None); return Py_None; @@ -1022,8 +1051,9 @@ font_render(FontObject* self, PyObject* args) Py_ssize_t i, num_coords; FT_Fixed *coords; FT_Fixed coord; - if (!PyArg_ParseTuple(args, "O", &axes)) + if (!PyArg_ParseTuple(args, "O", &axes)) { return NULL; + } if (!PyList_Check(axes)) { PyErr_SetString(PyExc_TypeError, "argument must be a list"); @@ -1037,13 +1067,13 @@ font_render(FontObject* self, PyObject* args) } for (i = 0; i < num_coords; i++) { item = PyList_GET_ITEM(axes, i); - if (PyFloat_Check(item)) + if (PyFloat_Check(item)) { coord = PyFloat_AS_DOUBLE(item); - else if (PyLong_Check(item)) + } else if (PyLong_Check(item)) { coord = (float) PyLong_AS_LONG(item); - else if (PyNumber_Check(item)) + } else if (PyNumber_Check(item)) { coord = PyFloat_AsDouble(item); - else { + } else { free(coords); PyErr_SetString(PyExc_TypeError, "list must contain numbers"); return NULL; @@ -1053,8 +1083,9 @@ font_render(FontObject* self, PyObject* args) error = FT_Set_Var_Design_Coordinates(self->face, num_coords, coords); free(coords); - if (error) + if (error) { return geterror(error); + } Py_INCREF(Py_None); return Py_None; @@ -1090,16 +1121,18 @@ static PyMethodDef font_methods[] = { static PyObject* font_getattr_family(FontObject* self, void* closure) { - if (self->face->family_name) + if (self->face->family_name) { return PyUnicode_FromString(self->face->family_name); + } Py_RETURN_NONE; } static PyObject* font_getattr_style(FontObject* self, void* closure) { - if (self->face->style_name) + if (self->face->style_name) { return PyUnicode_FromString(self->face->style_name); + } Py_RETURN_NONE; } @@ -1200,8 +1233,9 @@ setup_module(PyObject* m) { /* Ready object type */ PyType_Ready(&Font_Type); - if (FT_Init_FreeType(&library)) + if (FT_Init_FreeType(&library)) { return 0; /* leave it uninitialized */ + } FT_Library_Version(library, &major, &minor, &patch); @@ -1230,8 +1264,9 @@ PyInit__imagingft(void) { m = PyModule_Create(&module_def); - if (setup_module(m) < 0) + if (setup_module(m) < 0) { return NULL; + } return m; } diff --git a/src/_imagingmath.c b/src/_imagingmath.c index bc66a581a22..959859a1d3e 100644 --- a/src/_imagingmath.c +++ b/src/_imagingmath.c @@ -88,12 +88,14 @@ void name(Imaging out, Imaging im1, Imaging im2)\ static int powi(int x, int y) { double v = pow(x, y) + 0.5; - if (errno == EDOM) + if (errno == EDOM) { return 0; - if (v < MIN_INT32) + } + if (v < MIN_INT32) { v = MIN_INT32; - else if (v > MAX_INT32) + } else if (v > MAX_INT32) { v = MAX_INT32; + } return (int) v; } @@ -167,8 +169,9 @@ _unop(PyObject* self, PyObject* args) void (*unop)(Imaging, Imaging); Py_ssize_t op, i0, i1; - if (!PyArg_ParseTuple(args, "nnn", &op, &i0, &i1)) + if (!PyArg_ParseTuple(args, "nnn", &op, &i0, &i1)) { return NULL; + } out = (Imaging) i0; im1 = (Imaging) i1; @@ -190,8 +193,9 @@ _binop(PyObject* self, PyObject* args) void (*binop)(Imaging, Imaging, Imaging); Py_ssize_t op, i0, i1, i2; - if (!PyArg_ParseTuple(args, "nnnn", &op, &i0, &i1, &i2)) + if (!PyArg_ParseTuple(args, "nnnn", &op, &i0, &i1, &i2)) { return NULL; + } out = (Imaging) i0; im1 = (Imaging) i1; @@ -215,8 +219,9 @@ static void install(PyObject *d, char* name, void* value) { PyObject *v = PyLong_FromSsize_t((Py_ssize_t) value); - if (!v || PyDict_SetItemString(d, name, v)) + if (!v || PyDict_SetItemString(d, name, v)) { PyErr_Clear(); + } Py_XDECREF(v); } @@ -286,8 +291,9 @@ PyInit__imagingmath(void) { m = PyModule_Create(&module_def); - if (setup_module(m) < 0) + if (setup_module(m) < 0) { return NULL; + } return m; } diff --git a/src/_imagingmorph.c b/src/_imagingmorph.c index 050ae9f0287..4f7844604c6 100644 --- a/src/_imagingmorph.c +++ b/src/_imagingmorph.c @@ -85,8 +85,9 @@ apply(PyObject *self, PyObject* args) /* zero boundary conditions. TBD support other modes */ outrow[0] = outrow[width-1] = 0; if (row_idx==0 || row_idx == height-1) { - for(col_idx=0; col_idxstate, 0, sizeof(decoder->state)); @@ -81,8 +83,9 @@ PyImaging_DecoderNew(int contextsize) (void) PyErr_NoMemory(); return NULL; } - } else + } else { context = 0; + } /* Initialize decoder context */ decoder->state.context = context; @@ -104,8 +107,9 @@ PyImaging_DecoderNew(int contextsize) static void _dealloc(ImagingDecoderObject* decoder) { - if (decoder->cleanup) + if (decoder->cleanup) { decoder->cleanup(&decoder->state); + } free(decoder->state.buffer); free(decoder->state.context); Py_XDECREF(decoder->lock); @@ -121,8 +125,9 @@ _decode(ImagingDecoderObject* decoder, PyObject* args) int status; ImagingSectionCookie cookie; - if (!PyArg_ParseTuple(args, "y#", &buffer, &bufsize)) + if (!PyArg_ParseTuple(args, "y#", &buffer, &bufsize)) { return NULL; + } if (!decoder->pulls_fd) { ImagingSectionEnter(&cookie); @@ -164,11 +169,13 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) x0 = y0 = x1 = y1 = 0; /* FIXME: should publish the ImagingType descriptor */ - if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) + if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) { return NULL; + } im = PyImaging_AsImaging(op); - if (!im) + if (!im) { return NULL; + } decoder->im = im; @@ -203,8 +210,9 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) } /* malloc check ok, overflow checked above */ state->buffer = (UINT8*) malloc(state->bytes); - if (!state->buffer) + if (!state->buffer) { return PyErr_NoMemory(); + } } /* Keep a reference to the image object, to make sure it doesn't @@ -223,8 +231,9 @@ _setfd(ImagingDecoderObject* decoder, PyObject* args) PyObject* fd; ImagingCodecState state; - if (!PyArg_ParseTuple(args, "O", &fd)) + if (!PyArg_ParseTuple(args, "O", &fd)) { return NULL; + } state = &decoder->state; @@ -330,8 +339,9 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) int sign = 0; int ystep = 1; if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill, - &sign, &ystep)) + &sign, &ystep)) { return NULL; + } if (strcmp(mode, "F") != 0) { PyErr_SetString(PyExc_ValueError, "bad image mode"); @@ -339,8 +349,9 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) } decoder = PyImaging_DecoderNew(sizeof(BITSTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } decoder->decode = ImagingBitDecode; @@ -368,8 +379,9 @@ PyImaging_BcnDecoderNew(PyObject* self, PyObject* args) char* actual; int n = 0; int ystep = 1; - if (!PyArg_ParseTuple(args, "s|ii", &mode, &n, &ystep)) + if (!PyArg_ParseTuple(args, "s|ii", &mode, &n, &ystep)) { return NULL; + } switch (n) { case 1: /* BC1: 565 color, 1-bit alpha */ @@ -394,8 +406,9 @@ PyImaging_BcnDecoderNew(PyObject* self, PyObject* args) } decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } decoder->decode = ImagingBcnDecode; decoder->state.state = n; @@ -415,8 +428,9 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) ImagingDecoderObject* decoder; decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } decoder->decode = ImagingFliDecode; @@ -436,8 +450,9 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) char* mode; int bits = 8; int interlace = 0; - if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace)) + if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace)) { return NULL; + } if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) { PyErr_SetString(PyExc_ValueError, "bad image mode"); @@ -445,8 +460,9 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) } decoder = PyImaging_DecoderNew(sizeof(GIFDECODERSTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } decoder->decode = ImagingGifDecode; @@ -468,15 +484,18 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; - if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) + if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) { return NULL; + } decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingHexDecode; @@ -504,17 +523,20 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) int fp; uint32 ifdoffset; - if (! PyArg_ParseTuple(args, "sssiI", &mode, &rawmode, &compname, &fp, &ifdoffset)) + if (! PyArg_ParseTuple(args, "sssiI", &mode, &rawmode, &compname, &fp, &ifdoffset)) { return NULL; + } TRACE(("new tiff decoder %s\n", compname)); decoder = PyImaging_DecoderNew(sizeof(TIFFSTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } if (! ImagingLibTiffInit(&decoder->state, fp, ifdoffset)) { Py_DECREF(decoder); @@ -541,15 +563,18 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; - if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) + if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) { return NULL; + } decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingPackbitsDecode; @@ -567,12 +592,14 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) ImagingDecoderObject* decoder; decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } /* Unpack from PhotoYCC to RGB */ - if (get_unpacker(decoder, "RGB", "YCC;P") < 0) + if (get_unpacker(decoder, "RGB", "YCC;P") < 0) { return NULL; + } decoder->decode = ImagingPcdDecode; @@ -592,15 +619,18 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; int stride; - if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride)) + if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride)) { return NULL; + } decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->state.bytes = stride; @@ -623,15 +653,18 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args) char* rawmode; int stride = 0; int ystep = 1; - if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) { return NULL; + } decoder = PyImaging_DecoderNew(sizeof(RAWSTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingRawDecode; @@ -656,15 +689,18 @@ PyImaging_SgiRleDecoderNew(PyObject* self, PyObject* args) char* rawmode; int ystep = 1; int bpc = 1; - if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &bpc)) + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &bpc)) { return NULL; + } decoder = PyImaging_DecoderNew(sizeof(SGISTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->pulls_fd = 1; decoder->decode = ImagingSgiRleDecode; @@ -687,15 +723,18 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; - if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) + if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) { return NULL; + } decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingSunRleDecode; @@ -716,15 +755,18 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) char* rawmode; int ystep = 1; int depth = 8; - if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth)) + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth)) { return NULL; + } decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingTgaRleDecode; @@ -745,11 +787,13 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) ImagingDecoderObject* decoder; decoder = PyImaging_DecoderNew(0); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, "1", "1;R") < 0) + if (get_unpacker(decoder, "1", "1;R") < 0) { return NULL; + } decoder->decode = ImagingXbmDecode; @@ -773,15 +817,18 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; int interlaced = 0; - if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced)) + if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced)) { return NULL; + } decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingZipDecode; decoder->cleanup = ImagingZipDecodeCleanup; @@ -826,15 +873,18 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) int draft = 0; if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode, - &scale, &draft)) + &scale, &draft)) { return NULL; + } - if (!jpegmode) + if (!jpegmode) { jpegmode = ""; + } decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } // libjpeg-turbo supports different output formats. // We are choosing Pillow's native format (3 color bytes + 1 padding) @@ -843,8 +893,9 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) rawmode = "RGBX"; } - if (get_unpacker(decoder, mode, rawmode) < 0) + if (get_unpacker(decoder, mode, rawmode) < 0) { return NULL; + } decoder->decode = ImagingJpegDecode; decoder->cleanup = ImagingJpegDecodeCleanup; @@ -882,21 +933,24 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args) PY_LONG_LONG length = -1; if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format, - &reduce, &layers, &fd, &length)) + &reduce, &layers, &fd, &length)) { return NULL; + } - if (strcmp(format, "j2k") == 0) + if (strcmp(format, "j2k") == 0) { codec_format = OPJ_CODEC_J2K; - else if (strcmp(format, "jpt") == 0) + } else if (strcmp(format, "jpt") == 0) { codec_format = OPJ_CODEC_JPT; - else if (strcmp(format, "jp2") == 0) + } else if (strcmp(format, "jp2") == 0) { codec_format = OPJ_CODEC_JP2; - else + } else { return NULL; + } decoder = PyImaging_DecoderNew(sizeof(JPEG2KDECODESTATE)); - if (decoder == NULL) + if (decoder == NULL) { return NULL; + } decoder->pulls_fd = 1; decoder->decode = ImagingJpeg2KDecode; diff --git a/src/display.c b/src/display.c index 9a337d1c041..ce2cf7e9844 100644 --- a/src/display.c +++ b/src/display.c @@ -52,12 +52,14 @@ _new(const char* mode, int xsize, int ysize) { ImagingDisplayObject *display; - if (PyType_Ready(&ImagingDisplayType) < 0) + if (PyType_Ready(&ImagingDisplayType) < 0) { return NULL; + } display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType); - if (display == NULL) + if (display == NULL) { return NULL; + } display->dib = ImagingNewDIB(mode, xsize, ysize); if (!display->dib) { @@ -71,8 +73,9 @@ _new(const char* mode, int xsize, int ysize) static void _delete(ImagingDisplayObject* display) { - if (display->dib) + if (display->dib) { ImagingDeleteDIB(display->dib); + } PyObject_Del(display); } @@ -80,8 +83,9 @@ static PyObject* _expose(ImagingDisplayObject* display, PyObject* args) { HDC hdc; - if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) + if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) { return NULL; + } ImagingExposeDIB(display->dib, hdc); @@ -97,8 +101,9 @@ _draw(ImagingDisplayObject* display, PyObject* args) int src[4]; if (!PyArg_ParseTuple(args, F_HANDLE "(iiii)(iiii)", &hdc, dst+0, dst+1, dst+2, dst+3, - src+0, src+1, src+2, src+3)) + src+0, src+1, src+2, src+3)) { return NULL; + } ImagingDrawDIB(display->dib, hdc, dst, src); @@ -116,16 +121,20 @@ _paste(ImagingDisplayObject* display, PyObject* args) PyObject* op; int xy[4]; xy[0] = xy[1] = xy[2] = xy[3] = 0; - if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy+0, xy+1, xy+2, xy+3)) + if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy+0, xy+1, xy+2, xy+3)) { return NULL; + } im = PyImaging_AsImaging(op); - if (!im) + if (!im) { return NULL; + } - if (xy[2] <= xy[0]) + if (xy[2] <= xy[0]) { xy[2] = xy[0] + im->xsize; - if (xy[3] <= xy[1]) + } + if (xy[3] <= xy[1]) { xy[3] = xy[1] + im->ysize; + } ImagingPasteDIB(display->dib, im, xy); @@ -139,8 +148,9 @@ _query_palette(ImagingDisplayObject* display, PyObject* args) HDC hdc; int status; - if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) + if (!PyArg_ParseTuple(args, F_HANDLE, &hdc)) { return NULL; + } status = ImagingQueryPaletteDIB(display->dib, hdc); @@ -153,8 +163,9 @@ _getdc(ImagingDisplayObject* display, PyObject* args) HWND window; HDC dc; - if (!PyArg_ParseTuple(args, F_HANDLE, &window)) + if (!PyArg_ParseTuple(args, F_HANDLE, &window)) { return NULL; + } dc = GetDC(window); if (!dc) { @@ -171,8 +182,9 @@ _releasedc(ImagingDisplayObject* display, PyObject* args) HWND window; HDC dc; - if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, &window, &dc)) + if (!PyArg_ParseTuple(args, F_HANDLE F_HANDLE, &window, &dc)) { return NULL; + } ReleaseDC(window, dc); @@ -186,8 +198,9 @@ _frombytes(ImagingDisplayObject* display, PyObject* args) char* ptr; int bytes; - if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) + if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) { return NULL; + } if (display->dib->ysize * display->dib->linesize != bytes) { PyErr_SetString(PyExc_ValueError, "wrong size"); @@ -203,8 +216,9 @@ _frombytes(ImagingDisplayObject* display, PyObject* args) static PyObject* _tobytes(ImagingDisplayObject* display, PyObject* args) { - if (!PyArg_ParseTuple(args, ":tobytes")) + if (!PyArg_ParseTuple(args, ":tobytes")) { return NULL; + } return PyBytes_FromStringAndSize( display->dib->bits, display->dib->ysize * display->dib->linesize @@ -284,12 +298,14 @@ PyImaging_DisplayWin32(PyObject* self, PyObject* args) char *mode; int xsize, ysize; - if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) { return NULL; + } display = _new(mode, xsize, ysize); - if (display == NULL) + if (display == NULL) { return NULL; + } return (PyObject*) display; } @@ -324,8 +340,9 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) HMODULE user32; Func_SetThreadDpiAwarenessContext SetThreadDpiAwarenessContext_function; - if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &all_screens)) + if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &all_screens)) { return NULL; + } /* step 1: create a memory DC large enough to hold the entire screen */ @@ -361,25 +378,30 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) FreeLibrary(user32); bitmap = CreateCompatibleBitmap(screen, width, height); - if (!bitmap) + if (!bitmap) { goto error; + } - if (!SelectObject(screen_copy, bitmap)) + if (!SelectObject(screen_copy, bitmap)) { goto error; + } /* step 2: copy bits into memory DC bitmap */ rop = SRCCOPY; - if (includeLayeredWindows) + if (includeLayeredWindows) { rop |= CAPTUREBLT; - if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop)) + } + if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop)) { goto error; + } /* step 3: extract bits from bitmap */ buffer = PyBytes_FromStringAndSize(NULL, height * ((width*3 + 3) & -4)); - if (!buffer) + if (!buffer) { return NULL; + } core.bcSize = sizeof(core); core.bcWidth = width; @@ -387,8 +409,9 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) core.bcPlanes = 1; core.bcBitCount = 24; if (!GetDIBits(screen_copy, bitmap, 0, height, PyBytes_AS_STRING(buffer), - (BITMAPINFO*) &core, DIB_RGB_COLORS)) + (BITMAPINFO*) &core, DIB_RGB_COLORS)) { goto error; + } DeleteObject(bitmap); DeleteDC(screen_copy); @@ -418,12 +441,15 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) title_size = GetWindowTextLength(hwnd); if (title_size > 0) { title = PyUnicode_FromStringAndSize(NULL, title_size); - if (title) + if (title) { GetWindowTextW(hwnd, PyUnicode_AS_UNICODE(title), title_size+1); - } else + } + } else { title = PyUnicode_FromString(""); - if (!title) + } + if (!title) { return 0; + } /* get bounding boxes */ GetClientRect(hwnd, &inner); @@ -434,15 +460,17 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) inner.left, inner.top, inner.right, inner.bottom, outer.left, outer.top, outer.right, outer.bottom ); - if (!item) + if (!item) { return 0; + } status = PyList_Append(window_list, item); Py_DECREF(item); - if (status < 0) + if (status < 0) { return 0; + } return 1; } @@ -453,8 +481,9 @@ PyImaging_ListWindowsWin32(PyObject* self, PyObject* args) PyObject* window_list; window_list = PyList_New(0); - if (!window_list) + if (!window_list) { return NULL; + } EnumWindows(list_windows_callback, (LPARAM) window_list); @@ -556,8 +585,9 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) GetWindowLongPtr(wnd, sizeof(PyObject*)); current_threadstate = PyThreadState_Swap(NULL); PyEval_RestoreThread(threadstate); - } else + } else { return DefWindowProc(wnd, message, wParam, lParam); + } } /* process message */ @@ -575,28 +605,31 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom ); - if (result) + if (result) { Py_DECREF(result); - else + } else { callback_error("window damage callback"); + } result = PyObject_CallFunction( callback, "s" F_HANDLE "iiii", "clear", dc, 0, 0, rect.right-rect.left, rect.bottom-rect.top ); - if (result) + if (result) { Py_DECREF(result); - else + } else { callback_error("window clear callback"); + } result = PyObject_CallFunction( callback, "s" F_HANDLE "iiii", "repair", dc, 0, 0, rect.right-rect.left, rect.bottom-rect.top ); - if (result) + if (result) { Py_DECREF(result); - else + } else { callback_error("window repair callback"); + } ReleaseDC(wnd, dc); EndPaint(wnd, &ps); @@ -610,17 +643,19 @@ windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) if (result) { InvalidateRect(wnd, NULL, 1); Py_DECREF(result); - } else + } else { callback_error("window resize callback"); + } break; case WM_DESTROY: /* destroy window */ result = PyObject_CallFunction(callback, "s", "destroy"); - if (result) + if (result) { Py_DECREF(result); - else + } else { callback_error("window destroy callback"); + } Py_DECREF(callback); break; @@ -646,13 +681,16 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) char* title; PyObject* callback; int width = 0, height = 0; - if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height)) + if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height)) { return NULL; + } - if (width <= 0) + if (width <= 0) { width = CW_USEDEFAULT; - if (height <= 0) + } + if (height <= 0) { height = CW_USEDEFAULT; + } /* register toplevel window class */ windowClass.style = CS_CLASSDC; @@ -731,8 +769,9 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) int width, height; int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "y#(ii)(iiii):_load", &data, &datasize, - &width, &height, &x0, &x1, &y0, &y1)) + &width, &height, &x0, &x1, &y0, &y1)) { return NULL; + } /* step 1: copy metafile contents into METAFILE object */ @@ -806,8 +845,9 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) error: DeleteEnhMetaFile(meta); - if (bitmap) + if (bitmap) { DeleteObject(bitmap); + } DeleteDC(dc); @@ -838,8 +878,9 @@ PyImaging_GrabScreenX11(PyObject* self, PyObject* args) xcb_generic_error_t* error; PyObject* buffer = NULL; - if (!PyArg_ParseTuple(args, "|z", &display_name)) + if (!PyArg_ParseTuple(args, "|z", &display_name)) { return NULL; + } /* connect to X and get screen data */ @@ -893,8 +934,9 @@ PyImaging_GrabScreenX11(PyObject* self, PyObject* args) free(reply); xcb_disconnect(connection); - if (!buffer) + if (!buffer) { return NULL; + } return Py_BuildValue("(ii)N", width, height, buffer); } diff --git a/src/encode.c b/src/encode.c index e5649891348..b285292f327 100644 --- a/src/encode.c +++ b/src/encode.c @@ -55,12 +55,14 @@ PyImaging_EncoderNew(int contextsize) ImagingEncoderObject *encoder; void *context; - if(PyType_Ready(&ImagingEncoderType) < 0) + if(PyType_Ready(&ImagingEncoderType) < 0) { return NULL; + } encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } /* Clear the encoder state */ memset(&encoder->state, 0, sizeof(encoder->state)); @@ -73,8 +75,9 @@ PyImaging_EncoderNew(int contextsize) (void) PyErr_NoMemory(); return NULL; } - } else + } else { context = 0; + } /* Initialize encoder context */ encoder->state.context = context; @@ -93,8 +96,9 @@ PyImaging_EncoderNew(int contextsize) static void _dealloc(ImagingEncoderObject* encoder) { - if (encoder->cleanup) + if (encoder->cleanup) { encoder->cleanup(&encoder->state); + } free(encoder->state.buffer); free(encoder->state.context); Py_XDECREF(encoder->lock); @@ -125,19 +129,22 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) Py_ssize_t bufsize = 16384; - if (!PyArg_ParseTuple(args, "|n", &bufsize)) + if (!PyArg_ParseTuple(args, "|n", &bufsize)) { return NULL; + } buf = PyBytes_FromStringAndSize(NULL, bufsize); - if (!buf) + if (!buf) { return NULL; + } status = encoder->encode(encoder->im, &encoder->state, (UINT8*) PyBytes_AsString(buf), bufsize); /* adjust string length to avoid slicing in encoder */ - if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0) + if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0) { return NULL; + } result = Py_BuildValue("iiO", status, encoder->state.errcode, buf); @@ -179,14 +186,16 @@ _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) Py_ssize_t fh; Py_ssize_t bufsize = 16384; - if (!PyArg_ParseTuple(args, "n|n", &fh, &bufsize)) + if (!PyArg_ParseTuple(args, "n|n", &fh, &bufsize)) { return NULL; + } /* Allocate an encoder buffer */ /* malloc check ok, either constant int, or checked by PyArg_ParseTuple */ buf = (UINT8*) malloc(bufsize); - if (!buf) + if (!buf) { return PyErr_NoMemory(); + } ImagingSectionEnter(&cookie); @@ -197,12 +206,13 @@ _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) status = encoder->encode(encoder->im, &encoder->state, buf, bufsize); - if (status > 0) + if (status > 0) { if (write(fh, buf, status) < 0) { ImagingSectionLeave(&cookie); free(buf); return PyErr_SetFromErrno(PyExc_OSError); } + } } while (encoder->state.errcode == 0); @@ -228,11 +238,13 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args) x0 = y0 = x1 = y1 = 0; /* FIXME: should publish the ImagingType descriptor */ - if (!PyArg_ParseTuple(args, "O|(nnnn)", &op, &x0, &y0, &x1, &y1)) + if (!PyArg_ParseTuple(args, "O|(nnnn)", &op, &x0, &y0, &x1, &y1)) { return NULL; + } im = PyImaging_AsImaging(op); - if (!im) + if (!im) { return NULL; + } encoder->im = im; @@ -264,8 +276,9 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args) state->bytes = (state->bits * state->xsize+7)/8; /* malloc check ok, overflow checked above */ state->buffer = (UINT8*) malloc(state->bytes); - if (!state->buffer) + if (!state->buffer) { return PyErr_NoMemory(); + } } /* Keep a reference to the image object, to make sure it doesn't @@ -284,8 +297,9 @@ _setfd(ImagingEncoderObject* encoder, PyObject* args) PyObject* fd; ImagingCodecState state; - if (!PyArg_ParseTuple(args, "O", &fd)) + if (!PyArg_ParseTuple(args, "O", &fd)) { return NULL; + } state = &encoder->state; @@ -386,8 +400,9 @@ PyImaging_EpsEncoderNew(PyObject* self, PyObject* args) ImagingEncoderObject* encoder; encoder = PyImaging_EncoderNew(0); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } encoder->encode = ImagingEpsEncode; @@ -408,15 +423,18 @@ PyImaging_GifEncoderNew(PyObject* self, PyObject* args) char *rawmode; Py_ssize_t bits = 8; Py_ssize_t interlace = 0; - if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &bits, &interlace)) + if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &bits, &interlace)) { return NULL; + } encoder = PyImaging_EncoderNew(sizeof(GIFENCODERSTATE)); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } - if (get_packer(encoder, mode, rawmode) < 0) + if (get_packer(encoder, mode, rawmode) < 0) { return NULL; + } encoder->encode = ImagingGifEncode; @@ -473,15 +491,18 @@ PyImaging_RawEncoderNew(PyObject* self, PyObject* args) Py_ssize_t stride = 0; Py_ssize_t ystep = 1; - if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &stride, &ystep)) + if (!PyArg_ParseTuple(args, "ss|nn", &mode, &rawmode, &stride, &ystep)) { return NULL; + } encoder = PyImaging_EncoderNew(0); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } - if (get_packer(encoder, mode, rawmode) < 0) + if (get_packer(encoder, mode, rawmode) < 0) { return NULL; + } encoder->encode = ImagingRawEncode; @@ -505,15 +526,18 @@ PyImaging_TgaRleEncoderNew(PyObject* self, PyObject* args) char *rawmode; Py_ssize_t ystep = 1; - if (!PyArg_ParseTuple(args, "ss|n", &mode, &rawmode, &ystep)) + if (!PyArg_ParseTuple(args, "ss|n", &mode, &rawmode, &ystep)) { return NULL; + } encoder = PyImaging_EncoderNew(0); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } - if (get_packer(encoder, mode, rawmode) < 0) + if (get_packer(encoder, mode, rawmode) < 0) { return NULL; + } encoder->encode = ImagingTgaRleEncode; @@ -534,11 +558,13 @@ PyImaging_XbmEncoderNew(PyObject* self, PyObject* args) ImagingEncoderObject* encoder; encoder = PyImaging_EncoderNew(0); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } - if (get_packer(encoder, "1", "1;R") < 0) + if (get_packer(encoder, "1", "1;R") < 0) { return NULL; + } encoder->encode = ImagingXbmEncode; @@ -569,19 +595,22 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "ss|nnny#", &mode, &rawmode, &optimize, &compress_level, &compress_type, - &dictionary, &dictionary_size)) + &dictionary, &dictionary_size)) { return NULL; + } /* Copy to avoid referencing Python's memory */ if (dictionary && dictionary_size > 0) { /* malloc check ok, size comes from PyArg_ParseTuple */ char* p = malloc(dictionary_size); - if (!p) + if (!p) { return PyErr_NoMemory(); + } memcpy(p, dictionary, dictionary_size); dictionary = p; - } else + } else { dictionary = NULL; + } encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE)); if (encoder == NULL) { @@ -597,9 +626,10 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) encoder->encode = ImagingZipEncode; encoder->cleanup = ImagingZipEncodeCleanup; - if (rawmode[0] == 'P') + if (rawmode[0] == 'P') { /* disable filtering */ ((ZIPSTATE*)encoder->state.context)->mode = ZIP_PNG_PALETTE; + } ((ZIPSTATE*)encoder->state.context)->optimize = optimize; ((ZIPSTATE*)encoder->state.context)->compress_level = compress_level; @@ -675,11 +705,13 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename)); encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE)); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } - if (get_packer(encoder, mode, rawmode) < 0) + if (get_packer(encoder, mode, rawmode) < 0) { return NULL; + } if (! ImagingLibTiffEncodeInit(&encoder->state, filename, fp)) { Py_DECREF(encoder); @@ -1027,12 +1059,14 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) &mode, &rawmode, &quality, &progressive, &smooth, &optimize, &streamtype, &xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size, - &rawExif, &rawExifLen)) + &rawExif, &rawExifLen)) { return NULL; + } encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE)); - if (encoder == NULL) + if (encoder == NULL) { return NULL; + } // libjpeg-turbo supports different output formats. // We are choosing Pillow's native format (3 color bytes + 1 padding) @@ -1041,8 +1075,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) rawmode = "RGBX"; } - if (get_packer(encoder, mode, rawmode) < 0) + if (get_packer(encoder, mode, rawmode) < 0) { return NULL; + } // Freed in JpegEncode, Case 5 qarrays = get_qtables_arrays(qtables, &qtablesLen); @@ -1054,8 +1089,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) return PyErr_NoMemory(); memcpy(p, extra, extra_size); extra = p; - } else + } else { extra = NULL; + } if (rawExif && rawExifLen > 0) { /* malloc check ok, length is from python parsearg */ @@ -1066,8 +1102,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) } memcpy(pp, rawExif, rawExifLen); rawExif = pp; - } else + } else { rawExif = NULL; + } encoder->encode = ImagingJpegEncode; @@ -1111,10 +1148,12 @@ j2k_decode_coord_tuple(PyObject *tuple, int *x, int *y) *x = (int)PyLong_AsLong(PyTuple_GET_ITEM(tuple, 0)); *y = (int)PyLong_AsLong(PyTuple_GET_ITEM(tuple, 1)); - if (*x < 0) + if (*x < 0) { *x = 0; - if (*y < 0) + } + if (*y < 0) { *y = 0; + } } } @@ -1144,45 +1183,50 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args) &quality_mode, &quality_layers, &num_resolutions, &cblk_size, &precinct_size, &irreversible, &progression, &cinema_mode, - &fd)) + &fd)) { return NULL; + } - if (strcmp (format, "j2k") == 0) + if (strcmp (format, "j2k") == 0) { codec_format = OPJ_CODEC_J2K; - else if (strcmp (format, "jpt") == 0) + } else if (strcmp (format, "jpt") == 0) { codec_format = OPJ_CODEC_JPT; - else if (strcmp (format, "jp2") == 0) + } else if (strcmp (format, "jp2") == 0) { codec_format = OPJ_CODEC_JP2; - else + } else { return NULL; + } - if (strcmp(progression, "LRCP") == 0) + if (strcmp(progression, "LRCP") == 0) { prog_order = OPJ_LRCP; - else if (strcmp(progression, "RLCP") == 0) + } else if (strcmp(progression, "RLCP") == 0) { prog_order = OPJ_RLCP; - else if (strcmp(progression, "RPCL") == 0) + } else if (strcmp(progression, "RPCL") == 0) { prog_order = OPJ_RPCL; - else if (strcmp(progression, "PCRL") == 0) + } else if (strcmp(progression, "PCRL") == 0) { prog_order = OPJ_PCRL; - else if (strcmp(progression, "CPRL") == 0) + } else if (strcmp(progression, "CPRL") == 0) { prog_order = OPJ_CPRL; - else + } else { return NULL; + } - if (strcmp(cinema_mode, "no") == 0) + if (strcmp(cinema_mode, "no") == 0) { cine_mode = OPJ_OFF; - else if (strcmp(cinema_mode, "cinema2k-24") == 0) + } else if (strcmp(cinema_mode, "cinema2k-24") == 0) { cine_mode = OPJ_CINEMA2K_24; - else if (strcmp(cinema_mode, "cinema2k-48") == 0) + } else if (strcmp(cinema_mode, "cinema2k-48") == 0) { cine_mode = OPJ_CINEMA2K_48; - else if (strcmp(cinema_mode, "cinema4k-24") == 0) + } else if (strcmp(cinema_mode, "cinema4k-24") == 0) { cine_mode = OPJ_CINEMA4K_24; - else + } else { return NULL; + } encoder = PyImaging_EncoderNew(sizeof(JPEG2KENCODESTATE)); - if (!encoder) + if (!encoder) { return NULL; + } encoder->encode = ImagingJpeg2KEncode; encoder->cleanup = ImagingJpeg2KEncodeCleanup; diff --git a/src/libImaging/Access.c b/src/libImaging/Access.c index 15ffa11fc77..755e2639afe 100644 --- a/src/libImaging/Access.c +++ b/src/libImaging/Access.c @@ -22,8 +22,9 @@ static inline UINT32 hash(const char* mode) { UINT32 i = ACCESS_TABLE_HASH; - while (*mode) + while (*mode) { i = ((i<<5) + i) ^ (UINT8) *mode++; + } return i % ACCESS_TABLE_SIZE; } @@ -149,10 +150,11 @@ get_pixel_32B(Imaging im, int x, int y, void* color) static void put_pixel(Imaging im, int x, int y, const void* color) { - if (im->image8) + if (im->image8) { im->image8[y][x] = *((UINT8*) color); - else + } else { memcpy(&im->image32[y][x], color, sizeof(INT32)); + } } static void @@ -237,8 +239,9 @@ ImagingAccess ImagingAccessNew(Imaging im) { ImagingAccess access = &access_table[hash(im->mode)]; - if (im->mode[0] != access->mode[0] || strcmp(im->mode, access->mode) != 0) + if (im->mode[0] != access->mode[0] || strcmp(im->mode, access->mode) != 0) { return NULL; + } return access; } diff --git a/src/libImaging/AlphaComposite.c b/src/libImaging/AlphaComposite.c index a074334aaab..20b1df9e53a 100644 --- a/src/libImaging/AlphaComposite.c +++ b/src/libImaging/AlphaComposite.c @@ -33,19 +33,22 @@ ImagingAlphaComposite(Imaging imDst, Imaging imSrc) if (!imDst || !imSrc || strcmp(imDst->mode, "RGBA") || imDst->type != IMAGING_TYPE_UINT8 || - imDst->bands != 4) + imDst->bands != 4) { return ImagingError_ModeError(); + } if (strcmp(imDst->mode, imSrc->mode) || imDst->type != imSrc->type || imDst->bands != imSrc->bands || imDst->xsize != imSrc->xsize || - imDst->ysize != imSrc->ysize) + imDst->ysize != imSrc->ysize) { return ImagingError_Mismatch(); + } imOut = ImagingNewDirty(imDst->mode, imDst->xsize, imDst->ysize); - if (!imOut) + if (!imOut) { return NULL; + } for (y = 0; y < imDst->ysize; y++) { rgba8* dst = (rgba8*) imDst->image[y]; diff --git a/src/libImaging/Bands.c b/src/libImaging/Bands.c index 7fff0448639..39ce5c49c18 100644 --- a/src/libImaging/Bands.c +++ b/src/libImaging/Bands.c @@ -26,23 +26,28 @@ ImagingGetBand(Imaging imIn, int band) int x, y; /* Check arguments */ - if (!imIn || imIn->type != IMAGING_TYPE_UINT8) + if (!imIn || imIn->type != IMAGING_TYPE_UINT8) { return (Imaging) ImagingError_ModeError(); + } - if (band < 0 || band >= imIn->bands) + if (band < 0 || band >= imIn->bands) { return (Imaging) ImagingError_ValueError("band index out of range"); + } /* Shortcuts */ - if (imIn->bands == 1) + if (imIn->bands == 1) { return ImagingCopy(imIn); + } /* Special case for LXXA etc */ - if (imIn->bands == 2 && band == 1) + if (imIn->bands == 2 && band == 1) { band = 3; + } imOut = ImagingNewDirty("L", imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } /* Extract band from image */ for (y = 0; y < imIn->ysize; y++) { @@ -173,24 +178,29 @@ ImagingPutBand(Imaging imOut, Imaging imIn, int band) int x, y; /* Check arguments */ - if (!imIn || imIn->bands != 1 || !imOut) + if (!imIn || imIn->bands != 1 || !imOut) { return (Imaging) ImagingError_ModeError(); + } - if (band < 0 || band >= imOut->bands) + if (band < 0 || band >= imOut->bands) { return (Imaging) ImagingError_ValueError("band index out of range"); + } if (imIn->type != imOut->type || imIn->xsize != imOut->xsize || - imIn->ysize != imOut->ysize) + imIn->ysize != imOut->ysize) { return (Imaging) ImagingError_Mismatch(); + } /* Shortcuts */ - if (imOut->bands == 1) + if (imOut->bands == 1) { return ImagingCopy2(imOut, imIn); + } /* Special case for LXXA etc */ - if (imOut->bands == 2 && band == 1) + if (imOut->bands == 2 && band == 1) { band = 3; + } /* Insert band into image */ for (y = 0; y < imIn->ysize; y++) { @@ -211,15 +221,18 @@ ImagingFillBand(Imaging imOut, int band, int color) int x, y; /* Check arguments */ - if (!imOut || imOut->type != IMAGING_TYPE_UINT8) + if (!imOut || imOut->type != IMAGING_TYPE_UINT8) { return (Imaging) ImagingError_ModeError(); + } - if (band < 0 || band >= imOut->bands) + if (band < 0 || band >= imOut->bands) { return (Imaging) ImagingError_ValueError("band index out of range"); + } /* Special case for LXXA etc */ - if (imOut->bands == 2 && band == 1) + if (imOut->bands == 2 && band == 1) { band = 3; + } color = CLIP8(color); @@ -263,16 +276,18 @@ ImagingMerge(const char* mode, Imaging bands[4]) bandsCount = i; imOut = ImagingNewDirty(mode, firstBand->xsize, firstBand->ysize); - if ( ! imOut) + if ( ! imOut) { return NULL; + } if (imOut->bands != bandsCount) { ImagingDelete(imOut); return (Imaging) ImagingError_ValueError("wrong number of bands"); } - if (imOut->bands == 1) + if (imOut->bands == 1) { return ImagingCopy2(imOut, firstBand); + } if (imOut->bands == 2) { for (y = 0; y < imOut->ysize; y++) { diff --git a/src/libImaging/BcnDecode.c b/src/libImaging/BcnDecode.c index 3e62aa6c998..f908a03adad 100644 --- a/src/libImaging/BcnDecode.c +++ b/src/libImaging/BcnDecode.c @@ -610,15 +610,21 @@ static int bc6_unquantize(UINT16 v, int prec, int sign) { int x; if (!sign) { x = v; - if (prec >= 15) return x; - if (x == 0) return 0; + if (prec >= 15) { + return x; + } + if (x == 0) { + return 0; + } if (x == ((1 << prec) - 1)) { return 0xffff; } return ((x << 15) + 0x4000) >> (prec - 1); } else { x = (INT16)v; - if (prec >= 16) return x; + if (prec >= 16) { + return x; + } if (x < 0) { s = 1; x = -x; @@ -820,7 +826,9 @@ static int decode_bcn(Imaging im, ImagingCodecState state, const UINT8* src, int put_block(im, state, (const char *)col, sizeof(col[0]), C); \ ptr += SZ; \ bytes -= SZ; \ - if (state->y >= ymax) return -1; \ + if (state->y >= ymax) {\ + return -1; \ + }\ } \ break @@ -836,7 +844,9 @@ static int decode_bcn(Imaging im, ImagingCodecState state, const UINT8* src, int put_block(im, state, (const char *)col, sizeof(col[0]), C); ptr += 16; bytes -= 16; - if (state->y >= ymax) return -1; \ + if (state->y >= ymax) {\ + return -1; \ + }\ } break; DECODE_LOOP(7, 16, rgba); diff --git a/src/libImaging/BitDecode.c b/src/libImaging/BitDecode.c index 97d263c0b28..92edd746f08 100644 --- a/src/libImaging/BitDecode.c +++ b/src/libImaging/BitDecode.c @@ -43,15 +43,17 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt bitstate->mask = (1<bits)-1; - if (bitstate->sign) + if (bitstate->sign) { bitstate->signmask = (1<<(bitstate->bits-1)); + } /* check image orientation */ if (state->ystep < 0) { state->y = state->ysize-1; state->ystep = -1; - } else + } else { state->ystep = 1; + } state->state = 1; @@ -67,12 +69,13 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt bytes--; /* get a byte from the input stream and insert in the bit buffer */ - if (bitstate->fill&1) + if (bitstate->fill&1) { /* fill MSB first */ bitstate->bitbuffer |= (unsigned long) byte << bitstate->bitcount; - else + } else { /* fill LSB first */ bitstate->bitbuffer = (bitstate->bitbuffer << 8) | byte; + } bitstate->bitcount += 8; @@ -85,35 +88,39 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (bitstate->fill&2) { /* store LSB first */ data = bitstate->bitbuffer & bitstate->mask; - if (bitstate->bitcount > 32) + if (bitstate->bitcount > 32) { /* bitbuffer overflow; restore it from last input byte */ bitstate->bitbuffer = byte >> (8 - (bitstate->bitcount - bitstate->bits)); - else + } else { bitstate->bitbuffer >>= bitstate->bits; - } else + } + } else { /* store MSB first */ data = (bitstate->bitbuffer >> (bitstate->bitcount - bitstate->bits)) & bitstate->mask; + } bitstate->bitcount -= bitstate->bits; if (bitstate->lutsize > 0) { /* map through lookup table */ - if (data <= 0) + if (data <= 0) { pixel = bitstate->lut[0]; - else if (data >= bitstate->lutsize) + } else if (data >= bitstate->lutsize) { pixel = bitstate->lut[bitstate->lutsize-1]; - else + } else { pixel = bitstate->lut[data]; + } } else { /* convert */ - if (data & bitstate->signmask) + if (data & bitstate->signmask) { /* image memory contains signed data */ pixel = (FLOAT32) (INT32) (data | ~bitstate->mask); - else + } else { pixel = (FLOAT32) data; + } } *(FLOAT32*)(&im->image32[state->y][state->x]) = pixel; @@ -128,8 +135,9 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt } state->x = 0; /* reset bit buffer */ - if (bitstate->pad > 0) + if (bitstate->pad > 0) { bitstate->bitcount = 0; + } } } } diff --git a/src/libImaging/Blend.c b/src/libImaging/Blend.c index caaf2ba8bcc..0bac4cda9cb 100644 --- a/src/libImaging/Blend.c +++ b/src/libImaging/Blend.c @@ -28,24 +28,28 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) /* Check arguments */ if (!imIn1 || !imIn2 || imIn1->type != IMAGING_TYPE_UINT8 || imIn1->palette || strcmp(imIn1->mode, "1") == 0 - || imIn2->palette || strcmp(imIn2->mode, "1") == 0) + || imIn2->palette || strcmp(imIn2->mode, "1") == 0) { return ImagingError_ModeError(); + } if (imIn1->type != imIn2->type || imIn1->bands != imIn2->bands || imIn1->xsize != imIn2->xsize || - imIn1->ysize != imIn2->ysize) + imIn1->ysize != imIn2->ysize) { return ImagingError_Mismatch(); + } /* Shortcuts */ - if (alpha == 0.0) + if (alpha == 0.0) { return ImagingCopy(imIn1); - else if (alpha == 1.0) + } else if (alpha == 1.0) { return ImagingCopy(imIn2); + } imOut = ImagingNewDirty(imIn1->mode, imIn1->xsize, imIn1->ysize); - if (!imOut) + if (!imOut) { return NULL; + } if (alpha >= 0 && alpha <= 1.0) { /* Interpolate between bands */ @@ -53,9 +57,10 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) UINT8* in1 = (UINT8*) imIn1->image[y]; UINT8* in2 = (UINT8*) imIn2->image[y]; UINT8* out = (UINT8*) imOut->image[y]; - for (x = 0; x < imIn1->linesize; x++) + for (x = 0; x < imIn1->linesize; x++) { out[x] = (UINT8) ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); + } } } else { /* Extrapolation; must make sure to clip resulting values */ @@ -66,12 +71,13 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) for (x = 0; x < imIn1->linesize; x++) { float temp = (float) ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); - if (temp <= 0.0) + if (temp <= 0.0) { out[x] = 0; - else if (temp >= 255.0) + } else if (temp >= 255.0) { out[x] = 255; - else + } else { out[x] = (UINT8) temp; + } } } } diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index 9537c4f98ec..dcdc52cbc27 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -184,8 +184,9 @@ ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) int edgeB = MAX(imIn->xsize - radius - 1, 0); UINT32 *lineOut = calloc(imIn->xsize, sizeof(UINT32)); - if (lineOut == NULL) + if (lineOut == NULL) { return ImagingError_MemoryError(); + } // printf(">>> %d %d %d\n", radius, ww, fw); @@ -248,11 +249,13 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) imIn->type != imOut->type || imIn->bands != imOut->bands || imIn->xsize != imOut->xsize || - imIn->ysize != imOut->ysize) + imIn->ysize != imOut->ysize) { return ImagingError_Mismatch(); + } - if (imIn->type != IMAGING_TYPE_UINT8) + if (imIn->type != IMAGING_TYPE_UINT8) { return ImagingError_ModeError(); + } if (!(strcmp(imIn->mode, "RGB") == 0 || strcmp(imIn->mode, "RGBA") == 0 || @@ -261,12 +264,14 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float radius, int n) strcmp(imIn->mode, "CMYK") == 0 || strcmp(imIn->mode, "L") == 0 || strcmp(imIn->mode, "LA") == 0 || - strcmp(imIn->mode, "La") == 0)) + strcmp(imIn->mode, "La") == 0)) { return ImagingError_ModeError(); + } imTransposed = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize); - if (!imTransposed) + if (!imTransposed) { return NULL; + } /* Apply blur in one dimension. Use imOut as a destination at first pass, diff --git a/src/libImaging/Chops.c b/src/libImaging/Chops.c index a1673dff6c5..9dd708f7512 100644 --- a/src/libImaging/Chops.c +++ b/src/libImaging/Chops.c @@ -23,20 +23,22 @@ int x, y;\ Imaging imOut;\ imOut = create(imIn1, imIn2, mode);\ - if (!imOut)\ + if (!imOut) {\ return NULL;\ + }\ for (y = 0; y < imOut->ysize; y++) {\ UINT8* out = (UINT8*) imOut->image[y];\ UINT8* in1 = (UINT8*) imIn1->image[y];\ UINT8* in2 = (UINT8*) imIn2->image[y];\ for (x = 0; x < imOut->linesize; x++) {\ int temp = operation;\ - if (temp <= 0)\ + if (temp <= 0) {\ out[x] = 0;\ - else if (temp >= 255)\ + } else if (temp >= 255) {\ out[x] = 255;\ - else\ + } else {\ out[x] = temp;\ + }\ }\ }\ return imOut; @@ -45,8 +47,9 @@ int x, y;\ Imaging imOut;\ imOut = create(imIn1, imIn2, mode);\ - if (!imOut)\ + if (!imOut) {\ return NULL;\ + }\ for (y = 0; y < imOut->ysize; y++) {\ UINT8* out = (UINT8*) imOut->image[y];\ UINT8* in1 = (UINT8*) imIn1->image[y];\ @@ -63,11 +66,13 @@ create(Imaging im1, Imaging im2, char* mode) int xsize, ysize; if (!im1 || !im2 || im1->type != IMAGING_TYPE_UINT8 || - (mode != NULL && (strcmp(im1->mode, "1") || strcmp(im2->mode, "1")))) + (mode != NULL && (strcmp(im1->mode, "1") || strcmp(im2->mode, "1")))) { return (Imaging) ImagingError_ModeError(); + } if (im1->type != im2->type || - im1->bands != im2->bands) + im1->bands != im2->bands) { return (Imaging) ImagingError_Mismatch(); + } xsize = (im1->xsize < im2->xsize) ? im1->xsize : im2->xsize; ysize = (im1->ysize < im2->ysize) ? im1->ysize : im2->ysize; diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 9f57225431f..5e69b067dab 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -123,8 +123,9 @@ static void l2bit(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++) + for (x = 0; x < xsize; x++) { *out++ = (*in++ >= 128) ? 255 : 0; + } } static void @@ -206,8 +207,9 @@ static void la2l(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { *out++ = in[0]; + } } static void @@ -240,18 +242,20 @@ static void rgb2bit(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ *out++ = (L(in) >= 128000) ? 255 : 0; + } } static void rgb2l(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ *out++ = L24(in) >> 16; + } } static void @@ -653,12 +657,13 @@ i2l(UINT8* out, const UINT8* in_, int xsize) for (x = 0; x < xsize; x++, out++, in_ += 4) { INT32 v; memcpy(&v, in_, sizeof(v)); - if (v <= 0) + if (v <= 0) { *out = 0; - else if (v >= 255) + } else if (v >= 255) { *out = 255; - else + } else { *out = (UINT8) v; + } } } @@ -681,12 +686,13 @@ i2rgb(UINT8* out, const UINT8* in_, int xsize) int x; INT32* in = (INT32*) in_; for (x = 0; x < xsize; x++, in++, out+=4) { - if (*in <= 0) + if (*in <= 0) { out[0] = out[1] = out[2] = 0; - else if (*in >= 255) + } else if (*in >= 255) { out[0] = out[1] = out[2] = 255; - else + } else { out[0] = out[1] = out[2] = (UINT8) *in; + } out[3] = 255; } } @@ -741,12 +747,13 @@ f2l(UINT8* out, const UINT8* in_, int xsize) for (x = 0; x < xsize; x++, out++, in_ += 4) { FLOAT32 v; memcpy(&v, in_, sizeof(v)); - if (v <= 0.0) + if (v <= 0.0) { *out = 0; - else if (v >= 255.0) + } else if (v >= 255.0) { *out = 255; - else + } else { *out = (UINT8) v; + } } } @@ -797,8 +804,9 @@ static void ycbcr2l(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { *out++ = in[0]; + } } static void @@ -908,22 +916,26 @@ static void I16L_L(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++, in += 2) - if (in[1] != 0) + for (x = 0; x < xsize; x++, in += 2) { + if (in[1] != 0) { *out++ = 255; - else + } else { *out++ = in[0]; + } + } } static void I16B_L(UINT8* out, const UINT8* in, int xsize) { int x; - for (x = 0; x < xsize; x++, in += 2) - if (in[0] != 0) + for (x = 0; x < xsize; x++, in += 2) { + if (in[0] != 0) { *out++ = 255; - else + } else { *out++ = in[1]; + } + } } static struct { @@ -1056,8 +1068,9 @@ p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; /* FIXME: precalculate greyscale palette? */ - for (x = 0; x < xsize; x++) + for (x = 0; x < xsize; x++) { *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0; + } } static void @@ -1065,8 +1078,9 @@ pa2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; /* FIXME: precalculate greyscale palette? */ - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { *out++ = (L(&palette[in[0]*4]) >= 128000) ? 255 : 0; + } } static void @@ -1074,8 +1088,9 @@ p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; /* FIXME: precalculate greyscale palette? */ - for (x = 0; x < xsize; x++) + for (x = 0; x < xsize; x++) { *out++ = L(&palette[in[x]*4]) / 1000; + } } static void @@ -1083,8 +1098,9 @@ pa2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; /* FIXME: precalculate greyscale palette? */ - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { *out++ = L(&palette[in[0]*4]) / 1000; + } } static void @@ -1138,8 +1154,9 @@ pa2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) { int x; INT32* out = (INT32*) out_; - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { *out++ = L(&palette[in[0]*4]) / 1000; + } } static void @@ -1157,8 +1174,9 @@ pa2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) { int x; FLOAT32* out = (FLOAT32*) out_; - for (x = 0; x < xsize; x++, in += 4) + for (x = 0; x < xsize; x++, in += 4) { *out++ = (float) L(&palette[in[0]*4]) / 1000.0F; + } } static void @@ -1273,46 +1291,50 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) /* Map palette image to L, RGB, RGBA, or CMYK */ - if (!imIn->palette) + if (!imIn->palette) { return (Imaging) ImagingError_ValueError("no palette"); + } alpha = !strcmp(imIn->mode, "PA"); - if (strcmp(mode, "1") == 0) + if (strcmp(mode, "1") == 0) { convert = alpha ? pa2bit : p2bit; - else if (strcmp(mode, "L") == 0) + } else if (strcmp(mode, "L") == 0) { convert = alpha ? pa2l : p2l; - else if (strcmp(mode, "LA") == 0) + } else if (strcmp(mode, "LA") == 0) { convert = alpha ? pa2la : p2la; - else if (strcmp(mode, "PA") == 0) + } else if (strcmp(mode, "PA") == 0) { convert = p2pa; - else if (strcmp(mode, "I") == 0) + } else if (strcmp(mode, "I") == 0) { convert = alpha ? pa2i : p2i; - else if (strcmp(mode, "F") == 0) + } else if (strcmp(mode, "F") == 0) { convert = alpha ? pa2f : p2f; - else if (strcmp(mode, "RGB") == 0) + } else if (strcmp(mode, "RGB") == 0) { convert = alpha ? pa2rgb : p2rgb; - else if (strcmp(mode, "RGBA") == 0) + } else if (strcmp(mode, "RGBA") == 0) { convert = alpha ? pa2rgba : p2rgba; - else if (strcmp(mode, "RGBX") == 0) + } else if (strcmp(mode, "RGBX") == 0) { convert = alpha ? pa2rgba : p2rgba; - else if (strcmp(mode, "CMYK") == 0) + } else if (strcmp(mode, "CMYK") == 0) { convert = alpha ? pa2cmyk : p2cmyk; - else if (strcmp(mode, "YCbCr") == 0) + } else if (strcmp(mode, "YCbCr") == 0) { convert = alpha ? pa2ycbcr : p2ycbcr; - else if (strcmp(mode, "HSV") == 0) + } else if (strcmp(mode, "HSV") == 0) { convert = alpha ? pa2hsv : p2hsv; - else + } else { return (Imaging) ImagingError_ValueError("conversion not supported"); + } imOut = ImagingNew2Dirty(mode, imOut, imIn); - if (!imOut) + if (!imOut) { return NULL; + } ImagingSectionEnter(&cookie); - for (y = 0; y < imIn->ysize; y++) + for (y = 0; y < imIn->ysize; y++) { (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], imIn->xsize, imIn->palette->palette); + } ImagingSectionLeave(&cookie); return imOut; @@ -1330,26 +1352,30 @@ topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalett ImagingPalette palette = inpalette;; /* Map L or RGB/RGBX/RGBA to palette image */ - if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) + if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) { return (Imaging) ImagingError_ValueError("conversion not supported"); + } alpha = !strcmp(mode, "PA"); if (palette == NULL) { /* FIXME: make user configurable */ - if (imIn->bands == 1) + if (imIn->bands == 1) { palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */ - else + } else { palette = ImagingPaletteNewBrowser(); /* Standard colour cube */ + } } - if (!palette) + if (!palette) { return (Imaging) ImagingError_ValueError("no palette"); + } imOut = ImagingNew2Dirty(mode, imOut, imIn); if (!imOut) { - if (palette != inpalette) + if (palette != inpalette) { ImagingPaletteDelete(palette); + } return NULL; } @@ -1376,8 +1402,9 @@ topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalett /* Create mapping cache */ if (ImagingPaletteCachePrepare(palette) < 0) { ImagingDelete(imOut); - if (palette != inpalette) + if (palette != inpalette) { ImagingPaletteDelete(palette); + } return NULL; } @@ -1415,8 +1442,9 @@ topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalett /* get closest colour */ cache = &ImagingPaletteCache(palette, r, g, b); - if (cache[0] == 0x100) + if (cache[0] == 0x100) { ImagingPaletteCacheUpdate(palette, r, g, b); + } if (alpha) { out[x*4] = out[x*4+1] = out[x*4+2] = (UINT8) cache[0]; out[x*4+3] = 255; @@ -1464,8 +1492,9 @@ topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalett /* get closest colour */ cache = &ImagingPaletteCache(palette, r, g, b); - if (cache[0] == 0x100) + if (cache[0] == 0x100) { ImagingPaletteCacheUpdate(palette, r, g, b); + } if (alpha) { out[x*4] = out[x*4+1] = out[x*4+2] = (UINT8) cache[0]; out[x*4+3] = 255; @@ -1477,12 +1506,14 @@ topalette(Imaging imOut, Imaging imIn, const char *mode, ImagingPalette inpalett ImagingSectionLeave(&cookie); } - if (inpalette != palette) + if (inpalette != palette) { ImagingPaletteCacheDelete(palette); + } } - if (inpalette != palette) + if (inpalette != palette) { ImagingPaletteDelete(palette); + } return imOut; } @@ -1495,12 +1526,14 @@ tobilevel(Imaging imOut, Imaging imIn, int dither) int* errors; /* Map L or RGB to dithered 1 image */ - if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) + if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) { return (Imaging) ImagingError_ValueError("conversion not supported"); + } imOut = ImagingNew2Dirty("1", imOut, imIn); - if (!imOut) + if (!imOut) { return NULL; + } errors = calloc(imIn->xsize + 1, sizeof(int)); if (!errors) { @@ -1582,42 +1615,50 @@ convert(Imaging imOut, Imaging imIn, const char *mode, ImagingShuffler convert; int y; - if (!imIn) + if (!imIn) { return (Imaging) ImagingError_ModeError(); + } if (!mode) { /* Map palette image to full depth */ - if (!imIn->palette) + if (!imIn->palette) { return (Imaging) ImagingError_ModeError(); + } mode = imIn->palette->mode; - } else + } else { /* Same mode? */ - if (!strcmp(imIn->mode, mode)) + if (!strcmp(imIn->mode, mode)) { return ImagingCopy2(imOut, imIn); + } + } /* test for special conversions */ - if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) + if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) { return frompalette(imOut, imIn, mode); + } - if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) + if (strcmp(mode, "P") == 0 || strcmp(mode, "PA") == 0) { return topalette(imOut, imIn, mode, palette, dither); + } - if (dither && strcmp(mode, "1") == 0) + if (dither && strcmp(mode, "1") == 0) { return tobilevel(imOut, imIn, dither); + } /* standard conversion machinery */ convert = NULL; - for (y = 0; converters[y].from; y++) + for (y = 0; converters[y].from; y++) { if (!strcmp(imIn->mode, converters[y].from) && !strcmp(mode, converters[y].to)) { convert = converters[y].convert; break; } + } if (!convert) #ifdef notdef @@ -1632,13 +1673,15 @@ convert(Imaging imOut, Imaging imIn, const char *mode, #endif imOut = ImagingNew2Dirty(mode, imOut, imIn); - if (!imOut) + if (!imOut) { return NULL; + } ImagingSectionEnter(&cookie); - for (y = 0; y < imIn->ysize; y++) + for (y = 0; y < imIn->ysize; y++) { (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], imIn->xsize); + } ImagingSectionLeave(&cookie); return imOut; @@ -1727,17 +1770,19 @@ ImagingConvertInPlace(Imaging imIn, const char* mode) int y; /* limited support for inplace conversion */ - if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0) + if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0) { convert = l2bit; - else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0) + } else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0) { convert = bit2l; - else + } else { return ImagingError_ModeError(); + } ImagingSectionEnter(&cookie); - for (y = 0; y < imIn->ysize; y++) + for (y = 0; y < imIn->ysize; y++) { (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y], imIn->xsize); + } ImagingSectionLeave(&cookie); return imIn; diff --git a/src/libImaging/Copy.c b/src/libImaging/Copy.c index e0bdaf63ea5..5b4899f398f 100644 --- a/src/libImaging/Copy.c +++ b/src/libImaging/Copy.c @@ -25,21 +25,25 @@ _copy(Imaging imOut, Imaging imIn) ImagingSectionCookie cookie; int y; - if (!imIn) + if (!imIn) { return (Imaging) ImagingError_ValueError(NULL); + } imOut = ImagingNew2Dirty(imIn->mode, imOut, imIn); - if (!imOut) + if (!imOut) { return NULL; + } ImagingCopyPalette(imOut, imIn); ImagingSectionEnter(&cookie); - if (imIn->block != NULL && imOut->block != NULL) + if (imIn->block != NULL && imOut->block != NULL) { memcpy(imOut->block, imIn->block, imIn->ysize * imIn->linesize); - else - for (y = 0; y < imIn->ysize; y++) + } else { + for (y = 0; y < imIn->ysize; y++) { memcpy(imOut->image[y], imIn->image[y], imIn->linesize); + } + } ImagingSectionLeave(&cookie); return imOut; diff --git a/src/libImaging/Crop.c b/src/libImaging/Crop.c index a2b6796819b..29b4cf9d957 100644 --- a/src/libImaging/Crop.c +++ b/src/libImaging/Crop.c @@ -27,8 +27,9 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) int dx0, dy0, dx1, dy1; INT32 zero = 0; - if (!imIn) + if (!imIn) { return (Imaging) ImagingError_ModeError(); + } xsize = sx1 - sx0; if (xsize < 0) @@ -38,13 +39,15 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) ysize = 0; imOut = ImagingNewDirty(imIn->mode, xsize, ysize); - if (!imOut) + if (!imOut) { return NULL; + } ImagingCopyPalette(imOut, imIn); - if (sx0 < 0 || sy0 < 0 || sx1 > imIn->xsize || sy1 > imIn->ysize) + if (sx0 < 0 || sy0 < 0 || sx1 > imIn->xsize || sy1 > imIn->ysize) { (void) ImagingFill(imOut, &zero); + } dx0 = -sx0; dy0 = -sy0; diff --git a/src/libImaging/Dib.c b/src/libImaging/Dib.c index 5042902316d..202b0c9fab9 100644 --- a/src/libImaging/Dib.c +++ b/src/libImaging/Dib.c @@ -40,8 +40,9 @@ ImagingGetModeDIB(int size_out[2]) mode = "P"; if (!(GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)) { mode = "RGB"; - if (GetDeviceCaps(dc, BITSPIXEL) == 1) + if (GetDeviceCaps(dc, BITSPIXEL) == 1) { mode = "1"; + } } if (size_out) { @@ -66,14 +67,16 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) /* Check mode */ if (strcmp(mode, "1") != 0 && strcmp(mode, "L") != 0 && - strcmp(mode, "RGB") != 0) + strcmp(mode, "RGB") != 0) { return (ImagingDIB) ImagingError_ModeError(); + } /* Create DIB context and info header */ /* malloc check ok, small constant allocation */ dib = (ImagingDIB) malloc(sizeof(*dib)); - if (!dib) + if (!dib) { return (ImagingDIB) ImagingError_MemoryError(); + } /* malloc check ok, small constant allocation */ dib->info = (BITMAPINFO*) malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); @@ -113,9 +116,9 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) dib->pixelsize = strlen(mode); dib->linesize = (xsize * dib->pixelsize + 3) & -4; - if (dib->pixelsize == 1) + if (dib->pixelsize == 1) { dib->pack = dib->unpack = (ImagingShuffler) memcpy; - else { + } else { dib->pack = ImagingPackBGR; dib->unpack = ImagingPackBGR; } @@ -174,14 +177,16 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) * images. */ i = 10; - for (r = 0; r < 256; r += 51) - for (g = 0; g < 256; g += 51) + for (r = 0; r < 256; r += 51) { + for (g = 0; g < 256; g += 51) { for (b = 0; b < 256; b += 51) { pal->palPalEntry[i].peRed = r; pal->palPalEntry[i].peGreen = g; pal->palPalEntry[i].peBlue = b; i++; } + } + } for (r = 1; r < 22-1; r++) { /* Black and white are already provided by the cube. */ pal->palPalEntry[i].peRed = @@ -195,14 +200,16 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) /* Colour DIB. Alternate palette. */ i = 10; - for (r = 0; r < 256; r += 37) - for (g = 0; g < 256; g += 32) + for (r = 0; r < 256; r += 37) { + for (g = 0; g < 256; g += 32) { for (b = 0; b < 256; b += 64) { pal->palPalEntry[i].peRed = r; pal->palPalEntry[i].peGreen = g; pal->palPalEntry[i].peBlue = b; i++; } + } + } #endif @@ -223,9 +230,10 @@ ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4]) /* FIXME: check size! */ int y; - for (y = 0; y < im->ysize; y++) + for (y = 0; y < im->ysize; y++) { dib->pack(dib->bits + dib->linesize*(dib->ysize-(xy[1]+y)-1) + xy[0]*dib->pixelsize, im->image[y], im->xsize); + } } @@ -234,8 +242,9 @@ ImagingExposeDIB(ImagingDIB dib, void *dc) { /* Copy bitmap to display */ - if (dib->palette != 0) + if (dib->palette != 0) { SelectPalette((HDC) dc, dib->palette, FALSE); + } BitBlt((HDC) dc, 0, 0, dib->xsize, dib->ysize, dib->dc, 0, 0, SRCCOPY); } @@ -251,8 +260,9 @@ ImagingDrawDIB(ImagingDIB dib, void *dc, int dst[4], int src[4]) dib->info, DIB_RGB_COLORS, SRCCOPY); } else { /* stretchblt (displays) */ - if (dib->palette != 0) + if (dib->palette != 0) { SelectPalette((HDC) dc, dib->palette, FALSE); + } StretchBlt((HDC) dc, dst[0], dst[1], dst[2]-dst[0], dst[3]-dst[1], dib->dc, src[0], src[1], src[2]-src[0], src[3]-src[1], SRCCOPY); @@ -275,8 +285,9 @@ ImagingQueryPaletteDIB(ImagingDIB dib, void *dc) /* Restore palette */ SelectPalette((HDC) dc, now, FALSE); - } else + } else { n = 0; + } return n; /* number of colours that was changed */ } @@ -286,14 +297,16 @@ ImagingDeleteDIB(ImagingDIB dib) { /* Clean up */ - if (dib->palette) + if (dib->palette) { DeleteObject(dib->palette); + } if (dib->bitmap) { SelectObject(dib->dc, dib->old_bitmap); DeleteObject(dib->bitmap); } - if (dib->dc) + if (dib->dc) { DeleteDC(dib->dc); + } free(dib->info); } diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 58adc1b6355..35e6e4893fa 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -79,8 +79,9 @@ point8(Imaging im, int x, int y, int ink) static inline void point32(Imaging im, int x, int y, int ink) { - if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) + if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) { im->image32[y][x] = ink; + } } static inline void @@ -103,16 +104,19 @@ hline8(Imaging im, int x0, int y0, int x1, int ink) int tmp, pixelwidth; if (y0 >= 0 && y0 < im->ysize) { - if (x0 > x1) + if (x0 > x1) { tmp = x0, x0 = x1, x1 = tmp; - if (x0 < 0) + } + if (x0 < 0) { x0 = 0; - else if (x0 >= im->xsize) + } else if (x0 >= im->xsize) { return; - if (x1 < 0) + } + if (x1 < 0) { return; - else if (x1 >= im->xsize) + } else if (x1 >= im->xsize) { x1 = im->xsize-1; + } if (x0 <= x1) { pixelwidth = strncmp(im->mode, "I;16", 4) == 0 ? 2 : 1; memset(im->image8[y0] + x0 * pixelwidth, (UINT8) ink, @@ -128,19 +132,23 @@ hline32(Imaging im, int x0, int y0, int x1, int ink) INT32* p; if (y0 >= 0 && y0 < im->ysize) { - if (x0 > x1) + if (x0 > x1) { tmp = x0, x0 = x1, x1 = tmp; - if (x0 < 0) + } + if (x0 < 0) { x0 = 0; - else if (x0 >= im->xsize) + } else if (x0 >= im->xsize) { return; - if (x1 < 0) + } + if (x1 < 0) { return; - else if (x1 >= im->xsize) + } else if (x1 >= im->xsize) { x1 = im->xsize-1; + } p = im->image32[y0]; - while (x0 <= x1) + while (x0 <= x1) { p[x0++] = ink; + } } } @@ -151,16 +159,19 @@ hline32rgba(Imaging im, int x0, int y0, int x1, int ink) unsigned int tmp1; if (y0 >= 0 && y0 < im->ysize) { - if (x0 > x1) + if (x0 > x1) { tmp = x0, x0 = x1, x1 = tmp; - if (x0 < 0) + } + if (x0 < 0) { x0 = 0; - else if (x0 >= im->xsize) + } else if (x0 >= im->xsize) { return; - if (x1 < 0) + } + if (x1 < 0) { return; - else if (x1 >= im->xsize) + } else if (x1 >= im->xsize) { x1 = im->xsize-1; + } if (x0 <= x1) { UINT8* out = (UINT8*) im->image[y0]+x0*4; UINT8* in = (UINT8*) &ink; @@ -183,19 +194,21 @@ line8(Imaging im, int x0, int y0, int x1, int y1, int ink) /* normalize coordinates */ dx = x1-x0; - if (dx < 0) + if (dx < 0) { dx = -dx, xs = -1; - else + } else { xs = 1; + } dy = y1-y0; - if (dy < 0) + if (dy < 0) { dy = -dy, ys = -1; - else + } else { ys = 1; + } n = (dx > dy) ? dx : dy; - if (dx == 0) + if (dx == 0) { /* vertical */ for (i = 0; i < dy; i++) { @@ -203,7 +216,7 @@ line8(Imaging im, int x0, int y0, int x1, int y1, int ink) y0 += ys; } - else if (dy == 0) + } else if (dy == 0) { /* horizontal */ for (i = 0; i < dx; i++) { @@ -211,7 +224,7 @@ line8(Imaging im, int x0, int y0, int x1, int y1, int ink) x0 += xs; } - else if (dx > dy) { + } else if (dx > dy) { /* bresenham, horizontal slope */ n = dx; @@ -259,19 +272,21 @@ line32(Imaging im, int x0, int y0, int x1, int y1, int ink) /* normalize coordinates */ dx = x1-x0; - if (dx < 0) + if (dx < 0) { dx = -dx, xs = -1; - else + } else { xs = 1; + } dy = y1-y0; - if (dy < 0) + if (dy < 0) { dy = -dy, ys = -1; - else + } else { ys = 1; + } n = (dx > dy) ? dx : dy; - if (dx == 0) + if (dx == 0) { /* vertical */ for (i = 0; i < dy; i++) { @@ -279,7 +294,7 @@ line32(Imaging im, int x0, int y0, int x1, int y1, int ink) y0 += ys; } - else if (dy == 0) + } else if (dy == 0) { /* horizontal */ for (i = 0; i < dx; i++) { @@ -287,7 +302,7 @@ line32(Imaging im, int x0, int y0, int x1, int y1, int ink) x0 += xs; } - else if (dx > dy) { + } else if (dx > dy) { /* bresenham, horizontal slope */ n = dx; @@ -335,19 +350,21 @@ line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink) /* normalize coordinates */ dx = x1-x0; - if (dx < 0) + if (dx < 0) { dx = -dx, xs = -1; - else + } else { xs = 1; + } dy = y1-y0; - if (dy < 0) + if (dy < 0) { dy = -dy, ys = -1; - else + } else { ys = 1; + } n = (dx > dy) ? dx : dy; - if (dx == 0) + if (dx == 0) { /* vertical */ for (i = 0; i < dy; i++) { @@ -355,7 +372,7 @@ line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink) y0 += ys; } - else if (dy == 0) + } else if (dy == 0) { /* horizontal */ for (i = 0; i < dx; i++) { @@ -363,7 +380,7 @@ line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink) x0 += xs; } - else if (dx > dy) { + } else if (dx > dy) { /* bresenham, horizontal slope */ n = dx; @@ -406,12 +423,13 @@ static int x_cmp(const void *x0, const void *x1) { float diff = *((float*)x0) - *((float*)x1); - if (diff < 0) + if (diff < 0) { return -1; - else if (diff > 0) + } else if (diff > 0) { return 1; - else + } else { return 0; + } } @@ -566,25 +584,28 @@ add_edge(Edge *e, int x0, int y0, int x1, int y1) { /* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */ - if (x0 <= x1) + if (x0 <= x1) { e->xmin = x0, e->xmax = x1; - else + } else { e->xmin = x1, e->xmax = x0; + } - if (y0 <= y1) + if (y0 <= y1) { e->ymin = y0, e->ymax = y1; - else + } else { e->ymin = y1, e->ymax = y0; + } if (y0 == y1) { e->d = 0; e->dx = 0.0; } else { e->dx = ((float)(x1-x0)) / (y1-y0); - if (y0 == e->ymin) + if (y0 == e->ymin) { e->d = 1; - else + } else { e->d = -1; + } } e->x0 = x0; @@ -701,23 +722,27 @@ ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1, DRAWINIT(); - if (y0 > y1) + if (y0 > y1) { tmp = y0, y0 = y1, y1 = tmp; + } if (fill) { - if (y0 < 0) + if (y0 < 0) { y0 = 0; - else if (y0 >= im->ysize) + } else if (y0 >= im->ysize) { return 0; + } - if (y1 < 0) + if (y1 < 0) { return 0; - else if (y1 > im->ysize) + } else if (y1 > im->ysize) { y1 = im->ysize; + } - for (y = y0; y <= y1; y++) + for (y = y0; y <= y1; y++) { draw->hline(im, x0, y, x1, ink); + } } else { /* outline */ @@ -743,8 +768,9 @@ ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_, DRAW* draw; INT32 ink; - if (count <= 0) + if (count <= 0) { return 0; + } DRAWINIT(); @@ -757,18 +783,21 @@ ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_, (void) ImagingError_MemoryError(); return -1; } - for (i = n = 0; i < count-1; i++) + for (i = n = 0; i < count-1; i++) { add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3]); - if (xy[i+i] != xy[0] || xy[i+i+1] != xy[1]) + } + if (xy[i+i] != xy[0] || xy[i+i+1] != xy[1]) { add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[0], xy[1]); + } draw->polygon(im, n, e, ink, 0); free(e); } else { /* Outline */ - for (i = 0; i < count-1; i++) + for (i = 0; i < count-1; i++) { draw->line(im, xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3], ink); + } draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink); } @@ -838,8 +867,9 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, DRAWINIT(); - while (end < start) + while (end < start) { end += 360; + } if (end - start > 360) { // no need to go in loops @@ -848,8 +878,9 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, w = x1 - x0; h = y1 - y0; - if (w <= 0 || h <= 0) + if (w <= 0 || h <= 0) { return 0; + } cx = (x0 + x1) / 2; cy = (y0 + y1) / 2; @@ -860,10 +891,11 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, i = end; } ellipsePoint(cx, cy, w, h, i, &x, &y); - if (i != start) + if (i != start) { draw->line(im, lx, ly, x, y, ink); - else + } else { sx = x, sy = y; + } lx = x, ly = y; } @@ -874,8 +906,9 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, draw->line(im, cx, cy, sx, sy, ink); } } else if (mode == CHORD) { - if (x != sx || y != sy) + if (x != sx || y != sy) { draw->line(im, x, y, sx, sy, ink); + } } } } else { @@ -930,10 +963,11 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, i = end; } ellipsePoint(cx, cy, w, h, i, &x, &y); - if (i == start) + if (i == start) { sx_inner = x, sy_inner = y; - else + } else { add_edge(&e[n++], lx_inner, ly_inner, x, y); + } lx_inner = x, ly_inner = y; } } @@ -1026,8 +1060,9 @@ ImagingOutlineNew(void) ImagingOutline outline; outline = calloc(1, sizeof(struct ImagingOutlineInstance)); - if (!outline) + if (!outline) { return (ImagingOutline) ImagingError_MemoryError(); + } outline->edges = NULL; outline->count = outline->size = 0; @@ -1040,11 +1075,13 @@ ImagingOutlineNew(void) void ImagingOutlineDelete(ImagingOutline outline) { - if (!outline) + if (!outline) { return; + } - if (outline->edges) + if (outline->edges) { free(outline->edges); + } free(outline); } @@ -1068,8 +1105,9 @@ allocate(ImagingOutline outline, int extra) /* malloc check ok, overflow checked above */ e = realloc(outline->edges, outline->size * sizeof(Edge)); } - if (!e) + if (!e) { return NULL; + } outline->edges = e; } @@ -1095,8 +1133,9 @@ ImagingOutlineLine(ImagingOutline outline, float x1, float y1) Edge* e; e = allocate(outline, 1); - if (!e) + if (!e) { return -1; /* out of memory */ + } add_edge(e, (int) outline->x, (int) outline->y, (int) x1, (int) y1); @@ -1117,8 +1156,9 @@ ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, #define STEPS 32 e = allocate(outline, STEPS); - if (!e) + if (!e) { return -1; /* out of memory */ + } xo = outline->x; yo = outline->y; @@ -1153,8 +1193,9 @@ ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, int ImagingOutlineClose(ImagingOutline outline) { - if (outline->x == outline->x0 && outline->y == outline->y0) + if (outline->x == outline->x0 && outline->y == outline->y0) { return 0; + } return ImagingOutlineLine(outline, outline->x0, outline->y0); } @@ -1191,14 +1232,16 @@ ImagingOutlineTransform(ImagingOutline outline, double a[6]) y0 = eIn->y0; /* FIXME: ouch! */ - if (eIn->x0 == eIn->xmin) + if (eIn->x0 == eIn->xmin) { x1 = eIn->xmax; - else + } else { x1 = eIn->xmin; - if (eIn->y0 == eIn->ymin) + } + if (eIn->y0 == eIn->ymin) { y1 = eIn->ymax; - else + } else { y1 = eIn->ymin; + } /* full moon tonight! if this doesn't work, you may need to upgrade your compiler (make sure you have the right service diff --git a/src/libImaging/Effects.c b/src/libImaging/Effects.c index 7b4ff0b4380..b9ff889d231 100644 --- a/src/libImaging/Effects.c +++ b/src/libImaging/Effects.c @@ -34,12 +34,14 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality) /* Check arguments */ width = extent[2] - extent[0]; height = extent[3] - extent[1]; - if (width < 0.0 || height < 0.0 || quality < 2) + if (width < 0.0 || height < 0.0 || quality < 2) { return (Imaging) ImagingError_ValueError(NULL); + } im = ImagingNewDirty("L", xsize, ysize); - if (!im) + if (!im) { return NULL; + } dr = width/(xsize-1); di = height/(ysize-1); @@ -82,8 +84,9 @@ ImagingEffectNoise(int xsize, int ysize, float sigma) double this, next; imOut = ImagingNewDirty("L", xsize, ysize); - if (!imOut) + if (!imOut) { return NULL; + } next = 0.0; nextok = 0; @@ -123,20 +126,23 @@ ImagingEffectSpread(Imaging imIn, int distance) imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } #define SPREAD(type, image)\ - for (y = 0; y < imOut->ysize; y++)\ + for (y = 0; y < imOut->ysize; y++) {\ for (x = 0; x < imOut->xsize; x++) {\ int xx = x + (rand() % distance) - distance/2;\ int yy = y + (rand() % distance) - distance/2;\ if (xx >= 0 && xx < imIn->xsize && yy >= 0 && yy < imIn->ysize) {\ imOut->image[yy][xx] = imIn->image[y][x];\ imOut->image[y][x] = imIn->image[yy][xx];\ - } else\ + } else {\ imOut->image[y][x] = imIn->image[y][x];\ - } + }\ + }\ + } if (imIn->image8) { SPREAD(UINT8, image8); diff --git a/src/libImaging/EpsEncode.c b/src/libImaging/EpsEncode.c index 2a6aad4a3eb..ac8a4059c3c 100644 --- a/src/libImaging/EpsEncode.c +++ b/src/libImaging/EpsEncode.c @@ -40,15 +40,17 @@ ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) for (;;) { if (state->state == NEWLINE) { - if (bytes < 1) + if (bytes < 1) { break; + } *ptr++ = '\n'; bytes--; state->state = HEXBYTE; } - if (bytes < 2) + if (bytes < 2) { break; + } i = in[state->x++]; *ptr++ = hex[(i>>4)&15]; @@ -56,8 +58,9 @@ ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) bytes -= 2; /* Skip junk bytes */ - if (im->bands == 3 && (state->x & 3) == 3) + if (im->bands == 3 && (state->x & 3) == 3) { state->x++; + } if (++state->count >= 79/2) { state->state = NEWLINE; diff --git a/src/libImaging/Except.c b/src/libImaging/Except.c index 4850b4f8747..15ae21cc821 100644 --- a/src/libImaging/Except.c +++ b/src/libImaging/Except.c @@ -56,8 +56,9 @@ ImagingError_Mismatch(void) void * ImagingError_ValueError(const char *message) { - if (!message) + if (!message) { message = "exception: bad argument to function"; + } fprintf(stderr, "*** %s\n", message); return NULL; } diff --git a/src/libImaging/File.c b/src/libImaging/File.c index 117af9e3573..14688d661e0 100644 --- a/src/libImaging/File.c +++ b/src/libImaging/File.c @@ -31,15 +31,18 @@ ImagingSaveRaw(Imaging im, FILE* fp) /* @PIL227: FIXME: for mode "1", map != 0 to 255 */ /* PGM "L" */ - for (y = 0; y < im->ysize; y++) + for (y = 0; y < im->ysize; y++) { fwrite(im->image[y], 1, im->xsize, fp); + } } else { /* PPM "RGB" or other internal format */ - for (y = 0; y < im->ysize; y++) - for (x = i = 0; x < im->xsize; x++, i += im->pixelsize) + for (y = 0; y < im->ysize; y++) { + for (x = i = 0; x < im->xsize; x++, i += im->pixelsize) { fwrite(im->image[y]+i, 1, im->bands, fp); + } + } } diff --git a/src/libImaging/Fill.c b/src/libImaging/Fill.c index 27022bc48d5..da143b4f943 100644 --- a/src/libImaging/Fill.c +++ b/src/libImaging/Fill.c @@ -30,27 +30,33 @@ ImagingFill(Imaging im, const void* colour) /* use generic API */ ImagingAccess access = ImagingAccessNew(im); if (access) { - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) + for (y = 0; y < im->ysize; y++) { + for (x = 0; x < im->xsize; x++) { access->put_pixel(im, x, y, colour); + } + } ImagingAccessDelete(im, access); } else { /* wipe the image */ - for (y = 0; y < im->ysize; y++) + for (y = 0; y < im->ysize; y++) { memset(im->image[y], 0, im->linesize); + } } } else { INT32 c = 0L; ImagingSectionEnter(&cookie); memcpy(&c, colour, im->pixelsize); if (im->image32 && c != 0L) { - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) + for (y = 0; y < im->ysize; y++) { + for (x = 0; x < im->xsize; x++) { im->image32[y][x] = c; + } + } } else { unsigned char cc = (unsigned char) *(UINT8*) colour; - for (y = 0; y < im->ysize; y++) + for (y = 0; y < im->ysize; y++) { memset(im->image[y], cc, im->linesize); + } } ImagingSectionLeave(&cookie); } diff --git a/src/libImaging/Filter.c b/src/libImaging/Filter.c index b033abf6623..0897ddbfe78 100644 --- a/src/libImaging/Filter.c +++ b/src/libImaging/Filter.c @@ -29,10 +29,12 @@ static inline UINT8 clip8(float in) { - if (in <= 0.0) + if (in <= 0.0) { return 0; - if (in >= 255.0) + } + if (in >= 255.0) { return 255; + } return (UINT8) in; } @@ -43,32 +45,40 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) int x, y; ImagingSectionCookie cookie; - if (xmargin < 0 && ymargin < 0) + if (xmargin < 0 && ymargin < 0) { return (Imaging) ImagingError_ValueError("bad kernel size"); + } imOut = ImagingNewDirty( imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin); - if (!imOut) + if (!imOut) { return NULL; + } #define EXPAND_LINE(type, image, yin, yout) {\ - for (x = 0; x < xmargin; x++)\ + for (x = 0; x < xmargin; x++) {\ imOut->image[yout][x] = imIn->image[yin][0];\ - for (x = 0; x < imIn->xsize; x++)\ + }\ + for (x = 0; x < imIn->xsize; x++) {\ imOut->image[yout][x+xmargin] = imIn->image[yin][x];\ - for (x = 0; x < xmargin; x++)\ + }\ + for (x = 0; x < xmargin; x++) {\ imOut->image[yout][xmargin+imIn->xsize+x] =\ imIn->image[yin][imIn->xsize-1];\ - } + }\ +} #define EXPAND(type, image) {\ - for (y = 0; y < ymargin; y++)\ + for (y = 0; y < ymargin; y++) {\ EXPAND_LINE(type, image, 0, y);\ - for (y = 0; y < imIn->ysize; y++)\ + }\ + for (y = 0; y < imIn->ysize; y++) {\ EXPAND_LINE(type, image, y, y+ymargin);\ - for (y = 0; y < ymargin; y++)\ + }\ + for (y = 0; y < ymargin; y++) {\ EXPAND_LINE(type, image, imIn->ysize-1, ymargin+imIn->ysize+y);\ - } + }\ +} ImagingSectionEnter(&cookie); if (imIn->image8) { @@ -330,18 +340,22 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel, Imaging imOut; ImagingSectionCookie cookie; - if ( ! im || im->type != IMAGING_TYPE_UINT8) + if ( ! im || im->type != IMAGING_TYPE_UINT8) { return (Imaging) ImagingError_ModeError(); + } - if (im->xsize < xsize || im->ysize < ysize) + if (im->xsize < xsize || im->ysize < ysize) { return ImagingCopy(im); + } - if ((xsize != 3 && xsize != 5) || xsize != ysize) + if ((xsize != 3 && xsize != 5) || xsize != ysize) { return (Imaging) ImagingError_ValueError("bad kernel size"); + } imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); - if (!imOut) + if (!imOut) { return NULL; + } ImagingSectionEnter(&cookie); if (xsize == 3) { diff --git a/src/libImaging/FliDecode.c b/src/libImaging/FliDecode.c index 5351d5664ac..84508013df8 100644 --- a/src/libImaging/FliDecode.c +++ b/src/libImaging/FliDecode.c @@ -41,8 +41,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt /* If not even the chunk size is present, we'd better leave */ - if (bytes < 4) + if (bytes < 4) { return 0; + } /* We don't decode anything unless we have a full chunk in the input buffer */ @@ -50,8 +51,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt ptr = buf; framesize = I32(ptr); - if (framesize < I32(ptr)) + if (framesize < I32(ptr)) { return 0; + } /* Make sure this is a frame chunk. The Python driver takes case of other chunk types. */ @@ -112,8 +114,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (data[1] >= 128) { ERR_IF_DATA_OOB(4) i = 256-data[1]; /* run */ - if (x + i + i > state->xsize) + if (x + i + i > state->xsize) { break; + } for (j = 0; j < i; j++) { local_buf[x++] = data[2]; local_buf[x++] = data[3]; @@ -121,16 +124,18 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt data += 2 + 2; } else { i = 2 * (int) data[1]; /* chunk */ - if (x + i > state->xsize) + if (x + i > state->xsize) { break; + } ERR_IF_DATA_OOB(2+i) memcpy(local_buf + x, data + 2, i); data += 2 + i; x += i; } } - if (p < packets) + if (p < packets) { break; /* didn't process all packets */ + } } if (l < lines) { /* didn't process all lines */ @@ -151,22 +156,25 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt x += data[0]; /* skip pixels */ if (data[1] & 0x80) { i = 256-data[1]; /* run */ - if (x + i > state->xsize) + if (x + i > state->xsize) { break; + } ERR_IF_DATA_OOB(3) memset(out + x, data[2], i); data += 3; } else { i = data[1]; /* chunk */ - if (x + i > state->xsize) + if (x + i > state->xsize) { break; + } ERR_IF_DATA_OOB(2+i) memcpy(out + x, data + 2, i); data += i + 2; } } - if (p < packets) + if (p < packets) { break; /* didn't process all packets */ + } } if (y < ymax) { /* didn't process all lines */ @@ -176,8 +184,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt break; case 13: /* FLI BLACK chunk */ - for (y = 0; y < state->ysize; y++) + for (y = 0; y < state->ysize; y++) { memset(im->image[y], 0, state->xsize); + } break; case 15: /* FLI BRUN chunk */ @@ -197,8 +206,9 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt data += i + 1; } else { i = data[0]; - if (x + i > state->xsize) + if (x + i > state->xsize) { break; /* safety first */ + } memset(out + x, data[1], i); data += 2; } diff --git a/src/libImaging/Geometry.c b/src/libImaging/Geometry.c index fd5e2595824..06d0cf24d63 100644 --- a/src/libImaging/Geometry.c +++ b/src/libImaging/Geometry.c @@ -20,10 +20,12 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) ImagingSectionCookie cookie; int x, y, xr; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) + } + if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); @@ -32,8 +34,9 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn) INT* in = (INT *)imIn->image[y]; \ INT* out = (INT *)imOut->image[y]; \ xr = imIn->xsize-1; \ - for (x = 0; x < imIn->xsize; x++, xr--) \ + for (x = 0; x < imIn->xsize; x++, xr--) { \ out[xr] = in[x]; \ + } \ } ImagingSectionEnter(&cookie); @@ -62,18 +65,21 @@ ImagingFlipTopBottom(Imaging imOut, Imaging imIn) ImagingSectionCookie cookie; int y, yr; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) + } + if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); ImagingSectionEnter(&cookie); yr = imIn->ysize - 1; - for (y = 0; y < imIn->ysize; y++, yr--) + for (y = 0; y < imIn->ysize; y++, yr--) { memcpy(imOut->image[yr], imIn->image[y], imIn->linesize); + } ImagingSectionLeave(&cookie); @@ -88,10 +94,12 @@ ImagingRotate90(Imaging imOut, Imaging imIn) int x, y, xx, yy, xr, xxsize, yysize; int xxx, yyy, xxxsize, yyysize; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + } + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); @@ -144,10 +152,12 @@ ImagingTranspose(Imaging imOut, Imaging imIn) int x, y, xx, yy, xxsize, yysize; int xxx, yyy, xxxsize, yyysize; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + } + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); @@ -199,10 +209,12 @@ ImagingTransverse(Imaging imOut, Imaging imIn) int x, y, xr, yr, xx, yy, xxsize, yysize; int xxx, yyy, xxxsize, yyysize; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + } + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); @@ -255,10 +267,12 @@ ImagingRotate180(Imaging imOut, Imaging imIn) ImagingSectionCookie cookie; int x, y, xr, yr; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) + } + if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); @@ -267,8 +281,9 @@ ImagingRotate180(Imaging imOut, Imaging imIn) INT* in = (INT *)imIn->image[y]; \ INT* out = (INT *)imOut->image[yr]; \ xr = imIn->xsize-1; \ - for (x = 0; x < imIn->xsize; x++, xr--) \ + for (x = 0; x < imIn->xsize; x++, xr--) { \ out[xr] = in[x]; \ + } \ } ImagingSectionEnter(&cookie); @@ -299,10 +314,12 @@ ImagingRotate270(Imaging imOut, Imaging imIn) int x, y, xx, yy, yr, xxsize, yysize; int xxx, yyy, xxxsize, yyysize; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); - if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + } + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) { return (Imaging) ImagingError_Mismatch(); + } ImagingCopyPalette(imOut, imIn); @@ -415,8 +432,9 @@ nearest_filter8(void* out, Imaging im, double xin, double yin) { int x = COORD(xin); int y = COORD(yin); - if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { return 0; + } ((UINT8*)out)[0] = im->image8[y][x]; return 1; } @@ -426,8 +444,9 @@ nearest_filter16(void* out, Imaging im, double xin, double yin) { int x = COORD(xin); int y = COORD(yin); - if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { return 0; + } memcpy(out, im->image8[y] + x * sizeof(INT16), sizeof(INT16)); return 1; } @@ -437,8 +456,9 @@ nearest_filter32(void* out, Imaging im, double xin, double yin) { int x = COORD(xin); int y = COORD(yin); - if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { return 0; + } memcpy(out, &im->image32[y][x], sizeof(INT32)); return 1; } @@ -455,8 +475,9 @@ nearest_filter32(void* out, Imaging im, double xin, double yin) double v1, v2;\ double dx, dy;\ type* in;\ - if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize)\ + if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize) {\ return 0;\ + }\ xin -= 0.5;\ yin -= 0.5;\ x = FLOOR(xin);\ @@ -472,8 +493,9 @@ nearest_filter32(void* out, Imaging im, double xin, double yin) if (y+1 >= 0 && y+1 < im->ysize) {\ in = (type*) ((image)[y+1] + offset);\ BILINEAR(v2, in[x0], in[x1], dx);\ - } else\ + } else {\ v2 = v1;\ + }\ BILINEAR(v1, v1, v2, dy);\ } @@ -552,8 +574,9 @@ bilinear_filter32RGB(void* out, Imaging im, double xin, double yin) double v1, v2, v3, v4;\ double dx, dy;\ type* in;\ - if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize)\ + if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize) {\ return 0;\ + }\ xin -= 0.5;\ yin -= 0.5;\ x = FLOOR(xin);\ @@ -572,18 +595,21 @@ bilinear_filter32RGB(void* out, Imaging im, double xin, double yin) if (y+1 >= 0 && y+1 < im->ysize) {\ in = (type*) ((image)[y+1] + offset);\ BICUBIC(v2, in[x0], in[x1], in[x2], in[x3], dx);\ - } else\ + } else {\ v2 = v1;\ + }\ if (y+2 >= 0 && y+2 < im->ysize) {\ in = (type*) ((image)[y+2] + offset);\ BICUBIC(v3, in[x0], in[x1], in[x2], in[x3], dx);\ - } else\ + } else {\ v3 = v2;\ + }\ if (y+3 >= 0 && y+3 < im->ysize) {\ in = (type*) ((image)[y+3] + offset);\ BICUBIC(v4, in[x0], in[x1], in[x2], in[x3], dx);\ - } else\ + } else {\ v4 = v3;\ + }\ BICUBIC(v1, v1, v2, v3, v4, dy);\ } @@ -593,12 +619,13 @@ bicubic_filter8(void* out, Imaging im, double xin, double yin) { BICUBIC_HEAD(UINT8); BICUBIC_BODY(UINT8, im->image8, 1, 0); - if (v1 <= 0.0) + if (v1 <= 0.0) { ((UINT8*)out)[0] = 0; - else if (v1 >= 255.0) + } else if (v1 >= 255.0) { ((UINT8*)out)[0] = 255; - else + } else { ((UINT8*)out)[0] = (UINT8) v1; + } return 1; } @@ -643,12 +670,13 @@ bicubic_filter32LA(void* out, Imaging im, double xin, double yin) ((UINT8*)out)[2] = (UINT8) v1; } BICUBIC_BODY(UINT8, im->image, 4, 3); - if (v1 <= 0.0) + if (v1 <= 0.0) { ((UINT8*)out)[3] = 0; - else if (v1 >= 255.0) + } else if (v1 >= 255.0) { ((UINT8*)out)[3] = 255; - else + } else { ((UINT8*)out)[3] = (UINT8) v1; + } return 1; } @@ -659,12 +687,13 @@ bicubic_filter32RGB(void* out, Imaging im, double xin, double yin) BICUBIC_HEAD(UINT8); for (b = 0; b < im->bands; b++) { BICUBIC_BODY(UINT8, im->image, 4, b); - if (v1 <= 0.0) + if (v1 <= 0.0) { ((UINT8*)out)[b] = 0; - else if (v1 >= 255.0) + } else if (v1 >= 255.0) { ((UINT8*)out)[b] = 255; - else + } else { ((UINT8*)out)[b] = (UINT8) v1; + } } return 1; } @@ -678,7 +707,7 @@ getfilter(Imaging im, int filterid) { switch (filterid) { case IMAGING_TRANSFORM_NEAREST: - if (im->image8) + if (im->image8) { switch (im->type) { case IMAGING_TYPE_UINT8: return nearest_filter8; @@ -692,19 +721,21 @@ getfilter(Imaging im, int filterid) return nearest_filter32; } } - else + } else { return nearest_filter32; + } break; case IMAGING_TRANSFORM_BILINEAR: - if (im->image8) + if (im->image8) { return bilinear_filter8; - else if (im->image32) { + } else if (im->image32) { switch (im->type) { case IMAGING_TYPE_UINT8: - if (im->bands == 2) + if (im->bands == 2) { return bilinear_filter32LA; - else + } else { return bilinear_filter32RGB; + } case IMAGING_TYPE_INT32: return bilinear_filter32I; case IMAGING_TYPE_FLOAT32: @@ -713,15 +744,16 @@ getfilter(Imaging im, int filterid) } break; case IMAGING_TRANSFORM_BICUBIC: - if (im->image8) + if (im->image8) { return bicubic_filter8; - else if (im->image32) { + } else if (im->image32) { switch (im->type) { case IMAGING_TYPE_UINT8: - if (im->bands == 2) + if (im->bands == 2) { return bicubic_filter32LA; - else + } else { return bicubic_filter32RGB; + } case IMAGING_TYPE_INT32: return bicubic_filter32I; case IMAGING_TYPE_FLOAT32: @@ -751,32 +783,39 @@ ImagingGenericTransform( double xx, yy; ImagingTransformFilter filter = getfilter(imIn, filterid); - if (!filter) + if (!filter) { return (Imaging) ImagingError_ValueError("bad filter number"); + } - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); + } ImagingCopyPalette(imOut, imIn); ImagingSectionEnter(&cookie); - if (x0 < 0) + if (x0 < 0) { x0 = 0; - if (y0 < 0) + } + if (y0 < 0) { y0 = 0; - if (x1 > imOut->xsize) + } + if (x1 > imOut->xsize) { x1 = imOut->xsize; - if (y1 > imOut->ysize) + } + if (y1 > imOut->ysize) { y1 = imOut->ysize; + } for (y = y0; y < y1; y++) { out = imOut->image[y] + x0*imOut->pixelsize; for (x = x0; x < x1; x++) { if ( ! transform(&xx, &yy, x-x0, y-y0, transform_data) || ! filter(out, imIn, xx, yy)) { - if (fill) + if (fill) { memset(out, 0, imOut->pixelsize); + } } out += imOut->pixelsize; } @@ -801,19 +840,24 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, int xmin, xmax; int *xintab; - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); + } ImagingCopyPalette(imOut, imIn); - if (x0 < 0) + if (x0 < 0) { x0 = 0; - if (y0 < 0) + } + if (y0 < 0) { y0 = 0; - if (x1 > imOut->xsize) + } + if (x1 > imOut->xsize) { x1 = imOut->xsize; - if (y1 > imOut->ysize) + } + if (y1 > imOut->ysize) { y1 = imOut->ysize; + } /* malloc check ok, uses calloc for overflow */ xintab = (int*) calloc(imOut->xsize, sizeof(int)); @@ -833,8 +877,9 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, xin = COORD(xo); if (xin >= 0 && xin < (int) imIn->xsize) { xmax = x+1; - if (x < xmin) + if (x < xmin) { xmin = x; + } xintab[x] = xin; } xo += a[0]; @@ -845,12 +890,14 @@ ImagingScaleAffine(Imaging imOut, Imaging imIn, int yi = COORD(yo);\ pixel *in, *out;\ out = imOut->image[y];\ - if (fill && x1 > x0)\ + if (fill && x1 > x0) {\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ + }\ if (yi >= 0 && yi < imIn->ysize) {\ in = imIn->image[yi];\ - for (x = xmin; x < xmax; x++)\ + for (x = xmin; x < xmax; x++) {\ out[x] = in[xintab[x]];\ + }\ }\ yo += a[4];\ } @@ -915,14 +962,16 @@ affine_fixed(Imaging imOut, Imaging imIn, xx = a2;\ yy = a5;\ out = imOut->image[y];\ - if (fill && x1 > x0)\ + if (fill && x1 > x0) {\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ + }\ for (x = x0; x < x1; x++, out++) {\ xin = xx >> 16;\ if (xin >= 0 && xin < xsize) {\ yin = yy >> 16;\ - if (yin >= 0 && yin < ysize)\ + if (yin >= 0 && yin < ysize) {\ *out = imIn->image[yin][xin];\ + }\ }\ xx += a0;\ yy += a3;\ @@ -933,10 +982,11 @@ affine_fixed(Imaging imOut, Imaging imIn, ImagingSectionEnter(&cookie); - if (imIn->image8) + if (imIn->image8) { AFFINE_TRANSFORM_FIXED(UINT8, image8) - else + } else { AFFINE_TRANSFORM_FIXED(INT32, image32) + } ImagingSectionLeave(&cookie); @@ -973,24 +1023,30 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); } - if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) { return (Imaging) ImagingError_ModeError(); + } - if (x0 < 0) + if (x0 < 0) { x0 = 0; - if (y0 < 0) + } + if (y0 < 0) { y0 = 0; - if (x1 > imOut->xsize) + } + if (x1 > imOut->xsize) { x1 = imOut->xsize; - if (y1 > imOut->ysize) + } + if (y1 > imOut->ysize) { y1 = imOut->ysize; + } /* translate all four corners to check if they are within the range that can be represented by the fixed point arithmetics */ if (check_fixed(a, 0, 0) && check_fixed(a, x1-x0, y1-y0) && - check_fixed(a, 0, y1-y0) && check_fixed(a, x1-x0, 0)) + check_fixed(a, 0, y1-y0) && check_fixed(a, x1-x0, 0)) { return affine_fixed(imOut, imIn, x0, y0, x1, y1, a, filterid, fill); + } /* FIXME: cannot really think of any reasonable case when the following code is used. maybe we should fall back on the slow @@ -1010,14 +1066,16 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, xx = xo;\ yy = yo;\ out = imOut->image[y];\ - if (fill && x1 > x0)\ + if (fill && x1 > x0) {\ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ + }\ for (x = x0; x < x1; x++, out++) {\ xin = COORD(xx);\ if (xin >= 0 && xin < xsize) {\ yin = COORD(yy);\ - if (yin >= 0 && yin < ysize)\ + if (yin >= 0 && yin < ysize) {\ *out = imIn->image[yin][xin];\ + }\ }\ xx += a[0];\ yy += a[3];\ @@ -1028,10 +1086,11 @@ ImagingTransformAffine(Imaging imOut, Imaging imIn, ImagingSectionEnter(&cookie); - if (imIn->image8) + if (imIn->image8) { AFFINE_TRANSFORM(UINT8, image8) - else + } else { AFFINE_TRANSFORM(INT32, image32) + } ImagingSectionLeave(&cookie); diff --git a/src/libImaging/GetBBox.c b/src/libImaging/GetBBox.c index acb50fd95e7..dd875c55730 100644 --- a/src/libImaging/GetBBox.c +++ b/src/libImaging/GetBBox.c @@ -36,7 +36,7 @@ ImagingGetBBox(Imaging im, int bbox[4]) #define GETBBOX(image, mask)\ for (y = 0; y < im->ysize; y++) {\ has_data = 0;\ - for (x = 0; x < im->xsize; x++)\ + for (x = 0; x < im->xsize; x++) {\ if (im->image[y][x] & mask) {\ has_data = 1;\ if (x < bbox[0])\ @@ -44,6 +44,7 @@ ImagingGetBBox(Imaging im, int bbox[4]) if (x >= bbox[2])\ bbox[2] = x+1;\ }\ + }\ if (has_data) {\ if (bbox[1] < 0)\ bbox[1] = y;\ @@ -72,8 +73,9 @@ ImagingGetBBox(Imaging im, int bbox[4]) } /* Check that we got a box */ - if (bbox[1] < 0) + if (bbox[1] < 0) { return 0; /* no data */ + } return 1; /* ok */ } @@ -94,21 +96,24 @@ ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj) #define GETPROJ(image, mask)\ for (y = 0; y < im->ysize; y++) {\ has_data = 0;\ - for (x = 0; x < im->xsize; x++)\ + for (x = 0; x < im->xsize; x++) {\ if (im->image[y][x] & mask) {\ has_data = 1;\ xproj[x] = 1;\ }\ - if (has_data)\ - yproj[y] = 1;\ - } + }\ + if (has_data) {\ + yproj[y] = 1;\ + }\ + } if (im->image8) { GETPROJ(image8, 0xff); } else { INT32 mask = 0xffffffff; - if (im->bands == 3) + if (im->bands == 3) { ((UINT8*) &mask)[3] = 0; + } GETPROJ(image32, mask); } @@ -128,8 +133,9 @@ ImagingGetExtrema(Imaging im, void *extrema) return -1; /* mismatch */ } - if (!im->xsize || !im->ysize) + if (!im->xsize || !im->ysize) { return 0; /* zero size */ + } switch (im->type) { case IMAGING_TYPE_UINT8: @@ -137,10 +143,11 @@ ImagingGetExtrema(Imaging im, void *extrema) for (y = 0; y < im->ysize; y++) { UINT8* in = im->image8[y]; for (x = 0; x < im->xsize; x++) { - if (imin > in[x]) + if (imin > in[x]) { imin = in[x]; - else if (imax < in[x]) + } else if (imax < in[x]) { imax = in[x]; + } } } ((UINT8*) extrema)[0] = (UINT8) imin; @@ -151,10 +158,11 @@ ImagingGetExtrema(Imaging im, void *extrema) for (y = 0; y < im->ysize; y++) { INT32* in = im->image32[y]; for (x = 0; x < im->xsize; x++) { - if (imin > in[x]) + if (imin > in[x]) { imin = in[x]; - else if (imax < in[x]) + } else if (imax < in[x]) { imax = in[x]; + } } } memcpy(extrema, &imin, sizeof(imin)); @@ -165,10 +173,11 @@ ImagingGetExtrema(Imaging im, void *extrema) for (y = 0; y < im->ysize; y++) { FLOAT32* in = (FLOAT32*) im->image32[y]; for (x = 0; x < im->xsize; x++) { - if (fmin > in[x]) + if (fmin > in[x]) { fmin = in[x]; - else if (fmax < in[x]) + } else if (fmax < in[x]) { fmax = in[x]; + } } } memcpy(extrema, &fmin, sizeof(fmin)); @@ -192,10 +201,11 @@ ImagingGetExtrema(Imaging im, void *extrema) #else memcpy(&v, pixel, sizeof(v)); #endif - if (imin > v) + if (imin > v) { imin = v; - else if (imax < v) + } else if (imax < v) { imax = v; + } } } v = (UINT16) imin; @@ -264,19 +274,23 @@ getcolors32(Imaging im, int maxcolors, int* size) /* printf("code_size=%d\n", code_size); */ /* printf("code_poly=%d\n", code_poly); */ - if (!code_size) + if (!code_size) { return ImagingError_MemoryError(); /* just give up */ + } - if (!im->image32) + if (!im->image32) { return ImagingError_ModeError(); + } table = calloc(code_size + 1, sizeof(ImagingColorItem)); - if (!table) + if (!table) { return ImagingError_MemoryError(); + } pixel_mask = 0xffffffff; - if (im->bands == 3) + if (im->bands == 3) { ((UINT8*) &pixel_mask)[3] = 0; + } colors = 0; @@ -289,8 +303,9 @@ getcolors32(Imaging im, int maxcolors, int* size) v = &table[i]; if (!v->count) { /* add to table */ - if (colors++ == maxcolors) + if (colors++ == maxcolors) { goto overflow; + } v->x = x; v->y = y; v->pixel = pixel; v->count = 1; @@ -300,15 +315,17 @@ getcolors32(Imaging im, int maxcolors, int* size) continue; } incr = (h ^ (h >> 3)) & code_mask; - if (!incr) + if (!incr) { incr = code_mask; + } for (;;) { i = (i + incr) & code_mask; v = &table[i]; if (!v->count) { /* add to table */ - if (colors++ == maxcolors) + if (colors++ == maxcolors) { goto overflow; + } v->x = x; v->y = y; v->pixel = pixel; v->count = 1; @@ -318,8 +335,9 @@ getcolors32(Imaging im, int maxcolors, int* size) break; } incr = incr << 1; - if (incr > code_mask) + if (incr > code_mask) { incr = incr ^ code_poly; + } } } } @@ -329,8 +347,9 @@ getcolors32(Imaging im, int maxcolors, int* size) /* pack the table */ for (x = y = 0; x < (int) code_size; x++) if (table[x].count) { - if (x != y) + if (x != y) { table[y] = table[x]; + } y++; } table[y].count = 0; /* mark end of table */ diff --git a/src/libImaging/GifDecode.c b/src/libImaging/GifDecode.c index 8598cb6a9a9..5728ae1ce1a 100644 --- a/src/libImaging/GifDecode.c +++ b/src/libImaging/GifDecode.c @@ -142,11 +142,13 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ssize_t /* New GIF block */ /* We don't start decoding unless we have a full block */ - if (bytes < 1) + if (bytes < 1) { return ptr - buffer; + } c = *ptr; - if (bytes < c+1) + if (bytes < c+1) { return ptr - buffer; + } context->blocksize = c; @@ -167,13 +169,15 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ssize_t expanded. */ if (c == context->clear) { - if (state->state != 2) + if (state->state != 2) { state->state = 1; + } continue; } - if (c == context->end) + if (c == context->end) { break; + } i = 1; p = &context->lastdata; diff --git a/src/libImaging/GifEncode.c b/src/libImaging/GifEncode.c index 90441d184e2..e9c6c314918 100644 --- a/src/libImaging/GifEncode.c +++ b/src/libImaging/GifEncode.c @@ -48,12 +48,14 @@ emit(GIFENCODERSTATE *context, int byte) /* add current block to end of flush queue */ if (context->block) { block = context->flush; - while (block && block->next) + while (block && block->next) { block = block->next; - if (block) + } + if (block) { block->next = context->block; - else + } else { context->flush = context->block; + } } /* get a new block */ @@ -63,8 +65,9 @@ emit(GIFENCODERSTATE *context, int byte) } else { /* malloc check ok, small constant allocation */ block = malloc(sizeof(GIFENCODERBLOCK)); - if (!block) + if (!block) { return 0; + } } block->size = 0; @@ -154,14 +157,16 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (context->interlace) { context->interlace = 1; context->step = 8; - } else + } else { context->step = 1; + } context->last = -1; /* sanity check */ - if (state->xsize <= 0 || state->ysize <= 0) + if (state->xsize <= 0 || state->ysize <= 0) { state->state = ENCODE_EOF; + } } @@ -231,9 +236,9 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) this = state->buffer[state->x++]; - if (this == context->last) + if (this == context->last) { context->count++; - else { + } else { EMIT_RUN(label1); context->last = this; context->count = 1; @@ -263,12 +268,14 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (context->block) { GIFENCODERBLOCK* block; block = context->flush; - while (block && block->next) + while (block && block->next) { block = block->next; - if (block) + } + if (block) { block->next = context->block; - else + } else { context->flush = context->block; + } context->block = NULL; } @@ -287,8 +294,9 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (block->size > 0) { /* make sure it fits into the output buffer */ - if (bytes < block->size+1) + if (bytes < block->size+1) { return ptr - buf; + } ptr[0] = block->size; memcpy(ptr+1, block->data, block->size); @@ -300,16 +308,18 @@ ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) context->flush = block->next; - if (context->free) + if (context->free) { free(context->free); + } context->free = block; } if (state->state == EXIT) { /* this was the last block! */ - if (context->free) + if (context->free) { free(context->free); + } state->errcode = IMAGING_CODEC_END; return ptr - buf; } diff --git a/src/libImaging/HexDecode.c b/src/libImaging/HexDecode.c index 9b20bc2accc..1def8766f46 100644 --- a/src/libImaging/HexDecode.c +++ b/src/libImaging/HexDecode.c @@ -30,8 +30,9 @@ ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { - if (bytes < 2) + if (bytes < 2) { return ptr - buf; + } a = HEX(ptr[0]); b = HEX(ptr[1]); diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index 0d08e9f086a..b6f552ab3eb 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -29,8 +29,9 @@ void ImagingHistogramDelete(ImagingHistogram h) { - if (h->histogram) + if (h->histogram) { free(h->histogram); + } free(h); } @@ -59,15 +60,18 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) INT32 imin, imax; FLOAT32 fmin, fmax, scale; - if (!im) + if (!im) { return ImagingError_ModeError(); + } if (imMask) { /* Validate mask */ - if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) + if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) { return ImagingError_Mismatch(); - if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) + } + if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) { return ImagingError_ValueError("bad transparency mask"); + } } h = ImagingHistogramNew(im); @@ -75,12 +79,15 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) if (imMask) { /* mask */ if (im->image8) { - ImagingSectionEnter(&cookie); - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) - if (imMask->image8[y][x] != 0) - h->histogram[im->image8[y][x]]++; - ImagingSectionLeave(&cookie); + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) { + for (x = 0; x < im->xsize; x++) { + if (imMask->image8[y][x] != 0) { + h->histogram[im->image8[y][x]]++; + } + } + } + ImagingSectionLeave(&cookie); } else { /* yes, we need the braces. C isn't Python! */ if (im->type != IMAGING_TYPE_UINT8) { ImagingHistogramDelete(h); @@ -89,25 +96,29 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) ImagingSectionEnter(&cookie); for (y = 0; y < im->ysize; y++) { UINT8* in = (UINT8*) im->image32[y]; - for (x = 0; x < im->xsize; x++) + for (x = 0; x < im->xsize; x++) { if (imMask->image8[y][x] != 0) { h->histogram[(*in++)]++; h->histogram[(*in++)+256]++; h->histogram[(*in++)+512]++; h->histogram[(*in++)+768]++; - } else + } else { in += 4; + } + } } ImagingSectionLeave(&cookie); } } else { /* mask not given; process pixels in image */ if (im->image8) { - ImagingSectionEnter(&cookie); - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) { + for (x = 0; x < im->xsize; x++) { h->histogram[im->image8[y][x]]++; - ImagingSectionLeave(&cookie); + } + } + ImagingSectionLeave(&cookie); } else { switch (im->type) { case IMAGING_TYPE_UINT8: @@ -128,20 +139,23 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) ImagingHistogramDelete(h); return ImagingError_ValueError("min/max not given"); } - if (!im->xsize || !im->ysize) + if (!im->xsize || !im->ysize) { break; + } memcpy(&imin, minmax, sizeof(imin)); memcpy(&imax, ((char*)minmax) + sizeof(imin), sizeof(imax)); - if (imin >= imax) + if (imin >= imax) { break; + } ImagingSectionEnter(&cookie); scale = 255.0F / (imax - imin); for (y = 0; y < im->ysize; y++) { INT32* in = im->image32[y]; for (x = 0; x < im->xsize; x++) { i = (int) (((*in++)-imin)*scale); - if (i >= 0 && i < 256) + if (i >= 0 && i < 256) { h->histogram[i]++; + } } } ImagingSectionLeave(&cookie); @@ -151,12 +165,14 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) ImagingHistogramDelete(h); return ImagingError_ValueError("min/max not given"); } - if (!im->xsize || !im->ysize) + if (!im->xsize || !im->ysize) { break; + } memcpy(&fmin, minmax, sizeof(fmin)); memcpy(&fmax, ((char*)minmax) + sizeof(fmin), sizeof(fmax)); - if (fmin >= fmax) + if (fmin >= fmax) { break; + } ImagingSectionEnter(&cookie); scale = 255.0F / (fmax - fmin); for (y = 0; y < im->ysize; y++) { diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c index d304511d1a9..d7a0a5b9f97 100644 --- a/src/libImaging/Jpeg2KDecode.c +++ b/src/libImaging/Jpeg2KDecode.c @@ -84,10 +84,11 @@ struct j2k_decode_unpacker { static inline unsigned j2ku_shift(unsigned x, int n) { - if (n < 0) + if (n < 0) { return x >> -n; - else + } else { return x << n; + } } static void @@ -104,11 +105,13 @@ j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, unsigned x, y; - if (csiz == 3) + if (csiz == 3) { csiz = 4; + } - if (shift < 0) + if (shift < 0) { offset += 1 << (-shift - 1); + } /* csiz*h*w + offset = tileinfo.datasize */ switch (csiz) { @@ -116,24 +119,27 @@ j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (y = 0; y < h; ++y) { const UINT8 *data = &tiledata[y * w]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *row++ = j2ku_shift(offset + *data++, shift); + } } break; case 2: for (y = 0; y < h; ++y) { const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *row++ = j2ku_shift(offset + *data++, shift); + } } break; case 4: for (y = 0; y < h; ++y) { const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *row++ = j2ku_shift(offset + *data++, shift); + } } break; } @@ -154,35 +160,40 @@ j2ku_gray_i(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, unsigned x, y; - if (csiz == 3) + if (csiz == 3) { csiz = 4; + } - if (shift < 0) + if (shift < 0) { offset += 1 << (-shift - 1); + } switch (csiz) { case 1: for (y = 0; y < h; ++y) { const UINT8 *data = &tiledata[y * w]; UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *row++ = j2ku_shift(offset + *data++, shift); + } } break; case 2: for (y = 0; y < h; ++y) { const UINT16 *data = (const UINT16 *)&tiledata[2 * y * w]; UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *row++ = j2ku_shift(offset + *data++, shift); + } } break; case 4: for (y = 0; y < h; ++y) { const UINT32 *data = (const UINT32 *)&tiledata[4 * y * w]; UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *row++ = j2ku_shift(offset + *data++, shift); + } } break; } @@ -203,11 +214,13 @@ j2ku_gray_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, unsigned x, y; - if (shift < 0) + if (shift < 0) { offset += 1 << (-shift - 1); + } - if (csiz == 3) + if (csiz == 3) { csiz = 4; + } switch (csiz) { case 1: @@ -267,15 +280,19 @@ j2ku_graya_la(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, unsigned x, y; - if (csiz == 3) + if (csiz == 3) { csiz = 4; - if (acsiz == 3) + } + if (acsiz == 3) { acsiz = 4; + } - if (shift < 0) + if (shift < 0) { offset += 1 << (-shift - 1); - if (ashift < 0) + } + if (ashift < 0) { aoffset += 1 << (-ashift - 1); + } atiledata = tiledata + csiz * w * h; @@ -325,11 +342,13 @@ j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; csiz[n] = (in->comps[n].prec + 7) >> 3; - if (csiz[n] == 3) + if (csiz[n] == 3) { csiz[n] = 4; + } - if (shifts[n] < 0) + if (shifts[n] < 0) { offsets[n] += 1 << (-shifts[n] - 1); + } cptr += csiz[n] * w * h; } @@ -337,8 +356,9 @@ j2ku_srgb_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (y = 0; y < h; ++y) { const UINT8 *data[3]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; - for (n = 0; n < 3; ++n) + for (n = 0; n < 3; ++n) { data[n] = &cdata[n][csiz[n] * y * w]; + } for (x = 0; x < w; ++x) { for (n = 0; n < 3; ++n) { @@ -377,11 +397,13 @@ j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; csiz[n] = (in->comps[n].prec + 7) >> 3; - if (csiz[n] == 3) + if (csiz[n] == 3) { csiz[n] = 4; + } - if (shifts[n] < 0) + if (shifts[n] < 0) { offsets[n] += 1 << (-shifts[n] - 1); + } cptr += csiz[n] * w * h; } @@ -390,8 +412,9 @@ j2ku_sycc_rgb(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *data[3]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; UINT8 *row_start = row; - for (n = 0; n < 3; ++n) + for (n = 0; n < 3; ++n) { data[n] = &cdata[n][csiz[n] * y * w]; + } for (x = 0; x < w; ++x) { for (n = 0; n < 3; ++n) { @@ -432,11 +455,13 @@ j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; csiz[n] = (in->comps[n].prec + 7) >> 3; - if (csiz[n] == 3) + if (csiz[n] == 3) { csiz[n] = 4; + } - if (shifts[n] < 0) + if (shifts[n] < 0) { offsets[n] += 1 << (-shifts[n] - 1); + } cptr += csiz[n] * w * h; } @@ -444,8 +469,9 @@ j2ku_srgba_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, for (y = 0; y < h; ++y) { const UINT8 *data[4]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; - for (n = 0; n < 4; ++n) + for (n = 0; n < 4; ++n) { data[n] = &cdata[n][csiz[n] * y * w]; + } for (x = 0; x < w; ++x) { for (n = 0; n < 4; ++n) { @@ -483,11 +509,13 @@ j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, offsets[n] = in->comps[n].sgnd ? 1 << (in->comps[n].prec - 1) : 0; csiz[n] = (in->comps[n].prec + 7) >> 3; - if (csiz[n] == 3) + if (csiz[n] == 3) { csiz[n] = 4; + } - if (shifts[n] < 0) + if (shifts[n] < 0) { offsets[n] += 1 << (-shifts[n] - 1); + } cptr += csiz[n] * w * h; } @@ -496,8 +524,9 @@ j2ku_sycca_rgba(opj_image_t *in, const JPEG2KTILEINFO *tileinfo, const UINT8 *data[4]; UINT8 *row = (UINT8 *)im->image[y0 + y] + x0 * 4; UINT8 *row_start = row; - for (n = 0; n < 4; ++n) + for (n = 0; n < 4; ++n) { data[n] = &cdata[n][csiz[n] * y * w]; + } for (x = 0; x < w; ++x) { for (n = 0; n < 4; ++n) { @@ -584,10 +613,11 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) possibly support is 4GB. We can't go larger than this, because OpenJPEG truncates this value for the final box in the file, and the box lengths in OpenJPEG are currently 32 bit. */ - if (context->length < 0) + if (context->length < 0) { opj_stream_set_user_data_length(stream, 0xffffffff); - else + } else { opj_stream_set_user_data_length(stream, context->length); + } #endif /* Setup decompression context */ @@ -696,8 +726,9 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) goto quick_exit; } - if (!should_continue) + if (!should_continue) { break; + } /* Adjust the tile co-ordinates based on the reduction (OpenJPEG doesn't do this for us) */ @@ -784,12 +815,15 @@ j2k_decode_entry(Imaging im, ImagingCodecState state) } quick_exit: - if (codec) + if (codec) { opj_destroy_codec(codec); - if (image) + } + if (image) { opj_image_destroy(image); - if (stream) + } + if (stream) { opj_stream_destroy(stream); + } return -1; } @@ -804,8 +838,9 @@ ImagingJpeg2KDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t return -1; } - if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) + if (state->state == J2K_STATE_DONE || state->state == J2K_STATE_FAILED) { return -1; + } if (state->state == J2K_STATE_START) { state->state = J2K_STATE_DECODING; diff --git a/src/libImaging/Jpeg2KEncode.c b/src/libImaging/Jpeg2KEncode.c index aef1a4b949e..49ef5e254d4 100644 --- a/src/libImaging/Jpeg2KEncode.c +++ b/src/libImaging/Jpeg2KEncode.c @@ -106,8 +106,9 @@ j2k_pack_l(Imaging im, UINT8 *buf, unsigned x,y; for (y = 0; y < h; ++y) { UINT8 *data = (UINT8 *)(im->image[y + y0] + x0); - for (x = 0; x < w; ++x) + for (x = 0; x < w; ++x) { *ptr++ = *data++; + } } } @@ -240,8 +241,9 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) } else { rate = ((float)(components * im->xsize * im->ysize * 8) / (params->tcp_rates[n] * 8)); - if (rate > CINEMA_24_CS_LENGTH) + if (rate > CINEMA_24_CS_LENGTH) { params->tcp_rates[n] = max_rate; + } } } @@ -257,8 +259,9 @@ j2k_set_cinema_params(Imaging im, int components, opj_cparameters_t *params) } else { rate = ((float)(components * im->xsize * im->ysize * 8) / (params->tcp_rates[n] * 8)); - if (rate > CINEMA_48_CS_LENGTH) + if (rate > CINEMA_48_CS_LENGTH) { params->tcp_rates[n] = max_rate; + } } } @@ -397,8 +400,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) float *pq; if (len) { - if (len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) + if (len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) { len = sizeof(params.tcp_rates)/sizeof(params.tcp_rates[0]); + } params.tcp_numlayers = (int)len; @@ -423,8 +427,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) params.cp_disto_alloc = 1; } - if (context->num_resolutions) + if (context->num_resolutions) { params.numresolution = context->num_resolutions; + } if (context->cblk_width >= 4 && context->cblk_width <= 1024 && context->cblk_height >= 4 && context->cblk_height <= 1024 @@ -455,18 +460,21 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) case OPJ_CINEMA2K_24: case OPJ_CINEMA2K_48: params.cp_rsiz = OPJ_CINEMA2K; - if (params.numresolution > 6) + if (params.numresolution > 6) { params.numresolution = 6; + } break; case OPJ_CINEMA4K_24: params.cp_rsiz = OPJ_CINEMA4K; - if (params.numresolution > 7) + if (params.numresolution > 7) { params.numresolution = 7; + } break; } - if (context->cinema_mode != OPJ_OFF) + if (context->cinema_mode != OPJ_OFF) { j2k_set_cinema_params(im, components, ¶ms); + } /* Set up the reference grid in the image */ image->x0 = params.image_offset_x0; @@ -526,10 +534,12 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) unsigned ty1 = ty0 + tile_height; unsigned pixy, pixh; - if (ty0 < params.image_offset_y0) + if (ty0 < params.image_offset_y0) { ty0 = params.image_offset_y0; - if (ty1 > ysiz) + } + if (ty1 > ysiz) { ty1 = ysiz; + } pixy = ty0 - params.image_offset_y0; pixh = ty1 - ty0; @@ -540,10 +550,12 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) unsigned pixx, pixw; unsigned data_size; - if (tx0 < params.image_offset_x0) + if (tx0 < params.image_offset_x0) { tx0 = params.image_offset_x0; - if (tx1 > xsiz) + } + if (tx1 > xsiz) { tx1 = xsiz; + } pixx = tx0 - params.image_offset_x0; pixw = tx1 - tx0; @@ -572,12 +584,15 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) ret = -1; quick_exit: - if (codec) + if (codec) { opj_destroy_codec(codec); - if (image) + } + if (image) { opj_image_destroy(image); - if (stream) + } + if (stream) { opj_stream_destroy(stream); + } return ret; } @@ -585,8 +600,9 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) int ImagingJpeg2KEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) { - if (state->state == J2K_STATE_FAILED) + if (state->state == J2K_STATE_FAILED) { return -1; + } if (state->state == J2K_STATE_START) { @@ -611,8 +627,9 @@ ImagingJpeg2KEncodeCleanup(ImagingCodecState state) { context->quality_layers = NULL; } - if (context->error_msg) + if (context->error_msg) { free ((void *)context->error_msg); + } context->error_msg = NULL; diff --git a/src/libImaging/JpegDecode.c b/src/libImaging/JpegDecode.c index ce0c2ca7ce5..fb611204477 100644 --- a/src/libImaging/JpegDecode.c +++ b/src/libImaging/JpegDecode.c @@ -176,8 +176,9 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t by if (context->source.skip > 0) { skip_input_data(&context->cinfo, context->source.skip); - if (context->source.skip > 0) + if (context->source.skip > 0) { return context->source.pub.next_input_byte - buf; + } } switch (state->state) { @@ -193,43 +194,46 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t by } while (ok == JPEG_HEADER_TABLES_ONLY); - if (ok == JPEG_SUSPENDED) + if (ok == JPEG_SUSPENDED) { break; + } /* Decoder settings */ /* jpegmode indicates whats in the file; if not set, we'll trust the decoder */ - if (strcmp(context->jpegmode, "L") == 0) + if (strcmp(context->jpegmode, "L") == 0) { context->cinfo.jpeg_color_space = JCS_GRAYSCALE; - else if (strcmp(context->jpegmode, "RGB") == 0) + } else if (strcmp(context->jpegmode, "RGB") == 0) { context->cinfo.jpeg_color_space = JCS_RGB; - else if (strcmp(context->jpegmode, "CMYK") == 0) + } else if (strcmp(context->jpegmode, "CMYK") == 0) { context->cinfo.jpeg_color_space = JCS_CMYK; - else if (strcmp(context->jpegmode, "YCbCr") == 0) + } else if (strcmp(context->jpegmode, "YCbCr") == 0) { context->cinfo.jpeg_color_space = JCS_YCbCr; - else if (strcmp(context->jpegmode, "YCbCrK") == 0) { + } else if (strcmp(context->jpegmode, "YCbCrK") == 0) { context->cinfo.jpeg_color_space = JCS_YCCK; } /* rawmode indicates what we want from the decoder. if not set, conversions are disabled */ - if (strcmp(context->rawmode, "L") == 0) + if (strcmp(context->rawmode, "L") == 0) { context->cinfo.out_color_space = JCS_GRAYSCALE; - else if (strcmp(context->rawmode, "RGB") == 0) + } else if (strcmp(context->rawmode, "RGB") == 0) { context->cinfo.out_color_space = JCS_RGB; + } #ifdef JCS_EXTENSIONS - else if (strcmp(context->rawmode, "RGBX") == 0) + else if (strcmp(context->rawmode, "RGBX") == 0) { context->cinfo.out_color_space = JCS_EXT_RGBX; + } #endif else if (strcmp(context->rawmode, "CMYK") == 0 || - strcmp(context->rawmode, "CMYK;I") == 0) + strcmp(context->rawmode, "CMYK;I") == 0) { context->cinfo.out_color_space = JCS_CMYK; - else if (strcmp(context->rawmode, "YCbCr") == 0) + } else if (strcmp(context->rawmode, "YCbCr") == 0) { context->cinfo.out_color_space = JCS_YCbCr; - else if (strcmp(context->rawmode, "YCbCrK") == 0) + } else if (strcmp(context->rawmode, "YCbCrK") == 0) { context->cinfo.out_color_space = JCS_YCCK; - else { + } else { /* Disable decoder conversions */ context->cinfo.jpeg_color_space = JCS_UNKNOWN; context->cinfo.out_color_space = JCS_UNKNOWN; @@ -251,8 +255,9 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t by /* Set things up for decompression (this processes the entire file if necessary to return data line by line) */ - if (!jpeg_start_decompress(&context->cinfo)) + if (!jpeg_start_decompress(&context->cinfo)) { break; + } state->state++; /* fall through */ @@ -263,15 +268,17 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t by ok = 1; while (state->y < state->ysize) { ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1); - if (ok != 1) + if (ok != 1) { break; + } state->shuffle((UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->buffer, state->xsize); state->y++; } - if (ok != 1) + if (ok != 1) { break; + } state->state++; /* fall through */ @@ -280,8 +287,9 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t by /* Finish decompression */ if (!jpeg_finish_decompress(&context->cinfo)) { /* FIXME: add strictness mode test */ - if (state->y < state->ysize) + if (state->y < state->ysize) { break; + } } /* Clean up */ diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 2eb35d6bf5b..5f3b29c669b 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -127,17 +127,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) break; case 24: context->cinfo.input_components = 3; - if (strcmp(im->mode, "YCbCr") == 0) + if (strcmp(im->mode, "YCbCr") == 0) { context->cinfo.in_color_space = JCS_YCbCr; - else + } else { context->cinfo.in_color_space = JCS_RGB; + } break; case 32: context->cinfo.input_components = 4; context->cinfo.in_color_space = JCS_CMYK; #ifdef JCS_EXTENSIONS - if (strcmp(context->rawmode, "RGBX") == 0) + if (strcmp(context->rawmode, "RGBX") == 0) { context->cinfo.in_color_space = JCS_EXT_RGBX; + } #endif break; default: @@ -214,8 +216,9 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) break; } } - if (context->progressive) - jpeg_simple_progression(&context->cinfo); + if (context->progressive) { + jpeg_simple_progression(&context->cinfo); + } context->cinfo.smoothing_factor = context->smooth; context->cinfo.optimize_coding = (boolean) context->optimize; if (context->xdpi > 0 && context->ydpi > 0) { @@ -261,17 +264,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (context->extra) { /* copy extra buffer to output buffer */ unsigned int n = context->extra_size - context->extra_offset; - if (n > context->destination.pub.free_in_buffer) + if (n > context->destination.pub.free_in_buffer) { n = context->destination.pub.free_in_buffer; + } memcpy(context->destination.pub.next_output_byte, context->extra + context->extra_offset, n); context->destination.pub.next_output_byte += n; context->destination.pub.free_in_buffer -= n; context->extra_offset += n; - if (context->extra_offset >= context->extra_size) + if (context->extra_offset >= context->extra_size) { state->state++; - else + } else { break; + } } else state->state++; @@ -286,21 +291,24 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) (UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->xsize); ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1); - if (ok != 1) + if (ok != 1) { break; + } state->y++; } - if (ok != 1) + if (ok != 1) { break; + } state->state++; /* fall through */ case 5: /* Finish compression */ - if (context->destination.pub.free_in_buffer < 100) + if (context->destination.pub.free_in_buffer < 100) { break; + } jpeg_finish_compress(&context->cinfo); /* Clean up */ diff --git a/src/libImaging/Matrix.c b/src/libImaging/Matrix.c index 22d5d642cfa..e02b7b3129e 100644 --- a/src/libImaging/Matrix.c +++ b/src/libImaging/Matrix.c @@ -27,14 +27,16 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) int x, y; /* Assume there's enough data in the buffer */ - if (!im) + if (!im) { return (Imaging) ImagingError_ModeError(); + } if (strcmp(mode, "L") == 0 && im->bands == 3) { imOut = ImagingNewDirty("L", im->xsize, im->ysize); - if (!imOut) + if (!imOut) { return NULL; + } for (y = 0; y < im->ysize; y++) { UINT8* in = (UINT8*) im->image[y]; @@ -50,8 +52,9 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) } else if (strlen(mode) == 3 && im->bands == 3) { imOut = ImagingNewDirty(mode, im->xsize, im->ysize); - if (!imOut) + if (!imOut) { return NULL; + } for (y = 0; y < im->ysize; y++) { UINT8* in = (UINT8*) im->image[y]; @@ -67,8 +70,9 @@ ImagingConvertMatrix(Imaging im, const char *mode, float m[]) in += 4; out += 4; } } - } else + } else { return (Imaging) ImagingError_ModeError(); + } return imOut; } diff --git a/src/libImaging/ModeFilter.c b/src/libImaging/ModeFilter.c index 5237d07328f..b1cf7442c82 100644 --- a/src/libImaging/ModeFilter.c +++ b/src/libImaging/ModeFilter.c @@ -25,12 +25,14 @@ ImagingModeFilter(Imaging im, int size) UINT8 maxpixel; int histogram[256]; - if (!im || im->bands != 1 || im->type != IMAGING_TYPE_UINT8) + if (!im || im->bands != 1 || im->type != IMAGING_TYPE_UINT8) { return (Imaging) ImagingError_ModeError(); + } imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); - if (!imOut) + if (!imOut) { return NULL; + } size = size / 2; @@ -46,27 +48,32 @@ ImagingModeFilter(Imaging im, int size) the added complexity... */ memset(histogram, 0, sizeof(histogram)); - for (yy = y - size; yy <= y + size; yy++) + for (yy = y - size; yy <= y + size; yy++) { if (yy >= 0 && yy < imOut->ysize) { UINT8* in = &IMAGING_PIXEL_L(im, 0, yy); - for (xx = x - size; xx <= x + size; xx++) - if (xx >= 0 && xx < imOut->xsize) + for (xx = x - size; xx <= x + size; xx++) { + if (xx >= 0 && xx < imOut->xsize) { histogram[in[xx]]++; + } + } } + } /* find most frequent pixel value in this region */ maxpixel = 0; maxcount = histogram[maxpixel]; - for (i = 1; i < 256; i++) + for (i = 1; i < 256; i++) { if (histogram[i] > maxcount) { maxcount = histogram[i]; maxpixel = (UINT8) i; } + } - if (maxcount > 2) + if (maxcount > 2) { out[x] = maxpixel; - else + } else { out[x] = IMAGING_PIXEL_L(im, x, y); + } } diff --git a/src/libImaging/Negative.c b/src/libImaging/Negative.c index fe08e4753d9..09c946bbef8 100644 --- a/src/libImaging/Negative.c +++ b/src/libImaging/Negative.c @@ -26,16 +26,20 @@ ImagingNegative(Imaging im) Imaging imOut; int x, y; - if (!im) + if (!im) { return (Imaging) ImagingError_ModeError(); + } imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); - if (!imOut) + if (!imOut) { return NULL; + } - for (y = 0; y < im->ysize; y++) - for (x = 0; x < im->linesize; x++) + for (y = 0; y < im->ysize; y++) { + for (x = 0; x < im->linesize; x++) { imOut->image[y][x] = ~im->image[y][x]; + } + } return imOut; } diff --git a/src/libImaging/Offset.c b/src/libImaging/Offset.c index 0f9793c3c21..29f038153f6 100644 --- a/src/libImaging/Offset.c +++ b/src/libImaging/Offset.c @@ -24,38 +24,44 @@ ImagingOffset(Imaging im, int xoffset, int yoffset) int x, y; Imaging imOut; - if (!im) + if (!im) { return (Imaging) ImagingError_ModeError(); + } imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize); - if (!imOut) + if (!imOut) { return NULL; + } ImagingCopyPalette(imOut, im); /* make offsets positive to avoid negative coordinates */ xoffset %= im->xsize; xoffset = im->xsize - xoffset; - if (xoffset < 0) + if (xoffset < 0) { xoffset += im->xsize; + } yoffset %= im->ysize; yoffset = im->ysize - yoffset; - if (yoffset < 0) + if (yoffset < 0) { yoffset += im->ysize; + } #define OFFSET(image)\ - for (y = 0; y < im->ysize; y++)\ + for (y = 0; y < im->ysize; y++) {\ for (x = 0; x < im->xsize; x++) {\ int yi = (y + yoffset) % im->ysize;\ int xi = (x + xoffset) % im->xsize;\ imOut->image[y][x] = im->image[yi][xi];\ + }\ } - if (im->image8) + if (im->image8) { OFFSET(image8) - else + } else { OFFSET(image32) + } return imOut; } diff --git a/src/libImaging/Pack.c b/src/libImaging/Pack.c index a239464d4a0..0be7ad8c9eb 100644 --- a/src/libImaging/Pack.c +++ b/src/libImaging/Pack.c @@ -80,16 +80,18 @@ pack1(UINT8* out, const UINT8* in, int pixels) /* bilevel (black is 0) */ b = 0; m = 128; for (i = 0; i < pixels; i++) { - if (in[i] != 0) + if (in[i] != 0) { b |= m; + } m >>= 1; if (m == 0) { *out++ = b; b = 0; m = 128; } } - if (m != 128) + if (m != 128) { *out++ = b; + } } static void @@ -99,16 +101,18 @@ pack1I(UINT8* out, const UINT8* in, int pixels) /* bilevel (black is 1) */ b = 0; m = 128; for (i = 0; i < pixels; i++) { - if (in[i] == 0) + if (in[i] == 0) { b |= m; + } m >>= 1; if (m == 0) { *out++ = b; b = 0; m = 128; } } - if (m != 128) + if (m != 128) { *out++ = b; + } } static void @@ -118,16 +122,18 @@ pack1R(UINT8* out, const UINT8* in, int pixels) /* bilevel, lsb first (black is 0) */ b = 0; m = 1; for (i = 0; i < pixels; i++) { - if (in[i] != 0) + if (in[i] != 0) { b |= m; + } m <<= 1; if (m == 256){ *out++ = b; b = 0; m = 1; } } - if (m != 1) + if (m != 1) { *out++ = b; + } } static void @@ -137,16 +143,18 @@ pack1IR(UINT8* out, const UINT8* in, int pixels) /* bilevel, lsb first (black is 1) */ b = 0; m = 1; for (i = 0; i < pixels; i++) { - if (in[i] == 0) + if (in[i] == 0) { b |= m; + } m <<= 1; if (m == 256){ *out++ = b; b = 0; m = 1; } } - if (m != 1) + if (m != 1) { *out++ = b; + } } static void @@ -154,8 +162,9 @@ pack1L(UINT8* out, const UINT8* in, int pixels) { int i; /* bilevel, stored as bytes */ - for (i = 0; i < pixels; i++) + for (i = 0; i < pixels; i++) { out[i] = (in[i] != 0) ? 255 : 0; + } } static void @@ -167,8 +176,9 @@ packP4(UINT8* out, const UINT8* in, int pixels) in += 2; pixels -= 2; } - if (pixels) + if (pixels) { out[0] = (in[0] << 4); + } } static void @@ -407,12 +417,13 @@ packI16B(UINT8* out, const UINT8* in_, int pixels) for (i = 0; i < pixels; i++) { INT32 in; memcpy(&in, in_, sizeof(in)); - if (in <= 0) + if (in <= 0) { tmp_ = 0; - else if (in > 65535) + } else if (in > 65535) { tmp_ = 65535; - else + } else { tmp_ = in; + } C16B; out += 2; in_ += sizeof(in); } @@ -496,40 +507,45 @@ copy4I(UINT8* out, const UINT8* in, int pixels) { /* RGBA, CMYK quadruples, inverted */ int i; - for (i = 0; i < pixels*4; i++) + for (i = 0; i < pixels*4; i++) { out[i] = ~in[i]; + } } static void band0(UINT8* out, const UINT8* in, int pixels) { int i; - for (i = 0; i < pixels; i++, in += 4) + for (i = 0; i < pixels; i++, in += 4) { out[i] = in[0]; + } } static void band1(UINT8* out, const UINT8* in, int pixels) { int i; - for (i = 0; i < pixels; i++, in += 4) + for (i = 0; i < pixels; i++, in += 4) { out[i] = in[1]; + } } static void band2(UINT8* out, const UINT8* in, int pixels) { int i; - for (i = 0; i < pixels; i++, in += 4) + for (i = 0; i < pixels; i++, in += 4) { out[i] = in[2]; + } } static void band3(UINT8* out, const UINT8* in, int pixels) { int i; - for (i = 0; i < pixels; i++, in += 4) + for (i = 0; i < pixels; i++, in += 4) { out[i] = in[3]; + } } static struct { @@ -673,12 +689,14 @@ ImagingFindPacker(const char* mode, const char* rawmode, int* bits_out) int i; /* find a suitable pixel packer */ - for (i = 0; packers[i].rawmode; i++) + for (i = 0; packers[i].rawmode; i++) { if (strcmp(packers[i].mode, mode) == 0 && strcmp(packers[i].rawmode, rawmode) == 0) { - if (bits_out) + if (bits_out) { *bits_out = packers[i].bits; + } return packers[i].pack; } + } return NULL; } diff --git a/src/libImaging/PackDecode.c b/src/libImaging/PackDecode.c index b9c846f4b04..34671828aef 100644 --- a/src/libImaging/PackDecode.c +++ b/src/libImaging/PackDecode.c @@ -28,8 +28,9 @@ ImagingPackbitsDecode(Imaging im, ImagingCodecState state, for (;;) { - if (bytes < 1) + if (bytes < 1) { return ptr - buf; + } if (ptr[0] & 0x80) { @@ -40,8 +41,9 @@ ImagingPackbitsDecode(Imaging im, ImagingCodecState state, } /* Run */ - if (bytes < 2) + if (bytes < 2) { return ptr - buf; + } for (n = 257 - ptr[0]; n > 0; n--) { if (state->x >= state->bytes) { @@ -58,8 +60,9 @@ ImagingPackbitsDecode(Imaging im, ImagingCodecState state, /* Literal */ n = ptr[0]+2; - if (bytes < n) + if (bytes < n) { return ptr - buf; + } for (i = 1; i < n; i++) { if (state->x >= state->bytes) { diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c index 7aee6e8eef4..ac548f50c76 100644 --- a/src/libImaging/Palette.c +++ b/src/libImaging/Palette.c @@ -30,12 +30,14 @@ ImagingPaletteNew(const char* mode) int i; ImagingPalette palette; - if (strcmp(mode, "RGB") && strcmp(mode, "RGBA")) + if (strcmp(mode, "RGB") && strcmp(mode, "RGBA")) { return (ImagingPalette) ImagingError_ModeError(); + } palette = calloc(1, sizeof(struct ImagingPaletteInstance)); - if (!palette) + if (!palette) { return (ImagingPalette) ImagingError_MemoryError(); + } strncpy(palette->mode, mode, IMAGING_MODE_LENGTH-1); palette->mode[IMAGING_MODE_LENGTH-1] = 0; @@ -60,8 +62,9 @@ ImagingPaletteNewBrowser(void) ImagingPalette palette; palette = ImagingPaletteNew("RGB"); - if (!palette) + if (!palette) { return NULL; + } /* Blank out unused entries */ /* FIXME: Add 10-level windows palette here? */ @@ -74,14 +77,16 @@ ImagingPaletteNewBrowser(void) /* Simple 6x6x6 colour cube */ - for (b = 0; b < 256; b += 51) - for (g = 0; g < 256; g += 51) + for (b = 0; b < 256; b += 51) { + for (g = 0; g < 256; g += 51) { for (r = 0; r < 256; r += 51) { palette->palette[i*4+0] = r; palette->palette[i*4+1] = g; palette->palette[i*4+2] = b; i++; } + } + } /* Blank out unused entries */ /* FIXME: add 30-level greyscale wedge here? */ @@ -102,12 +107,14 @@ ImagingPaletteDuplicate(ImagingPalette palette) ImagingPalette new_palette; - if (!palette) + if (!palette) { return NULL; + } /* malloc check ok, small constant allocation */ new_palette = malloc(sizeof(struct ImagingPaletteInstance)); - if (!new_palette) + if (!new_palette) { return (ImagingPalette) ImagingError_MemoryError(); + } memcpy(new_palette, palette, sizeof(struct ImagingPaletteInstance)); @@ -123,8 +130,9 @@ ImagingPaletteDelete(ImagingPalette palette) /* Destroy palette object */ if (palette) { - if (palette->cache) + if (palette->cache) { free(palette->cache); + } free(palette); } } @@ -209,8 +217,9 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0); dmin[i] = tmin; - if (tmax < dmax) + if (tmax < dmax) { dmax = tmax; /* keep the smallest max distance only */ + } } @@ -220,10 +229,11 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) * all slots in that box. We only check boxes for which the min * distance is less than or equal the smallest max distance */ - for (i = 0; i < BOXVOLUME; i++) + for (i = 0; i < BOXVOLUME; i++) { d[i] = (unsigned int) ~0; + } - for (i = 0; i < 256; i++) + for (i = 0; i < 256; i++) { if (dmin[i] <= dmax) { @@ -262,6 +272,7 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) rx += 2 * RSTEP * RSTEP; } } + } /* Step 3 -- Update cache */ @@ -269,10 +280,13 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) * cache slot in the box. Update the cache. */ j = 0; - for (r = r0; r < r1; r+=4) - for (g = g0; g < g1; g+=4) - for (b = b0; b < b1; b+=4) + for (r = r0; r < r1; r+=4) { + for (g = g0; g < g1; g+=4) { + for (b = b0; b < b1; b+=4) { ImagingPaletteCache(palette, r, g, b) = c[j++]; + } + } + } } @@ -297,8 +311,9 @@ ImagingPaletteCachePrepare(ImagingPalette palette) } /* Mark all entries as empty */ - for (i = 0; i < entries; i++) + for (i = 0; i < entries; i++) { palette->cache[i] = 0x100; + } } diff --git a/src/libImaging/Paste.c b/src/libImaging/Paste.c index 0bda25739ae..6dfff4afb53 100644 --- a/src/libImaging/Paste.c +++ b/src/libImaging/Paste.c @@ -37,8 +37,9 @@ paste(Imaging imOut, Imaging imIn, int dx, int dy, int sx, int sy, xsize *= pixelsize; - for (y = 0; y < ysize; y++) + for (y = 0; y < ysize; y++) { memcpy(imOut->image[y+dy]+dx, imIn->image[y+sy]+sx, xsize); + } } static inline void @@ -57,8 +58,9 @@ paste_mask_1(Imaging imOut, Imaging imIn, Imaging imMask, UINT8* in = imIn->image8[y+sy]+sx; UINT8* mask = imMask->image8[y+sy]+sx; for (x = 0; x < xsize; x++) { - if (*mask++) + if (*mask++) { *out = *in; + } out++, in++; } } @@ -70,8 +72,9 @@ paste_mask_1(Imaging imOut, Imaging imIn, Imaging imMask, INT32* in = imIn->image32[y+sy]+sx; UINT8* mask = imMask->image8[y+sy]+sx; for (x = 0; x < xsize; x++) { - if (*mask++) + if (*mask++) { *out = *in; + } out++, in++; } } @@ -231,17 +234,22 @@ ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, /* Determine which region to copy */ sx0 = sy0 = 0; - if (dx0 < 0) + if (dx0 < 0) { xsize += dx0, sx0 = -dx0, dx0 = 0; - if (dx0 + xsize > imOut->xsize) + } + if (dx0 + xsize > imOut->xsize) { xsize = imOut->xsize - dx0; - if (dy0 < 0) + } + if (dy0 < 0) { ysize += dy0, sy0 = -dy0, dy0 = 0; - if (dy0 + ysize > imOut->ysize) + } + if (dy0 + ysize > imOut->ysize) { ysize = imOut->ysize - dy0; + } - if (xsize <= 0 || ysize <= 0) + if (xsize <= 0 || ysize <= 0) { return 0; + } if (!imMask) { ImagingSectionEnter(&cookie); @@ -297,15 +305,17 @@ fill(Imaging imOut, const void* ink_, int dx, int dy, dx *= pixelsize; xsize *= pixelsize; - for (y = 0; y < ysize; y++) + for (y = 0; y < ysize; y++) { memset(imOut->image[y+dy]+dx, ink8, xsize); + } } else { for (y = 0; y < ysize; y++) { INT32* out = imOut->image32[y+dy]+dx; - for (x = 0; x < xsize; x++) + for (x = 0; x < xsize; x++) { out[x] = ink32; + } } } @@ -331,8 +341,9 @@ fill_mask_1(Imaging imOut, const void* ink_, Imaging imMask, UINT8* out = imOut->image8[y+dy]+dx; UINT8* mask = imMask->image8[y+sy]+sx; for (x = 0; x < xsize; x++) { - if (*mask++) + if (*mask++) { *out = ink8; + } out++; } } @@ -343,8 +354,9 @@ fill_mask_1(Imaging imOut, const void* ink_, Imaging imMask, INT32* out = imOut->image32[y+dy]+dx; UINT8* mask = imMask->image8[y+sy]+sx; for (x = 0; x < xsize; x++) { - if (*mask++) + if (*mask++) { *out = ink32; + } out++; } } @@ -494,17 +506,22 @@ ImagingFill2(Imaging imOut, const void* ink, Imaging imMask, /* Determine which region to fill */ sx0 = sy0 = 0; - if (dx0 < 0) + if (dx0 < 0) { xsize += dx0, sx0 = -dx0, dx0 = 0; - if (dx0 + xsize > imOut->xsize) + } + if (dx0 + xsize > imOut->xsize) { xsize = imOut->xsize - dx0; - if (dy0 < 0) + } + if (dy0 < 0) { ysize += dy0, sy0 = -dy0, dy0 = 0; - if (dy0 + ysize > imOut->ysize) + } + if (dy0 + ysize > imOut->ysize) { ysize = imOut->ysize - dy0; + } - if (xsize <= 0 || ysize <= 0) + if (xsize <= 0 || ysize <= 0) { return 0; + } if (!imMask) { ImagingSectionEnter(&cookie); diff --git a/src/libImaging/PcdDecode.c b/src/libImaging/PcdDecode.c index 6f86dc96b35..ff192a1744e 100644 --- a/src/libImaging/PcdDecode.c +++ b/src/libImaging/PcdDecode.c @@ -38,8 +38,9 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { /* We need data for two full lines before we can do anything */ - if (bytes < chunk) + if (bytes < chunk) { return ptr - buf; + } /* Unpack first line */ out = state->buffer; @@ -53,8 +54,9 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt state->shuffle((UINT8*) im->image[state->y], state->buffer, state->xsize); - if (++state->y >= state->ysize) + if (++state->y >= state->ysize) { return -1; /* This can hardly happen */ + } /* Unpack second line */ out = state->buffer; @@ -68,8 +70,9 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt state->shuffle((UINT8*) im->image[state->y], state->buffer, state->xsize); - if (++state->y >= state->ysize) + if (++state->y >= state->ysize) { return -1; + } ptr += chunk; bytes -= chunk; diff --git a/src/libImaging/PcxDecode.c b/src/libImaging/PcxDecode.c index a4e40864b4d..eb0fecc83be 100644 --- a/src/libImaging/PcxDecode.c +++ b/src/libImaging/PcxDecode.c @@ -31,14 +31,16 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt for (;;) { - if (bytes < 1) + if (bytes < 1) { return ptr - buf; + } if ((*ptr & 0xC0) == 0xC0) { /* Run */ - if (bytes < 2) + if (bytes < 2) { return ptr - buf; + } n = ptr[0] & 0x3F; diff --git a/src/libImaging/Point.c b/src/libImaging/Point.c index a80b9e7d2fc..76c0e591dae 100644 --- a/src/libImaging/Point.c +++ b/src/libImaging/Point.c @@ -35,8 +35,9 @@ im_point_8_8(Imaging imOut, Imaging imIn, im_point_context* context) for (y = 0; y < imIn->ysize; y++) { UINT8* in = imIn->image8[y]; UINT8* out = imOut->image8[y]; - for (x = 0; x < imIn->xsize; x++) + for (x = 0; x < imIn->xsize; x++) { out[x] = table[in[x]]; + } } } @@ -103,8 +104,9 @@ im_point_8_32(Imaging imOut, Imaging imIn, im_point_context* context) for (y = 0; y < imIn->ysize; y++) { UINT8* in = imIn->image8[y]; INT32* out = imOut->image32[y]; - for (x = 0; x < imIn->xsize; x++) + for (x = 0; x < imIn->xsize; x++) { memcpy(out + x, table + in[x] * sizeof(INT32), sizeof(INT32)); + } } } @@ -119,10 +121,11 @@ im_point_32_8(Imaging imOut, Imaging imIn, im_point_context* context) UINT8* out = imOut->image8[y]; for (x = 0; x < imIn->xsize; x++) { int v = in[x]; - if (v < 0) + if (v < 0) { v = 0; - else if (v > 65535) + } else if (v > 65535) { v = 65535; + } out[x] = table[v]; } } @@ -138,21 +141,26 @@ ImagingPoint(Imaging imIn, const char* mode, const void* table) im_point_context context; void (*point)(Imaging imIn, Imaging imOut, im_point_context* context); - if (!imIn) + if (!imIn) { return (Imaging) ImagingError_ModeError(); + } - if (!mode) + if (!mode) { mode = imIn->mode; + } if (imIn->type != IMAGING_TYPE_UINT8) { - if (imIn->type != IMAGING_TYPE_INT32 || strcmp(mode, "L") != 0) + if (imIn->type != IMAGING_TYPE_INT32 || strcmp(mode, "L") != 0) { goto mode_mismatch; - } else if (!imIn->image8 && strcmp(imIn->mode, mode) != 0) + } + } else if (!imIn->image8 && strcmp(imIn->mode, mode) != 0) { goto mode_mismatch; + } imOut = ImagingNew(mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } /* find appropriate handler */ if (imIn->type == IMAGING_TYPE_UINT8) { @@ -175,10 +183,12 @@ ImagingPoint(Imaging imIn, const char* mode, const void* table) point = im_point_8_8; break; } - } else + } else { point = im_point_8_32; - } else + } + } else { point = im_point_32_8; + } ImagingCopyPalette(imOut, imIn); @@ -213,8 +223,9 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) return (Imaging) ImagingError_ModeError(); imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } switch (imIn->type) { case IMAGING_TYPE_INT32: @@ -223,8 +234,9 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) INT32* in = imIn->image32[y]; INT32* out = imOut->image32[y]; /* FIXME: add clipping? */ - for (x = 0; x < imIn->xsize; x++) + for (x = 0; x < imIn->xsize; x++) { out[x] = in[x] * scale + offset; + } } ImagingSectionLeave(&cookie); break; @@ -233,8 +245,9 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) for (y = 0; y < imIn->ysize; y++) { FLOAT32* in = (FLOAT32*) imIn->image32[y]; FLOAT32* out = (FLOAT32*) imOut->image32[y]; - for (x = 0; x < imIn->xsize; x++) + for (x = 0; x < imIn->xsize; x++) { out[x] = in[x] * scale + offset; + } } ImagingSectionLeave(&cookie); break; diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index b94dc6e1d09..fdf1ea3b638 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -157,7 +157,9 @@ create_pixel_hash(Pixel *pixelData,uint32_t nPixels) /* malloc check ok, small constant allocation */ d=malloc(sizeof(PixelHashData)); - if (!d) return NULL; + if (!d) { + return NULL; + } hash=hashtable_new(pixel_hash,pixel_cmp); hashtable_set_user_data(hash,d); d->scale=0; @@ -197,7 +199,9 @@ static void destroy_pixel_hash(HashTable *hash) { PixelHashData *d=(PixelHashData *)hashtable_get_user_data(hash); - if (d) free(d); + if (d) { + free(d); + } hashtable_free(hash); } @@ -214,7 +218,9 @@ static int compute_box_volume(BoxNode *b) { unsigned char rl,rh,gl,gh,bl,bh; - if (b->volume>=0) return b->volume; + if (b->volume>=0) { + return b->volume; + } if (!b->head[0]) { b->volume=0; } else { @@ -242,7 +248,9 @@ hash_to_list(const HashTable *h, const Pixel pixel, const uint32_t count, void * /* malloc check ok, small constant allocation */ p=malloc(sizeof(PixelList)); - if (!p) return; + if (!p) { + return; + } p->flag=0; p->p=q; @@ -250,7 +258,9 @@ hash_to_list(const HashTable *h, const Pixel pixel, const uint32_t count, void * for (i=0;i<3;i++) { p->next[i]=pl[i]; p->prev[i]=NULL; - if (pl[i]) pl[i]->prev[i]=p; + if (pl[i]) { + pl[i]->prev[i]=p; + } pl[i]=p; } } @@ -268,7 +278,9 @@ mergesort_pixels(PixelList *head, int i) } for (c=t=head;c&&t;c=c->next[i],t=(t->next[i])?t->next[i]->next[i]:NULL); if (c) { - if (c->prev[i]) c->prev[i]->next[i]=NULL; + if (c->prev[i]) { + c->prev[i]->next[i]=NULL; + } c->prev[i]=NULL; } a=mergesort_pixels(head,i); @@ -285,9 +297,13 @@ mergesort_pixels(PixelList *head, int i) } c->prev[i]=p; c->next[i]=NULL; - if (p) p->next[i]=c; + if (p) { + p->next[i]=c; + } p=c; - if (!head) head=c; + if (!head) { + head=c; + } } if (a) { c->next[i]=a; @@ -442,17 +458,29 @@ splitlists(PixelList *h[3], for (c=h[i];c;c=n) { n=c->next[i]; if (c->flag) { /* move pixel to right list*/ - if (r) r->next[i]=c; else nh[1][i]=c; + if (r) { + r->next[i]=c; + } else { + nh[1][i]=c; + } c->prev[i]=r; r=c; } else { /* move pixel to left list */ - if (l) l->next[i]=c; else nh[0][i]=c; + if (l) { + l->next[i]=c; + } else { + nh[0][i]=c; + } c->prev[i]=l; l=c; } } - if (l) l->next[i]=NULL; - if (r) r->next[i]=NULL; + if (l) { + l->next[i]=NULL; + } + if (r) { + r->next[i]=NULL; + } nt[0][i]=l; nt[1][i]=r; } @@ -720,7 +748,9 @@ annotate_hash_table(BoxNode *n,HashTable *h,uint32_t *box) return 0; } } - if (n->head[0]) (*box)++; + if (n->head[0]) { + (*box)++; + } return 1; } @@ -756,7 +786,9 @@ resort_distance_tables(uint32_t *avgDist, for (k=j;k&&(*(skRow[k-1])>*(skRow[k]));k--) { skRow[k]=skRow[k-1]; } - if (k!=j) skRow[k]=skElt; + if (k!=j) { + skRow[k]=skElt; + } } } return 1; @@ -987,7 +1019,9 @@ compute_palette_from_median_cut( if (!(i%100)) { printf ("%05d\r",i); fflush(stdout); } if (checkContained(root,pixelData+i)>1) { printf ("pixel in two boxes\n"); - for(i=0;i<3;i++) free (avg[i]); + for(i=0;i<3;i++) { + free (avg[i]); + } free(count); return 0; } @@ -996,7 +1030,9 @@ compute_palette_from_median_cut( #ifndef NO_OUTPUT printf ("pixel lookup failed\n"); #endif - for(i=0;i<3;i++) free (avg[i]); + for(i=0;i<3;i++) { + free (avg[i]); + } free(count); return 0; } @@ -1004,7 +1040,9 @@ compute_palette_from_median_cut( #ifndef NO_OUTPUT printf ("panic - paletteEntry>=nPaletteEntries (%d>=%d)\n",(int)paletteEntry,(int)nPaletteEntries); #endif - for(i=0;i<3;i++) free (avg[i]); + for(i=0;i<3;i++) { + free (avg[i]); + } free(count); return 0; } @@ -1016,7 +1054,9 @@ compute_palette_from_median_cut( /* malloc check ok, using calloc */ p=calloc(nPaletteEntries, sizeof(Pixel)); if (!p) { - for(i=0;i<3;i++) free (avg[i]); + for(i=0;i<3;i++) { + free (avg[i]); + } free(count); return 0; } @@ -1026,7 +1066,9 @@ compute_palette_from_median_cut( p[i].c.b=(int)(.5+(double)avg[2][i]/(double)count[i]); } *palette=p; - for(i=0;i<3;i++) free (avg[i]); + for(i=0;i<3;i++) { + free (avg[i]); + } free(count); return 1; } @@ -1156,24 +1198,46 @@ k_means(Pixel *pixelData, #ifndef NO_OUTPUT printf (".(%d)",changes);fflush(stdout); #endif - if (changes<=threshold) break; + if (changes<=threshold) { + break; + } } #ifndef NO_OUTPUT printf("]\n"); #endif - if (avgDistSortKey) free(avgDistSortKey); - if (avgDist) free(avgDist); - for(i=0;i<3;i++) if (avg[i]) free (avg[i]); - if (count) free(count); + if (avgDistSortKey) { + free(avgDistSortKey); + } + if (avgDist) { + free(avgDist); + } + for(i=0;i<3;i++) { + if (avg[i]) { + free (avg[i]); + } + } + if (count) { + free(count); + } return 1; error_3: - if (avgDistSortKey) free(avgDistSortKey); + if (avgDistSortKey) { + free(avgDistSortKey); + } error_2: - if (avgDist) free(avgDist); + if (avgDist) { + free(avgDist); + } error_1: - for(i=0;i<3;i++) if (avg[i]) free (avg[i]); - if (count) free(count); + for(i=0;i<3;i++) { + if (avg[i]) { + free (avg[i]); + } + } + if (count) { + free(count); + } return 0; } @@ -1345,7 +1409,9 @@ quantize(Pixel *pixelData, #ifndef NO_OUTPUT printf ("k means...\n"); fflush(stdout); timer=clock(); #endif - if (kmeans) k_means(pixelData,nPixels,p,nPaletteEntries,qp,kmeans-1); + if (kmeans) { + k_means(pixelData,nPixels,p,nPaletteEntries,qp,kmeans-1); + } #ifndef NO_OUTPUT printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC); #endif @@ -1357,8 +1423,12 @@ quantize(Pixel *pixelData, #ifndef NO_OUTPUT printf ("cleanup..."); fflush(stdout); timer=clock(); #endif - if (avgDist) free(avgDist); - if (avgDistSortKey) free(avgDistSortKey); + if (avgDist) { + free(avgDist); + } + if (avgDistSortKey) { + free(avgDistSortKey); + } destroy_pixel_hash(h); #ifndef NO_OUTPUT printf ("done (%f)\n",(clock()-timer)/(double)CLOCKS_PER_SEC); @@ -1367,15 +1437,25 @@ quantize(Pixel *pixelData, return 1; error_7: - if (avgDistSortKey) free(avgDistSortKey); + if (avgDistSortKey) { + free(avgDistSortKey); + } error_6: - if (avgDist) free(avgDist); + if (avgDist) { + free(avgDist); + } error_5: - if (qp) free(qp); + if (qp) { + free(qp); + } error_4: - if (p) free(p); + if (p) { + free(p); + } error_3: - if (root) free_box_tree(root); + if (root) { + free_box_tree(root); + } error_1: destroy_pixel_hash(h); error_0: @@ -1430,7 +1510,9 @@ quantize2(Pixel *pixelData, /* malloc check ok, using calloc */ p=calloc(nQuantPixels, sizeof(Pixel)); - if (!p) return 0; + if (!p) { + return 0; + } mean[0]=mean[1]=mean[2]=0; h=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); for (i=0;i 256) + } + if (colors < 1 || colors > 256) { /* FIXME: for colors > 256, consider returning an RGB image instead (see @PIL205) */ return (Imaging) ImagingError_ValueError("bad number of colors"); + } if (strcmp(im->mode, "L") != 0 && strcmp(im->mode, "P") != 0 && - strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0) + strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0) { return ImagingError_ModeError(); + } /* only octree and imagequant supports RGBA */ - if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3) + if (!strcmp(im->mode, "RGBA") && mode != 2 && mode != 3) { return ImagingError_ModeError(); + } if (im->xsize > INT_MAX / im->ysize) { return ImagingError_MemoryError(); } /* malloc check ok, using calloc for final overflow, x*y above */ p = calloc(im->xsize * im->ysize, sizeof(Pixel)); - if (!p) + if (!p) { return ImagingError_MemoryError(); + } /* collect statistics */ @@ -1543,18 +1632,19 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) /* FIXME: converting a "L" image to "P" with 256 colors should be done by a simple copy... */ - for (i = y = 0; y < im->ysize; y++) + for (i = y = 0; y < im->ysize; y++) { for (x = 0; x < im->xsize; x++, i++) { p[i].c.r = p[i].c.g = p[i].c.b = im->image8[y][x]; p[i].c.a = 255; } + } } else if (!strcmp(im->mode, "P")) { /* palette */ pp = im->palette->palette; - for (i = y = 0; y < im->ysize; y++) + for (i = y = 0; y < im->ysize; y++) { for (x = 0; x < im->xsize; x++, i++) { v = im->image8[y][x]; p[i].c.r = pp[v*4+0]; @@ -1562,13 +1652,16 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) p[i].c.b = pp[v*4+2]; p[i].c.a = pp[v*4+3]; } + } } else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) { /* true colour */ - for (i = y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++, i++) + for (i = y = 0; y < im->ysize; y++) { + for (x = 0; x < im->xsize; x++, i++) { p[i].v = im->image32[y][x]; + } + } } else { free(p); @@ -1647,9 +1740,11 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) imOut = ImagingNewDirty("P", im->xsize, im->ysize); ImagingSectionEnter(&cookie); - for (i = y = 0; y < im->ysize; y++) - for (x = 0; x < im->xsize; x++) + for (i = y = 0; y < im->ysize; y++) { + for (x = 0; x < im->xsize; x++) { imOut->image8[y][x] = (unsigned char) newData[i++]; + } + } free(newData); diff --git a/src/libImaging/QuantHash.c b/src/libImaging/QuantHash.c index 3fcbf3c0263..6ff95d8857c 100644 --- a/src/libImaging/QuantHash.c +++ b/src/libImaging/QuantHash.c @@ -67,7 +67,9 @@ static uint32_t _findPrime(uint32_t start,int dir) { continue; } for (t=2;t=sqrt((double)start)) { break; @@ -144,7 +146,9 @@ static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int upd node->next=*n; *n=node; h->count++; - if (resize) _hashtable_resize(h); + if (resize) { + _hashtable_resize(h); + } return 1; } else { return 0; @@ -169,13 +173,17 @@ static int _hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val,int resize } if (!update) { t=malloc(sizeof(HashNode)); - if (!t) return 0; + if (!t) { + return 0; + } t->next=*n; *n=t; t->key=key; t->value=val; h->count++; - if (resize) _hashtable_resize(h); + if (resize) { + _hashtable_resize(h); + } return 1; } else { return 0; @@ -206,7 +214,9 @@ int hashtable_insert_or_update_computed(HashTable *h, } } t=malloc(sizeof(HashNode)); - if (!t) return 0; + if (!t) { + return 0; + } t->key=key; t->next=*n; *n=t; diff --git a/src/libImaging/QuantHeap.c b/src/libImaging/QuantHeap.c index 498d44b1dfb..6877e34a3e8 100644 --- a/src/libImaging/QuantHeap.c +++ b/src/libImaging/QuantHeap.c @@ -46,15 +46,21 @@ void ImagingQuantHeapFree(Heap *h) { static int _heap_grow(Heap *h,unsigned int newsize) { void *newheap; - if (!newsize) newsize=h->heapsize<<1; - if (newsizeheapsize) return 0; + if (!newsize) { + newsize=h->heapsize<<1; + } + if (newsizeheapsize) { + return 0; + } if (newsize > INT_MAX / sizeof(void *)){ return 0; } /* malloc check ok, using calloc for overflow, also checking above due to memcpy below*/ newheap=calloc(newsize, sizeof(void *)); - if (!newheap) return 0; + if (!newheap) { + return 0; + } memcpy(newheap,h->heap,sizeof(void *)*h->heapsize); free(h->heap); h->heap=newheap; @@ -140,7 +146,9 @@ Heap *ImagingQuantHeapNew(HeapCmpFunc cf) { /* malloc check ok, small constant allocation */ h=malloc(sizeof(Heap)); - if (!h) return NULL; + if (!h) { + return NULL; + } h->heapsize=INITIAL_SIZE; /* malloc check ok, using calloc for overflow */ h->heap=calloc(h->heapsize, sizeof(void *)); diff --git a/src/libImaging/QuantOctree.c b/src/libImaging/QuantOctree.c index 83d9875446b..fa45ae707b0 100644 --- a/src/libImaging/QuantOctree.c +++ b/src/libImaging/QuantOctree.c @@ -56,7 +56,9 @@ new_color_cube(int r, int g, int b, int a) { /* malloc check ok, small constant allocation */ cube = malloc(sizeof(struct _ColorCube)); - if (!cube) return NULL; + if (!cube) { + return NULL; + } cube->rBits = MAX(r, 0); cube->gBits = MAX(g, 0); @@ -175,7 +177,9 @@ create_sorted_color_palette(const ColorCube cube) { } /* malloc check ok, calloc + overflow check above for memcpy */ buckets = calloc(cube->size, sizeof(struct _ColorBucket)); - if (!buckets) return NULL; + if (!buckets) { + return NULL; + } memcpy(buckets, cube->buckets, sizeof(struct _ColorBucket)*cube->size); qsort(buckets, cube->size, sizeof(struct _ColorBucket), @@ -203,7 +207,9 @@ static ColorCube copy_color_cube(const ColorCube cube, ColorCube result; result = new_color_cube(rBits, gBits, bBits, aBits); - if (!result) return NULL; + if (!result) { + return NULL; + } if (cube->rBits > rBits) { dst_reduce[0] = cube->rBits - result->rBits; @@ -268,7 +274,9 @@ subtract_color_buckets(ColorCube cube, ColorBucket buckets, long nBuckets) { subtrahend = &buckets[i]; // If the subtrahend contains no buckets, there is nothing to subtract. - if (subtrahend->count == 0) continue; + if (subtrahend->count == 0) { + continue; + } avg_color_from_color_bucket(subtrahend, &p); minuend = color_bucket_from_cube(cube, &p); @@ -325,7 +333,9 @@ create_palette_array(const ColorBucket palette, unsigned int paletteLength) { /* malloc check ok, calloc for overflow */ paletteArray = calloc(paletteLength, sizeof(Pixel)); - if (!paletteArray) return NULL; + if (!paletteArray) { + return NULL; + } for (i=0; i nQuantPixels) + if (nCoarseColors > nQuantPixels) { nCoarseColors = nQuantPixels; + } /* how many space do we have in our palette for fine colors? */ nFineColors = nQuantPixels - nCoarseColors; /* create fine color palette */ paletteBucketsFine = create_sorted_color_palette(fineCube); - if (!paletteBucketsFine) goto error; + if (!paletteBucketsFine) { + goto error; + } /* remove the used fine colors from the coarse cube */ subtract_color_buckets(coarseCube, paletteBucketsFine, nFineColors); @@ -430,7 +447,9 @@ int quantize_octree(Pixel *pixelData, /* create our palette buckets with fine and coarse combined */ paletteBucketsCoarse = create_sorted_color_palette(coarseCube); - if (!paletteBucketsCoarse) goto error; + if (!paletteBucketsCoarse) { + goto error; + } paletteBuckets = combined_palette(paletteBucketsCoarse, nCoarseColors, paletteBucketsFine, nFineColors); @@ -438,19 +457,25 @@ int quantize_octree(Pixel *pixelData, paletteBucketsFine = NULL; free(paletteBucketsCoarse); paletteBucketsCoarse = NULL; - if (!paletteBuckets) goto error; + if (!paletteBuckets) { + goto error; + } /* add all coarse colors to our coarse lookup cube. */ coarseLookupCube = new_color_cube(cubeBits[4], cubeBits[5], cubeBits[6], cubeBits[7]); - if (!coarseLookupCube) goto error; + if (!coarseLookupCube) { + goto error; + } add_lookup_buckets(coarseLookupCube, paletteBuckets, nCoarseColors, 0); /* expand coarse cube (64) to larger fine cube (4k). the value of each coarse bucket is then present in the according 64 fine buckets. */ lookupCube = copy_color_cube(coarseLookupCube, cubeBits[0], cubeBits[1], cubeBits[2], cubeBits[3]); - if (!lookupCube) goto error; + if (!lookupCube) { + goto error; + } /* add fine colors to the lookup cube */ add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors); @@ -458,12 +483,16 @@ int quantize_octree(Pixel *pixelData, /* create result pixels and map palette indices */ /* malloc check ok, calloc for overflow */ qp = calloc(nPixels, sizeof(Pixel)); - if (!qp) goto error; + if (!qp) { + goto error; + } map_image_pixels(pixelData, nPixels, lookupCube, qp); /* convert palette buckets to RGB pixel palette */ *palette = create_palette_array(paletteBuckets, nQuantPixels); - if (!(*palette)) goto error; + if (!(*palette)) { + goto error; + } *quantizedPixels = qp; *paletteLength = nQuantPixels; diff --git a/src/libImaging/QuantPngQuant.c b/src/libImaging/QuantPngQuant.c index a9a547540bf..ef40b282b57 100644 --- a/src/libImaging/QuantPngQuant.c +++ b/src/libImaging/QuantPngQuant.c @@ -95,9 +95,15 @@ quantize_pngquant( result = 1; err: - if (attr) liq_attr_destroy(attr); - if (image) liq_image_destroy(image); - if (remap) liq_result_destroy(remap); + if (attr) { + liq_attr_destroy(attr); + } + if (image) { + liq_image_destroy(image); + } + if (remap) { + liq_result_destroy(remap); + } free(charMatrix); free(charMatrixRows); if (!result) { diff --git a/src/libImaging/RankFilter.c b/src/libImaging/RankFilter.c index 0164861bb07..e4f2679b2cd 100644 --- a/src/libImaging/RankFilter.c +++ b/src/libImaging/RankFilter.c @@ -30,15 +30,23 @@ static type Rank##type(type a[], int n, int k)\ i = l;\ j = m;\ do {\ - while (a[i] < x) i++;\ - while (x < a[j]) j--;\ + while (a[i] < x) {\ + i++;\ + }\ + while (x < a[j]) {\ + j--;\ + }\ if (i <= j) {\ SWAP(type, a[i], a[j]);\ i++; j--;\ }\ } while (i <= j);\ - if (j < k) l = i;\ - if (k < i) m = j;\ + if (j < k) {\ + l = i;\ + }\ + if (k < i) {\ + m = j;\ + }\ }\ return a[k];\ } @@ -54,11 +62,13 @@ ImagingRankFilter(Imaging im, int size, int rank) int x, y; int i, margin, size2; - if (!im || im->bands != 1 || im->type == IMAGING_TYPE_SPECIAL) + if (!im || im->bands != 1 || im->type == IMAGING_TYPE_SPECIAL) { return (Imaging) ImagingError_ModeError(); + } - if (!(size & 1)) + if (!(size & 1)) { return (Imaging) ImagingError_ValueError("bad filter size"); + } /* malloc check ok, for overflow in the define below */ if (size > INT_MAX / size || @@ -69,35 +79,40 @@ ImagingRankFilter(Imaging im, int size, int rank) size2 = size * size; margin = (size-1) / 2; - if (rank < 0 || rank >= size2) + if (rank < 0 || rank >= size2) { return (Imaging) ImagingError_ValueError("bad rank value"); + } imOut = ImagingNew(im->mode, im->xsize - 2*margin, im->ysize - 2*margin); - if (!imOut) + if (!imOut) { return NULL; + } /* malloc check ok, checked above */ #define RANK_BODY(type) do {\ type* buf = malloc(size2 * sizeof(type));\ - if (!buf)\ + if (!buf) {\ goto nomemory;\ - for (y = 0; y < imOut->ysize; y++)\ + }\ + for (y = 0; y < imOut->ysize; y++) {\ for (x = 0; x < imOut->xsize; x++) {\ - for (i = 0; i < size; i++)\ + for (i = 0; i < size; i++) {\ memcpy(buf + i*size, &IMAGING_PIXEL_##type(im, x, y+i),\ size * sizeof(type));\ + }\ IMAGING_PIXEL_##type(imOut, x, y) = Rank##type(buf, size2, rank);\ }\ + }\ free(buf); \ } while (0) - if (im->image8) + if (im->image8) { RANK_BODY(UINT8); - else if (im->type == IMAGING_TYPE_INT32) + } else if (im->type == IMAGING_TYPE_INT32) { RANK_BODY(INT32); - else if (im->type == IMAGING_TYPE_FLOAT32) + } else if (im->type == IMAGING_TYPE_FLOAT32) { RANK_BODY(FLOAT32); - else { + } else { /* safety net (we shouldn't end up here) */ ImagingDelete(imOut); return (Imaging) ImagingError_ModeError(); diff --git a/src/libImaging/RawDecode.c b/src/libImaging/RawDecode.c index 5e95905cc0b..ca3d37149a4 100644 --- a/src/libImaging/RawDecode.c +++ b/src/libImaging/RawDecode.c @@ -47,8 +47,9 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (state->ystep < 0) { state->y = state->ysize-1; state->ystep = -1; - } else + } else { state->ystep = 1; + } state->state = LINE; @@ -62,8 +63,9 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt /* Skip padding between lines */ - if (bytes < rawstate->skip) + if (bytes < rawstate->skip) { return ptr - buf; + } ptr += rawstate->skip; bytes -= rawstate->skip; @@ -72,8 +74,9 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt } - if (bytes < state->bytes) + if (bytes < state->bytes) { return ptr - buf; + } /* Unpack data */ state->shuffle((UINT8*) im->image[state->y + state->yoff] + diff --git a/src/libImaging/RawEncode.c b/src/libImaging/RawEncode.c index da614a24b62..3c593480b2d 100644 --- a/src/libImaging/RawEncode.c +++ b/src/libImaging/RawEncode.c @@ -49,8 +49,9 @@ ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (state->ystep < 0) { state->y = state->ysize-1; state->ystep = -1; - } else + } else { state->ystep = 1; + } state->state = 1; @@ -68,9 +69,10 @@ ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) state->shuffle(ptr, (UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->xsize); - if (state->bytes > state->count) + if (state->bytes > state->count) { /* zero-pad the buffer, if necessary */ memset(ptr + state->count, 0, state->bytes - state->count); + } ptr += state->bytes; bytes -= state->bytes; diff --git a/src/libImaging/Reduce.c b/src/libImaging/Reduce.c index d6ef92f5b41..f6488deb269 100644 --- a/src/libImaging/Reduce.c +++ b/src/libImaging/Reduce.c @@ -1374,11 +1374,13 @@ ImagingReduce(Imaging imIn, int xscale, int yscale, int box[4]) ImagingSectionCookie cookie; Imaging imOut = NULL; - if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) + if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) { return (Imaging) ImagingError_ModeError(); + } - if (imIn->type == IMAGING_TYPE_SPECIAL) + if (imIn->type == IMAGING_TYPE_SPECIAL) { return (Imaging) ImagingError_ModeError(); + } imOut = ImagingNewDirty(imIn->mode, (box[2] + xscale - 1) / xscale, diff --git a/src/libImaging/Resample.c b/src/libImaging/Resample.c index 0dc08611da0..ec35303d8c5 100644 --- a/src/libImaging/Resample.c +++ b/src/libImaging/Resample.c @@ -13,28 +13,34 @@ struct filter { static inline double box_filter(double x) { - if (x > -0.5 && x <= 0.5) + if (x > -0.5 && x <= 0.5) { return 1.0; + } return 0.0; } static inline double bilinear_filter(double x) { - if (x < 0.0) + if (x < 0.0) { x = -x; - if (x < 1.0) + } + if (x < 1.0) { return 1.0-x; + } return 0.0; } static inline double hamming_filter(double x) { - if (x < 0.0) + if (x < 0.0) { x = -x; - if (x == 0.0) + } + if (x == 0.0) { return 1.0; - if (x >= 1.0) + } + if (x >= 1.0) { return 0.0; + } x = x * M_PI; return sin(x) / x * (0.54f + 0.46f * cos(x)); } @@ -43,20 +49,24 @@ static inline double bicubic_filter(double x) { /* https://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm */ #define a -0.5 - if (x < 0.0) + if (x < 0.0) { x = -x; - if (x < 1.0) + } + if (x < 1.0) { return ((a + 2.0) * x - (a + 3.0)) * x*x + 1; - if (x < 2.0) + } + if (x < 2.0) { return (((x - 5) * x + 8) * x - 4) * a; + } return 0.0; #undef a } static inline double sinc_filter(double x) { - if (x == 0.0) + if (x == 0.0) { return 1.0; + } x = x * M_PI; return sin(x) / x; } @@ -64,8 +74,9 @@ static inline double sinc_filter(double x) static inline double lanczos_filter(double x) { /* truncated sinc */ - if (-3.0 <= x && x < 3.0) + if (-3.0 <= x && x < 3.0) { return sinc_filter(x) * sinc_filter(x/3); + } return 0.0; } @@ -224,12 +235,14 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, ss = 1.0 / filterscale; // Round the value xmin = (int) (center - support + 0.5); - if (xmin < 0) + if (xmin < 0) { xmin = 0; + } // Round the value xmax = (int) (center + support + 0.5); - if (xmax > inSize) + if (xmax > inSize) { xmax = inSize; + } xmax -= xmin; k = &kk[xx * ksize]; for (x = 0; x < xmax; x++) { @@ -238,8 +251,9 @@ precompute_coeffs(int inSize, float in0, float in1, int outSize, ww += w; } for (x = 0; x < xmax; x++) { - if (ww != 0.0) + if (ww != 0.0) { k[x] /= ww; + } } // Remaining values should stay empty if they are used despite of xmax. for (; x < ksize; x++) { @@ -295,8 +309,9 @@ ImagingResampleHorizontal_8bpc(Imaging imOut, Imaging imIn, int offset, xmax = bounds[xx * 2 + 1]; k = &kk[xx * ksize]; ss0 = 1 << (PRECISION_BITS -1); - for (x = 0; x < xmax; x++) + for (x = 0; x < xmax; x++) { ss0 += ((UINT8) imIn->image8[yy + offset][x + xmin]) * k[x]; + } imOut->image8[yy][xx] = clip8(ss0); } } @@ -379,8 +394,9 @@ ImagingResampleVertical_8bpc(Imaging imOut, Imaging imIn, int offset, ymax = bounds[yy * 2 + 1]; for (xx = 0; xx < imOut->xsize; xx++) { ss0 = 1 << (PRECISION_BITS -1); - for (y = 0; y < ymax; y++) + for (y = 0; y < ymax; y++) { ss0 += ((UINT8) imIn->image8[y + ymin][xx]) * k[y]; + } imOut->image8[yy][xx] = clip8(ss0); } } @@ -460,8 +476,9 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, int offset, xmax = bounds[xx * 2 + 1]; k = &kk[xx * ksize]; ss = 0.0; - for (x = 0; x < xmax; x++) + for (x = 0; x < xmax; x++) { ss += IMAGING_PIXEL_I(imIn, x + xmin, yy + offset) * k[x]; + } IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss); } } @@ -474,8 +491,9 @@ ImagingResampleHorizontal_32bpc(Imaging imOut, Imaging imIn, int offset, xmax = bounds[xx * 2 + 1]; k = &kk[xx * ksize]; ss = 0.0; - for (x = 0; x < xmax; x++) + for (x = 0; x < xmax; x++) { ss += IMAGING_PIXEL_F(imIn, x + xmin, yy + offset) * k[x]; + } IMAGING_PIXEL_F(imOut, xx, yy) = ss; } } @@ -503,8 +521,9 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, int offset, k = &kk[yy * ksize]; for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; - for (y = 0; y < ymax; y++) + for (y = 0; y < ymax; y++) { ss += IMAGING_PIXEL_I(imIn, xx, y + ymin) * k[y]; + } IMAGING_PIXEL_I(imOut, xx, yy) = ROUND_UP(ss); } } @@ -517,8 +536,9 @@ ImagingResampleVertical_32bpc(Imaging imOut, Imaging imIn, int offset, k = &kk[yy * ksize]; for (xx = 0; xx < imOut->xsize; xx++) { ss = 0.0; - for (y = 0; y < ymax; y++) + for (y = 0; y < ymax; y++) { ss += IMAGING_PIXEL_F(imIn, xx, y + ymin) * k[y]; + } IMAGING_PIXEL_F(imOut, xx, yy) = ss; } } @@ -546,8 +566,9 @@ ImagingResample(Imaging imIn, int xsize, int ysize, int filter, float box[4]) ResampleFunction ResampleHorizontal; ResampleFunction ResampleVertical; - if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) + if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "1") == 0) { return (Imaging) ImagingError_ModeError(); + } if (imIn->type == IMAGING_TYPE_SPECIAL) { return (Imaging) ImagingError_ModeError(); diff --git a/src/libImaging/SgiRleDecode.c b/src/libImaging/SgiRleDecode.c index 2d903cc047b..a03ecd456e8 100644 --- a/src/libImaging/SgiRleDecode.c +++ b/src/libImaging/SgiRleDecode.c @@ -33,11 +33,13 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize) for (;n > 0; n--) { pixel = *src++; - if (n == 1 && pixel != 0) + if (n == 1 && pixel != 0) { return n; + } count = pixel & RLE_MAX_RUN; - if (!count) + if (!count) { return count; + } if (x + count > xsize) { return -1; } @@ -71,11 +73,13 @@ static int expandrow2(UINT8* dest, const UINT8* src, int n, int z, int xsize) { pixel = src[1]; src+=2; - if (n == 1 && pixel != 0) + if (n == 1 && pixel != 0) { return n; + } count = pixel & RLE_MAX_RUN; - if (!count) + if (!count) { return count; + } if (x + count > xsize) { return -1; } @@ -151,11 +155,13 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state, goto sgi_finish_decode; } /* populate offsets table */ - for (c->tabindex = 0, c->bufindex = 0; c->tabindex < c->tablen; c->tabindex++, c->bufindex+=4) + for (c->tabindex = 0, c->bufindex = 0; c->tabindex < c->tablen; c->tabindex++, c->bufindex+=4) { read4B(&c->starttab[c->tabindex], &ptr[c->bufindex]); + } /* populate lengths table */ - for (c->tabindex = 0, c->bufindex = c->tablen * sizeof(UINT32); c->tabindex < c->tablen; c->tabindex++, c->bufindex+=4) + for (c->tabindex = 0, c->bufindex = c->tablen * sizeof(UINT32); c->tabindex < c->tablen; c->tabindex++, c->bufindex+=4) { read4B(&c->lengthtab[c->tabindex], &ptr[c->bufindex]); + } state->count += c->tablen * sizeof(UINT32) * 2; diff --git a/src/libImaging/Storage.c b/src/libImaging/Storage.c index ab476939ac0..c9a24e6aa7a 100644 --- a/src/libImaging/Storage.c +++ b/src/libImaging/Storage.c @@ -244,17 +244,21 @@ ImagingNewPrologue(const char *mode, int xsize, int ysize) void ImagingDelete(Imaging im) { - if (!im) + if (!im) { return; + } - if (im->palette) + if (im->palette) { ImagingPaletteDelete(im->palette); + } - if (im->destroy) + if (im->destroy) { im->destroy(im); + } - if (im->image) + if (im->image) { free(im->image); + } free(im); } @@ -399,8 +403,9 @@ ImagingAllocateArray(Imaging im, int dirty, int block_size) aligned_linesize = (im->linesize + arena->alignment - 1) & -arena->alignment; lines_per_block = (block_size - (arena->alignment - 1)) / aligned_linesize; - if (lines_per_block == 0) + if (lines_per_block == 0) { lines_per_block = 1; + } blocks_count = (im->ysize + lines_per_block - 1) / lines_per_block; // printf("NEW size: %dx%d, ls: %d, lpb: %d, blocks: %d\n", // im->xsize, im->ysize, aligned_linesize, lines_per_block, blocks_count); @@ -457,8 +462,9 @@ ImagingAllocateArray(Imaging im, int dirty, int block_size) static void ImagingDestroyBlock(Imaging im) { - if (im->block) + if (im->block) { free(im->block); + } } Imaging @@ -510,8 +516,9 @@ ImagingNewInternal(const char* mode, int xsize, int ysize, int dirty) } im = ImagingNewPrologue(mode, xsize, ysize); - if ( ! im) + if ( ! im) { return NULL; + } if (ImagingAllocateArray(im, dirty, ImagingDefaultArena.block_size)) { return im; @@ -550,8 +557,9 @@ ImagingNewBlock(const char* mode, int xsize, int ysize) } im = ImagingNewPrologue(mode, xsize, ysize); - if ( ! im) + if ( ! im) { return NULL; + } if (ImagingAllocateBlock(im)) { return im; @@ -576,8 +584,9 @@ ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn) } else { /* create new image */ imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize); - if (!imOut) + if (!imOut) { return NULL; + } } return imOut; @@ -587,8 +596,9 @@ void ImagingCopyPalette(Imaging destination, Imaging source) { if (source->palette) { - if (destination->palette) + if (destination->palette) { ImagingPaletteDelete(destination->palette); + } destination->palette = ImagingPaletteDuplicate(source->palette); } } diff --git a/src/libImaging/SunRleDecode.c b/src/libImaging/SunRleDecode.c index e627c2c9a28..acb39133a19 100644 --- a/src/libImaging/SunRleDecode.c +++ b/src/libImaging/SunRleDecode.c @@ -31,13 +31,15 @@ ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t for (;;) { - if (bytes < 1) + if (bytes < 1) { return ptr - buf; + } if (ptr[0] == 0x80) { - if (bytes < 2) + if (bytes < 2) { break; + } n = ptr[1]; @@ -55,8 +57,9 @@ ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t } else { /* Run (3 bytes) */ - if (bytes < 3) + if (bytes < 3) { break; + } /* from (https://www.fileformat.info/format/sunraster/egff.htm) diff --git a/src/libImaging/TgaRleDecode.c b/src/libImaging/TgaRleDecode.c index bdbf27a14bc..b1364e004ad 100644 --- a/src/libImaging/TgaRleDecode.c +++ b/src/libImaging/TgaRleDecode.c @@ -33,8 +33,9 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, if (state->ystep < 0) { state->y = state->ysize-1; state->ystep = -1; - } else + } else { state->ystep = 1; + } state->state = 1; @@ -44,15 +45,17 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, for (;;) { - if (bytes < 1) + if (bytes < 1) { return ptr - buf; + } if (ptr[0] & 0x80) { /* Run (1 + pixelsize bytes) */ - if (bytes < 1 + depth) + if (bytes < 1 + depth) { break; + } n = depth * ((ptr[0] & 0x7f) + 1); @@ -61,12 +64,13 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, return -1; } - if (depth == 1) + if (depth == 1) { memset(state->buffer + state->x, ptr[1], n); - else { + } else { int i; - for (i = 0; i < n; i += depth) + for (i = 0; i < n; i += depth) { memcpy(state->buffer + state->x + i, ptr+1, depth); + } } ptr += 1 + depth; @@ -77,8 +81,9 @@ ImagingTgaRleDecode(Imaging im, ImagingCodecState state, /* Literal (1+n+1 bytes block) */ n = depth * (ptr[0] + 1); - if (bytes < 1 + n) + if (bytes < 1 + n) { break; + } if (state->x + n > state->bytes) { state->errcode = IMAGING_CODEC_OVERRUN; diff --git a/src/libImaging/TgaRleEncode.c b/src/libImaging/TgaRleEncode.c index 2fb831e6b14..c65dcf5ec50 100644 --- a/src/libImaging/TgaRleEncode.c +++ b/src/libImaging/TgaRleEncode.c @@ -22,8 +22,9 @@ ImagingTgaRleEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (state->ystep < 0) { state->ystep = -1; state->y = state->ysize - 1; - } else + } else { state->ystep = 1; + } state->state = 1; } @@ -46,8 +47,9 @@ ImagingTgaRleEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) assert(state->x <= state->xsize); /* Make sure we have space for the descriptor. */ - if (bytes < 1) + if (bytes < 1) { break; + } if (state->x == state->xsize) { state->x = 0; @@ -59,12 +61,13 @@ ImagingTgaRleEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } } - if (state->x == 0) + if (state->x == 0) { state->shuffle( state->buffer, (UINT8*)im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->xsize); + } row = state->buffer; @@ -87,28 +90,32 @@ ImagingTgaRleEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) */ maxLookup = state->x + 126; /* A packet must not span multiple rows. */ - if (maxLookup > state->xsize - 1) + if (maxLookup > state->xsize - 1) { maxLookup = state->xsize - 1; + } if (isRaw) { - while (state->x < maxLookup) - if (!comparePixels(row, state->x, bytesPerPixel)) + while (state->x < maxLookup) { + if (!comparePixels(row, state->x, bytesPerPixel)) { ++state->x; - else { + } else { /* Two identical pixels will go to RLE packet. */ --state->x; break; } + } state->count += (state->x - startX) * bytesPerPixel; } else { descriptor |= 0x80; - while (state->x < maxLookup) - if (comparePixels(row, state->x, bytesPerPixel)) + while (state->x < maxLookup) { + if (comparePixels(row, state->x, bytesPerPixel)) { ++state->x; - else + } else { break; + } + } } } @@ -132,12 +139,14 @@ ImagingTgaRleEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) assert(state->x > 0); assert(state->count <= state->x * bytesPerPixel); - if (bytes == 0) + if (bytes == 0) { break; + } flushCount = state->count; - if (flushCount > bytes) + if (flushCount > bytes) { flushCount = bytes; + } memcpy( dst, diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 6235937d17b..917da6ab32e 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -350,8 +350,9 @@ unpackLI(UINT8* out, const UINT8* in, int pixels) { /* negative */ int i; - for (i = 0; i < pixels; i++) + for (i = 0; i < pixels; i++) { out[i] = ~in[i]; + } } static void @@ -1115,8 +1116,9 @@ static void NAME(UINT8* out_, const UINT8* in, int pixels)\ {\ int i;\ OUTTYPE* out = (OUTTYPE*) out_;\ - for (i = 0; i < pixels; i++, in += sizeof(INTYPE))\ + for (i = 0; i < pixels; i++, in += sizeof(INTYPE)) {\ out[i] = (OUTTYPE) ((INTYPE) GET);\ + }\ } #define UNPACK(NAME, COPY, INTYPE, OUTTYPE)\ @@ -1521,13 +1523,15 @@ ImagingFindUnpacker(const char* mode, const char* rawmode, int* bits_out) int i; /* find a suitable pixel unpacker */ - for (i = 0; unpackers[i].rawmode; i++) + for (i = 0; unpackers[i].rawmode; i++) { if (strcmp(unpackers[i].mode, mode) == 0 && strcmp(unpackers[i].rawmode, rawmode) == 0) { - if (bits_out) + if (bits_out) { *bits_out = unpackers[i].bits; + } return unpackers[i].unpack; } + } /* FIXME: configure a general unpacker based on the type codes... */ diff --git a/src/libImaging/UnsharpMask.c b/src/libImaging/UnsharpMask.c index a034bebf2fe..59e595e8213 100644 --- a/src/libImaging/UnsharpMask.c +++ b/src/libImaging/UnsharpMask.c @@ -14,10 +14,12 @@ typedef UINT8 pixel[4]; static inline UINT8 clip8(int in) { - if (in >= 255) + if (in >= 255) { return 255; - if (in <= 0) + } + if (in <= 0) { return 0; + } return (UINT8) in; } @@ -39,8 +41,9 @@ ImagingUnsharpMask(Imaging imOut, Imaging imIn, float radius, int percent, /* First, do a gaussian blur on the image, putting results in imOut temporarily. All format checks are in gaussian blur. */ result = ImagingGaussianBlur(imOut, imIn, radius, 3); - if (!result) + if (!result) { return NULL; + } /* Now, go through each pixel, compare "normal" pixel to blurred pixel. If the difference is more than threshold values, apply diff --git a/src/libImaging/XbmDecode.c b/src/libImaging/XbmDecode.c index 088c71df3d1..607f1058a00 100644 --- a/src/libImaging/XbmDecode.c +++ b/src/libImaging/XbmDecode.c @@ -27,8 +27,9 @@ ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt UINT8* ptr; - if (!state->state) + if (!state->state) { state->state = SKIP; + } ptr = buf; @@ -39,21 +40,24 @@ ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt /* Skip forward until next 'x' */ while (bytes > 0) { - if (*ptr == 'x') + if (*ptr == 'x') { break; + } ptr++; bytes--; } - if (bytes == 0) + if (bytes == 0) { return ptr - buf; + } state->state = BYTE; } - if (bytes < 3) + if (bytes < 3) { return ptr - buf; + } state->buffer[state->x] = (HEX(ptr[1])<<4) + HEX(ptr[2]); diff --git a/src/libImaging/ZipDecode.c b/src/libImaging/ZipDecode.c index 43601c38eeb..b0f8ad3261e 100644 --- a/src/libImaging/ZipDecode.c +++ b/src/libImaging/ZipDecode.c @@ -53,8 +53,9 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (!state->state) { /* Initialization */ - if (context->mode == ZIP_PNG || context->mode == ZIP_PNG_PALETTE) + if (context->mode == ZIP_PNG || context->mode == ZIP_PNG_PALETTE) { context->prefix = 1; /* PNG */ + } /* overflow check for malloc */ if (state->bytes > INT_MAX - 1) { @@ -121,12 +122,13 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt if (err < 0) { /* Something went wrong inside the compression library */ - if (err == Z_DATA_ERROR) + if (err == Z_DATA_ERROR) { state->errcode = IMAGING_CODEC_BROKEN; - else if (err == Z_MEM_ERROR) + } else if (err == Z_MEM_ERROR) { state->errcode = IMAGING_CODEC_MEMORY; - else + } else { state->errcode = IMAGING_CODEC_CONFIG; + } free(context->previous); context->previous = NULL; inflateEnd(&context->z_stream); @@ -149,28 +151,33 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt case 1: /* prior */ bpp = (state->bits + 7) / 8; - for (i = bpp+1; i <= row_len; i++) + for (i = bpp+1; i <= row_len; i++) { state->buffer[i] += state->buffer[i-bpp]; + } break; case 2: /* up */ - for (i = 1; i <= row_len; i++) + for (i = 1; i <= row_len; i++) { state->buffer[i] += context->previous[i]; + } break; case 3: /* average */ bpp = (state->bits + 7) / 8; - for (i = 1; i <= bpp; i++) + for (i = 1; i <= bpp; i++) { state->buffer[i] += context->previous[i]/2; - for (; i <= row_len; i++) + } + for (; i <= row_len; i++) { state->buffer[i] += (state->buffer[i-bpp] + context->previous[i])/2; + } break; case 4: /* paeth filtering */ bpp = (state->bits + 7) / 8; - for (i = 1; i <= bpp; i++) + for (i = 1; i <= bpp; i++) { state->buffer[i] += context->previous[i]; + } for (; i <= row_len; i++) { int a, b, c; int pa, pb, pc; @@ -201,8 +208,9 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt break; case ZIP_TIFF_PREDICTOR: bpp = (state->bits + 7) / 8; - for (i = bpp+1; i <= row_len; i++) + for (i = bpp+1; i <= row_len; i++) { state->buffer[i] += state->buffer[i-bpp]; + } break; } diff --git a/src/libImaging/ZipEncode.c b/src/libImaging/ZipEncode.c index fa1c4e72876..6b44ed81a24 100644 --- a/src/libImaging/ZipEncode.c +++ b/src/libImaging/ZipEncode.c @@ -128,12 +128,13 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (err < 0) { /* Something went wrong inside the compression library */ - if (err == Z_DATA_ERROR) + if (err == Z_DATA_ERROR) { state->errcode = IMAGING_CODEC_BROKEN; - else if (err == Z_MEM_ERROR) + } else if (err == Z_MEM_ERROR) { state->errcode = IMAGING_CODEC_MEMORY; - else + } else { state->errcode = IMAGING_CODEC_CONFIG; + } free(context->paeth); free(context->average); free(context->up); @@ -282,12 +283,13 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (err < 0) { /* Something went wrong inside the compression library */ - if (err == Z_DATA_ERROR) + if (err == Z_DATA_ERROR) { state->errcode = IMAGING_CODEC_BROKEN; - else if (err == Z_MEM_ERROR) + } else if (err == Z_MEM_ERROR) { state->errcode = IMAGING_CODEC_MEMORY; - else + } else { state->errcode = IMAGING_CODEC_CONFIG; + } free(context->paeth); free(context->average); free(context->up); @@ -331,8 +333,9 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) break; } - if (context->z_stream.avail_out == 0) + if (context->z_stream.avail_out == 0) { break; /* Buffer full */ + } } diff --git a/src/map.c b/src/map.c index 47cc9d6de7a..ef29062cc76 100644 --- a/src/map.c +++ b/src/map.c @@ -47,12 +47,14 @@ PyImaging_MapperNew(const char* filename, int readonly) { ImagingMapperObject *mapper; - if (PyType_Ready(&ImagingMapperType) < 0) + if (PyType_Ready(&ImagingMapperType) < 0) { return NULL; + } mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType); - if (mapper == NULL) + if (mapper == NULL) { return NULL; + } mapper->base = NULL; mapper->size = mapper->offset = 0; @@ -101,12 +103,15 @@ static void mapping_dealloc(ImagingMapperObject* mapper) { #ifdef _WIN32 - if (mapper->base != 0) + if (mapper->base != 0) { UnmapViewOfFile(mapper->base); - if (mapper->hMap != (HANDLE)-1) + } + if (mapper->hMap != (HANDLE)-1) { CloseHandle(mapper->hMap); - if (mapper->hFile != (HANDLE)-1) + } + if (mapper->hFile != (HANDLE)-1) { CloseHandle(mapper->hFile); + } mapper->base = 0; mapper->hMap = mapper->hFile = (HANDLE)-1; #endif @@ -122,18 +127,22 @@ mapping_read(ImagingMapperObject* mapper, PyObject* args) PyObject* buf; int size = -1; - if (!PyArg_ParseTuple(args, "|i", &size)) + if (!PyArg_ParseTuple(args, "|i", &size)) { return NULL; + } /* check size */ - if (size < 0 || mapper->offset + size > mapper->size) + if (size < 0 || mapper->offset + size > mapper->size) { size = mapper->size - mapper->offset; - if (size < 0) + } + if (size < 0) { size = 0; + } buf = PyBytes_FromStringAndSize(NULL, size); - if (!buf) + if (!buf) { return NULL; + } if (size > 0) { memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size); @@ -148,8 +157,9 @@ mapping_seek(ImagingMapperObject* mapper, PyObject* args) { int offset; int whence = 0; - if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) + if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) { return NULL; + } switch (whence) { case 0: /* SEEK_SET */ @@ -193,17 +203,19 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) int stride; int orientation; if (!PyArg_ParseTuple(args, "s(ii)ii", &mode, &xsize, &ysize, - &stride, &orientation)) + &stride, &orientation)) { return NULL; + } if (stride <= 0) { /* FIXME: maybe we should call ImagingNewPrologue instead */ - if (!strcmp(mode, "L") || !strcmp(mode, "P")) + if (!strcmp(mode, "L") || !strcmp(mode, "P")) { stride = xsize; - else if (!strcmp(mode, "I;16") || !strcmp(mode, "I;16B")) + } else if (!strcmp(mode, "I;16") || !strcmp(mode, "I;16B")) { stride = xsize * 2; - else + } else { stride = xsize * 4; + } } size = ysize * stride; @@ -214,16 +226,20 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) } im = ImagingNewPrologue(mode, xsize, ysize); - if (!im) + if (!im) { return NULL; + } /* setup file pointers */ - if (orientation > 0) - for (y = 0; y < ysize; y++) + if (orientation > 0) { + for (y = 0; y < ysize; y++) { im->image[y] = mapper->base + mapper->offset + y * stride; - else - for (y = 0; y < ysize; y++) + } + } else { + for (y = 0; y < ysize; y++) { im->image[ysize-y-1] = mapper->base + mapper->offset + y * stride; + } + } im->destroy = ImagingDestroyMap; @@ -279,8 +295,9 @@ PyObject* PyImaging_Mapper(PyObject* self, PyObject* args) { char* filename; - if (!PyArg_ParseTuple(args, "s", &filename)) + if (!PyArg_ParseTuple(args, "s", &filename)) { return NULL; + } return (PyObject*) PyImaging_MapperNew(filename, 1); } @@ -319,8 +336,9 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) int ystep; if (!PyArg_ParseTuple(args, "O(ii)sn(sii)", &target, &xsize, &ysize, - &codec, &offset, &mode, &stride, &ystep)) + &codec, &offset, &mode, &stride, &ystep)) { return NULL; + } if (!PyImaging_CheckBuffer(target)) { PyErr_SetString(PyExc_TypeError, "expected string or buffer"); @@ -328,12 +346,13 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) } if (stride <= 0) { - if (!strcmp(mode, "L") || !strcmp(mode, "P")) + if (!strcmp(mode, "L") || !strcmp(mode, "P")) { stride = xsize; - else if (!strncmp(mode, "I;16", 4)) + } else if (!strncmp(mode, "I;16", 4)) { stride = xsize * 2; - else + } else { stride = xsize * 4; + } } if (stride > 0 && ysize > PY_SSIZE_T_MAX / stride) { @@ -349,8 +368,9 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) } /* check buffer size */ - if (PyImaging_GetBuffer(target, &view) < 0) + if (PyImaging_GetBuffer(target, &view) < 0) { return NULL; + } if (view.len < 0) { PyErr_SetString(PyExc_ValueError, "buffer has negative size"); @@ -371,12 +391,15 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) } /* setup file pointers */ - if (ystep > 0) - for (y = 0; y < ysize; y++) + if (ystep > 0) { + for (y = 0; y < ysize; y++) { im->image[y] = (char*)view.buf + offset + y * stride; - else - for (y = 0; y < ysize; y++) + } + } else { + for (y = 0; y < ysize; y++) { im->image[ysize-y-1] = (char*)view.buf + offset + y * stride; + } + } im->destroy = mapping_destroy_buffer; diff --git a/src/outline.c b/src/outline.c index 8a7cc32adec..d082febaa15 100644 --- a/src/outline.c +++ b/src/outline.c @@ -39,12 +39,14 @@ _outline_new(void) { OutlineObject *self; - if (PyType_Ready(&OutlineType) < 0) + if (PyType_Ready(&OutlineType) < 0) { return NULL; + } self = PyObject_New(OutlineObject, &OutlineType); - if (self == NULL) + if (self == NULL) { return NULL; + } self->outline = ImagingOutlineNew(); @@ -61,8 +63,9 @@ _outline_dealloc(OutlineObject* self) ImagingOutline PyOutline_AsOutline(PyObject* outline) { - if (PyOutline_Check(outline)) + if (PyOutline_Check(outline)) { return ((OutlineObject*) outline)->outline; + } return NULL; } @@ -74,8 +77,9 @@ PyOutline_AsOutline(PyObject* outline) PyObject* PyOutline_Create(PyObject* self, PyObject* args) { - if (!PyArg_ParseTuple(args, ":outline")) + if (!PyArg_ParseTuple(args, ":outline")) { return NULL; + } return (PyObject*) _outline_new(); } @@ -88,8 +92,9 @@ static PyObject* _outline_move(OutlineObject* self, PyObject* args) { float x0, y0; - if (!PyArg_ParseTuple(args, "ff", &x0, &y0)) + if (!PyArg_ParseTuple(args, "ff", &x0, &y0)) { return NULL; + } ImagingOutlineMove(self->outline, x0, y0); @@ -101,8 +106,9 @@ static PyObject* _outline_line(OutlineObject* self, PyObject* args) { float x1, y1; - if (!PyArg_ParseTuple(args, "ff", &x1, &y1)) + if (!PyArg_ParseTuple(args, "ff", &x1, &y1)) { return NULL; + } ImagingOutlineLine(self->outline, x1, y1); @@ -114,8 +120,9 @@ static PyObject* _outline_curve(OutlineObject* self, PyObject* args) { float x1, y1, x2, y2, x3, y3; - if (!PyArg_ParseTuple(args, "ffffff", &x1, &y1, &x2, &y2, &x3, &y3)) + if (!PyArg_ParseTuple(args, "ffffff", &x1, &y1, &x2, &y2, &x3, &y3)) { return NULL; + } ImagingOutlineCurve(self->outline, x1, y1, x2, y2, x3, y3); @@ -126,8 +133,9 @@ _outline_curve(OutlineObject* self, PyObject* args) static PyObject* _outline_close(OutlineObject* self, PyObject* args) { - if (!PyArg_ParseTuple(args, ":close")) + if (!PyArg_ParseTuple(args, ":close")) { return NULL; + } ImagingOutlineClose(self->outline); @@ -139,8 +147,9 @@ static PyObject* _outline_transform(OutlineObject* self, PyObject* args) { double a[6]; - if (!PyArg_ParseTuple(args, "(dddddd)", a+0, a+1, a+2, a+3, a+4, a+5)) + if (!PyArg_ParseTuple(args, "(dddddd)", a+0, a+1, a+2, a+3, a+4, a+5)) { return NULL; + } ImagingOutlineTransform(self->outline, a); diff --git a/src/path.c b/src/path.c index f69755d1685..3cbdd5d4497 100644 --- a/src/path.c +++ b/src/path.c @@ -61,8 +61,9 @@ alloc_array(Py_ssize_t count) return NULL; } xy = malloc(2 * count * sizeof(double) + 1); - if (!xy) + if (!xy) { PyErr_NoMemory(); + } return xy; } @@ -74,8 +75,9 @@ path_new(Py_ssize_t count, double* xy, int duplicate) if (duplicate) { /* duplicate path */ double* p = alloc_array(count); - if (!p) + if (!p) { return NULL; + } memcpy(p, xy, count * 2 * sizeof(double)); xy = p; } @@ -120,8 +122,9 @@ PyPath_Flatten(PyObject* data, double **pxy) /* This was another path object. */ PyPathObject *path = (PyPathObject*) data; xy = alloc_array(path->count); - if (!xy) + if (!xy) { return -1; + } memcpy(xy, path->xy, 2 * path->count * sizeof(double)); *pxy = xy; return path->count; @@ -134,10 +137,12 @@ PyPath_Flatten(PyObject* data, double **pxy) float *ptr = (float*) buffer.buf; n = buffer.len / (2 * sizeof(float)); xy = alloc_array(n); - if (!xy) + if (!xy) { return -1; - for (i = 0; i < n+n; i++) + } + for (i = 0; i < n+n; i++) { xy[i] = ptr[i]; + } *pxy = xy; PyBuffer_Release(&buffer); return n; @@ -153,26 +158,28 @@ PyPath_Flatten(PyObject* data, double **pxy) j = 0; n = PyObject_Length(data); /* Just in case __len__ breaks (or doesn't exist) */ - if (PyErr_Occurred()) + if (PyErr_Occurred()) { return -1; + } /* Allocate for worst case */ xy = alloc_array(n); - if (!xy) + if (!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)) + if (PyFloat_Check(op)) { xy[j++] = PyFloat_AS_DOUBLE(op); - else if (PyLong_Check(op)) + } else if (PyLong_Check(op)) { xy[j++] = (float) PyLong_AS_LONG(op); - else if (PyNumber_Check(op)) + } else if (PyNumber_Check(op)) { xy[j++] = PyFloat_AsDouble(op); - else if (PyArg_ParseTuple(op, "dd", &x, &y)) { + } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { xy[j++] = x; xy[j++] = y; } else { @@ -184,13 +191,13 @@ PyPath_Flatten(PyObject* data, double **pxy) for (i = 0; i < n; i++) { double x, y; PyObject *op = PyTuple_GET_ITEM(data, i); - if (PyFloat_Check(op)) + if (PyFloat_Check(op)) { xy[j++] = PyFloat_AS_DOUBLE(op); - else if (PyLong_Check(op)) + } else if (PyLong_Check(op)) { xy[j++] = (float) PyLong_AS_LONG(op); - else if (PyNumber_Check(op)) + } else if (PyNumber_Check(op)) { xy[j++] = PyFloat_AsDouble(op); - else if (PyArg_ParseTuple(op, "dd", &x, &y)) { + } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { xy[j++] = x; xy[j++] = y; } else { @@ -213,13 +220,13 @@ PyPath_Flatten(PyObject* data, double **pxy) return -1; } } - if (PyFloat_Check(op)) + if (PyFloat_Check(op)) { xy[j++] = PyFloat_AS_DOUBLE(op); - else if (PyLong_Check(op)) + } else if (PyLong_Check(op)) { xy[j++] = (float) PyLong_AS_LONG(op); - else if (PyNumber_Check(op)) + } else if (PyNumber_Check(op)) { xy[j++] = PyFloat_AsDouble(op); - else if (PyArg_ParseTuple(op, "dd", &x, &y)) { + } else if (PyArg_ParseTuple(op, "dd", &x, &y)) { xy[j++] = x; xy[j++] = y; } else { @@ -257,19 +264,22 @@ PyPath_Create(PyObject* self, PyObject* args) /* number of vertices */ xy = alloc_array(count); - if (!xy) + if (!xy) { return NULL; + } } else { /* sequence or other path */ PyErr_Clear(); - if (!PyArg_ParseTuple(args, "O", &data)) + if (!PyArg_ParseTuple(args, "O", &data)) { return NULL; + } count = PyPath_Flatten(data, &xy); - if (count < 0) + if (count < 0) { return NULL; + } } return (PyObject*) path_new(count, xy, 0); @@ -291,8 +301,9 @@ path_compact(PyPathObject* self, PyObject* args) double cityblock = 2.0; - if (!PyArg_ParseTuple(args, "|d:compact", &cityblock)) + if (!PyArg_ParseTuple(args, "|d:compact", &cityblock)) { return NULL; + } xy = self->xy; @@ -323,8 +334,9 @@ path_getbbox(PyPathObject* self, PyObject* args) double *xy; double x0, y0, x1, y1; - if (!PyArg_ParseTuple(args, ":getbbox")) + if (!PyArg_ParseTuple(args, ":getbbox")) { return NULL; + } xy = self->xy; @@ -332,14 +344,18 @@ path_getbbox(PyPathObject* self, PyObject* args) y0 = y1 = xy[1]; for (i = 1; i < self->count; i++) { - if (xy[i+i] < x0) + if (xy[i+i] < x0) { x0 = xy[i+i]; - if (xy[i+i] > x1) + } + if (xy[i+i] > x1) { x1 = xy[i+i]; - if (xy[i+i+1] < y0) + } + if (xy[i+i+1] < y0) { y0 = xy[i+i+1]; - if (xy[i+i+1] > y1) + } + if (xy[i+i+1] > y1) { y1 = xy[i+i+1]; + } } return Py_BuildValue("dddd", x0, y0, x1, y1); @@ -348,8 +364,9 @@ path_getbbox(PyPathObject* self, PyObject* args) static PyObject* path_getitem(PyPathObject* self, Py_ssize_t i) { - if (i < 0) + if (i < 0) { i = self->count + i; + } if (i < 0 || i >= self->count) { PyErr_SetString(PyExc_IndexError, "path index out of range"); return NULL; @@ -362,16 +379,19 @@ static PyObject* path_getslice(PyPathObject* self, Py_ssize_t ilow, Py_ssize_t ihigh) { /* adjust arguments */ - if (ilow < 0) + if (ilow < 0) { ilow = 0; - else if (ilow >= self->count) + } else if (ilow >= self->count) { ilow = self->count; - if (ihigh < 0) + } + if (ihigh < 0) { ihigh = 0; - if (ihigh < ilow) + } + if (ihigh < ilow) { ihigh = ilow; - else if (ihigh > self->count) + } else if (ihigh > self->count) { ihigh = self->count; + } return (PyObject*) path_new(ihigh - ilow, self->xy + ilow * 2, 1); } @@ -390,8 +410,9 @@ path_map(PyPathObject* self, PyObject* args) double *xy; PyObject* function; - if (!PyArg_ParseTuple(args, "O:map", &function)) + if (!PyArg_ParseTuple(args, "O:map", &function)) { return NULL; + } xy = self->xy; @@ -432,8 +453,9 @@ path_setitem(PyPathObject* self, Py_ssize_t i, PyObject* op) xy = &self->xy[i+i]; - if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1])) + if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1])) { return -1; + } return 0; } @@ -445,16 +467,18 @@ path_tolist(PyPathObject* self, PyObject* args) Py_ssize_t i; int flat = 0; - if (!PyArg_ParseTuple(args, "|i:tolist", &flat)) + if (!PyArg_ParseTuple(args, "|i:tolist", &flat)) { return NULL; + } if (flat) { list = PyList_New(self->count*2); for (i = 0; i < self->count*2; i++) { PyObject* item; item = PyFloat_FromDouble(self->xy[i]); - if (!item) + if (!item) { goto error; + } PyList_SetItem(list, i, item); } } else { @@ -462,8 +486,9 @@ path_tolist(PyPathObject* self, PyObject* args) for (i = 0; i < self->count; i++) { PyObject* item; item = Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]); - if (!item) + if (!item) { goto error; + } PyList_SetItem(list, i, item); } } @@ -487,19 +512,20 @@ path_transform(PyPathObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "(dddddd)|d:transform", &a, &b, &c, &d, &e, &f, - &wrap)) + &wrap)) { return NULL; + } xy = self->xy; /* transform the coordinate set */ - if (b == 0.0 && d == 0.0) + if (b == 0.0 && d == 0.0) { /* scaling */ for (i = 0; i < self->count; i++) { xy[i+i] = a*xy[i+i]+c; xy[i+i+1] = e*xy[i+i+1]+f; } - else + } else { /* affine transform */ for (i = 0; i < self->count; i++) { double x = xy[i+i]; @@ -507,11 +533,14 @@ path_transform(PyPathObject* self, PyObject* args) xy[i+i] = a*x+b*y+c; xy[i+i+1] = d*x+e*y+f; } + } /* special treatment of geographical map data */ - if (wrap != 0.0) - for (i = 0; i < self->count; i++) + if (wrap != 0.0) { + for (i = 0; i < self->count; i++) { xy[i+i] = fmod(xy[i+i], wrap); + } + } Py_INCREF(Py_None); return Py_None; @@ -542,16 +571,18 @@ path_subscript(PyPathObject* self, PyObject* item) { if (PyIndex_Check(item)) { Py_ssize_t i; i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) + if (i == -1 && PyErr_Occurred()) { return NULL; + } return path_getitem(self, i); } if (PySlice_Check(item)) { int len = 4; Py_ssize_t start, stop, step, slicelength; - if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) + if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) { return NULL; + } if (slicelength <= 0) { double *xy = alloc_array(0); From 9f2773b3f747e7d61faf66435e462fbe02cd5b76 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 11 May 2020 07:19:52 +1000 Subject: [PATCH 107/262] Added braces Co-authored-by: Hugo van Kemenade --- src/libImaging/JpegEncode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 5f3b29c669b..8882b61be11 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -277,8 +277,9 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } else { break; } - } else + } else { state->state++; + } case 4: if (1024 > context->destination.pub.free_in_buffer){ From 3a75e843f4af6220e70cb81b49dfdcc702a80797 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 May 2020 07:46:12 +1000 Subject: [PATCH 108/262] Added braces --- src/_imaging.c | 18 ++++++++++++------ src/_imagingft.c | 9 +++++---- src/encode.c | 7 +++++-- src/libImaging/Convert.c | 13 ++++++------- src/libImaging/Crop.c | 6 ++++-- src/libImaging/Draw.c | 3 ++- src/libImaging/GetBBox.c | 11 +++++++---- src/libImaging/GifDecode.c | 34 ++++++++++++++++++---------------- src/libImaging/Histo.c | 3 ++- src/libImaging/Point.c | 3 ++- src/libImaging/Quant.c | 12 +++++++++--- src/libImaging/RawEncode.c | 3 ++- src/libImaging/XbmEncode.c | 3 ++- src/libImaging/ZipEncode.c | 3 ++- 14 files changed, 78 insertions(+), 50 deletions(-) diff --git a/src/_imaging.c b/src/_imaging.c index f0ba220408c..40bfbf2fe12 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -490,8 +490,9 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) case IMAGING_TYPE_FLOAT32: return PyFloat_FromDouble(pixel.f); case IMAGING_TYPE_SPECIAL: - if (strncmp(im->mode, "I;16", 4) == 0) + if (strncmp(im->mode, "I;16", 4) == 0) { return PyLong_FromLong(pixel.h); + } break; } @@ -1456,8 +1457,9 @@ _point(ImagingObject* self, PyObject* args) lut[i*4] = CLIP8(data[i]); lut[i*4+1] = CLIP8(data[i+256]); lut[i*4+2] = CLIP8(data[i+512]); - if (n > 768) + if (n > 768) { lut[i*4+3] = CLIP8(data[i+768]); + } } im = ImagingPoint(self->image, mode, (void*) lut); } else { @@ -1523,16 +1525,18 @@ _putdata(ImagingObject* self, PyObject* args) /* Plain string data */ for (i = y = 0; i < n; i += image->xsize, y++) { x = n - i; - if (x > (int) image->xsize) + if (x > (int) image->xsize) { x = image->xsize; + } memcpy(image->image8[y], p+i, x); } } else { /* Scaled and clipped string data */ for (i = x = y = 0; i < n; i++) { image->image8[y][x] = CLIP8((int) (p[i] * scale + offset)); - if (++x >= (int) image->xsize) + if (++x >= (int) image->xsize) { x = 0, y++; + } } } } else { @@ -1932,12 +1936,14 @@ im_setmode(ImagingObject* self, PyObject* args) /* color to color */ strcpy(im->mode, mode); im->bands = modelen; - if (!strcmp(mode, "RGBA")) + if (!strcmp(mode, "RGBA")) { (void) ImagingFillBand(im, 3, 255); + } } else { /* trying doing an in-place conversion */ - if (!ImagingConvertInPlace(im, mode)) + if (!ImagingConvertInPlace(im, mode)) { return NULL; + } } if (self->access) { diff --git a/src/_imagingft.c b/src/_imagingft.c index 795ab4d2013..a9b00431a4e 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -406,11 +406,11 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * direction = RAQM_DIRECTION_DEFAULT; if (dir) { - if (strcmp(dir, "rtl") == 0) + if (strcmp(dir, "rtl") == 0) { direction = RAQM_DIRECTION_RTL; - else if (strcmp(dir, "ltr") == 0) + } else if (strcmp(dir, "ltr") == 0) { direction = RAQM_DIRECTION_LTR; - else if (strcmp(dir, "ttb") == 0) { + } else if (strcmp(dir, "ttb") == 0) { direction = RAQM_DIRECTION_TTB; if (p_raqm.version_atleast == NULL || !(*p_raqm.version_atleast)(0, 7, 0)) { PyErr_SetString(PyExc_ValueError, "libraqm 0.7 or greater required for 'ttb' direction"); @@ -694,8 +694,9 @@ font_getsize(FontObject* self, PyObject* args) offset = -glyph_info[i].y_advance - face->glyph->metrics.height - face->glyph->metrics.vertBearingY; - if (offset < 0) + if (offset < 0) { y_max -= offset; + } } if (bbox.xMax > x_max) { diff --git a/src/encode.c b/src/encode.c index b285292f327..1d463e9c4cd 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1085,8 +1085,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) if (extra && extra_size > 0) { /* malloc check ok, length is from python parsearg */ char* p = malloc(extra_size); // Freed in JpegEncode, Case 5 - if (!p) + if (!p) { return PyErr_NoMemory(); + } memcpy(p, extra, extra_size); extra = p; } else { @@ -1097,7 +1098,9 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) /* malloc check ok, length is from python parsearg */ char* pp = malloc(rawExifLen); // Freed in JpegEncode, Case 5 if (!pp) { - if (extra) free(extra); + if (extra) { + free(extra); + } return PyErr_NoMemory(); } memcpy(pp, rawExif, rawExifLen); diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 5e69b067dab..b0b794d722b 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1660,17 +1660,16 @@ convert(Imaging imOut, Imaging imIn, const char *mode, } } - if (!convert) + if (!convert) { #ifdef notdef return (Imaging) ImagingError_ValueError("conversion not supported"); #else - { - static char buf[256]; - /* FIXME: may overflow if mode is too large */ - sprintf(buf, "conversion from %s to %s not supported", imIn->mode, mode); - return (Imaging) ImagingError_ValueError(buf); - } + static char buf[256]; + /* FIXME: may overflow if mode is too large */ + sprintf(buf, "conversion from %s to %s not supported", imIn->mode, mode); + return (Imaging) ImagingError_ValueError(buf); #endif + } imOut = ImagingNew2Dirty(mode, imOut, imIn); if (!imOut) { diff --git a/src/libImaging/Crop.c b/src/libImaging/Crop.c index 29b4cf9d957..d136edbfc74 100644 --- a/src/libImaging/Crop.c +++ b/src/libImaging/Crop.c @@ -32,11 +32,13 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) } xsize = sx1 - sx0; - if (xsize < 0) + if (xsize < 0) { xsize = 0; + } ysize = sy1 - sy0; - if (ysize < 0) + if (ysize < 0) { ysize = 0; + } imOut = ImagingNewDirty(imIn->mode, xsize, ysize); if (!imOut) { diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 35e6e4893fa..10aaae1f2a6 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -941,8 +941,9 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, } lx = x, ly = y; } - if (n == 0) + if (n == 0) { return 0; + } if (inner) { // Inner circle diff --git a/src/libImaging/GetBBox.c b/src/libImaging/GetBBox.c index dd875c55730..9a8ae1f320c 100644 --- a/src/libImaging/GetBBox.c +++ b/src/libImaging/GetBBox.c @@ -39,15 +39,18 @@ ImagingGetBBox(Imaging im, int bbox[4]) for (x = 0; x < im->xsize; x++) {\ if (im->image[y][x] & mask) {\ has_data = 1;\ - if (x < bbox[0])\ + if (x < bbox[0]) {\ bbox[0] = x;\ - if (x >= bbox[2])\ + }\ + if (x >= bbox[2]) {\ bbox[2] = x+1;\ + }\ }\ }\ if (has_data) {\ - if (bbox[1] < 0)\ - bbox[1] = y;\ + if (bbox[1] < 0) {\ + bbox[1] = y;\ + }\ bbox[3] = y+1;\ }\ } diff --git a/src/libImaging/GifDecode.c b/src/libImaging/GifDecode.c index 5728ae1ce1a..62170b15f2d 100644 --- a/src/libImaging/GifDecode.c +++ b/src/libImaging/GifDecode.c @@ -52,8 +52,9 @@ default:\ return -1;\ }\ - if (state->y < state->ysize)\ + if (state->y < state->ysize) {\ out = im->image8[state->y + state->yoff] + state->xoff;\ + }\ } @@ -70,24 +71,25 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ssize_t if (!state->state) { - /* Initialise state */ - if (context->bits < 0 || context->bits > 12) { - state->errcode = IMAGING_CODEC_CONFIG; - return -1; - } + /* Initialise state */ + if (context->bits < 0 || context->bits > 12) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } - /* Clear code */ - context->clear = 1 << context->bits; + /* Clear code */ + context->clear = 1 << context->bits; - /* End code */ - context->end = context->clear + 1; + /* End code */ + context->end = context->clear + 1; - /* Interlace */ - if (context->interlace) { - context->interlace = 1; - context->step = context->repeat = 8; - } else - context->step = 1; + /* Interlace */ + if (context->interlace) { + context->interlace = 1; + context->step = context->repeat = 8; + } else { + context->step = 1; + } state->state = 1; } diff --git a/src/libImaging/Histo.c b/src/libImaging/Histo.c index b6f552ab3eb..050c2840f46 100644 --- a/src/libImaging/Histo.c +++ b/src/libImaging/Histo.c @@ -179,8 +179,9 @@ ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) FLOAT32* in = (FLOAT32*) im->image32[y]; for (x = 0; x < im->xsize; x++) { i = (int) (((*in++)-fmin)*scale); - if (i >= 0 && i < 256) + if (i >= 0 && i < 256) { h->histogram[i]++; + } } } ImagingSectionLeave(&cookie); diff --git a/src/libImaging/Point.c b/src/libImaging/Point.c index 76c0e591dae..b70840b0707 100644 --- a/src/libImaging/Point.c +++ b/src/libImaging/Point.c @@ -219,8 +219,9 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) if (!imIn || (strcmp(imIn->mode, "I") != 0 && strcmp(imIn->mode, "I;16") != 0 && - strcmp(imIn->mode, "F") != 0)) + strcmp(imIn->mode, "F") != 0)) { return (Imaging) ImagingError_ModeError(); + } imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); if (!imOut) { diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index fdf1ea3b638..6c9f8d9b70d 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -689,8 +689,12 @@ static void free_box_tree(BoxNode *n) { PixelList *p,*pp; - if (n->l) free_box_tree(n->l); - if (n->r) free_box_tree(n->r); + if (n->l) { + free_box_tree(n->l); + } + if (n->r) { + free_box_tree(n->r); + } for (p=n->head[0];p;p=pp) { pp=p->next[0]; free(p); @@ -1008,7 +1012,9 @@ compute_palette_from_median_cut( /* malloc check ok, using calloc */ if (!(avg[i]=calloc(nPaletteEntries, sizeof(uint32_t)))) { for(i=0;i<3;i++) { - if (avg[i]) free (avg[i]); + if (avg[i]) { + free (avg[i]); + } } free(count); return 0; diff --git a/src/libImaging/RawEncode.c b/src/libImaging/RawEncode.c index 3c593480b2d..fb4ab334682 100644 --- a/src/libImaging/RawEncode.c +++ b/src/libImaging/RawEncode.c @@ -41,8 +41,9 @@ ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } state->count = state->bytes; state->bytes = bytes; - } else + } else { state->count = state->bytes; + } /* The "ystep" field specifies the orientation */ diff --git a/src/libImaging/XbmEncode.c b/src/libImaging/XbmEncode.c index cf72da61958..d1bc086dbcb 100644 --- a/src/libImaging/XbmEncode.c +++ b/src/libImaging/XbmEncode.c @@ -90,8 +90,9 @@ ImagingXbmEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) bytes--; state->count = 0; } - } else + } else { *ptr++ = '\n'; + } bytes -= 5; diff --git a/src/libImaging/ZipEncode.c b/src/libImaging/ZipEncode.c index 6b44ed81a24..0b443567874 100644 --- a/src/libImaging/ZipEncode.c +++ b/src/libImaging/ZipEncode.c @@ -307,8 +307,9 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } - if (context->z_stream.avail_out == 0) + if (context->z_stream.avail_out == 0) { break; /* Buffer full */ + } case 2: From 43072e3ca332c7fb204fc72e9dfa566f3b2cbd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20N=C3=BC=C3=9Flein?= Date: Mon, 11 May 2020 10:26:49 +0200 Subject: [PATCH 109/262] Update docs/installation.rst Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 1da7fe77277..122d19bc6a1 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -328,7 +328,7 @@ In Fedora, the command is:: .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. -Prerequisites for **Ubuntu 16.04 LTS or later** are installed with:: +Prerequisites for **Ubuntu 16.04 LTS - 20.04 LTS** are installed with:: sudo apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ From 15ae39674e63c67a0421f217f340c1037e85d2bf Mon Sep 17 00:00:00 2001 From: Simon Andrieux Date: Mon, 11 May 2020 17:38:42 +0200 Subject: [PATCH 110/262] fix reading from empty buffer when loading .gbr --- Tests/test_file_gbr.py | 7 +++++++ src/PIL/GbrImagePlugin.py | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index f183390bccf..25e624886dc 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -15,3 +15,10 @@ def test_gbr_file(): with Image.open("Tests/images/gbr.gbr") as im: with Image.open("Tests/images/gbr.png") as target: assert_image_equal(target, im) + + +def test_multiples_operation(): + with Image.open("Tests/images/gbr.gbr") as im: + rect = (0, 0, 10, 10) + im.crop(rect) + im.crop(rect) diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index 292de435cef..23a7a5c7570 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -84,8 +84,9 @@ def _open(self): self._data_size = width * height * color_depth def load(self): - 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)) # From f0871b70e7b06aecd4ed751d570faf2ae8bbd0bb Mon Sep 17 00:00:00 2001 From: David Walker Date: Tue, 12 May 2020 00:11:42 -0700 Subject: [PATCH 111/262] Update src/PIL/ImageChops.py Apply wording suggestions about ImageChops.multiply Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageChops.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageChops.py b/src/PIL/ImageChops.py index 904df484e86..c1a2574e449 100644 --- a/src/PIL/ImageChops.py +++ b/src/PIL/ImageChops.py @@ -240,8 +240,10 @@ def subtract_modulo(image1, image2): def logical_and(image1, image2): """Logical AND between two images. - Both of the images must have mode "1". For an AND in RGB mode, use a - multiply() by a black-and-white mask. + Both of the images must have mode "1". If you would like to perform a + logical AND on an image with a mode other than "1", try + :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask + as the second image. .. code-block:: python From 837bc0ad9c9dcb972ed72046c0bdabd456911fa9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 13 May 2020 10:21:10 +0300 Subject: [PATCH 112/262] Fix broken string --- Tests/test_image_reduce.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_reduce.py b/Tests/test_image_reduce.py index 729645a0b2b..353d0def04e 100644 --- a/Tests/test_image_reduce.py +++ b/Tests/test_image_reduce.py @@ -174,8 +174,10 @@ def assert_compare_images(a, b, max_average_diff, max_diff=255): average_diff = sum(i * num for i, num in enumerate(ch_hist)) / ( a.size[0] * a.size[1] ) - msg = "average pixel value difference {:.4f} > expected {:.4f} " - "for '{}' band".format(average_diff, max_average_diff, band) + msg = ( + "average pixel value difference {:.4f} > expected {:.4f} " + "for '{}' band".format(average_diff, max_average_diff, band) + ) assert max_average_diff >= average_diff, msg last_diff = [i for i, num in enumerate(ch_hist) if num > 0][-1] From 228301373f5b1adacf775dff26ff69bb10b5efa6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 May 2020 06:57:15 +1000 Subject: [PATCH 113/262] Fixed comparison warnings --- src/libImaging/Jpeg.h | 2 +- src/libImaging/Jpeg2KEncode.c | 6 +++--- src/libImaging/QuantPngQuant.c | 4 ++-- src/libImaging/QuantPngQuant.h | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libImaging/Jpeg.h b/src/libImaging/Jpeg.h index df6d8a90319..280b6d638c5 100644 --- a/src/libImaging/Jpeg.h +++ b/src/libImaging/Jpeg.h @@ -110,7 +110,7 @@ typedef struct { int extra_offset; - int rawExifLen; /* EXIF data length */ + size_t rawExifLen; /* EXIF data length */ char* rawExif; /* EXIF buffer pointer */ } JPEGENCODERSTATE; diff --git a/src/libImaging/Jpeg2KEncode.c b/src/libImaging/Jpeg2KEncode.c index 49ef5e254d4..5b18e472c53 100644 --- a/src/libImaging/Jpeg2KEncode.c +++ b/src/libImaging/Jpeg2KEncode.c @@ -50,7 +50,7 @@ static OPJ_SIZE_T j2k_write(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { ImagingCodecState state = (ImagingCodecState)p_user_data; - int result; + unsigned int result; result = _imaging_write_pyFd(state->fd, p_buffer, p_nb_bytes); @@ -399,8 +399,8 @@ j2k_encode_entry(Imaging im, ImagingCodecState state) Py_ssize_t n; float *pq; - if (len) { - if (len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) { + if (len > 0) { + if ((unsigned)len > sizeof(params.tcp_rates) / sizeof(params.tcp_rates[0])) { len = sizeof(params.tcp_rates)/sizeof(params.tcp_rates[0]); } diff --git a/src/libImaging/QuantPngQuant.c b/src/libImaging/QuantPngQuant.c index ef40b282b57..753ceb02f25 100644 --- a/src/libImaging/QuantPngQuant.c +++ b/src/libImaging/QuantPngQuant.c @@ -20,8 +20,8 @@ int quantize_pngquant( Pixel *pixelData, - int width, - int height, + unsigned int width, + unsigned int height, uint32_t quantPixels, Pixel **palette, uint32_t *paletteLength, diff --git a/src/libImaging/QuantPngQuant.h b/src/libImaging/QuantPngQuant.h index d539a7a0d24..fb0b4cc0391 100644 --- a/src/libImaging/QuantPngQuant.h +++ b/src/libImaging/QuantPngQuant.h @@ -4,8 +4,8 @@ #include "QuantTypes.h" int quantize_pngquant(Pixel *, - int, - int, + unsigned int, + unsigned int, uint32_t, Pixel **, uint32_t *, From 720f79a629eb1efa5f63cf6382c6ac168efdee3a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 May 2020 18:16:54 +1000 Subject: [PATCH 114/262] Removed unused argument from CHOP --- src/libImaging/Chops.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libImaging/Chops.c b/src/libImaging/Chops.c index 9dd708f7512..a0a70abd97a 100644 --- a/src/libImaging/Chops.c +++ b/src/libImaging/Chops.c @@ -19,10 +19,10 @@ #include "Imaging.h" -#define CHOP(operation, mode)\ +#define CHOP(operation)\ int x, y;\ Imaging imOut;\ - imOut = create(imIn1, imIn2, mode);\ + imOut = create(imIn1, imIn2, NULL);\ if (!imOut) {\ return NULL;\ }\ @@ -83,43 +83,43 @@ create(Imaging im1, Imaging im2, char* mode) Imaging ImagingChopLighter(Imaging imIn1, Imaging imIn2) { - CHOP((in1[x] > in2[x]) ? in1[x] : in2[x], NULL); + CHOP((in1[x] > in2[x]) ? in1[x] : in2[x]); } Imaging ImagingChopDarker(Imaging imIn1, Imaging imIn2) { - CHOP((in1[x] < in2[x]) ? in1[x] : in2[x], NULL); + CHOP((in1[x] < in2[x]) ? in1[x] : in2[x]); } Imaging ImagingChopDifference(Imaging imIn1, Imaging imIn2) { - CHOP(abs((int) in1[x] - (int) in2[x]), NULL); + CHOP(abs((int) in1[x] - (int) in2[x])); } Imaging ImagingChopMultiply(Imaging imIn1, Imaging imIn2) { - CHOP((int) in1[x] * (int) in2[x] / 255, NULL); + CHOP((int) in1[x] * (int) in2[x] / 255); } Imaging ImagingChopScreen(Imaging imIn1, Imaging imIn2) { - CHOP(255 - ((int) (255 - in1[x]) * (int) (255 - in2[x])) / 255, NULL); + CHOP(255 - ((int) (255 - in1[x]) * (int) (255 - in2[x])) / 255); } Imaging ImagingChopAdd(Imaging imIn1, Imaging imIn2, float scale, int offset) { - CHOP(((int) in1[x] + (int) in2[x]) / scale + offset, NULL); + CHOP(((int) in1[x] + (int) in2[x]) / scale + offset); } Imaging ImagingChopSubtract(Imaging imIn1, Imaging imIn2, float scale, int offset) { - CHOP(((int) in1[x] - (int) in2[x]) / scale + offset, NULL); + CHOP(((int) in1[x] - (int) in2[x]) / scale + offset); } Imaging From b8ec793898c9cebd9f3b109d4083be09b17f8abd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 May 2020 18:29:52 +1000 Subject: [PATCH 115/262] Fixed ZeroDivisionError in thumbnail --- Tests/test_image_thumbnail.py | 6 ++++++ src/PIL/Image.py | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index f4ed8e746ff..da63efe5576 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -63,6 +63,12 @@ def test_aspect(): assert im.size == (75, 23) # ratio is 3.260869565217 +def test_division_by_zero(): + im = Image.new("L", (200, 2)) + im.thumbnail((75, 75)) + assert im.size == (75, 1) + + def test_float(): im = Image.new("L", (128, 128)) im.thumbnail((99.9, 99.9)) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0c8b42a0909..8c5fff8ed38 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2277,7 +2277,9 @@ def round_aspect(number, key): if x / y >= aspect: x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) else: - y = round_aspect(x / aspect, key=lambda n: abs(aspect - x / n)) + y = round_aspect( + x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) + ) size = (x, y) box = None From b3604167ad84329a41e615cdd10b627651a3c640 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 May 2020 20:47:57 +1000 Subject: [PATCH 116/262] Change STRIPBYTECOUNTS to LONG if necessary when saving --- Tests/test_file_tiff_metadata.py | 17 +++++++++++++++++ src/PIL/TiffImagePlugin.py | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 9fe601bd65b..876f790cff2 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -156,6 +156,23 @@ def test_write_metadata(tmp_path): assert value == reloaded[tag], "%s didn't roundtrip" % tag +def test_change_stripbytecounts_tag_type(tmp_path): + out = str(tmp_path / "temp.tiff") + with Image.open("Tests/images/hopper.tif") as im: + info = im.tag_v2 + + # Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT + im = im.resize((500, 500)) + + # STRIPBYTECOUNTS can be a SHORT or a LONG + info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT + + im.save(out, tiffinfo=info) + + with Image.open(out) as reloaded: + assert reloaded.tag_v2.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] == TiffTags.LONG + + def test_no_duplicate_50741_tag(): assert TAG_IDS["MakerNoteSafety"] == 50741 assert TAG_IDS["BestQualityScale"] == 50780 diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 33343762576..43a2f7c406f 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1508,7 +1508,10 @@ def _save(im, fp, filename): # data orientation stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) ifd[ROWSPERSTRIP] = im.size[1] - ifd[STRIPBYTECOUNTS] = stride * im.size[1] + stripByteCounts = stride * im.size[1] + if stripByteCounts >= 2 ** 16: + ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG + ifd[STRIPBYTECOUNTS] = stripByteCounts ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer # no compression by default: ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) From aa1761bc9fa31c3d7c7eba9d8e1986e6d6fdabe4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 May 2020 22:37:13 +1000 Subject: [PATCH 117/262] Replace tiff_jpeg with jpeg compression when saving --- Tests/test_file_libtiff.py | 8 ++++++++ src/PIL/TiffImagePlugin.py | 3 +++ 2 files changed, 11 insertions(+) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 9523e5901b5..6e82a798642 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -448,6 +448,14 @@ def test_compressions(self, tmp_path): assert size_compressed > size_jpeg assert size_jpeg > size_jpeg_30 + def test_tiff_jpeg_compression(self, tmp_path): + im = hopper("RGB") + out = str(tmp_path / "temp.tif") + im.save(out, compression="tiff_jpeg") + + with Image.open(out) as reloaded: + assert reloaded.info["compression"] == "jpeg" + def test_quality(self, tmp_path): im = hopper("RGB") out = str(tmp_path / "temp.tif") diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 33343762576..0ffcbb093e9 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1427,6 +1427,9 @@ def _save(im, fp, filename): compression = im.encoderinfo.get("compression", im.info.get("compression")) if compression is None: compression = "raw" + elif compression == "tiff_jpeg": + # OJPEG is obsolete, so use new-style JPEG compression instead + compression = "jpeg" libtiff = WRITE_LIBTIFF or compression != "raw" From fdc5993ace28ce57204f82380be6a3f433e14c76 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 May 2020 21:37:33 +1000 Subject: [PATCH 118/262] Improved grammar [ci skip] --- src/PIL/JpegImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 0e1a9cc0f87..89e70f0e989 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -593,9 +593,9 @@ def convert_dict_qtables(qtables): def get_sampling(im): - # There's no subsampling when image have only 1 layer + # There's no subsampling when images have only 1 layer # (grayscale images) or when they are CMYK (4 layers), - # so set subsampling to default value. + # so set subsampling to the default value. # # NOTE: currently Pillow can't encode JPEG to YCCK format. # If YCCK support is added in the future, subsampling code will have From 67d26ed051a4a12c2203731323f9dd62db862108 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 May 2020 09:24:06 +1000 Subject: [PATCH 119/262] Use _accept function in example plugin [ci skip] --- docs/handbook/writing-your-own-file-decoder.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 24e46ff0072..4be34eefc42 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -53,6 +53,11 @@ true color. from PIL import Image, ImageFile + + def _accept(prefix): + return prefix[:4] == b"SPAM" + + class SpamImageFile(ImageFile.ImageFile): format = "SPAM" @@ -60,12 +65,7 @@ true color. def _open(self): - # check header - header = self.fp.read(128) - if header[:4] != b"SPAM": - raise SyntaxError("not a SPAM file") - - header = header.split() + header = self.fp.read(128).split() # size in pixels (width, height) self._size = int(header[1]), int(header[2]) @@ -86,7 +86,7 @@ true color. ("raw", (0, 0) + self.size, 128, (self.mode, 0, 1)) ] - Image.register_open(SpamImageFile.format, SpamImageFile) + Image.register_open(SpamImageFile.format, SpamImageFile, _accept) Image.register_extension(SpamImageFile.format, ".spam") Image.register_extension(SpamImageFile.format, ".spa") # dos version From 45c4ba7a5f730821e8117944e959a7bd7b04c8cc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 May 2020 11:04:02 +1000 Subject: [PATCH 120/262] Simplified test case --- Tests/test_file_gbr.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 25e624886dc..b9597088994 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -17,8 +17,9 @@ def test_gbr_file(): assert_image_equal(target, im) -def test_multiples_operation(): +def test_multiple_load_operations(): with Image.open("Tests/images/gbr.gbr") as im: - rect = (0, 0, 10, 10) - im.crop(rect) - im.crop(rect) + im.load() + im.load() + with Image.open("Tests/images/gbr.png") as target: + assert_image_equal(target, im) From 75791835b3f38ee9db9dbc3a962bed3a78e7ffb1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 May 2020 11:08:05 +1000 Subject: [PATCH 121/262] Updated code to match other plugins --- src/PIL/GbrImagePlugin.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/PIL/GbrImagePlugin.py b/src/PIL/GbrImagePlugin.py index 23a7a5c7570..f9d9cc772d5 100644 --- a/src/PIL/GbrImagePlugin.py +++ b/src/PIL/GbrImagePlugin.py @@ -84,9 +84,12 @@ def _open(self): self._data_size = width * height * color_depth def load(self): - if not self.im: - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self._data_size)) + if self.im: + # Already loaded + return + + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self._data_size)) # From a995cc094f887d0c8045203427f52404a5521dd3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 May 2020 11:35:24 +1000 Subject: [PATCH 122/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f12a321c925..833d043575b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Fix repeatedly loading .gbr #4620 + [ElinksFr, radarhere] + - JPEG: Truncate icclist instead of setting to None #4613 [homm] From 3b621512156c46357c4f9b85fbcfbda0773bfc2b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 May 2020 16:05:54 +1000 Subject: [PATCH 123/262] Changed example function name to match use in code [ci skip] --- docs/handbook/writing-your-own-file-decoder.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index 4be34eefc42..42a6c48220b 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -17,8 +17,8 @@ itself. Such plug-ins usually have names like Pillow decodes files in 2 stages: 1. It loops over the available image plugins in the loaded order, and - calls the plugin's ``accept`` function with the first 16 bytes of - the file. If the ``accept`` function returns true, the plugin's + calls the plugin's ``_accept`` function with the first 16 bytes of + the file. If the ``_accept`` function returns true, the plugin's ``_open`` method is called to set up the image metadata and image tiles. The ``_open`` method is not for decoding the actual image data. From 6d5c2d0cdf161869180f4b32dee817df49cf512d Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 17 May 2020 14:35:58 +0300 Subject: [PATCH 124/262] Add testpaths to tell pytest where to search for tests --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 17e85bd216e..30843b847ac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,3 +10,4 @@ multi_line_output = 3 [tool:pytest] addopts = -rs +testpaths = Tests From 7daca6733d99c68fcbe0ca81a5fca559b3e112da Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Tue, 19 May 2020 15:35:32 -0700 Subject: [PATCH 125/262] Fix ImportError on Python 3.9.0b1 for Windows --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1236b5df5a8..97ff3f2b660 100755 --- a/setup.py +++ b/setup.py @@ -730,7 +730,11 @@ def build_extensions(self): if struct.unpack("h", b"\0\1")[0] == 1: defs.append(("WORDS_BIGENDIAN", None)) - if sys.platform == "win32" and not (PLATFORM_PYPY or PLATFORM_MINGW): + if ( + sys.platform == "win32" and + sys.version_info < (3, 9) and + not (PLATFORM_PYPY or PLATFORM_MINGW) + ): defs.append(("PILLOW_VERSION", '"\\"%s\\""' % PILLOW_VERSION)) else: defs.append(("PILLOW_VERSION", '"%s"' % PILLOW_VERSION)) From ac7848c78157b0e70a4944d56a3b2c5ad6fd8f19 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 20 May 2020 20:31:14 +1000 Subject: [PATCH 126/262] Lint fix --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 97ff3f2b660..8c9f0213f10 100755 --- a/setup.py +++ b/setup.py @@ -731,9 +731,9 @@ def build_extensions(self): defs.append(("WORDS_BIGENDIAN", None)) if ( - sys.platform == "win32" and - sys.version_info < (3, 9) and - not (PLATFORM_PYPY or PLATFORM_MINGW) + sys.platform == "win32" + and sys.version_info < (3, 9) + and not (PLATFORM_PYPY or PLATFORM_MINGW) ): defs.append(("PILLOW_VERSION", '"\\"%s\\""' % PILLOW_VERSION)) else: From f271bc8b2fb1b8a86e30a303aca9a2bf55d54c7d Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 21 May 2020 00:17:56 +0300 Subject: [PATCH 127/262] Link pilfont to its new home in pillow-scripts --- docs/reference/ImageFont.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index bb7538096b0..1aa22aa510a 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -9,13 +9,14 @@ this class store bitmap fonts, and are used with the :py:meth:`PIL.ImageDraw.Draw.text` method. PIL uses its own font file format to store bitmap fonts. You can use the -:command:`pilfont` utility to convert BDF and PCF font descriptors (X window -font formats) to this format. +:command:`pilfont` utility from +`pillow-scripts `_ +to convert BDF and PCF font descriptors (X window font formats) to this format. Starting with version 1.1.4, PIL can be configured to support TrueType and OpenType fonts (as well as other font formats supported by the FreeType library). For earlier versions, TrueType support is only available as part of -the imToolkit package +the imToolkit package. Example ------- From 995634c40154853c12c95c426807efe427b4d4e8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 May 2020 11:54:06 +1000 Subject: [PATCH 128/262] Updated macOS tested Pillow versions [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 4b9ebdfd073..58784ee1250 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -431,7 +431,7 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+------------------------------+--------------------------------+-----------------------+ |**Operating system** |**Tested Python versions** |**Latest tested Pillow version**|**Tested processors** | +----------------------------------+------------------------------+--------------------------------+-----------------------+ -| macOS 10.15 Catalina | 3.5, 3.6, 3.7, 3.8 | 7.0.0 |x86-64 | +| macOS 10.15 Catalina | 3.5, 3.6, 3.7, 3.8 | 7.1.2 |x86-64 | +----------------------------------+------------------------------+--------------------------------+-----------------------+ | macOS 10.14 Mojave | 2.7, 3.5, 3.6, 3.7 | 6.0.0 |x86-64 | | +------------------------------+--------------------------------+ + From 02929105a88623fef15e749b9e5b8a44e709147d Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 23 May 2020 17:58:06 +0200 Subject: [PATCH 129/262] move MSYS2 to GHA --- .appveyor.yml | 21 ------------ .github/workflows/test-windows.yml | 52 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e395f2ae6f6..5868061f463 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,11 +17,6 @@ environment: - PYTHON: C:/Python38-x64 - PYTHON: C:/Python35 - PYTHON: C:/Python35-x64 - - PYTHON: C:/msys64/mingw32 - EXECUTABLE: bin/python3 - PIP_DIR: bin - TEST_OPTIONS: --processes=0 - DEPLOY: NO - PYTHON: C:/vp/pypy3 EXECUTABLE: bin/pypy.exe VENV: YES @@ -41,33 +36,17 @@ install: c:\pillow\winbuild\appveyor_install_pypy3.cmd } - ps: | - if ($env:PYTHON -eq "c:/msys64/mingw32") - { - c:\msys64\usr\bin\bash -l -c c:\\pillow\\winbuild\\appveyor_install_msys2_deps.sh - } - else - { c:\python37\python.exe c:\pillow\winbuild\build_dep.py c:\pillow\winbuild\build_deps.cmd $host.SetShouldExit(0) - } - curl -fsSL -o gs952.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/gs952w32.exe - gs952.exe /S - path %path%;C:\Program Files (x86)\gs\gs9.52\bin build_script: - ps: | - if ($env:PYTHON -eq "c:/msys64/mingw32") - { - c:\msys64\usr\bin\bash -l -c c:\\pillow\\winbuild\\appveyor_build_msys2.sh - Write-Host "through install" - $host.SetShouldExit(0) - } - else - { & $env:PYTHON/$env:EXECUTABLE c:\pillow\winbuild\build.py $host.SetShouldExit(0) - } - cd c:\pillow - '%PYTHON%\%EXECUTABLE% selftest.py --installed' diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index d45dc946769..d8ffd517200 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -385,3 +385,55 @@ jobs: with: name: ${{ steps.wheel.outputs.dist }} path: dist + + msys: + runs-on: windows-2019 + + strategy: + fail-fast: false + matrix: + mingw: ["MINGW32", "MINGW64"] + include: + - mingw: "MINGW32" + package: "mingw-w64-i686" + - mingw: "MINGW64" + package: "mingw-w64-x86_64" + + defaults: + run: + shell: bash.exe --login "{0}" + env: + MSYSTEM: ${{ matrix.mingw }} + CHERE_INVOKING: 1 + + timeout-minutes: 30 + name: MSYS2 ${{ matrix.mingw }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up MSYS + run: echo ::add-path::C:\msys64\usr\bin\ + shell: pwsh + + - name: Install MinGW packages + run: | + pacman -S --noconfirm \ + ${{ matrix.package }}-python3-pip \ + ${{ matrix.package }}-python3-setuptools \ + ${{ matrix.package }}-python3-pytest \ + ${{ matrix.package }}-python3-pytest-cov \ + ${{ matrix.package }}-python3-olefile \ + ${{ matrix.package }}-libjpeg-turbo \ + ${{ matrix.package }}-libimagequant + python3 -m pip install -U pip codecov + + - name: Build Pillow + run: | + python3 setup.py install + + - name: Test Pillow + run: | + python3 selftest.py --installed + python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests + codecov --file coverage.xml --name MSYS2 ${{ matrix.mingw }} From 479d583a4a359ab11089bbaefefbc7c7f07ea6d8 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 23 May 2020 19:41:37 +0200 Subject: [PATCH 130/262] add MSYS coverage --- .github/workflows/test-windows.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index d8ffd517200..3fe3cbc15c6 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -426,7 +426,6 @@ jobs: ${{ matrix.package }}-python3-olefile \ ${{ matrix.package }}-libjpeg-turbo \ ${{ matrix.package }}-libimagequant - python3 -m pip install -U pip codecov - name: Build Pillow run: | @@ -436,4 +435,14 @@ jobs: run: | python3 selftest.py --installed python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests - codecov --file coverage.xml --name MSYS2 ${{ matrix.mingw }} + + - name: After success + run: | + .ci/after_success.sh + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + file: ./coverage.xml + flags: GHA_Windows + name: MSYS2 ${{ matrix.mingw }} From a212225b379f1c6565ad494ee61b0d6016993be1 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 23 May 2020 20:06:09 +0200 Subject: [PATCH 131/262] add dependencies to MSYS --- .github/workflows/test-windows.yml | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 3fe3cbc15c6..7e52651bae7 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -412,24 +412,40 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up MSYS + - name: Set up shell run: echo ::add-path::C:\msys64\usr\bin\ shell: pwsh - - name: Install MinGW packages + - name: Install Dependencies run: | pacman -S --noconfirm \ ${{ matrix.package }}-python3-pip \ ${{ matrix.package }}-python3-setuptools \ ${{ matrix.package }}-python3-pytest \ ${{ matrix.package }}-python3-pytest-cov \ + ${{ matrix.package }}-python3-cffi \ ${{ matrix.package }}-python3-olefile \ + ${{ matrix.package }}-python3-numpy \ + ${{ matrix.package }}-python3-pyqt5 \ + ${{ matrix.package }}-python3-numpy \ + ${{ matrix.package }}-freetype \ + ${{ matrix.package }}-lcms2 \ + ${{ matrix.package }}-libwebp \ ${{ matrix.package }}-libjpeg-turbo \ - ${{ matrix.package }}-libimagequant + ${{ matrix.package }}-libimagequant \ + ${{ matrix.package }}-libraqm \ + ${{ matrix.package }}-ghostscript \ + subversion + + python3 -m pip install pyroma + + pushd depends && ./install_extra_test_images.sh && popd - name: Build Pillow run: | - python3 setup.py install + # libtiff is unable to open files + # OpenJPEG fails due to link error (#2848) + python3 setup.py build_ext --disable-tiff --disable-jpeg2000 install - name: Test Pillow run: | From d2f90d6cacaeb9465ba4d9bbb91ca74755ec5bfa Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 23 May 2020 21:24:11 +0200 Subject: [PATCH 132/262] fix freetype and raqm on MSYS --- Tests/helper.py | 6 ++++++ Tests/test_imagefont.py | 3 +++ Tests/test_imagefontctl.py | 2 +- src/_imagingft.c | 11 ++++++++--- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index a8ca85dc44f..ed89a78e1f5 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -288,6 +288,12 @@ def is_pypy(): return hasattr(sys, "pypy_translation_info") +def is_mingw(): + import sysconfig + + return sysconfig.get_platform() == "mingw" + + if sys.platform == "win32": IMCONVERT = os.environ.get("MAGICK_HOME", "") if IMCONVERT: diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0e642cde28e..bd79f08e39b 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -13,6 +13,7 @@ assert_image_equal, assert_image_similar, assert_image_similar_tofile, + is_mingw, is_pypy, is_win32, skip_unless_feature, @@ -660,6 +661,7 @@ def test_variation_get(self): {"name": b"Size", "minimum": 0, "maximum": 300, "default": 0} ] + @pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test") def test_variation_set_by_name(self): font = self.get_font() @@ -692,6 +694,7 @@ def _check_text(font, path, epsilon): font.set_variation_by_name(name) _check_text(font, "Tests/images/variation_tiny_name.png", 40) + @pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test") def test_variation_set_by_axes(self): font = self.get_font() diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 6d7a9c2f485..386dd3be63d 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -60,7 +60,7 @@ def test_complex_unicode_text(): target = "Tests/images/test_complex_unicode_text2.png" with Image.open(target) as target_img: - assert_image_similar(im, target_img, 2.3) + assert_image_similar(im, target_img, 2.33) def test_text_direction_rtl(): diff --git a/src/_imagingft.c b/src/_imagingft.c index a9b00431a4e..e6862ae01a9 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -31,7 +31,7 @@ #define KEEP_PY_UNICODE -#if !defined(_MSC_VER) +#ifndef _WIN32 #include #endif @@ -155,20 +155,25 @@ setraqm(void) p_raqm.raqm = NULL; /* Microsoft needs a totally different system */ -#if !defined(_MSC_VER) +#ifndef _WIN32 p_raqm.raqm = dlopen("libraqm.so.0", RTLD_LAZY); if (!p_raqm.raqm) { p_raqm.raqm = dlopen("libraqm.dylib", RTLD_LAZY); } #else p_raqm.raqm = LoadLibrary("libraqm"); + + /* Cygwin / MinGW */ + if (!p_raqm.raqm) { + p_raqm.raqm = LoadLibrary("libraqm-0"); + } #endif if (!p_raqm.raqm) { return 1; } -#if !defined(_MSC_VER) +#ifndef _WIN32 p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast"); p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create"); p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text"); From 82204d54cf6467107110e218f98b9fb725437a8f Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 24 May 2020 01:07:42 +0200 Subject: [PATCH 133/262] fix #2848 --- .github/workflows/test-windows.yml | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7e52651bae7..2ad2bf1f601 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -432,6 +432,7 @@ jobs: ${{ matrix.package }}-lcms2 \ ${{ matrix.package }}-libwebp \ ${{ matrix.package }}-libjpeg-turbo \ + ${{ matrix.package }}-openjpeg2 \ ${{ matrix.package }}-libimagequant \ ${{ matrix.package }}-libraqm \ ${{ matrix.package }}-ghostscript \ @@ -444,8 +445,7 @@ jobs: - name: Build Pillow run: | # libtiff is unable to open files - # OpenJPEG fails due to link error (#2848) - python3 setup.py build_ext --disable-tiff --disable-jpeg2000 install + python3 setup.py build_ext --disable-tiff install - name: Test Pillow run: | diff --git a/setup.py b/setup.py index 8c9f0213f10..b7982a5bac4 100755 --- a/setup.py +++ b/setup.py @@ -711,7 +711,7 @@ def build_extensions(self): if feature.jpeg2000: libs.append(feature.jpeg2000) defs.append(("HAVE_OPENJPEG", None)) - if sys.platform == "win32": + if sys.platform == "win32" and not PLATFORM_MINGW: defs.append(("OPJ_STATIC", None)) if feature.zlib: libs.append(feature.zlib) From c0f7a91dcdf0ae1b046d7d75d3d94328c709fd61 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 24 May 2020 03:25:27 +0200 Subject: [PATCH 134/262] add C coverage for MSYS --- .github/workflows/test-windows.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 2ad2bf1f601..2e172da76b9 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -445,20 +445,16 @@ jobs: - name: Build Pillow run: | # libtiff is unable to open files - python3 setup.py build_ext --disable-tiff install + CFLAGS="-coverage" python3 setup.py build_ext --disable-tiff install - name: Test Pillow run: | python3 selftest.py --installed python3 -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests - - name: After success - run: | - .ci/after_success.sh - - name: Upload coverage - uses: codecov/codecov-action@v1 - with: - file: ./coverage.xml - flags: GHA_Windows - name: MSYS2 ${{ matrix.mingw }} + run: | + python3 -m pip install codecov + bash <(curl -s https://codecov.io/bash) -F GHA_Windows + env: + CODECOV_NAME: MSYS2 ${{ matrix.mingw }} From 660894cd367b79e1280cea8fa67536416d1a9138 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 24 May 2020 23:58:30 +1000 Subject: [PATCH 135/262] Write JFIF header when saving JPEG --- Tests/test_file_jpeg.py | 20 +++++++++++--------- src/libImaging/JpegEncode.c | 1 + 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 08db11645a6..616bb4dd8e5 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -91,15 +91,17 @@ def test_cmyk(self): assert k > 0.9 def test_dpi(self): - def test(xdpi, ydpi=None): - with Image.open(TEST_FILE) as im: - im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) - return im.info.get("dpi") - - assert test(72) == (72, 72) - assert test(300) == (300, 300) - assert test(100, 200) == (100, 200) - assert test(0) is None # square pixels + for test_image_path in [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"]: + + def test(xdpi, ydpi=None): + with Image.open(test_image_path) as im: + im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + return im.info.get("dpi") + + assert test(72) == (72, 72) + assert test(300) == (300, 300) + assert test(100, 200) == (100, 200) + assert test(0) is None # square pixels def test_icc(self, tmp_path): # Test ICC support diff --git a/src/libImaging/JpegEncode.c b/src/libImaging/JpegEncode.c index 8882b61be11..b255025faab 100644 --- a/src/libImaging/JpegEncode.c +++ b/src/libImaging/JpegEncode.c @@ -222,6 +222,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) context->cinfo.smoothing_factor = context->smooth; context->cinfo.optimize_coding = (boolean) context->optimize; if (context->xdpi > 0 && context->ydpi > 0) { + context->cinfo.write_JFIF_header = TRUE; context->cinfo.density_unit = 1; /* dots per inch */ context->cinfo.X_density = context->xdpi; context->cinfo.Y_density = context->ydpi; From 66954ad17611b3bb26091a7bd6c8b28a9f91f603 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 25 May 2020 18:51:30 +0200 Subject: [PATCH 136/262] deprecate Image.show(command="...") --- Tests/test_image.py | 12 ++++++++++++ docs/deprecations.rst | 8 ++++++++ src/PIL/Image.py | 8 +++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 4d1b66dff61..f284f89a740 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -664,6 +664,18 @@ def test_overrun(self): except OSError as e: assert str(e) == "buffer overrun when reading image file" + def test_show_deprecation(self, monkeypatch): + monkeypatch.setattr(Image, "_show", lambda *args, **kwargs: None) + + im = Image.new("RGB", (50, 50), "white") + + with pytest.warns(None) as raised: + im.show() + assert not raised + + with pytest.warns(DeprecationWarning): + im.show(command="mock") + class MockEncoder: pass diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 203921c0b84..508b1242bf6 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,6 +12,14 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. +Image.show +~~~~~~~~~~ + +.. deprecated:: 7.2.0 + +The ``command`` parameter was deprecated and will be removed in a future release. +Use a subclass of ``ImageShow.Viewer`` instead. + ImageFile.raise_ioerror ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8c5fff8ed38..3d6da8b84bb 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2172,9 +2172,15 @@ def show(self, title=None, command=None): :param title: Optional title to use for the image window, where possible. - :param command: command used to show the image """ + if command is not None: + warnings.warn( + "The command parameter was deprecated and will be removed in a future" + "release. Use a subclass of ImageShow.Viewer instead.", + DeprecationWarning, + ) + _show(self, title=title, command=command) def split(self): From d54fee986342283a43e4a37719651b484c4b0ef3 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 25 May 2020 22:21:51 +0200 Subject: [PATCH 137/262] move import --- Tests/helper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index ed89a78e1f5..7e8abc9c9a8 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -6,6 +6,7 @@ import os import shutil import sys +import sysconfig import tempfile from io import BytesIO @@ -289,8 +290,6 @@ def is_pypy(): def is_mingw(): - import sysconfig - return sysconfig.get_platform() == "mingw" From 696aa7972df42dbd0408b79c3b5178d515845be1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 May 2020 07:15:20 +1000 Subject: [PATCH 138/262] Parametrized test --- Tests/test_file_jpeg.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 616bb4dd8e5..258908871c1 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -90,18 +90,19 @@ def test_cmyk(self): ] assert k > 0.9 - def test_dpi(self): - for test_image_path in [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"]: - - def test(xdpi, ydpi=None): - with Image.open(test_image_path) as im: - im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) - return im.info.get("dpi") - - assert test(72) == (72, 72) - assert test(300) == (300, 300) - assert test(100, 200) == (100, 200) - assert test(0) is None # square pixels + @pytest.mark.parametrize( + "test_image_path", [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"], + ) + def test_dpi(self, test_image_path): + def test(xdpi, ydpi=None): + with Image.open(test_image_path) as im: + im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + return im.info.get("dpi") + + assert test(72) == (72, 72) + assert test(300) == (300, 300) + assert test(100, 200) == (100, 200) + assert test(0) is None # square pixels def test_icc(self, tmp_path): # Test ICC support From a78de17346da722bc015cf35af0189adcdc0e2b9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 May 2020 07:32:06 +1000 Subject: [PATCH 139/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 833d043575b..2716b912804 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Fixed ZeroDivisionError in Image.thumbnail #4625 + [radarhere] + +- Replaced TiffImagePlugin DEBUG with logging #4550 + [radarhere] + - Fix repeatedly loading .gbr #4620 [ElinksFr, radarhere] From adde69300263496e4aa425927c76c434ddd16efb Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 26 May 2020 17:23:27 +0200 Subject: [PATCH 140/262] enable -e -o pipefail in MSYS tests --- .github/workflows/test-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 66ec957df2f..7ae26b8834c 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -170,7 +170,7 @@ jobs: defaults: run: - shell: bash.exe --login "{0}" + shell: bash.exe --login -eo pipefail "{0}" env: MSYSTEM: ${{ matrix.mingw }} CHERE_INVOKING: 1 From 7b8805fceee461d3ba13f3dc0177e24ffa374bcc Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 27 May 2020 06:32:22 +0200 Subject: [PATCH 141/262] do not render glyphs during text layout --- src/_imagingft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_imagingft.c b/src/_imagingft.c index a9b00431a4e..9fe189c5f3b 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -553,7 +553,7 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObje return 0; } - load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP; + load_flags = FT_LOAD_NO_BITMAP; if (mask) { load_flags |= FT_LOAD_TARGET_MONO; } From 9067e68e64dbb04fe3fd920ee29d60d40ec26f7f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 May 2020 22:43:06 +1000 Subject: [PATCH 142/262] Corrected undefined behaviour --- src/libImaging/QuantOctree.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libImaging/QuantOctree.c b/src/libImaging/QuantOctree.c index fa45ae707b0..e1205acc3ba 100644 --- a/src/libImaging/QuantOctree.c +++ b/src/libImaging/QuantOctree.c @@ -28,6 +28,7 @@ #include #include +#include "ImagingUtils.h" #include "QuantOctree.h" typedef struct _ColorBucket{ @@ -152,10 +153,10 @@ static void avg_color_from_color_bucket(const ColorBucket bucket, Pixel *dst) { float count = bucket->count; if (count != 0) { - dst->c.r = (int)(bucket->r / count); - dst->c.g = (int)(bucket->g / count); - dst->c.b = (int)(bucket->b / count); - dst->c.a = (int)(bucket->a / count); + dst->c.r = CLIP8((int)(bucket->r / count)); + dst->c.g = CLIP8((int)(bucket->g / count)); + dst->c.b = CLIP8((int)(bucket->b / count)); + dst->c.a = CLIP8((int)(bucket->a / count)); } else { dst->c.r = 0; dst->c.g = 0; From d2e23e386b7be9775aba20471067caac0f217d46 Mon Sep 17 00:00:00 2001 From: nulano Date: Thu, 28 May 2020 12:07:53 +0100 Subject: [PATCH 143/262] simplify code Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageGrab.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index cb685b1ede9..d16dd08f643 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -100,10 +100,9 @@ def grabclipboard(): o = struct.unpack_from("I", data)[0] if data[16] != 0: files = data[o:].decode("utf-16le").split("\0") - return files[: files.index("")] else: files = data[o:].decode("mbcs").split("\0") - return files[: files.index("")] + return files[: files.index("")] if isinstance(data, bytes): import io From 9fa7ee6ee8d15ca5c808752cefae553c876a0811 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 29 May 2020 08:16:43 +1000 Subject: [PATCH 144/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2716b912804..62fccfe8fa9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Speed up text layout by not rendering glyphs #4652 + [nulano] + - Fixed ZeroDivisionError in Image.thumbnail #4625 [radarhere] From df1859437975f22673a48c0bfff1603a34448362 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Jun 2020 20:27:46 +1000 Subject: [PATCH 145/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 62fccfe8fa9..629ba722116 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Parse orientation from XMP tags #4560 + [radarhere] + - Speed up text layout by not rendering glyphs #4652 [nulano] From 3c7c227b3a0e6c13d704524e7a47e6d6e0feccc0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Jun 2020 21:59:05 +1000 Subject: [PATCH 146/262] Installed libxcb-xinerama0 to fix PyQt5 --- .ci/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 3a82216596d..8a3b8fee48d 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -34,7 +34,7 @@ if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then # arm64, ppc64le, s390x CPUs: # "ERROR: Could not find a version that satisfies the requirement pyqt5" if [[ $TRAVIS_CPU_ARCH == "amd64" ]]; then - sudo apt-get -qq install pyqt5-dev-tools + sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools pip install pyqt5 fi fi From 704892d08d47d804dbbc1ce9e7d8aef7ec8491fd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Jun 2020 22:29:25 +1000 Subject: [PATCH 147/262] Updated lcms2 to 2.10 --- 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 e42f471908d..1b6c9083aa5 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -195,9 +195,9 @@ def cmd_msbuild( # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.9/lcms2-2.9.tar.gz", - "filename": "lcms2-2.9.tar.gz", - "dir": "lcms2-2.9", + "url": SF_MIRROR + "/project/lcms/lcms/2.10/lcms2-2.10.tar.gz", + "filename": "lcms2-2.10.tar.gz", + "dir": "lcms2-2.10", "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always From 9fbd35fe87e16b9bded90b5c4682cfc86f5fe4d6 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 27 May 2020 23:21:32 +0200 Subject: [PATCH 148/262] use mode for getsize --- src/PIL/ImageFont.py | 6 ++++-- src/_imagingft.c | 12 +++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 25ceaa16aeb..98b6ab66ee0 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -259,7 +259,7 @@ def getsize( :return: (width, height) """ - size, offset = self.font.getsize(text, direction, features, language) + size, offset = self.font.getsize(text, False, direction, features, language) return ( size[0] + stroke_width * 2 + offset[0], size[1] + stroke_width * 2 + offset[1], @@ -468,7 +468,9 @@ def getmask2( :py:mod:`PIL.Image.core` interface module, and the text offset, the gap between the starting coordinate and the first marking """ - size, offset = self.font.getsize(text, direction, features, language) + size, offset = self.font.getsize( + text, mode == "1", direction, features, language + ) size = size[0] + stroke_width * 2, size[1] + stroke_width * 2 im = fill("L", size, 0) self.font.render( diff --git a/src/_imagingft.c b/src/_imagingft.c index 9fe189c5f3b..4f5456373e5 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -605,6 +605,8 @@ font_getsize(FontObject* self, PyObject* args) FT_Face face; int xoffset, yoffset; int horizontal_dir; + int mask = 0; + int load_flags; const char *dir = NULL; const char *lang = NULL; size_t i, count; @@ -614,11 +616,11 @@ font_getsize(FontObject* self, PyObject* args) /* calculate size and bearing for a given string */ PyObject* string; - if (!PyArg_ParseTuple(args, "O|zOz:getsize", &string, &dir, &features, &lang)) { + if (!PyArg_ParseTuple(args, "O|izOz:getsize", &string, &mask, &dir, &features, &lang)) { return NULL; } - count = text_layout(string, self, dir, features, lang, &glyph_info, 0); + count = text_layout(string, self, dir, features, lang, &glyph_info, mask); if (PyErr_Occurred()) { return NULL; } @@ -637,7 +639,11 @@ font_getsize(FontObject* self, PyObject* args) /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 * Yifu Yu, 2014-10-15 */ - error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP); + load_flags = FT_LOAD_NO_BITMAP; + if (mask) { + load_flags |= FT_LOAD_TARGET_MONO; + } + error = FT_Load_Glyph(face, index, load_flags); if (error) { return geterror(error); } From 2dd9324df210a60f663cd3c4d3795ee2457babc0 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 1 Jun 2020 19:21:40 +0200 Subject: [PATCH 149/262] add mono color text test --- Tests/images/text_mono.gif | Bin 0 -> 1560 bytes Tests/test_imagefont.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 Tests/images/text_mono.gif diff --git a/Tests/images/text_mono.gif b/Tests/images/text_mono.gif new file mode 100644 index 0000000000000000000000000000000000000000..b350c10e64a2f14c9220af9d49eca19a955431c5 GIT binary patch literal 1560 zcmXYwcRbZ!9LCQ@+`@HB%679uh-~E=Wy?tRDkCE!Nujz$5kgT((#W1_nk(MhphS#KeTfVvim@dhFOSW@hH&$B(nHu$(w?;^fJb ztgNhTY;5f8>>L~%oSd9oTwL7T+&nxyI2`WOsZ+eXynK9o{QUd^0s?}9f~TNIdU|?!d3k$#`}p|Wx^>If*Z21A+kSq2ckbNr_xHbh_wK!W_W}X}0s{ksf`abf zzyILDgNF|v5(tDxj~+dK{5Uu`I3y(G$&)8fpFRx@4Gjwmd-m*EczAe3L_}m{@$%)%*x1;(xVTrZUcG+(`pug+@$vC*-@Z*qNOC>mrpFgLkr)OkjWM*b&Wo2b&XXoVPDz7Yinz7Z|~^n=gww5 z?*8%PM^8^rZ*Om3UtfQJ|G>b&;NalU(9rPk@W{x>&!0a>M@PrT#>U6T$z<}cU%w_M zCMG8*fB*hXp-`r#rlzN-XJ%$*XJ_Z;=H}<;7Zw&47Z;b7mX?>7S5{V5S6Bc1`LnjR zw!Xf;v9Ynaxw*BqwY|N)v$ON}@88|s-MziN{r&xa|Ngh+AeE?8sPOUs* zbugM)#JVZ(V{IswPbX0;Ke;}Fm5mk+h$)Q#1;RMlaDWgiOicqw1x^G2uC*{=0ipdJ ziV6%iOXC5+2P4Dj-EdggrllyO=d)po&U>BW%)Tu1LbuTt7I)~X31@_^p+e{~Zz5bJ z_N1F++2$w`<_7?viPz@<1W@Wr4Y0%#FZjLDJQ#o{Og5nanpB|x04iCR4&VqI^Kf{u zgNv}WOsr}x*Bn<{uC8>tM6Z=6)82YFH3{9H2 z7|AB%j7XX>P{LVALFu$9B2ijlpSDzZWgt-jlDm3bAUOmB%ae>!5zcV$tVz02nn9Xn z-<)Z-MX@JkxNqJp&!Ij_z^Z@2qR6!`*J-4G(W=ykq6S@fPys`u0ghw`i_riqNfP#< z0_ZMQ7!U<$hzzhQlssqw4#q%fz*~U5Kn5(2*a!c&-vniT%TuEqct8dr^h}1F`=r%L zR2;Q^hcd|*bf?e}F{JVhFog0L=O!tE6*6@sond7VSO>jXOkLch!0lB^XEYBiwzq>6 z8&@I0^;j`hxGdxdJRz?*z#M|c@LoVeB#bySa~1~T)r9v#=5L7;G0Q}xws Date: Mon, 1 Jun 2020 10:16:30 +0300 Subject: [PATCH 150/262] Update ImageDraw docs formatting and links --- docs/reference/ImageDraw.rst | 72 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 3e453dd6f35..967437568fb 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -81,13 +81,13 @@ Example: Draw Partial Opacity Text from PIL import Image, ImageDraw, ImageFont # get an image - base = Image.open('Pillow/Tests/images/hopper.png').convert('RGBA') + base = Image.open("Pillow/Tests/images/hopper.png").convert("RGBA") # make a blank image for the text, initialized to transparent text color - txt = Image.new('RGBA', base.size, (255,255,255,0)) + txt = Image.new("RGBA", base.size, (255,255,255,0)) # get a font - fnt = ImageFont.truetype('Pillow/Tests/fonts/FreeMono.ttf', 40) + fnt = ImageFont.truetype("Pillow/Tests/fonts/FreeMono.ttf", 40) # get a drawing context d = ImageDraw.Draw(txt) @@ -280,15 +280,15 @@ Methods :param xy: Top left corner of the text. :param text: Text to be drawn. If it contains any newline characters, - the text is passed on to multiline_text() + the text is passed on to ``multiline_text()`` :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. - :param spacing: If the text is passed on to multiline_text(), + :param spacing: If the text is passed on to ``multiline_text()``, the number of pixels between lines. - :param align: If the text is passed on to multiline_text(), + :param align: If the text is passed on to ``multiline_text()``, "left", "center" or "right". - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). + :param direction: Direction of the text. It can be ``"rtl"`` (right to + left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). Requires libraqm. .. versionadded:: 4.2.0 @@ -296,9 +296,9 @@ Methods :param features: A list of OpenType font features to be used during text layout. This is usually used to turn on optional font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' + for example ``"dlig"`` or ``"ss01"``, but can be also + used to turn off default font features, for + example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported features, see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist @@ -310,8 +310,7 @@ Methods different glyph shapes or ligatures. This parameter tells the font which language the text is in, and to apply the correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - ` + It should be a `BCP 47 language code`_. Requires libraqm. .. versionadded:: 6.0.0 @@ -334,9 +333,9 @@ Methods :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: The number of pixels between lines. - :param align: "left", "center" or "right". - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). + :param align: ``"left"``, ``"center"`` or ``"right"``. + :param direction: Direction of the text. It can be ``"rtl"`` (right to + left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). Requires libraqm. .. versionadded:: 4.2.0 @@ -344,9 +343,9 @@ Methods :param features: A list of OpenType font features to be used during text layout. This is usually used to turn on optional font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' + for example ``"dlig"`` or ``"ss01"``, but can be also + used to turn off default font features, for + example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported features, see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist @@ -358,8 +357,7 @@ Methods different glyph shapes or ligatures. This parameter tells the font which language the text is in, and to apply the correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - ` + It should be a `BCP 47 language code`_. Requires libraqm. .. versionadded:: 6.0.0 @@ -369,21 +367,21 @@ Methods Return the size of the given string, in pixels. :param text: Text to be measured. If it contains any newline characters, - the text is passed on to multiline_textsize() + the text is passed on to ``multiline_textsize()`` :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. - :param spacing: If the text is passed on to multiline_textsize(), + :param spacing: If the text is passed on to ``multiline_textsize()``, the number of pixels between lines. - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). + :param direction: Direction of the text. It can be ``"rtl"`` (right to + left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). Requires libraqm. .. versionadded:: 4.2.0 :param features: A list of OpenType font features to be used during text layout. This is usually used to turn on optional font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' + for example ``"dlig"`` or ``"ss01"``, but can be also + used to turn off default font features, for + example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported features, see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist @@ -394,8 +392,7 @@ Methods different glyph shapes or ligatures. This parameter tells the font which language the text is in, and to apply the correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - ` + It should be a `BCP 47 language code`_. Requires libraqm. .. versionadded:: 6.0.0 @@ -411,8 +408,8 @@ Methods :param text: Text to be measured. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: The number of pixels between lines. - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). + :param direction: Direction of the text. It can be ``"rtl"`` (right to + left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). Requires libraqm. .. versionadded:: 4.2.0 @@ -420,9 +417,9 @@ Methods :param features: A list of OpenType font features to be used during text layout. This is usually used to turn on optional font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' + for example ``"dlig"`` or ``"ss01"``, but can be also + used to turn off default font features, for + example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported features, see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist @@ -434,8 +431,7 @@ Methods different glyph shapes or ligatures. This parameter tells the font which language the text is in, and to apply the correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - ` + It should be a `BCP 47 language code`_. Requires libraqm. .. versionadded:: 6.0.0 @@ -472,3 +468,5 @@ Methods tolerable difference of a pixel value from the 'background' in order for it to be replaced. Useful for filling regions of non- homogeneous, but similar, colors. + +.. _BCP 47 language code: https://www.w3.org/International/articles/language-tags/ From 087e42f7432e5cb697de569ad64b5ca3f778b93d Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 2 Jun 2020 21:28:34 +0300 Subject: [PATCH 151/262] Move OpenType docs link to a reference --- docs/reference/ImageDraw.rst | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 967437568fb..7bfe884a0fa 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -300,8 +300,7 @@ Methods used to turn off default font features, for example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported - features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + features, see `OpenType docs`_. Requires libraqm. .. versionadded:: 4.2.0 @@ -347,8 +346,7 @@ Methods used to turn off default font features, for example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported - features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + features, see `OpenType docs`_. Requires libraqm. .. versionadded:: 4.2.0 @@ -383,8 +381,7 @@ Methods used to turn off default font features, for example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported - features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + features, see `OpenType docs`_. Requires libraqm. .. versionadded:: 4.2.0 @@ -421,8 +418,7 @@ Methods used to turn off default font features, for example ``"-liga"`` to disable ligatures or ``"-kern"`` to disable kerning. To get all supported - features, see - https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist + features, see `OpenType docs`_. Requires libraqm. .. versionadded:: 4.2.0 @@ -470,3 +466,4 @@ Methods homogeneous, but similar, colors. .. _BCP 47 language code: https://www.w3.org/International/articles/language-tags/ +.. _OpenType docs: https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist From 87de783226f5efa9d1b437dee79e49ec9de2ce93 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 2 Jun 2020 21:48:53 +0300 Subject: [PATCH 152/262] Replace simple code formatting with :py:meth: markup --- docs/reference/ImageDraw.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 7bfe884a0fa..ff65f88aaae 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -280,12 +280,15 @@ Methods :param xy: Top left corner of the text. :param text: Text to be drawn. If it contains any newline characters, - the text is passed on to ``multiline_text()`` + the text is passed on to + :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text` :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. - :param spacing: If the text is passed on to ``multiline_text()``, + :param spacing: If the text is passed on to + :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text`, the number of pixels between lines. - :param align: If the text is passed on to ``multiline_text()``, + :param align: If the text is passed on to + :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text`, "left", "center" or "right". :param direction: Direction of the text. It can be ``"rtl"`` (right to left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). @@ -365,9 +368,10 @@ Methods Return the size of the given string, in pixels. :param text: Text to be measured. If it contains any newline characters, - the text is passed on to ``multiline_textsize()`` + the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.size` :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. - :param spacing: If the text is passed on to ``multiline_textsize()``, + :param spacing: If the text is passed on to + :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`, the number of pixels between lines. :param direction: Direction of the text. It can be ``"rtl"`` (right to left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). From 14302aa53d73d55b4bfcc32a68b5898dd7bffe50 Mon Sep 17 00:00:00 2001 From: Hugo Date: Tue, 2 Jun 2020 22:14:36 +0300 Subject: [PATCH 153/262] Fix parameter formatting --- docs/reference/ImageDraw.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index ff65f88aaae..6f1d093f92a 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -212,8 +212,8 @@ Methods .. versionadded:: 1.1.5 .. note:: This option was broken until version 1.1.6. - :param joint: Joint type between a sequence of lines. It can be "curve", - for rounded edges, or None. + :param joint: Joint type between a sequence of lines. It can be "curve", for rounded + edges, or ``None``. .. versionadded:: 5.3.0 @@ -281,7 +281,7 @@ Methods :param xy: Top left corner of the text. :param text: Text to be drawn. If it contains any newline characters, the text is passed on to - :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text` + :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text`. :param fill: Color to use for the text. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: If the text is passed on to @@ -322,9 +322,9 @@ Methods .. versionadded:: 6.2.0 :param stroke_fill: Color to use for the text stroke. If not given, will default to - the ``fill`` parameter. + the ``fill`` parameter. - .. versionadded:: 6.2.0 + .. versionadded:: 6.2.0 .. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None) @@ -368,7 +368,7 @@ Methods Return the size of the given string, in pixels. :param text: Text to be measured. If it contains any newline characters, - the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.size` + the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.size`. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: If the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`, From 95f3f359b7a0fa34a1490fa74f0e8bd881e2e392 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 3 Jun 2020 11:23:24 +0300 Subject: [PATCH 154/262] Fix name Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/reference/ImageDraw.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 6f1d093f92a..adfb46bcfc0 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -368,7 +368,7 @@ Methods Return the size of the given string, in pixels. :param text: Text to be measured. If it contains any newline characters, - the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.size`. + the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`. :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param spacing: If the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_textsize`, From ccac9e1a3a08886827af0dc443396af760c5cbe6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 May 2020 21:12:09 +1000 Subject: [PATCH 155/262] Changed to ImageFileDirectory_v2 --- Tests/test_file_jpeg.py | 23 ++++++++++++++++++----- src/PIL/Image.py | 6 +++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 08db11645a6..afca875de7e 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -219,7 +219,7 @@ def test_exif_gps(self): gps_index = 34853 expected_exif_gps = { 0: b"\x00\x00\x00\x01", - 2: (4294967295, 1), + 2: 4294967295, 5: b"\x01", 30: 65535, 29: "1999:99:99 99:99:99", @@ -241,7 +241,7 @@ def test_exif_rollback(self): 36867: "2099:09:29 10:10:10", 34853: { 0: b"\x00\x00\x00\x01", - 2: (4294967295, 1), + 2: 4294967295, 5: b"\x01", 30: 65535, 29: "1999:99:99 99:99:99", @@ -253,11 +253,11 @@ def test_exif_rollback(self): 271: "Make", 272: "XXX-XXX", 305: "PIL", - 42034: ((1, 1), (1, 1), (1, 1), (1, 1)), + 42034: (1, 1, 1, 1), 42035: "LensMake", 34856: b"\xaa\xaa\xaa\xaa\xaa\xaa", - 282: (4294967295, 1), - 33434: (4294967295, 1), + 282: 4294967295, + 33434: 4294967295, } with Image.open("Tests/images/exif_gps.jpg") as im: @@ -647,6 +647,19 @@ def test_invalid_exif(self): # OSError for unidentified image. assert im.info.get("dpi") == (72, 72) + def test_exif_x_resolution(self, tmp_path): + with Image.open("Tests/images/flower.jpg") as im: + exif = im.getexif() + assert exif[282] == 180 + + out = str(tmp_path / "out.jpg") + with pytest.warns(None) as record: + im.save(out, exif=exif) + assert len(record) == 0 + + with Image.open(out) as reloaded: + assert reloaded.getexif()[282] == 180 + def test_invalid_exif_x_resolution(self): # When no x or y resolution is defined in EXIF with Image.open("Tests/images/invalid-exif-without-x-resolution.jpg") as im: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9804f32586e..4475fdf0faa 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3248,7 +3248,7 @@ def __init__(self): def _fixup(self, value): try: - if len(value) == 1 and not isinstance(value, dict): + if len(value) == 1 and isinstance(value, tuple): return value[0] except Exception: pass @@ -3269,7 +3269,7 @@ def _get_ifd_dict(self, tag): else: from . import TiffImagePlugin - info = TiffImagePlugin.ImageFileDirectory_v1(self.head) + info = TiffImagePlugin.ImageFileDirectory_v2(self.head) info.load(self.fp) return self._fixup_dict(info) @@ -3294,7 +3294,7 @@ def load(self, data): # process dictionary from . import TiffImagePlugin - self._info = TiffImagePlugin.ImageFileDirectory_v1(self.head) + self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) self.endian = self._info._endian self.fp.seek(self._info.next) self._info.load(self.fp) From a5ef2b449994bd5a306898c6ceb095543a8e00ae Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Jun 2020 07:56:14 +1000 Subject: [PATCH 156/262] Parametrized test --- Tests/test_imagedraw.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 5aa9599ec93..7ecec48d350 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -873,8 +873,9 @@ def test_wide_line_dot(): assert_image_similar(im, Image.open(expected), 1) -def test_line_joint(): - for xy in [ +@pytest.mark.parametrize( + "xy", + [ [ (400, 280), (380, 280), @@ -951,16 +952,18 @@ def test_line_joint(): 250, 100, ], - ]: - im = Image.new("RGB", (500, 325)) - draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_line_joint_curve.png" + ], +) +def test_line_joint(xy): + im = Image.new("RGB", (500, 325)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_line_joint_curve.png" - # Act - draw.line(xy, GRAY, 50, "curve") + # Act + draw.line(xy, GRAY, 50, "curve") - # Assert - assert_image_similar(im, Image.open(expected), 3) + # Assert + assert_image_similar(im, Image.open(expected), 3) def test_textsize_empty_string(): From 57de57a384ee8c6b2db69512b8f50a7723b50f62 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Jun 2020 16:57:57 +1000 Subject: [PATCH 157/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 629ba722116..e2de9b8ca66 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Added support for 1-D NumPy arrays #4608 + [radarhere] + - Parse orientation from XMP tags #4560 [radarhere] From cda682efc41a56bfef25b681edf5a0d0c713ac73 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 4 Jun 2020 11:43:24 +0300 Subject: [PATCH 158/262] Parameter values in code formatting --- docs/reference/ImageDraw.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index adfb46bcfc0..1a0dd3b6878 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -212,8 +212,8 @@ Methods .. versionadded:: 1.1.5 .. note:: This option was broken until version 1.1.6. - :param joint: Joint type between a sequence of lines. It can be "curve", for rounded - edges, or ``None``. + :param joint: Joint type between a sequence of lines. It can be ``"curve"``, + for rounded edges, or ``None``. .. versionadded:: 5.3.0 @@ -289,7 +289,7 @@ Methods the number of pixels between lines. :param align: If the text is passed on to :py:meth:`~PIL.ImageDraw.ImageDraw.multiline_text`, - "left", "center" or "right". + ``"left"``, ``"center"`` or ``"right"``. :param direction: Direction of the text. It can be ``"rtl"`` (right to left), ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). Requires libraqm. From 5c3ae974cd4c6be886a827b9bf578ccfda1a6580 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Jun 2020 12:08:29 +1000 Subject: [PATCH 159/262] Updated flake8 to 3.8.2 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3939a0035f..83cbbbe780c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: types: [] - repo: https://gitlab.com/pycqa/flake8 - rev: 3.7.9 + rev: 3.8.2 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] From e228cfcec775d3855e5c8f05f82cf103efcc0875 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Jun 2020 12:07:57 +1000 Subject: [PATCH 160/262] Lint fixes --- Tests/test_font_pcf.py | 4 +- Tests/test_font_pcf_charsets.py | 4 +- Tests/test_image_filter.py | 4 +- Tests/test_imagecms.py | 4 +- src/PIL/JpegPresets.py | 80 ++++++++++++++++----------------- src/PIL/PalmImagePlugin.py | 52 ++++++++++----------- 6 files changed, 74 insertions(+), 74 deletions(-) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index afd0c38b2e6..e1000a8a29c 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -68,8 +68,8 @@ def test_textsize(request, tmp_path): (dx, dy) = font.getsize(chr(i)) assert dy == 20 assert dx in (0, 10) - for l in range(len(message)): - msg = message[: l + 1] + for j in range(len(message)): + msg = message[: j + 1] assert font.getsize(msg) == (len(msg) * 10, 20) diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index 8621f18aee4..8bedccbe837 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -103,8 +103,8 @@ def _test_textsize(request, tmp_path, encoding): assert dy == 20 assert dx in (0, 10) message = charsets[encoding]["message"].encode(encoding) - for l in range(len(message)): - msg = message[: l + 1] + for j in range(len(message)): + msg = message[: j + 1] assert font.getsize(msg) == (len(msg) * 10, 20) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index 42f4f448d01..a3cef5e6249 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -117,7 +117,7 @@ def test_consistency_3x3(): # fmt: off (-1, -1, 0, -1, 0, 1, - 0, 1, 1), + 0, 1, 1), # fmt: on 0.3, ) @@ -141,7 +141,7 @@ def test_consistency_5x5(): -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, -1, 0, 1, 1, 1, - 0, 1, 1, 1, 1), + 0, 1, 1, 1, 1), # fmt: on 0.3, ) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index ac34a81064e..0ce0455a16e 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -486,8 +486,8 @@ def create_test_image(): # fmt: off nine_grid_deltas = [ # noqa: E131 (-1, -1), (-1, 0), (-1, 1), - (0, -1), (0, 0), (0, 1), - (1, -1), (1, 0), (1, 1), + (0, -1), (0, 0), (0, 1), + (1, -1), (1, 0), (1, 1), ] # fmt: on chans = [] diff --git a/src/PIL/JpegPresets.py b/src/PIL/JpegPresets.py index 012bf81b01c..93c26b20536 100644 --- a/src/PIL/JpegPresets.py +++ b/src/PIL/JpegPresets.py @@ -112,16 +112,16 @@ ]}, 'web_high': {'subsampling': 0, # "4:4:4" 'quantization': [ - [6, 4, 4, 6, 9, 11, 12, 16, - 4, 5, 5, 6, 8, 10, 12, 12, - 4, 5, 5, 6, 10, 12, 14, 19, - 6, 6, 6, 11, 12, 15, 19, 28, - 9, 8, 10, 12, 16, 20, 27, 31, + [6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 14, 19, + 6, 6, 6, 11, 12, 15, 19, 28, + 9, 8, 10, 12, 16, 20, 27, 31, 11, 10, 12, 15, 20, 27, 31, 31, 12, 12, 14, 19, 27, 31, 31, 31, 16, 12, 19, 28, 31, 31, 31, 31], - [7, 7, 13, 24, 26, 31, 31, 31, - 7, 12, 16, 21, 31, 31, 31, 31, + [7, 7, 13, 24, 26, 31, 31, 31, + 7, 12, 16, 21, 31, 31, 31, 31, 13, 16, 17, 31, 31, 31, 31, 31, 24, 21, 31, 31, 31, 31, 31, 31, 26, 31, 31, 31, 31, 31, 31, 31, @@ -131,18 +131,18 @@ ]}, 'web_very_high': {'subsampling': 0, # "4:4:4" 'quantization': [ - [2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 4, 5, 7, 9, - 2, 2, 2, 4, 5, 7, 9, 12, - 3, 3, 4, 5, 8, 10, 12, 12, - 4, 4, 5, 7, 10, 12, 12, 12, - 5, 5, 7, 9, 12, 12, 12, 12, - 6, 6, 9, 12, 12, 12, 12, 12], - [3, 3, 5, 9, 13, 15, 15, 15, - 3, 4, 6, 11, 14, 12, 12, 12, - 5, 6, 9, 14, 12, 12, 12, 12, - 9, 11, 14, 12, 12, 12, 12, 12, + [2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 11, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 11, 14, 12, 12, 12, 12, 12, 13, 14, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, @@ -189,8 +189,8 @@ 'medium': {'subsampling': 2, # "4:2:0" 'quantization': [ [12, 8, 8, 12, 17, 21, 24, 17, - 8, 9, 9, 11, 15, 19, 12, 12, - 8, 9, 10, 12, 19, 12, 12, 12, + 8, 9, 9, 11, 15, 19, 12, 12, + 8, 9, 10, 12, 19, 12, 12, 12, 12, 11, 12, 21, 12, 12, 12, 12, 17, 15, 19, 12, 12, 12, 12, 12, 21, 19, 12, 12, 12, 12, 12, 12, @@ -207,16 +207,16 @@ ]}, 'high': {'subsampling': 0, # "4:4:4" 'quantization': [ - [6, 4, 4, 6, 9, 11, 12, 16, - 4, 5, 5, 6, 8, 10, 12, 12, - 4, 5, 5, 6, 10, 12, 12, 12, - 6, 6, 6, 11, 12, 12, 12, 12, - 9, 8, 10, 12, 12, 12, 12, 12, + [6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 12, 12, + 6, 6, 6, 11, 12, 12, 12, 12, + 9, 8, 10, 12, 12, 12, 12, 12, 11, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, 12, 12, 12, 12, 12, 12, 12], - [7, 7, 13, 24, 20, 20, 17, 17, - 7, 12, 16, 14, 14, 12, 12, 12, + [7, 7, 13, 24, 20, 20, 17, 17, + 7, 12, 16, 14, 14, 12, 12, 12, 13, 16, 14, 14, 12, 12, 12, 12, 24, 14, 14, 12, 12, 12, 12, 12, 20, 14, 12, 12, 12, 12, 12, 12, @@ -226,18 +226,18 @@ ]}, 'maximum': {'subsampling': 0, # "4:4:4" 'quantization': [ - [2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 4, 5, 7, 9, - 2, 2, 2, 4, 5, 7, 9, 12, - 3, 3, 4, 5, 8, 10, 12, 12, - 4, 4, 5, 7, 10, 12, 12, 12, - 5, 5, 7, 9, 12, 12, 12, 12, - 6, 6, 9, 12, 12, 12, 12, 12], - [3, 3, 5, 9, 13, 15, 15, 15, - 3, 4, 6, 10, 14, 12, 12, 12, - 5, 6, 9, 14, 12, 12, 12, 12, - 9, 10, 14, 12, 12, 12, 12, 12, + [2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 10, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 10, 14, 12, 12, 12, 12, 12, 13, 14, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, 15, 12, 12, 12, 12, 12, 12, 12, diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index 804ece34a6e..c8703d9ec11 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -30,15 +30,15 @@ (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), - (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), - (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), - (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), - (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), - (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), - (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), - (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), - (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), - (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), + (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), + (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), + (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), + (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), + (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), + (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), + (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), + (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), + (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), @@ -57,25 +57,25 @@ (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), - (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), - (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), - (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), - (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), - (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), - (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), - (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), - (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), - (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), - (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), + (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), + (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), + (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), + (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), + (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), + (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), + (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), + (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), + (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), + (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), - (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) + (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), + (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) # fmt: on From ec9015bea4a2dc8db32cc6c2e0e36fbf8d79abb1 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 6 Jun 2020 19:42:24 +1000 Subject: [PATCH 161/262] Renamed variable Co-authored-by: Hugo van Kemenade --- Tests/test_font_pcf.py | 4 ++-- Tests/test_font_pcf_charsets.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index e1000a8a29c..a6016371367 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -68,8 +68,8 @@ def test_textsize(request, tmp_path): (dx, dy) = font.getsize(chr(i)) assert dy == 20 assert dx in (0, 10) - for j in range(len(message)): - msg = message[: j + 1] + for i in range(len(message)): + msg = message[: i + 1] assert font.getsize(msg) == (len(msg) * 10, 20) diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index 8bedccbe837..4a39803be5d 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -103,8 +103,8 @@ def _test_textsize(request, tmp_path, encoding): assert dy == 20 assert dx in (0, 10) message = charsets[encoding]["message"].encode(encoding) - for j in range(len(message)): - msg = message[: j + 1] + for i in range(len(message)): + msg = message[: i + 1] assert font.getsize(msg) == (len(msg) * 10, 20) From 68ad4c0658781b167176339149f7d4bbca1a53e9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 6 Jun 2020 13:13:11 +0300 Subject: [PATCH 162/262] Add yesqa, put writing hooks first, autoupdate --- .pre-commit-config.yaml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83cbbbe780c..01a99784f2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,17 +8,28 @@ repos: files: \.py$ types: [] + - repo: https://github.com/timothycrosley/isort + rev: 4.3.21 + hooks: + - id: isort + + - repo: https://github.com/asottile/yesqa + rev: v1.1.1 + hooks: + - id: yesqa + + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.1.7 + hooks: + - id: remove-tabs + exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) + - repo: https://gitlab.com/pycqa/flake8 rev: 3.8.2 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] - - repo: https://github.com/timothycrosley/isort - rev: 4.3.21 - hooks: - - id: isort - - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.5.1 hooks: @@ -26,13 +37,7 @@ repos: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.5.0 + rev: v3.1.0 hooks: - id: check-merge-conflict - id: check-yaml - - - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.7 - hooks: - - id: remove-tabs - exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) From c8e3757e7277cb9b06139dedc6d967f0eed4a121 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 6 Jun 2020 13:15:17 +0300 Subject: [PATCH 163/262] Run yesqa to remove redundant noqa --- Tests/test_image_filter.py | 4 ++-- Tests/test_imagecms.py | 2 +- src/PIL/JpegPresets.py | 2 +- src/PIL/PalmImagePlugin.py | 2 +- winbuild/build_prepare.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index a3cef5e6249..ed71ea968b1 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -112,7 +112,7 @@ def test_kernel_not_enough_coefficients(): def test_consistency_3x3(): with Image.open("Tests/images/hopper.bmp") as source: with Image.open("Tests/images/hopper_emboss.bmp") as reference: - kernel = ImageFilter.Kernel( # noqa: E127 + kernel = ImageFilter.Kernel( (3, 3), # fmt: off (-1, -1, 0, @@ -134,7 +134,7 @@ def test_consistency_3x3(): def test_consistency_5x5(): with Image.open("Tests/images/hopper.bmp") as source: with Image.open("Tests/images/hopper_emboss_more.bmp") as reference: - kernel = ImageFilter.Kernel( # noqa: E127 + kernel = ImageFilter.Kernel( (5, 5), # fmt: off (-1, -1, -1, -1, 0, diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 0ce0455a16e..921fdc369d3 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -484,7 +484,7 @@ def assert_aux_channel_preserved(mode, transform_in_place, preserved_channel): def create_test_image(): # set up test image with something interesting in the tested aux channel. # fmt: off - nine_grid_deltas = [ # noqa: E131 + nine_grid_deltas = [ (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1), diff --git a/src/PIL/JpegPresets.py b/src/PIL/JpegPresets.py index 93c26b20536..118dab061f9 100644 --- a/src/PIL/JpegPresets.py +++ b/src/PIL/JpegPresets.py @@ -71,7 +71,7 @@ """ # fmt: off -presets = { # noqa: E128 +presets = { 'web_low': {'subsampling': 2, # "4:2:0" 'quantization': [ [20, 16, 25, 39, 50, 46, 62, 68, diff --git a/src/PIL/PalmImagePlugin.py b/src/PIL/PalmImagePlugin.py index c8703d9ec11..9fc55d79558 100644 --- a/src/PIL/PalmImagePlugin.py +++ b/src/PIL/PalmImagePlugin.py @@ -11,7 +11,7 @@ from ._binary import o8, o16be as o16b # fmt: off -_Palm8BitColormapValues = ( # noqa: E131 +_Palm8BitColormapValues = ( (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 1b6c9083aa5..8da48428399 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -154,7 +154,7 @@ def cmd_msbuild( # "bins": [r"libtiff\*.dll"], }, "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz", # noqa: E501 + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz", "filename": "libwebp-1.1.0.tar.gz", "dir": "libwebp-1.1.0", "build": [ From eeb9e719e33b26907cbfbf859d4c9e0c3224307c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Jun 2020 12:07:13 +1000 Subject: [PATCH 164/262] Fixed drawing a 1px high polygon --- Tests/images/imagedraw_polygon_1px_high.png | Bin 0 -> 73 bytes Tests/test_imagedraw.py | 14 ++++++++++++++ src/libImaging/Draw.c | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 Tests/images/imagedraw_polygon_1px_high.png diff --git a/Tests/images/imagedraw_polygon_1px_high.png b/Tests/images/imagedraw_polygon_1px_high.png new file mode 100644 index 0000000000000000000000000000000000000000..e06508a0af0a8baa0adeed753157ad0b0536664e GIT binary patch literal 73 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1SD^IDZKzv0-i38Ar*6y6B-zg{pXQjU=WsP VWZd*(1qV=$!PC{xWt~$(69Dik5B>lE literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 7ecec48d350..74b0b8ff3f2 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -531,6 +531,20 @@ def test_polygon_kite(): assert_image_equal(im, Image.open(expected)) +def test_polygon_1px_high(): + # Test drawing a 1px high polygon + # Arrange + im = Image.new("RGB", (3, 3)) + draw = ImageDraw.Draw(im) + expected = "Tests/images/imagedraw_polygon_1px_high.png" + + # Act + draw.polygon([(0, 1), (0, 1), (2, 1), (2, 1)], "#f00") + + # Assert + assert_image_equal(im, Image.open(expected)) + + def helper_rectangle(bbox): # Arrange im = Image.new("RGB", (W, H)) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 10aaae1f2a6..947d7f70f3d 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -489,15 +489,15 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, } for (i = 0; i < n; i++) { - if (e[i].ymin == e[i].ymax) { - continue; - } if (ymin > e[i].ymin) { ymin = e[i].ymin; } if (ymax < e[i].ymax) { ymax = e[i].ymax; } + if (e[i].ymin == e[i].ymax) { + continue; + } edge_table[edge_count++] = (e + i); } if (ymin < 0) { From 0cc2e696cb4ad46201e34e3b5694226fbd7c6294 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 7 Jun 2020 20:01:04 +1000 Subject: [PATCH 165/262] Corrected reading EXIF metadata without prefix --- Tests/test_file_webp_metadata.py | 9 +++++++++ src/PIL/Image.py | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 9fa20e403bf..a2a05f96b92 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -30,6 +30,15 @@ def test_read_exif_metadata(): assert exif_data == expected_exif +def test_read_exif_metadata_without_prefix(): + with Image.open("Tests/images/flower2.webp") as im: + # Assert prefix is not present + assert im.info["exif"][:6] != b"Exif\x00\x00" + + exif = im.getexif() + assert exif[305] == "Adobe Photoshop CS6 (Macintosh)" + + def test_write_exif_metadata(): file_path = "Tests/images/flower.jpg" test_buffer = BytesIO() diff --git a/src/PIL/Image.py b/src/PIL/Image.py index d64093df750..0388292134d 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3289,7 +3289,9 @@ def load(self, data): if not data: return - self.fp = io.BytesIO(data[6:]) + if data.startswith(b"Exif\x00\x00"): + data = data[6:] + self.fp = io.BytesIO(data) self.head = self.fp.read(8) # process dictionary from . import TiffImagePlugin From 16e933726fae67f245e297ee2eb82de3bd7dba39 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Jun 2020 07:37:12 +1000 Subject: [PATCH 166/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e2de9b8ca66..cc857aa7444 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Corrected reading EXIF metadata without prefix #4677 + [radarhere] + +- Fixed drawing a jointed line with a sequence of numeric values #4580 + [radarhere] + - Added support for 1-D NumPy arrays #4608 [radarhere] From 402a59f6eca27ba53703a71c56d4f858ab5e0771 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Jun 2020 13:17:08 +1000 Subject: [PATCH 167/262] Fixed formatting [ci skip] --- docs/reference/ImageDraw.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 1a0dd3b6878..782b0434b68 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -212,8 +212,7 @@ Methods .. versionadded:: 1.1.5 .. note:: This option was broken until version 1.1.6. - :param joint: Joint type between a sequence of lines. It can be ``"curve"``, - for rounded edges, or ``None``. + :param joint: Joint type between a sequence of lines. It can be ``"curve"``, for rounded edges, or ``None``. .. versionadded:: 5.3.0 From 9f1f19f999ea503a436ef4c1faf9fc30a49414f9 Mon Sep 17 00:00:00 2001 From: Eric L Frederich Date: Fri, 5 Jun 2020 10:47:17 -0400 Subject: [PATCH 168/262] Use --freeze feature of pre-commit autoupdate This prevents owners of repos used in pre-commit from force updating an existing tag to contain malacious code. The changes here were created simply by running `pre-commit autoupdate --freeze` --- .pre-commit-config.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 01a99784f2c..a37674036d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 6bedb5c58a7d8c25aa9509f8217bc24e9797e90d # frozen: 19.10b0 hooks: - id: black args: ["--target-version", "py35"] @@ -9,35 +9,35 @@ repos: types: [] - repo: https://github.com/timothycrosley/isort - rev: 4.3.21 + rev: 7c29dd9d55161704cfc45998c6f5c2c43d39264b # frozen: 4.3.21 hooks: - id: isort - repo: https://github.com/asottile/yesqa - rev: v1.1.1 + rev: b13a51aa54142c59219c764e9f9362c049b439ed # frozen: v1.2.0 hooks: - id: yesqa - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.7 + rev: ffbd448645bad2e7ca13f96fca5830058d27ccd5 # frozen: v1.1.7 hooks: - id: remove-tabs exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.2 + rev: 735cfe7e1c57a8e05f660ba75de72313005af54a # frozen: 3.8.2 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.5.1 + rev: 0d7d077d6ed5624854f93ac601739c1804ebeb98 # frozen: v1.5.1 hooks: - id: python-check-blanket-noqa - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.1.0 + rev: ebc15addedad713c86ef18ae9632c88e187dd0af # frozen: v3.1.0 hooks: - id: check-merge-conflict - id: check-yaml From 7ecb5aaf7e3dec7ebe170c0fff548a0e44b9196c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 5 May 2020 21:20:59 +1000 Subject: [PATCH 169/262] BYTE tags of variable length are only single strings --- Tests/test_file_tiff_metadata.py | 8 ++++---- src/PIL/TiffImagePlugin.py | 6 ++++-- src/encode.c | 26 +++++++++----------------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 9fe601bd65b..338d8d4fe31 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -319,13 +319,13 @@ def test_empty_values(): def test_PhotoshopInfo(tmp_path): with Image.open("Tests/images/issue_2278.tif") as im: - assert len(im.tag_v2[34377]) == 1 - assert isinstance(im.tag_v2[34377][0], bytes) + assert len(im.tag_v2[34377]) == 70 + assert isinstance(im.tag_v2[34377], bytes) out = str(tmp_path / "temp.tiff") im.save(out) with Image.open(out) as reloaded: - assert len(reloaded.tag_v2[34377]) == 1 - assert isinstance(reloaded.tag_v2[34377][0], bytes) + assert len(reloaded.tag_v2[34377]) == 70 + assert isinstance(reloaded.tag_v2[34377], bytes) def test_too_many_entries(): diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 8e757006214..a18621d2312 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -573,8 +573,10 @@ def _setitem(self, tag, value, legacy_api): # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. # Don't mess with the legacy api, since it's frozen. - if (info.length == 1) or ( - info.length is None and len(values) == 1 and not legacy_api + if ( + (info.length == 1) + or self.tagtype[tag] == TiffTags.BYTE + or (info.length is None and len(values) == 1 and not legacy_api) ): # Don't mess with the legacy api, since it's frozen. if legacy_api and self.tagtype[tag] in [ diff --git a/src/encode.c b/src/encode.c index 1d463e9c4cd..16d45e8f042 100644 --- a/src/encode.c +++ b/src/encode.c @@ -790,28 +790,24 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) if (!is_core_tag) { // Register field for non core tags. + if (type == TIFF_BYTE) { + is_var_length = 1; + } if (ImagingLibTiffMergeFieldInfo(&encoder->state, type, key_int, is_var_length)) { continue; } } - if (is_var_length) { + if (type == TIFF_BYTE) { + status = ImagingLibTiffSetField(&encoder->state, + (ttag_t) key_int, + PyBytes_Size(value), PyBytes_AsString(value)); + } else if (is_var_length) { Py_ssize_t len,i; TRACE(("Setting from Tuple: %d \n", key_int)); len = PyTuple_Size(value); - if (type == TIFF_BYTE) { - UINT8 *av; - /* malloc check ok, calloc checks for overflow */ - av = calloc(len, sizeof(UINT8)); - if (av) { - for (i=0;istate, (ttag_t) key_int, len, av); - free(av); - } - } else if (type == TIFF_SHORT) { + if (type == TIFF_SHORT) { UINT16 *av; /* malloc check ok, calloc checks for overflow */ av = calloc(len, sizeof(UINT16)); @@ -914,10 +910,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, (FLOAT64)PyFloat_AsDouble(value)); - } else if (type == TIFF_BYTE) { - status = ImagingLibTiffSetField(&encoder->state, - (ttag_t) key_int, - (UINT8)PyLong_AsLong(value)); } else if (type == TIFF_SBYTE) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, From 859b27572bc3581d5bb7be4db654c93abc9cb132 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 3 May 2020 19:41:38 +1000 Subject: [PATCH 170/262] Removed forcing of BYTE to ASCII --- Tests/test_file_libtiff.py | 17 ++++++++++++++++- src/encode.c | 3 +-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 9523e5901b5..f1ad1882825 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -299,7 +299,11 @@ def check_tags(tiffinfo): ) continue - if libtiff and isinstance(value, bytes): + if ( + libtiff + and isinstance(value, bytes) + and isinstance(tiffinfo, dict) + ): value = value.decode() assert reloaded_value == value @@ -322,6 +326,17 @@ def check_tags(tiffinfo): ) TiffImagePlugin.WRITE_LIBTIFF = False + def test_xmlpacket_tag(self, tmp_path): + TiffImagePlugin.WRITE_LIBTIFF = True + + out = str(tmp_path / "temp.tif") + hopper().save(out, tiffinfo={700: b"xmlpacket tag"}) + TiffImagePlugin.WRITE_LIBTIFF = False + + with Image.open(out) as reloaded: + if 700 in reloaded.tag_v2: + assert reloaded.tag_v2[700] == b"xmlpacket tag" + def test_int_dpi(self, tmp_path): # issue #1765 im = hopper("RGB") diff --git a/src/encode.c b/src/encode.c index 16d45e8f042..6506edb96d9 100644 --- a/src/encode.c +++ b/src/encode.c @@ -761,8 +761,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } } - if (PyBytes_Check(value) && - (type == TIFF_BYTE || type == TIFF_UNDEFINED)) { + if (PyBytes_Check(value) && type == TIFF_UNDEFINED) { // For backwards compatibility type = TIFF_ASCII; } From 2d284aea12f1d81b702305b34bd1d4c8e082034e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 6 May 2020 20:12:59 +1000 Subject: [PATCH 171/262] Allow writing of UNDEFINED tags --- Tests/test_file_libtiff.py | 27 ++++++++++++++++++++------- src/PIL/TiffImagePlugin.py | 16 +++++++++------- src/encode.c | 7 +------ 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index f1ad1882825..855a9ab3aa8 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -299,13 +299,6 @@ def check_tags(tiffinfo): ) continue - if ( - libtiff - and isinstance(value, bytes) - and isinstance(tiffinfo, dict) - ): - value = value.decode() - assert reloaded_value == value # Test with types @@ -682,6 +675,26 @@ def test_read_icc(self): TiffImagePlugin.READ_LIBTIFF = False assert icc == icc_libtiff + def test_write_icc(self, tmp_path): + def check_write(libtiff): + TiffImagePlugin.WRITE_LIBTIFF = libtiff + + with Image.open("Tests/images/hopper.iccprofile.tif") as img: + icc_profile = img.info["icc_profile"] + + out = str(tmp_path / "temp.tif") + img.save(out, icc_profile=icc_profile) + with Image.open(out) as reloaded: + assert icc_profile == reloaded.info["icc_profile"] + + libtiffs = [] + if Image.core.libtiff_support_custom_tags: + libtiffs.append(True) + libtiffs.append(False) + + for libtiff in libtiffs: + check_write(libtiff) + def test_multipage_compression(self): with Image.open("Tests/images/compression.tif") as im: diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index a18621d2312..ee183ccbac7 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -553,9 +553,10 @@ def _setitem(self, tag, value, legacy_api): ) elif all(isinstance(v, float) for v in values): self.tagtype[tag] = TiffTags.DOUBLE - else: - if all(isinstance(v, str) for v in values): - self.tagtype[tag] = TiffTags.ASCII + elif all(isinstance(v, str) for v in values): + self.tagtype[tag] = TiffTags.ASCII + elif all(isinstance(v, bytes) for v in values): + self.tagtype[tag] = TiffTags.BYTE if self.tagtype[tag] == TiffTags.UNDEFINED: values = [ @@ -1548,16 +1549,17 @@ def _save(im, fp, filename): # Custom items are supported for int, float, unicode, string and byte # values. Other types and tuples require a tagtype. if tag not in TiffTags.LIBTIFF_CORE: - if ( - TiffTags.lookup(tag).type == TiffTags.UNDEFINED - or not Image.core.libtiff_support_custom_tags - ): + if not Image.core.libtiff_support_custom_tags: continue if tag in ifd.tagtype: types[tag] = ifd.tagtype[tag] elif not (isinstance(value, (int, float, str, bytes))): continue + else: + type = TiffTags.lookup(tag).type + if type: + types[tag] = type if tag not in atts and tag not in blocklist: if isinstance(value, str): atts[tag] = value.encode("ascii", "replace") + b"\0" diff --git a/src/encode.c b/src/encode.c index 6506edb96d9..03a39448d2f 100644 --- a/src/encode.c +++ b/src/encode.c @@ -761,11 +761,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } } - if (PyBytes_Check(value) && type == TIFF_UNDEFINED) { - // For backwards compatibility - type = TIFF_ASCII; - } - if (PyTuple_Check(value)) { Py_ssize_t len; len = PyTuple_Size(value); @@ -797,7 +792,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) } } - if (type == TIFF_BYTE) { + if (type == TIFF_BYTE || type == TIFF_UNDEFINED) { status = ImagingLibTiffSetField(&encoder->state, (ttag_t) key_int, PyBytes_Size(value), PyBytes_AsString(value)); From 8b8b770b8af806f55f8be50b060cb444506f0ccf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 10 Jun 2020 18:15:25 +1000 Subject: [PATCH 172/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cc857aa7444..566e055a418 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Use ImageFileDirectory_v2 in Image.Exif #4637 + [radarhere] + - Corrected reading EXIF metadata without prefix #4677 [radarhere] From bb01312ba97b5dfabc5be228bbe264916798b7c8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 11 Jun 2020 22:42:13 +1000 Subject: [PATCH 173/262] Improved formatting --- docs/reference/ImageGrab.rst | 3 +- src/PIL/ImageCms.py | 188 ++++++++++++++++++----------------- src/PIL/ImageWin.py | 2 +- 3 files changed, 97 insertions(+), 96 deletions(-) diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 5cc8e55e04d..ff4646a5f50 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -26,8 +26,7 @@ or the clipboard to a PIL image memory. .. versionadded:: 6.2.0 - :param xdisplay: X11 Display address. Pass ``None`` to grab the default system screen. - Pass ``""`` to grab the default X11 screen on Windows or macOS. + :param xdisplay: X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS. .. versionadded:: 7.1.0 :return: An image diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 661c3f33be4..9abf69c025c 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -192,9 +192,9 @@ class ImageCmsTransform(Image.ImagePointHandler): """ Transform. This can be used with the procedural API, or with the standard - Image.point() method. + :py:func:`~PIL.Image.Image.point` method. - Will return the output profile in the output.info['icc_profile']. + Will return the output profile in the ``output.info['icc_profile']``. """ def __init__( @@ -251,7 +251,7 @@ def apply_in_place(self, im): def get_display_profile(handle=None): """ (experimental) Fetches the profile for the current display device. - :returns: None if the profile is not known. + :returns: ``None`` if the profile is not known. """ if sys.platform != "win32": @@ -292,27 +292,27 @@ def profileToProfile( ): """ (pyCMS) Applies an ICC transformation to a given image, mapping from - inputProfile to outputProfile. + ``inputProfile`` to ``outputProfile``. If the input or output profiles specified are not valid filenames, a - PyCMSError will be raised. If inPlace is True and outputMode != im.mode, - a PyCMSError will be raised. If an error occurs during application of - the profiles, a PyCMSError will be raised. If outputMode is not a mode - supported by the outputProfile (or by pyCMS), a PyCMSError will be - raised. - - This function applies an ICC transformation to im from inputProfile's - color space to outputProfile's color space using the specified rendering + **PyCMSError** will be raised. If ``inPlace`` is ``True`` and + ``outputMode != im.mode``, a **PyCMSError** will be raised. If an error + occurs during application of the profiles, a **PyCMSError** will be raised. + If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), + a **PyCMSError** will be raised. + + This function applies an ICC transformation to im from ``inputProfile``'s + color space to ``outputProfile``'s color space using the specified rendering intent to decide how to handle out-of-gamut colors. - OutputMode can be used to specify that a color mode conversion is to + ``outputMode`` can be used to specify that a color mode conversion is to be done using these profiles, but the specified profiles must be able to handle that mode. I.e., if converting im from RGB to CMYK using profiles, the input profile must handle RGB data, and the output profile must handle CMYK data. - :param im: An open PIL image object (i.e. Image.new(...) or - Image.open(...), etc.) + :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...) + or Image.open(...), etc.) :param inputProfile: String, as a valid filename path to the ICC input profile you wish to use for this image, or a profile object :param outputProfile: String, as a valid filename path to the ICC output @@ -332,12 +332,12 @@ def profileToProfile( MUST be the same mode as the input, or omitted completely. If omitted, the outputMode will be the same as the mode of the input image (im.mode) - :param inPlace: Boolean. If True, the original image is modified in-place, - and None is returned. If False (default), a new Image object is - returned with the transform applied. + :param inPlace: Boolean. If ``True``, the original image is modified in-place, + and ``None`` is returned. If ``False`` (default), a new + :py:class:`~PIL.Image.Image` object is returned with the transform applied. :param flags: Integer (0-...) specifying additional flags - :returns: Either None or a new PIL image object, depending on value of - inPlace + :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on + the value of ``inPlace`` :exception PyCMSError: """ @@ -381,7 +381,7 @@ def getOpenProfile(profileFilename): The PyCMSProfile object can be passed back into pyCMS for use in creating transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). - If profileFilename is not a valid filename for an ICC profile, a PyCMSError + If ``profileFilename`` is not a valid filename for an ICC profile, a **PyCMSError** will be raised. :param profileFilename: String, as a valid filename path to the ICC profile @@ -405,21 +405,21 @@ def buildTransform( flags=0, ): """ - (pyCMS) Builds an ICC transform mapping from the inputProfile to the - outputProfile. Use applyTransform to apply the transform to a given + (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the + ``outputProfile``. Use applyTransform to apply the transform to a given image. If the input or output profiles specified are not valid filenames, a - PyCMSError will be raised. If an error occurs during creation of the - transform, a PyCMSError will be raised. + **PyCMSError** will be raised. If an error occurs during creation of the + transform, a **PyCMSError** will be raised. - If inMode or outMode are not a mode supported by the outputProfile (or - by pyCMS), a PyCMSError will be raised. + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a **PyCMSError** will be raised. - This function builds and returns an ICC transform from the inputProfile - to the outputProfile using the renderingIntent to determine what to do + This function builds and returns an ICC transform from the ``inputProfile`` + to the ``outputProfile`` using the ``renderingIntent`` to determine what to do with out-of-gamut colors. It will ONLY work for converting images that - are in inMode to images that are in outMode color format (PIL mode, + are in ``inMode`` to images that are in ``outMode`` color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). Building the transform is a fair part of the overhead in @@ -432,7 +432,7 @@ def buildTransform( The reason pyCMS returns a class object rather than a handle directly to the transform is that it needs to keep track of the PIL input/output modes that the transform is meant for. These attributes are stored in - the "inMode" and "outMode" attributes of the object (which can be + the ``inMode`` and ``outMode`` attributes of the object (which can be manually overridden if you really want to, but I don't know of any time that would be of use, or would even work). @@ -488,25 +488,25 @@ def buildProofTransform( flags=FLAGS["SOFTPROOFING"], ): """ - (pyCMS) Builds an ICC transform mapping from the inputProfile to the - outputProfile, but tries to simulate the result that would be - obtained on the proofProfile device. + (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the + ``outputProfile``, but tries to simulate the result that would be + obtained on the ``proofProfile`` device. If the input, output, or proof profiles specified are not valid - filenames, a PyCMSError will be raised. + filenames, a **PyCMSError** will be raised. - If an error occurs during creation of the transform, a PyCMSError will - be raised. + If an error occurs during creation of the transform, a **PyCMSError** + will be raised. - If inMode or outMode are not a mode supported by the outputProfile - (or by pyCMS), a PyCMSError will be raised. + If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` + (or by pyCMS), a **PyCMSError** will be raised. - This function builds and returns an ICC transform from the inputProfile - to the outputProfile, but tries to simulate the result that would be - obtained on the proofProfile device using renderingIntent and - proofRenderingIntent to determine what to do with out-of-gamut + This function builds and returns an ICC transform from the ``inputProfile`` + to the ``outputProfile``, but tries to simulate the result that would be + obtained on the ``proofProfile`` device using ``renderingIntent`` and + ``proofRenderingIntent`` to determine what to do with out-of-gamut colors. This is known as "soft-proofing". It will ONLY work for - converting images that are in inMode to images that are in outMode + converting images that are in ``inMode`` to images that are in outMode color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). Usage of the resulting transform object is exactly the same as with @@ -514,7 +514,7 @@ def buildProofTransform( Proof profiling is generally used when using an output device to get a good idea of what the final printed/displayed image would look like on - the proofProfile device when it's quicker and easier to use the + the ``proofProfile`` device when it's quicker and easier to use the output device for judging color. Generally, this means that the output device is a monitor, or a dye-sub printer (etc.), and the simulated device is something more expensive, complicated, or time consuming @@ -596,39 +596,40 @@ def applyTransform(im, transform, inPlace=False): """ (pyCMS) Applies a transform to a given image. - If im.mode != transform.inMode, a PyCMSError is raised. + If ``im.mode != transform.inMode``, a **PyCMSError** is raised. - If inPlace is True and transform.inMode != transform.outMode, a - PyCMSError is raised. + If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a + **PyCMSError** is raised. - If im.mode, transform.inMode, or transform.outMode is not supported by - pyCMSdll or the profiles you used for the transform, a PyCMSError is - raised. + If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not + supported by pyCMSdll or the profiles you used for the transform, a + **PyCMSError** is raised. - If an error occurs while the transform is being applied, a PyCMSError + If an error occurs while the transform is being applied, a **PyCMSError** is raised. This function applies a pre-calculated transform (from ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) - to an image. The transform can be used for multiple images, saving + to an image. The transform can be used for multiple images, saving considerable calculation time if doing the same conversion multiple times. If you want to modify im in-place instead of receiving a new image as - the return value, set inPlace to True. This can only be done if - transform.inMode and transform.outMode are the same, because we can't + the return value, set ``inPlace`` to ``True``. This can only be done if + ``transform.inMode`` and ``transform.outMode`` are the same, because we can't change the mode in-place (the buffer sizes for some modes are - different). The default behavior is to return a new Image object of - the same dimensions in mode transform.outMode. + different). The default behavior is to return a new :py:class:`~PIL.Image.Image` + object of the same dimensions in mode ``transform.outMode``. - :param im: A PIL Image object, and im.mode must be the same as the inMode - supported by the transform. + :param im: An :py:class:`~PIL.Image.Image` object, and im.mode must be the same + as the ``inMode`` supported by the transform. :param transform: A valid CmsTransform class object - :param inPlace: Bool. If True, im is modified in place and None is - returned, if False, a new Image object with the transform applied is - returned (and im is not changed). The default is False. - :returns: Either None, or a new PIL Image object, depending on the value of - inPlace. The profile will be returned in the image's - info['icc_profile']. + :param inPlace: Bool. If ``True``, ``im` is modified in place and ``None`` is + returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the + transform applied is returned (and ``im`` is not changed). The default is + ``False``. + :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object, + depending on the value of ``inPlace``. The profile will be returned in + the image's ``info['icc_profile']``. :exception PyCMSError: """ @@ -648,11 +649,12 @@ def createProfile(colorSpace, colorTemp=-1): """ (pyCMS) Creates a profile. - If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, a **PyCMSError** is raised - If using LAB and colorTemp != a positive integer, a PyCMSError is raised. + If using LAB and ``colorTemp`` is not a positive integer, a **PyCMSError** is + raised. - If an error occurs while creating the profile, a PyCMSError is raised. + If an error occurs while creating the profile, a **PyCMSError** is raised. Use this function to create common profiles on-the-fly instead of having to supply a profile on disk and knowing the path to it. It @@ -696,9 +698,9 @@ def getProfileName(profile): (pyCMS) Gets the internal product name for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised If an error occurs while trying to obtain the - name tag, a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a **PyCMSError** is raised If an error occurs while trying to obtain the + name tag, a **PyCMSError** is raised. Use this function to obtain the INTERNAL name of the profile (stored in an ICC tag in the profile itself), usually the one used when the @@ -737,10 +739,10 @@ def getProfileInfo(profile): """ (pyCMS) Gets the internal product information for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, + a **PyCMSError** is raised. - If an error occurs while trying to obtain the info tag, a PyCMSError + If an error occurs while trying to obtain the info tag, a **PyCMSError** is raised Use this function to obtain the information stored in the profile's @@ -777,10 +779,10 @@ def getProfileCopyright(profile): """ (pyCMS) Gets the copyright for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + **PyCMSError** is raised. - If an error occurs while trying to obtain the copyright tag, a PyCMSError + If an error occurs while trying to obtain the copyright tag, a **PyCMSError** is raised Use this function to obtain the information stored in the profile's @@ -805,11 +807,11 @@ def getProfileManufacturer(profile): """ (pyCMS) Gets the manufacturer for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + **PyCMSError** is raised. If an error occurs while trying to obtain the manufacturer tag, a - PyCMSError is raised + **PyCMSError** is raised Use this function to obtain the information stored in the profile's manufacturer tag. @@ -833,10 +835,10 @@ def getProfileModel(profile): """ (pyCMS) Gets the model for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + **PyCMSError** is raised. - If an error occurs while trying to obtain the model tag, a PyCMSError + If an error occurs while trying to obtain the model tag, a **PyCMSError** is raised Use this function to obtain the information stored in the profile's @@ -862,10 +864,10 @@ def getProfileDescription(profile): """ (pyCMS) Gets the description for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + **PyCMSError** is raised. - If an error occurs while trying to obtain the description tag, a PyCMSError + If an error occurs while trying to obtain the description tag, a **PyCMSError** is raised Use this function to obtain the information stored in the profile's @@ -891,11 +893,11 @@ def getDefaultIntent(profile): """ (pyCMS) Gets the default intent name for the given profile. - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. + If ``profile`` isn't a valid CmsProfile object or filename to a profile, a + **PyCMSError** is raised. If an error occurs while trying to obtain the default intent, a - PyCMSError is raised. + **PyCMSError** is raised. Use this function to determine the default (and usually best optimized) rendering intent for this profile. Most profiles support multiple @@ -931,14 +933,14 @@ def isIntentSupported(profile, intent, direction): (pyCMS) Checks if a given intent is supported. Use this function to verify that you can use your desired - renderingIntent with profile, and that profile can be used for the + ``intent`` with ``profile``, and that ``profile`` can be used for the input/output/proof profile as you desire. Some profiles are created specifically for one "direction", can cannot - be used for others. Some profiles can only be used for certain - rendering intents... so it's best to either verify this before trying + be used for others. Some profiles can only be used for certain + rendering intents, so it's best to either verify this before trying to create a transform with them (using this function), or catch the - potential PyCMSError that will occur if they don't support the modes + potential **PyCMSError** that will occur if they don't support the modes you select. :param profile: EITHER a valid CmsProfile object, OR a string of the diff --git a/src/PIL/ImageWin.py b/src/PIL/ImageWin.py index 927b1694b3e..afba61c323f 100644 --- a/src/PIL/ImageWin.py +++ b/src/PIL/ImageWin.py @@ -173,7 +173,7 @@ def frombytes(self, buffer): Load display memory contents from byte data. :param buffer: A buffer containing display data (usually - data returned from tobytes) + data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) """ return self.image.frombytes(buffer) From e219671be1dca941e7a68013fb20d1dc2c5a69f7 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Fri, 12 Jun 2020 00:34:40 +0300 Subject: [PATCH 174/262] Fix exception causes in PdfParser.py --- src/PIL/PdfParser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index fdb35eded56..3c343c5e86f 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -251,8 +251,8 @@ def __setattr__(self, key, value): def __getattr__(self, key): try: value = self[key.encode("us-ascii")] - except KeyError: - raise AttributeError(key) + except KeyError as e: + raise AttributeError(key) from e if isinstance(value, bytes): value = decode_text(value) if key.endswith("Date"): @@ -811,11 +811,11 @@ def get_value(cls, data, offset, expect_indirect=None, max_nesting=-1): if m: try: stream_len = int(result[b"Length"]) - except (TypeError, KeyError, ValueError): + except (TypeError, KeyError, ValueError) as e: raise PdfFormatError( "bad or missing Length in stream dict (%r)" % result.get(b"Length", None) - ) + ) from e stream_data = data[m.end() : m.end() + stream_len] m = cls.re_stream_end.match(data, m.end() + stream_len) check_format_condition(m, "stream end not found") From ab0ac93cb83b2234ae8fb01a165721c1fe03feb8 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Fri, 12 Jun 2020 07:51:38 +1000 Subject: [PATCH 175/262] Added period Co-authored-by: Hugo van Kemenade --- src/PIL/ImageCms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 9abf69c025c..44d6fe5c4f7 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -743,7 +743,7 @@ def getProfileInfo(profile): a **PyCMSError** is raised. If an error occurs while trying to obtain the info tag, a **PyCMSError** - is raised + is raised. Use this function to obtain the information stored in the profile's info tag. This often contains details about the profile, and how it From 7dd818fc360cd6d9a8a27aa84e62402b677107a1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 12 Jun 2020 08:09:51 +1000 Subject: [PATCH 176/262] Added periods --- src/PIL/ImageCms.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 44d6fe5c4f7..2fde70379ca 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -649,7 +649,7 @@ def createProfile(colorSpace, colorTemp=-1): """ (pyCMS) Creates a profile. - If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, a **PyCMSError** is raised + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, a **PyCMSError** is raised. If using LAB and ``colorTemp`` is not a positive integer, a **PyCMSError** is raised. @@ -783,7 +783,7 @@ def getProfileCopyright(profile): **PyCMSError** is raised. If an error occurs while trying to obtain the copyright tag, a **PyCMSError** - is raised + is raised. Use this function to obtain the information stored in the profile's copyright tag. @@ -811,7 +811,7 @@ def getProfileManufacturer(profile): **PyCMSError** is raised. If an error occurs while trying to obtain the manufacturer tag, a - **PyCMSError** is raised + **PyCMSError** is raised. Use this function to obtain the information stored in the profile's manufacturer tag. @@ -839,7 +839,7 @@ def getProfileModel(profile): **PyCMSError** is raised. If an error occurs while trying to obtain the model tag, a **PyCMSError** - is raised + is raised. Use this function to obtain the information stored in the profile's model tag. @@ -868,7 +868,7 @@ def getProfileDescription(profile): **PyCMSError** is raised. If an error occurs while trying to obtain the description tag, a **PyCMSError** - is raised + is raised. Use this function to obtain the information stored in the profile's description tag. From 09d58147ca415c4e6f526df557841b03f3afcd25 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 12 Jun 2020 23:57:21 +1000 Subject: [PATCH 177/262] Changed errors to be code formatted, instead of bold --- src/PIL/Image.py | 2 +- src/PIL/ImageCms.py | 66 ++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index bb81750e6da..930252023e2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2163,7 +2163,7 @@ def seek(self, frame): """ Seeks to the given frame in this sequence file. If you seek beyond the end of the sequence, the method raises an - **EOFError** exception. When a sequence file is opened, the + ``EOFError`` exception. When a sequence file is opened, the library automatically seeks to frame 0. See :py:meth:`~PIL.Image.Image.tell`. diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 2fde70379ca..723e7ceb7fe 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -295,11 +295,11 @@ def profileToProfile( ``inputProfile`` to ``outputProfile``. If the input or output profiles specified are not valid filenames, a - **PyCMSError** will be raised. If ``inPlace`` is ``True`` and - ``outputMode != im.mode``, a **PyCMSError** will be raised. If an error - occurs during application of the profiles, a **PyCMSError** will be raised. + ``PyCMSError`` will be raised. If ``inPlace`` is ``True`` and + ``outputMode != im.mode``, a ``PyCMSError`` will be raised. If an error + occurs during application of the profiles, a ``PyCMSError`` will be raised. If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), - a **PyCMSError** will be raised. + a ``PyCMSError`` will be raised. This function applies an ICC transformation to im from ``inputProfile``'s color space to ``outputProfile``'s color space using the specified rendering @@ -381,7 +381,7 @@ def getOpenProfile(profileFilename): The PyCMSProfile object can be passed back into pyCMS for use in creating transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). - If ``profileFilename`` is not a valid filename for an ICC profile, a **PyCMSError** + If ``profileFilename`` is not a valid filename for an ICC profile, a ``PyCMSError`` will be raised. :param profileFilename: String, as a valid filename path to the ICC profile @@ -410,11 +410,11 @@ def buildTransform( image. If the input or output profiles specified are not valid filenames, a - **PyCMSError** will be raised. If an error occurs during creation of the - transform, a **PyCMSError** will be raised. + ``PyCMSError`` will be raised. If an error occurs during creation of the + transform, a ``PyCMSError`` will be raised. If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a **PyCMSError** will be raised. + (or by pyCMS), a ``PyCMSError`` will be raised. This function builds and returns an ICC transform from the ``inputProfile`` to the ``outputProfile`` using the ``renderingIntent`` to determine what to do @@ -493,13 +493,13 @@ def buildProofTransform( obtained on the ``proofProfile`` device. If the input, output, or proof profiles specified are not valid - filenames, a **PyCMSError** will be raised. + filenames, a ``PyCMSError`` will be raised. - If an error occurs during creation of the transform, a **PyCMSError** + If an error occurs during creation of the transform, a ``PyCMSError`` will be raised. If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a **PyCMSError** will be raised. + (or by pyCMS), a ``PyCMSError`` will be raised. This function builds and returns an ICC transform from the ``inputProfile`` to the ``outputProfile``, but tries to simulate the result that would be @@ -596,16 +596,16 @@ def applyTransform(im, transform, inPlace=False): """ (pyCMS) Applies a transform to a given image. - If ``im.mode != transform.inMode``, a **PyCMSError** is raised. + If ``im.mode != transform.inMode``, a ``PyCMSError`` is raised. If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not supported by pyCMSdll or the profiles you used for the transform, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. - If an error occurs while the transform is being applied, a **PyCMSError** + If an error occurs while the transform is being applied, a ``PyCMSError`` is raised. This function applies a pre-calculated transform (from @@ -649,12 +649,12 @@ def createProfile(colorSpace, colorTemp=-1): """ (pyCMS) Creates a profile. - If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, a **PyCMSError** is raised. + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, a ``PyCMSError`` is raised. - If using LAB and ``colorTemp`` is not a positive integer, a **PyCMSError** is + If using LAB and ``colorTemp`` is not a positive integer, a ``PyCMSError`` is raised. - If an error occurs while creating the profile, a **PyCMSError** is raised. + If an error occurs while creating the profile, a ``PyCMSError`` is raised. Use this function to create common profiles on-the-fly instead of having to supply a profile on disk and knowing the path to it. It @@ -699,8 +699,8 @@ def getProfileName(profile): (pyCMS) Gets the internal product name for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a **PyCMSError** is raised If an error occurs while trying to obtain the - name tag, a **PyCMSError** is raised. + a ``PyCMSError`` is raised If an error occurs while trying to obtain the + name tag, a ``PyCMSError`` is raised. Use this function to obtain the INTERNAL name of the profile (stored in an ICC tag in the profile itself), usually the one used when the @@ -740,9 +740,9 @@ def getProfileInfo(profile): (pyCMS) Gets the internal product information for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a **PyCMSError** is raised. + a ``PyCMSError`` is raised. - If an error occurs while trying to obtain the info tag, a **PyCMSError** + If an error occurs while trying to obtain the info tag, a ``PyCMSError`` is raised. Use this function to obtain the information stored in the profile's @@ -780,9 +780,9 @@ def getProfileCopyright(profile): (pyCMS) Gets the copyright for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. - If an error occurs while trying to obtain the copyright tag, a **PyCMSError** + If an error occurs while trying to obtain the copyright tag, a ``PyCMSError`` is raised. Use this function to obtain the information stored in the profile's @@ -808,10 +808,10 @@ def getProfileManufacturer(profile): (pyCMS) Gets the manufacturer for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. If an error occurs while trying to obtain the manufacturer tag, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. Use this function to obtain the information stored in the profile's manufacturer tag. @@ -836,9 +836,9 @@ def getProfileModel(profile): (pyCMS) Gets the model for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. - If an error occurs while trying to obtain the model tag, a **PyCMSError** + If an error occurs while trying to obtain the model tag, a ``PyCMSError`` is raised. Use this function to obtain the information stored in the profile's @@ -865,9 +865,9 @@ def getProfileDescription(profile): (pyCMS) Gets the description for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. - If an error occurs while trying to obtain the description tag, a **PyCMSError** + If an error occurs while trying to obtain the description tag, a ``PyCMSError`` is raised. Use this function to obtain the information stored in the profile's @@ -894,10 +894,10 @@ def getDefaultIntent(profile): (pyCMS) Gets the default intent name for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. If an error occurs while trying to obtain the default intent, a - **PyCMSError** is raised. + ``PyCMSError`` is raised. Use this function to determine the default (and usually best optimized) rendering intent for this profile. Most profiles support multiple @@ -940,7 +940,7 @@ def isIntentSupported(profile, intent, direction): be used for others. Some profiles can only be used for certain rendering intents, so it's best to either verify this before trying to create a transform with them (using this function), or catch the - potential **PyCMSError** that will occur if they don't support the modes + potential ``PyCMSError`` that will occur if they don't support the modes you select. :param profile: EITHER a valid CmsProfile object, OR a string of the From 097104278b762277ff6f05df50556139a12d20c1 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 13 Jun 2020 04:01:38 +0200 Subject: [PATCH 178/262] add docs for features module --- docs/reference/ImageGrab.rst | 9 ++++-- docs/reference/features.rst | 59 ++++++++++++++++++++++++++++++++++++ docs/reference/index.rst | 1 + src/PIL/Image.py | 10 +++--- src/PIL/ImageFont.py | 5 +++ src/PIL/features.py | 50 ++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 docs/reference/features.rst diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index ff4646a5f50..943fdf69be7 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -9,7 +9,7 @@ or the clipboard to a PIL image memory. .. versionadded:: 1.1.3 -.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None) +.. py:function:: grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None) Take a snapshot of the screen. The pixels inside the bounding box are returned as an "RGBA" on macOS, or an "RGB" image otherwise. @@ -26,12 +26,15 @@ or the clipboard to a PIL image memory. .. versionadded:: 6.2.0 - :param xdisplay: X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS. + :param xdisplay: + X11 Display address. Pass ``None`` to grab the default system screen. Pass ``""`` to grab the default X11 screen on Windows or macOS. + + You can check X11 support using :py:func:`PIL.features.check_feature` with ``feature="xcb"``. .. versionadded:: 7.1.0 :return: An image -.. py:function:: PIL.ImageGrab.grabclipboard() +.. py:function:: grabclipboard() Take a snapshot of the clipboard image, if any. Only macOS and Windows are currently supported. diff --git a/docs/reference/features.rst b/docs/reference/features.rst new file mode 100644 index 00000000000..3a10d6201aa --- /dev/null +++ b/docs/reference/features.rst @@ -0,0 +1,59 @@ +.. py:currentmodule:: PIL.features + +:py:mod:`features` Module +========================== + +The :py:mod:`PIL.features` module can be used to detect which Pillow features are available on your system. + +.. autofunction:: PIL.features.pilinfo +.. autofunction:: PIL.features.check +.. autofunction:: PIL.features.get_supported + +Modules +------- + +Support for the following modules can be checked: + +* ``pil``: The Pillow core module, required for all functionality. +* ``tkinter``: Tkinter support. +* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`. +* ``littlecms2``: LittleCMS2 support via :py:mod:`PIL.ImageCms`. +* ``webp``: WebP image support. + +.. autofunction:: PIL.features.check_module +.. autofunction:: PIL.features.get_supported_modules + +Codecs +------ + +These are only checked during Pillow compilation. +If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead. + +Support for the following codecs can be checked: + +* ``jpg``: (compile time) LibJpeg support, required for JPEG based image formats. +* ``jpg_2000``: (compile time) OpenJpeg support, required for JPEG 2000 image formats. +* ``zlib``: (compile time) ZLib support, required for ZLib compressed formats, such as PNG. +* ``libtiff``: (compile time) LibTiff support, required for Tiff based image formats. + +.. autofunction:: PIL.features.check_codec +.. autofunction:: PIL.features.get_supported_codecs + +Features +-------- + +Some of these are only checked during Pillow compilation. +If the required library was uninstalled from the system, the relevant module may fail to load instead. + +Support for the following features can be checked: + +* ``libjpeg_turbo``: Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. +* ``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`. +* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. +* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library. + +.. autofunction:: PIL.features.check_feature +.. autofunction:: PIL.features.get_supported_features diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8c09e7b67f7..91cde0400c0 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -30,6 +30,7 @@ Reference PSDraw PixelAccess PyAccess + features ../PIL plugins internal_design diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 930252023e2..9d94bce0ee4 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1051,10 +1051,12 @@ def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1): of colors. :param colors: The desired number of colors, <= 256 - :param method: 0 = median cut - 1 = maximum coverage - 2 = fast octree - 3 = libimagequant + :param method: ``Image.MEDIANCUT=0`` (median cut), + ``Image.MAXCOVERAGE=1`` (maximum coverage), + ``Image.FASTOCTREE=2`` (fast octree), + ``Image.LIBIMAGEQUANT=3`` (libimagequant; check support using + :py:func:`PIL.features.check_feature` + with ``feature="libimagequant"``). :param kmeans: Integer :param palette: Quantize to the palette of given :py:class:`PIL.Image.Image`. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 25ceaa16aeb..79c1617135e 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -637,6 +637,11 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): encoding of any text provided in subsequent operations. :param layout_engine: Which layout engine to use, if available: `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`. + + You can check support for Raqm layout using + :py:func:`PIL.features.check_feature` with ``feature="raqm"``. + + .. versionadded:: 4.2.0 :return: A font object. :exception OSError: If the file could not be read. """ diff --git a/src/PIL/features.py b/src/PIL/features.py index ac06c0f7142..74952545a6c 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -17,6 +17,13 @@ def check_module(feature): + """ + Checks if a module is available. + + :param feature: The module to check for. + :returns: True if available, False otherwise. + :raises ValueError: If the module is not defined in this version of Pillow. + """ if not (feature in modules): raise ValueError("Unknown module %s" % feature) @@ -30,6 +37,9 @@ def check_module(feature): def get_supported_modules(): + """ + :returns: A list of all supported modules. + """ return [f for f in modules if check_module(f)] @@ -37,6 +47,13 @@ def get_supported_modules(): def check_codec(feature): + """ + Checks if a codec is available. + + :param feature: The codec to check for. + :returns: True if available, False otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ if feature not in codecs: raise ValueError("Unknown codec %s" % feature) @@ -46,6 +63,9 @@ def check_codec(feature): def get_supported_codecs(): + """ + :returns: A list of all supported codecs. + """ return [f for f in codecs if check_codec(f)] @@ -61,6 +81,13 @@ def get_supported_codecs(): def check_feature(feature): + """ + Checks if a feature is available. + + :param feature: The feature to check for. + :returns: True if available, False if unavailable, None if unknown. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ if feature not in features: raise ValueError("Unknown feature %s" % feature) @@ -74,10 +101,19 @@ def check_feature(feature): def get_supported_features(): + """ + :returns: A list of all supported features. + """ return [f for f in features if check_feature(f)] def check(feature): + """ + :param feature: A module, feature, or codec name. + :returns: + True if the module, feature, or codec is available, False or None otherwise. + """ + if feature in modules: return check_module(feature) if feature in codecs: @@ -89,6 +125,10 @@ def check(feature): def get_supported(): + """ + :returns: A list of all supported modules, features, and codecs. + """ + ret = get_supported_modules() ret.extend(get_supported_features()) ret.extend(get_supported_codecs()) @@ -96,6 +136,16 @@ def get_supported(): def pilinfo(out=None, supported_formats=True): + """ + Prints information about this installation of Pillow. + This function can be called with ``python -m PIL``. + + :param out: + The output stream to print to. Defaults to ``sys.stdout`` if None. + :param supported_formats: + If True, a list of all supported image file formats will be printed. + """ + if out is None: out = sys.stdout From 0b6f9091c5d686710ea6db3a2e76b5f9c5971afa Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 13 Jun 2020 06:44:05 +0200 Subject: [PATCH 179/262] corrected comment [ci skip] --- src/_imagingft.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_imagingft.c b/src/_imagingft.c index e6862ae01a9..554c72786e7 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -162,8 +162,7 @@ setraqm(void) } #else p_raqm.raqm = LoadLibrary("libraqm"); - - /* Cygwin / MinGW */ + /* MSYS */ if (!p_raqm.raqm) { p_raqm.raqm = LoadLibrary("libraqm-0"); } From 52f251abc2c62800426ad7eb41a5cfd3daa16db7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Jun 2020 22:00:11 +1000 Subject: [PATCH 180/262] Removed workaround for earlier versions --- src/PIL/ImageQt.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index dfe2f80bd81..a15f4ab5e4e 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -142,12 +142,7 @@ def _toqclass_helper(im): data = im.tobytes("raw", "BGRX") format = QImage.Format_RGB32 elif im.mode == "RGBA": - try: - data = im.tobytes("raw", "BGRA") - except SystemError: - # workaround for earlier versions - r, g, b, a = im.split() - im = Image.merge("RGBA", (b, g, r, a)) + data = im.tobytes("raw", "BGRA") format = QImage.Format_ARGB32 else: raise ValueError("unsupported image mode %r" % im.mode) From e19713ebc9a95eabad3292a5ddc35a4e9eb45947 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Jun 2020 12:03:03 +1000 Subject: [PATCH 181/262] Added release notes for 7.2.0 [ci skip] --- docs/releasenotes/7.2.0.rst | 29 +++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 30 insertions(+) create mode 100644 docs/releasenotes/7.2.0.rst diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst new file mode 100644 index 00000000000..aeea7e9d927 --- /dev/null +++ b/docs/releasenotes/7.2.0.rst @@ -0,0 +1,29 @@ +7.2.0 +----- + +API Changes +=========== + +Replaced TiffImagePlugin DEBUG with logging +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``TiffImagePlugin.DEBUG = True`` has been a way to print various debugging +information when interacting with TIFF images. This has now been removed +in favour of Python's ``logging`` module, already used in other places in the +Pillow source code. + +Corrected default offset when writing EXIF data +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Previously, the default ``offset`` argument for +:py:meth:`~PIL.Image.Exif.tobytes` was 0, which did not include the magic +header. It is now 8. + +Moved to ImageFileDirectory_v2 in Image.Exif +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Moved from the legacy :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v1` to +:py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v2` in +:py:class:`PIL.Image.Exif`. This means that Exif RATIONALs and SIGNED_RATIONALs +as now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a +tuple with a numerator and a denominator. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 575931c4d93..4dd7d5e6364 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -6,6 +6,7 @@ Release Notes .. toctree:: :maxdepth: 2 + 7.2.0 7.1.2 7.1.1 7.1.0 From d05a08a298657d65233fdafbdca3403fe9fe3311 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 00:45:29 +0200 Subject: [PATCH 182/262] formatting improvements Co-authored-by: Hugo --- docs/reference/features.rst | 13 +++++++------ src/PIL/features.py | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 3a10d6201aa..196f938ed69 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -1,3 +1,4 @@ +.. py:module:: PIL.features .. py:currentmodule:: PIL.features :py:mod:`features` Module @@ -17,7 +18,7 @@ Support for the following modules can be checked: * ``pil``: The Pillow core module, required for all functionality. * ``tkinter``: Tkinter support. * ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`. -* ``littlecms2``: LittleCMS2 support via :py:mod:`PIL.ImageCms`. +* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`. * ``webp``: WebP image support. .. autofunction:: PIL.features.check_module @@ -31,10 +32,10 @@ If the required library was uninstalled from the system, the ``pil`` core module Support for the following codecs can be checked: -* ``jpg``: (compile time) LibJpeg support, required for JPEG based image formats. -* ``jpg_2000``: (compile time) OpenJpeg support, required for JPEG 2000 image formats. -* ``zlib``: (compile time) ZLib support, required for ZLib compressed formats, such as PNG. -* ``libtiff``: (compile time) LibTiff support, required for Tiff based image formats. +* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. +* ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats. +* ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG. +* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats. .. autofunction:: PIL.features.check_codec .. autofunction:: PIL.features.get_supported_codecs @@ -47,7 +48,7 @@ If the required library was uninstalled from the system, the relevant module may Support for the following features can be checked: -* ``libjpeg_turbo``: Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. +* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. * ``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. diff --git a/src/PIL/features.py b/src/PIL/features.py index 74952545a6c..33e89cf2441 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -21,7 +21,7 @@ def check_module(feature): Checks if a module is available. :param feature: The module to check for. - :returns: True if available, False otherwise. + :returns: ``True`` if available, ``False`` otherwise. :raises ValueError: If the module is not defined in this version of Pillow. """ if not (feature in modules): @@ -51,7 +51,7 @@ def check_codec(feature): Checks if a codec is available. :param feature: The codec to check for. - :returns: True if available, False otherwise. + :returns: ``True`` if available, ``False`` otherwise. :raises ValueError: If the codec is not defined in this version of Pillow. """ if feature not in codecs: @@ -85,7 +85,7 @@ def check_feature(feature): Checks if a feature is available. :param feature: The feature to check for. - :returns: True if available, False if unavailable, None if unknown. + :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. :raises ValueError: If the feature is not defined in this version of Pillow. """ if feature not in features: @@ -111,7 +111,8 @@ def check(feature): """ :param feature: A module, feature, or codec name. :returns: - True if the module, feature, or codec is available, False or None otherwise. + ``True`` if the module, feature, or codec is available, + ``False`` or ``None`` otherwise. """ if feature in modules: @@ -141,9 +142,9 @@ def pilinfo(out=None, supported_formats=True): This function can be called with ``python -m PIL``. :param out: - The output stream to print to. Defaults to ``sys.stdout`` if None. + The output stream to print to. Defaults to ``sys.stdout`` if ``None``. :param supported_formats: - If True, a list of all supported image file formats will be printed. + If ``True``, a list of all supported image file formats will be printed. """ if out is None: From 7119ef54fb3f3c798ae000c71b10e0763a8587c9 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 08:35:55 +0200 Subject: [PATCH 183/262] fix PyAccess docs using deferred_error --- src/PIL/PyAccess.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index 359a9491975..494f5f9f478 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -23,23 +23,29 @@ import logging import sys -from cffi import FFI +try: + from cffi import FFI + + defs = """ + struct Pixel_RGBA { + unsigned char r,g,b,a; + }; + struct Pixel_I16 { + unsigned char l,r; + }; + """ + ffi = FFI() + ffi.cdef(defs) +except ImportError as ex: + # Allow error import for doc purposes, but error out when accessing + # anything in core. + from ._util import deferred_error + + FFI = ffi = deferred_error(ex) logger = logging.getLogger(__name__) -defs = """ -struct Pixel_RGBA { - unsigned char r,g,b,a; -}; -struct Pixel_I16 { - unsigned char l,r; -}; -""" -ffi = FFI() -ffi.cdef(defs) - - class PyAccess: def __init__(self, img, readonly=False): vals = dict(img.im.unsafe_ptrs) From e134216609975e9190a427361d5339d9356d52b8 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 14 Jun 2020 16:53:30 +1000 Subject: [PATCH 184/262] Fixed typo [ci skip] Co-authored-by: Hugo van Kemenade --- docs/releasenotes/7.2.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst index aeea7e9d927..904e9d5ab32 100644 --- a/docs/releasenotes/7.2.0.rst +++ b/docs/releasenotes/7.2.0.rst @@ -25,5 +25,5 @@ Moved to ImageFileDirectory_v2 in Image.Exif Moved from the legacy :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v1` to :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v2` in :py:class:`PIL.Image.Exif`. This means that Exif RATIONALs and SIGNED_RATIONALs -as now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a +are now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a tuple with a numerator and a denominator. From 4a9afc79bf3011792339134896ca0fc9dfd4a46a Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 12:00:23 +0200 Subject: [PATCH 185/262] improve ImageShow docs --- docs/PIL.rst | 8 -- docs/reference/ImageShow.rst | 27 +++++ docs/reference/index.rst | 1 + src/PIL/ImageShow.py | 194 ++++++++++++++++++++--------------- 4 files changed, 140 insertions(+), 90 deletions(-) create mode 100644 docs/reference/ImageShow.rst diff --git a/docs/PIL.rst b/docs/PIL.rst index fe69fed620a..21adc5a163e 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -62,14 +62,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageShow` Module ------------------------ - -.. automodule:: PIL.ImageShow - :members: - :undoc-members: - :show-inheritance: - :mod:`ImageTransform` Module ---------------------------- diff --git a/docs/reference/ImageShow.rst b/docs/reference/ImageShow.rst new file mode 100644 index 00000000000..0b012d856db --- /dev/null +++ b/docs/reference/ImageShow.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageShow +.. py:currentmodule:: PIL.ImageShow + +:py:mod:`ImageShow` Module +========================== + +The :py:mod:`ImageShow` Module is used to display images. +All default viewers convert the image to be shown to PNG format. + +.. autofunction:: PIL.ImageShow.show + +.. autoclass:: WindowsViewer +.. autoclass:: MacViewer + +.. class:: UnixViewer + + The following viewers may be registered on Unix-based systems, if the given command is found: + + .. autoclass:: PIL.ImageShow.DisplayViewer + .. autoclass:: PIL.ImageShow.EogViewer + .. autoclass:: PIL.ImageShow.XVViewer + +.. autofunction:: PIL.ImageShow.register +.. autoclass:: PIL.ImageShow.Viewer + :member-order: bysource + :members: + :undoc-members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8c09e7b67f7..25c84126dd6 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -22,6 +22,7 @@ Reference ImagePath ImageQt ImageSequence + ImageShow ImageStat ImageTk ImageWin diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index fc50894236b..4a597ce9aba 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -24,6 +24,14 @@ def register(viewer, order=1): + """ + The :py:func:`register` function is used to register additional viewers. + + :param viewer: The viewer to be registered. + :param order: + A negative integer to prepend this viewer to the list, + or a positive integer to append it. + """ try: if issubclass(viewer, Viewer): viewer = viewer() @@ -40,9 +48,9 @@ def show(image, title=None, **options): Display a given image. :param image: An image object. - :param title: Optional title. Not all viewers can display the title. + :param title: Optional title. Not all viewers can display the title. :param \**options: Additional viewer options. - :returns: True if a suitable viewer was found, false otherwise. + :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. """ for viewer in _viewers: if viewer.show(image, title=title, **options): @@ -56,6 +64,10 @@ class Viewer: # main api def show(self, image, **options): + """ + The main function for displaying an image. + Converts the given image to the target format and displays it. + """ # save temporary image to disk if not ( @@ -70,25 +82,31 @@ def show(self, image, **options): # hook methods format = None + """The format to convert the image into.""" options = {} + """Additional options used to convert the image.""" def get_format(self, image): - """Return format name, or None to save as PGM/PPM""" + """Return format name, or ``None`` to save as PGM/PPM.""" return self.format def get_command(self, file, **options): + """ + Returns the command used to display the file. + Not implemented in the base class. + """ raise NotImplementedError def save_image(self, image): - """Save to temporary file, and return filename""" + """Save to temporary file and return filename.""" return image._dump(format=self.get_format(image), **self.options) def show_image(self, image, **options): - """Display given image""" + """Display the given image.""" return self.show_file(self.save_image(image), **options) def show_file(self, file, **options): - """Display given file""" + """Display the given file.""" os.system(self.get_command(file, **options)) return 1 @@ -96,104 +114,116 @@ def show_file(self, file, **options): # -------------------------------------------------------------------- -if sys.platform == "win32": +class WindowsViewer(Viewer): + """The default viewer on Windows is the default system application for PNG files.""" - class WindowsViewer(Viewer): - format = "PNG" - options = {"compress_level": 1} + format = "PNG" + options = {"compress_level": 1} + + def get_command(self, file, **options): + return ( + 'start "Pillow" /WAIT "%s" ' + "&& ping -n 2 127.0.0.1 >NUL " + '&& del /f "%s"' % (file, file) + ) - def get_command(self, file, **options): - return ( - 'start "Pillow" /WAIT "%s" ' - "&& ping -n 2 127.0.0.1 >NUL " - '&& del /f "%s"' % (file, file) - ) +if sys.platform == "win32": register(WindowsViewer) -elif sys.platform == "darwin": - class MacViewer(Viewer): - format = "PNG" - options = {"compress_level": 1} +class MacViewer(Viewer): + """The default viewer on MacOS using ``Preview.app``.""" + + format = "PNG" + options = {"compress_level": 1} - def get_command(self, file, **options): - # on darwin open returns immediately resulting in the temp - # file removal while app is opening - command = "open -a Preview.app" - command = "({} {}; sleep 20; rm -f {})&".format( - command, quote(file), quote(file) + def get_command(self, file, **options): + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a Preview.app" + command = "({} {}; sleep 20; rm -f {})&".format( + command, quote(file), quote(file) + ) + return command + + def show_file(self, file, **options): + """Display given file""" + fd, path = tempfile.mkstemp() + with os.fdopen(fd, "w") as f: + f.write(file) + with open(path, "r") as f: + subprocess.Popen( + ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"], + shell=True, + stdin=f, ) - return command - - def show_file(self, file, **options): - """Display given file""" - fd, path = tempfile.mkstemp() - with os.fdopen(fd, "w") as f: - f.write(file) - with open(path, "r") as f: - subprocess.Popen( - ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"], - shell=True, - stdin=f, - ) - os.remove(path) - return 1 + os.remove(path) + return 1 + +if sys.platform == "darwin": register(MacViewer) -else: - # unixoids +class UnixViewer(Viewer): + format = "PNG" + options = {"compress_level": 1} - class UnixViewer(Viewer): - format = "PNG" - options = {"compress_level": 1} + def get_command(self, file, **options): + command = self.get_command_ex(file, **options)[0] + return "({} {}; rm -f {})&".format(command, quote(file), quote(file)) - def get_command(self, file, **options): + def show_file(self, file, **options): + """Display given file""" + fd, path = tempfile.mkstemp() + with os.fdopen(fd, "w") as f: + f.write(file) + with open(path, "r") as f: command = self.get_command_ex(file, **options)[0] - return "({} {}; rm -f {})&".format(command, quote(file), quote(file)) - - def show_file(self, file, **options): - """Display given file""" - fd, path = tempfile.mkstemp() - with os.fdopen(fd, "w") as f: - f.write(file) - with open(path, "r") as f: - command = self.get_command_ex(file, **options)[0] - subprocess.Popen( - ["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f - ) - os.remove(path) - return 1 + subprocess.Popen( + ["im=$(cat);" + command + " $im; rm -f $im"], shell=True, stdin=f + ) + os.remove(path) + return 1 - # implementations - class DisplayViewer(UnixViewer): - def get_command_ex(self, file, **options): - command = executable = "display" - return command, executable +class DisplayViewer(UnixViewer): + """The ImageMagick ``display`` command.""" - if shutil.which("display"): - register(DisplayViewer) + def get_command_ex(self, file, **options): + command = executable = "display" + return command, executable - class EogViewer(UnixViewer): - def get_command_ex(self, file, **options): - command = executable = "eog" - return command, executable - if shutil.which("eog"): - register(EogViewer) +class EogViewer(UnixViewer): + """The GNOME Image Viewer ``eog`` command.""" + + def get_command_ex(self, file, **options): + command = executable = "eog" + return command, executable - class XVViewer(UnixViewer): - def get_command_ex(self, file, title=None, **options): - # note: xv is pretty outdated. most modern systems have - # imagemagick's display command instead. - command = executable = "xv" - if title: - command += " -name %s" % quote(title) - return command, executable +class XVViewer(UnixViewer): + """ + The X Viewer ``xv`` command. + This viewer supports the ``title`` parameter. + """ + + def get_command_ex(self, file, title=None, **options): + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + command += " -name %s" % quote(title) + return command, executable + + +if sys.platform not in ("win32", "darwin"): # unixoids + if shutil.which("display"): + register(DisplayViewer) + if shutil.which("eog"): + register(EogViewer) if shutil.which("xv"): register(XVViewer) From f19e3ec1246d28bab3092d3c53cc4b17b55cdafd Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 13:46:52 +0200 Subject: [PATCH 186/262] promote JpegPresets from autodoc section --- docs/PIL.rst | 8 -------- docs/reference/JpegPresets.rst | 11 +++++++++++ docs/reference/index.rst | 1 + src/PIL/JpegPresets.py | 9 +++++---- 4 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 docs/reference/JpegPresets.rst diff --git a/docs/PIL.rst b/docs/PIL.rst index 21adc5a163e..e7939554d7e 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -70,14 +70,6 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`JpegPresets` Module -------------------------- - -.. automodule:: PIL.JpegPresets - :members: - :undoc-members: - :show-inheritance: - :mod:`PaletteFile` Module ------------------------- diff --git a/docs/reference/JpegPresets.rst b/docs/reference/JpegPresets.rst new file mode 100644 index 00000000000..bf93f891faa --- /dev/null +++ b/docs/reference/JpegPresets.rst @@ -0,0 +1,11 @@ +.. py:currentmodule:: PIL.JpegPresets + +:py:mod:`JpegPresets` Module +============================ + +.. automodule:: PIL.JpegPresets + + .. data:: presets + :annotation: + + A dict of all supported presets. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 25c84126dd6..9f9e7142b02 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -28,6 +28,7 @@ Reference ImageWin ExifTags TiffTags + JpegPresets PSDraw PixelAccess PyAccess diff --git a/src/PIL/JpegPresets.py b/src/PIL/JpegPresets.py index 118dab061f9..09691d79dbd 100644 --- a/src/PIL/JpegPresets.py +++ b/src/PIL/JpegPresets.py @@ -1,9 +1,11 @@ """ JPEG quality settings equivalent to the Photoshop settings. +Can be used when saving JPEG files. -More presets can be added to the presets dict if needed. - -Can be use when saving JPEG file. +The following presets are available by default: +``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, +``low``, ``medium``, ``high``, ``maximum``. +More presets can be added to the :py:data:`presets` dict if needed. To apply the preset, specify:: @@ -21,7 +23,6 @@ im.save("image_name.jpg", quality="web_high") - Subsampling ----------- From d2f7e46c5dfacfa732a4e2ef11f4585737fa93ca Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 13:47:59 +0200 Subject: [PATCH 187/262] convert comments into docstrings in autodoc files --- src/PIL/BdfFontFile.py | 14 ++++++-------- src/PIL/ContainerIO.py | 8 +++++--- src/PIL/FontFile.py | 7 ++----- src/PIL/GdImageFile.py | 28 ++++++++++++++++------------ src/PIL/GimpGradientFile.py | 21 +++++++++++---------- src/PIL/GimpPaletteFile.py | 4 +--- src/PIL/ImageDraw2.py | 4 ++++ src/PIL/PaletteFile.py | 4 +--- src/PIL/PcfFontFile.py | 5 +---- src/PIL/TarIO.py | 6 ++---- src/PIL/WalImageFile.py | 17 ++++++++++------- src/PIL/_binary.py | 4 ++++ 12 files changed, 63 insertions(+), 59 deletions(-) diff --git a/src/PIL/BdfFontFile.py b/src/PIL/BdfFontFile.py index 7a485cf8009..102b72e1d5a 100644 --- a/src/PIL/BdfFontFile.py +++ b/src/PIL/BdfFontFile.py @@ -17,12 +17,12 @@ # See the README file for information on usage and redistribution. # +""" +Parse X Bitmap Distribution Format (BDF) +""" -from . import FontFile, Image -# -------------------------------------------------------------------- -# parse X Bitmap Distribution Format (BDF) -# -------------------------------------------------------------------- +from . import FontFile, Image bdf_slant = { "R": "Roman", @@ -78,11 +78,9 @@ def bdf_char(f): return id, int(props["ENCODING"]), bbox, im -## -# Font file plugin for the X11 BDF format. - - class BdfFontFile(FontFile.FontFile): + """Font file plugin for the X11 BDF format.""" + def __init__(self, fp): super().__init__() diff --git a/src/PIL/ContainerIO.py b/src/PIL/ContainerIO.py index 5bb0086f6e7..45e80b39af7 100644 --- a/src/PIL/ContainerIO.py +++ b/src/PIL/ContainerIO.py @@ -14,14 +14,16 @@ # See the README file for information on usage and redistribution. # -## -# A file object that provides read access to a part of an existing -# file (for example a TAR file). import io class ContainerIO: + """ + A file object that provides read access to a part of an existing + file (for example a TAR file). + """ + def __init__(self, file, offset, length): """ Create file object. diff --git a/src/PIL/FontFile.py b/src/PIL/FontFile.py index 979a1e33c81..4243b28b65b 100644 --- a/src/PIL/FontFile.py +++ b/src/PIL/FontFile.py @@ -23,18 +23,15 @@ def puti16(fp, values): - # write network order (big-endian) 16-bit sequence + """write network order (big-endian) 16-bit sequence""" for v in values: if v < 0: v += 65536 fp.write(_binary.o16be(v)) -## -# Base class for raster font file handlers. - - class FontFile: + """Base class for raster font file handlers.""" bitmap = None diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index b3ab01a4ebd..abdc05f90a6 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -14,26 +14,30 @@ # -# NOTE: This format cannot be automatically recognized, so the -# class is not registered for use with Image.open(). To open a -# gd file, use the GdImageFile.open() function instead. +""" +.. note:: + This format cannot be automatically recognized, so the + class is not registered for use with :py:func:`PIL.Image.open()`. To open a + gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. -# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This -# implementation is provided for convenience and demonstrational -# purposes only. +.. warning:: + THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This + implementation is provided for convenience and demonstrational + purposes only. +""" from . import ImageFile, ImagePalette, UnidentifiedImageError from ._binary import i8, i16be as i16, i32be as i32 -## -# Image plugin for the GD uncompressed format. Note that this format -# is not supported by the standard Image.open function. To use -# this plugin, you have to import the GdImageFile module and -# use the GdImageFile.open function. - class GdImageFile(ImageFile.ImageFile): + """ + Image plugin for the GD uncompressed format. Note that this format + is not supported by the standard :py:func:`PIL.Image.open()` function. To use + this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and + use the :py:func:`PIL.GdImageFile.open()` function. + """ format = "GD" format_description = "GD uncompressed images" diff --git a/src/PIL/GimpGradientFile.py b/src/PIL/GimpGradientFile.py index 1cacf5718dc..7ab7f9990ac 100644 --- a/src/PIL/GimpGradientFile.py +++ b/src/PIL/GimpGradientFile.py @@ -13,17 +13,19 @@ # See the README file for information on usage and redistribution. # +""" +Stuff to translate curve segments to palette values (derived from +the corresponding code in GIMP, written by Federico Mena Quintero. +See the GIMP distribution for more information.) +""" + + from math import log, pi, sin, sqrt from ._binary import o8 -# -------------------------------------------------------------------- -# Stuff to translate curve segments to palette values (derived from -# the corresponding code in GIMP, written by Federico Mena Quintero. -# See the GIMP distribution for more information.) -# - EPSILON = 1e-10 +"""""" # Enable auto-doc for data member def linear(middle, pos): @@ -58,6 +60,7 @@ def sphere_decreasing(middle, pos): SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] +"""""" # Enable auto-doc for data member class GradientFile: @@ -98,11 +101,9 @@ def getpalette(self, entries=256): return b"".join(palette), "RGBA" -## -# File handler for GIMP's gradient format. - - class GimpGradientFile(GradientFile): + """File handler for GIMP's gradient format.""" + def __init__(self, fp): if fp.readline()[:13] != b"GIMP Gradient": diff --git a/src/PIL/GimpPaletteFile.py b/src/PIL/GimpPaletteFile.py index e3060ab8a8f..10fd3ad8174 100644 --- a/src/PIL/GimpPaletteFile.py +++ b/src/PIL/GimpPaletteFile.py @@ -18,11 +18,9 @@ from ._binary import o8 -## -# File handler for GIMP's palette format. - class GimpPaletteFile: + """File handler for GIMP's palette format.""" rawmode = "RGB" diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index 20b5fe4c499..f0b4698f3df 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -16,6 +16,10 @@ # See the README file for information on usage and redistribution. # + +"""WCK-style drawing interface operations""" + + from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath diff --git a/src/PIL/PaletteFile.py b/src/PIL/PaletteFile.py index 73f1b4b27c4..6ccaa1f536f 100644 --- a/src/PIL/PaletteFile.py +++ b/src/PIL/PaletteFile.py @@ -15,11 +15,9 @@ from ._binary import o8 -## -# File handler for Teragon-style palette files. - class PaletteFile: + """File handler for Teragon-style palette files.""" rawmode = "RGB" diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index c463533cd01..f8836ad888c 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -48,11 +48,8 @@ def sz(s, o): return s[o : s.index(b"\0", o)] -## -# Font file plugin for the X11 PCF format. - - class PcfFontFile(FontFile.FontFile): + """Font file plugin for the X11 PCF format.""" name = "name" diff --git a/src/PIL/TarIO.py b/src/PIL/TarIO.py index ede64645358..d108362fc9f 100644 --- a/src/PIL/TarIO.py +++ b/src/PIL/TarIO.py @@ -18,12 +18,10 @@ from . import ContainerIO -## -# A file object that provides read access to a given member of a TAR -# file. - class TarIO(ContainerIO.ContainerIO): + """A file object that provides read access to a given member of a TAR file.""" + def __init__(self, tarfile, file): """ Create file object. diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index d5a5c8e67bb..b578d698181 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -12,13 +12,16 @@ # See the README file for information on usage and redistribution. # -# NOTE: This format cannot be automatically recognized, so the reader -# is not registered for use with Image.open(). To open a WAL file, use -# the WalImageFile.open() function instead. +""" +This reader is based on the specification available from: +https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml +and has been tested with a few sample files found using google. -# This reader is based on the specification available from: -# https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml -# and has been tested with a few sample files found using google. +.. note:: + This format cannot be automatically recognized, so the reader + is not registered for use with :py:func:`PIL.Image.open()`. + To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. +""" import builtins @@ -31,7 +34,7 @@ def open(filename): Load texture from a Quake2 WAL texture file. By default, a Quake2 standard palette is attached to the texture. - To override the palette, use the putpalette method. + To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method. :param filename: WAL file name, or an opened file handle. :returns: An image instance. diff --git a/src/PIL/_binary.py b/src/PIL/_binary.py index 529b8c94b78..5564f450de8 100644 --- a/src/PIL/_binary.py +++ b/src/PIL/_binary.py @@ -11,6 +11,10 @@ # See the README file for information on usage and redistribution. # + +"""Binary input/output support routines.""" + + from struct import pack, unpack_from From 05932c4cbc9a57bd20063e424d9b5b71b8949f6a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Jun 2020 22:23:19 +1000 Subject: [PATCH 188/262] Updated harfbuzz to 2.6.7 --- Tests/images/variation_adobe.png | Bin 1479 -> 1482 bytes Tests/images/variation_adobe_axes.png | Bin 1448 -> 1439 bytes Tests/images/variation_adobe_name.png | Bin 1454 -> 1461 bytes .../images/variation_adobe_older_harfbuzz.png | Bin 0 -> 1479 bytes .../variation_adobe_older_harfbuzz_axes.png | Bin 0 -> 1448 bytes .../variation_adobe_older_harfbuzz_name.png | Bin 0 -> 1454 bytes Tests/test_imagefont.py | 44 +++++++++--------- winbuild/build_prepare.py | 6 +-- 8 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 Tests/images/variation_adobe_older_harfbuzz.png create mode 100644 Tests/images/variation_adobe_older_harfbuzz_axes.png create mode 100644 Tests/images/variation_adobe_older_harfbuzz_name.png diff --git a/Tests/images/variation_adobe.png b/Tests/images/variation_adobe.png index 71b879bc5a214d09e4b04898327324ce2432e9c4..3ac326f51966bc36b869ab74b5c487844515d102 100644 GIT binary patch delta 1425 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 LNkvXXu0mjf_V=Kc diff --git a/Tests/images/variation_adobe_axes.png b/Tests/images/variation_adobe_axes.png index 9376c1d7b8fc6b11907267aec1a32ad73a0c45fd..93cad98ff0c0c230206f864e5dd9d15505715775 100644 GIT binary patch delta 1317 zcmV+=1={+k3!e*+B!6Z}L_t(|ob8!QNHS{}$H$cD;-U$@+7!`+AV>?W=nh3>5iJ5& z-5^~=E-W||?V>0Mq7ot62SE^BAV`CvqzhKqMyV_+D(IeOH@lfA&3E|X z{jJ9L{rk)_KgaQ%k>_%`D8kq9`@b9blOVDH5Lp0-EC56n0Fy`sD1QW>CU`s^>*_!3 z0srT+5OsBRK|w+2Wlv8J+EyqOAt52m_T%GYe}Dfswa1>Rsj1b~Rf?ioT3P}E0=}oH z>ob^`L?ZDz&S10I@Ts)4v@aItj?~mtuf6%gSFc$a85v*y06<|buv)D~qw(9z{!Cdj zGc#{*Z{J?_XUZBL9)ISf?x$bW+S*!`O656@7xBCZ?L}xe=N%lqtPf5i71NV z@puIV1xlqdFE5W-ln4ZZ&d$!aw>K2t+S;0&oW$#=r>E<5I)9|qY&MULja5}uF|!(t z#$+w|D9F zdbGr&Y5L^k1fP9A-rE=Y9bmY*x$(U>7H}(z&*!7e@$oV1 zeZ9QA;D5VQDwV$X#sY31{sjjIqwR}}3)XceCMNKg0|NsK3k$5Pv4&e&F)=Y{duL~d zU6HM=ExBCY+uK`TU(bxVTrPz|@%Z@o4K0FTeNN&<-aFOn>grIYrly9)UT$x16A}_~ zb90?e=iS{M{u9yM-0VwlEZ|m_UavlqCU%ybnwzf9UohBzIqX51betLR>ALzfpWZCWZ_4Rd~PB%C>h|5x|)mp9A zXf%4YW-^)Ljwu1i;IgT zB_((@jYi`py05Pf&1Pj~SuB?A?d|I7YJaIz3KfI{lf`^5KH$BlYj(R`rBdN52?jo& zFO$g*4i5g)lQ%w?!UGIWr&BJMy9Godk-=a99j_bgZ+BSWa5$EhmNqvx?RI-WKtN1P zOioS?{+;?>Ha0fq=H{-iuR}vaB@#($YN{{)fl?IpTV?&}kiYK%5Lp0-EC56n0Fy`s bM;5@};f|xBWRC#L00000NkvXXu0mjfuBeav delta 1329 zcmV-112&%%M5`) zz`Obnd%*8n7NWMc*5BVBz3l1fLC0#fIxsMhIevY89T*t!PeIn8R+AVG5J3GUppP!$}$;l#-$Y3zI-=uTAB+G0zOC%B|d3t*KTePdI ziwXJo_?S#4fA(~>T8-8!EiJ`Eot>Q&MfLahyWgZsyd*0tD~rj@%F1F#kB^U0DV0ja zPPo0j4G9TBIisVa%(%s35sSs;<>ek}(k0%IH8(ehGPPPQJHckNp@i`8aEC>Phlf#4 zXlUro&CSQhhg>dCOiX-vd2zoNT{B&5Spr8PyRaI4W zbaY6i(*6B?k2J{z0nPVb7r(*2ySqd8hHPXqnZ;sZj+IJfL_`E0y}Z1fn3#BadO{;_ zZ*O9;m|f@K;2=Ie9{(8Xbh^UALXR}b3ErCVBF^8mpr9b`{e*>uIVpvH3K$+99z4|~ z7rY@$e<&29%+u3T-Ye2H{qgaEudC5$Jk=x@yz%faARqu8UtL|fQ)+T@5`Q^3IJmgD z=uSZp-jEd)6@`xX_V#$!+1}n(sZ_naz4i6=%m_`>YPI_H_0`=LIl}uhi5GGH39qfK zMVZyr)%=!we0+?Hi_6K$vD@uWPfz%-M00bqfBQ{x!5gv+1_R2JNF*mGC%?+PzrQ~? zIB*zJDwPt61V#0FJ*p`b3O6=3+-#ByUfRJc6bh8AP$+CR8$0Lu`8h8yucV}e9qR7x zrYLG~a1bA?sHi|S6A}{M-{092@#o+d7Z-rjbA*>G0cDr4rQhiI1$z-dmtDqBS;D5Wr z8=K9xyu7@%wPm$heSLkSqN1|1v+*yK$JyN6oS&b+zrPO(3X;p^DJd!Z{tW#M2D1KK n$lv#ooG+7V1s@pzkp;D diff --git a/Tests/images/variation_adobe_name.png b/Tests/images/variation_adobe_name.png index 9e5fe70e539e1640d6e9d807c2841487369d25b3..acb0df63c57628b321eb635ef0f588c374d838dd 100644 GIT binary patch delta 1326 zcmV+}1=0Gh3$+W7B!7HKL_t(|ob8%jNKKq4-~!7a5K_EP%q}yF4lzz6@efjLz`bow9E#!85-Np|Kh;; zza95+p0gbzcz;)C@AJIR`n$dgY6GJi-fOOyfK-QB{U z!to&nWtW$iGso}WzvmeGEBO8Xsi`T1kkM$oe*JoOic;xdGQWKJl5iX$R8v!fhwk0G zM~`=Pb)~H@5O?q1P1su|aNV<%N@YPo0mtOBvNDdD!r}FLvtE;{EYSJ?by?rOeOp>u z%6eI@vPw%!xfcZ`3x9#v>!k$v@i_ekfBW`rgiu{w9rvQAPoH);9Fa)m!i5X0(CX^y zyLaz`!Jt$s)#-E=izPolAJj1yCMG7bUK1e>8+_?&nM@|c`BD@GyLI5l-rnBLn>Rfk4}U^PrBXQ@4wK0=KR-V- zG!%_SJs!{e{Jhuey?F5=Gv(#Wm+R~6U0q#x9lPCbwOZ+8m&>)hyxiN{OMkq2^{U-& zkHunYwOXxKr?{=j2;Q3UA}Ow{gM)+0%1Sy-p-?O>Eis|-@o}a?VPRn?6k^T&@ZrOW z6aVk37Kz{C`2OB^z<~p-k=CBtD~cXNxOglJ|2q4<9fZGi9UPw?CA9N z_I7-s!NEa#JQ|H&x^xL4^x(k*-n~H)R#qSokVqs<+UDjaE1{~Yiiw)d=A#vU{`{%a z>6je3T)wupMo|>~AZ4@J@M{g!8xMq)g>O1qt(FxH27iNUwVFxLXfz3n%*@Q-YgJcQ zzkT~wEEboQmF?~A@$QWW!pf?ssKC?8G0vVno12@9M{{y=lC5R4+0dV;RH~Jgl}vi$ zfv~c)S}o#CNl8hvwYIjld?gq2lPRKy%_Y;1(XVSaV| zet%nA8*9+%bbkK)nSXO|gq4NgIv*Y$(l_x?_V@R%UAy-C_wU8U#m>%7W;`4Y-@0`x z3te)D_il$5Nw$g3&CTIydc8gz4j)a6#bQpU^MB*Vj|q#=?>QeoeoRr+uV24PN=opW z&!0c%*BcLnl@*W2&1N&6dHM3?U@(a1ghHW)h6aRCV`Jmd3L_&U2%(CKifA-SkNA8( zyr5F4+}_@1&BC9h&!0cf`y5#a{W;p5HC&3M7u+FI7cbLY-6(UT`n(${P(7JKpHMI;j8f6Ab2 zzu!MJH1zP{!#j8Gpg-y8=y>wv$=KM~(OC-%3#ng88w>{4chK#2x3;!237wstZnv8b zO-xKUolaH-l}gpq)ARcEYdU9OV1RY?S%0logTa6hnw*^E z78X`kR>I-1OeQlLjTVaqe>}`Co12?&-n{wx^{YamFdB_lu3Sm|UN9}kD(hc|`~rLr klT8In93Zj)5Lp2J1L6mgG~L9#cK`qY07*qoM6N<$f@oH){r~^~ delta 1325 zcmV+|1=9Mp3$6>0B!6{DL_t(|ob8%RNE=-gfNwNDVq#1{B=|%UMBRu|A1xBajSqZm zbf*>3q9TZ(K|wUS(1nkRq6=Gm;S0fbp{p!(ApsRZkRl-(>kBbj8?Z^mN|NbfVE${= zWKN9ZU%20Da?iPE&ix!OGn4Qrib4c-c)fN5{}MzN03r(jk&(9^k-iRpfNNR2XVB5n z@$yUH4n8P5H8qtv-rU?|8G2oGI^Dv;0zyckP?VOIKC2ex3MTXH?CjBTgiv;NHXdqe zX`#o}YPHMyFHLiE^P^3=0oy%GEEb1`hO$hKi;H8K$pIRT##5!($^xzb|0C<-;$mlK z$5YkV%8HGRWnUDO{Gw=oG#bit-y*x-;H#^v5kmR-`Rt1Z1_m}ZHd-u}H*elNm9w|E zx3RHdG#dT<{KCS*luBhtNC^8=uw-Ut#*^jT-tF)r?DtN)-99%r7at$*&<3B+FDxuP zK0bcx6pyz3RA_vBJSHYaAP{(V;jmkWXL5ag{r>&?rKKf=kVGPXX>4qakB?tlTN@r8 zwpy)AOG|5OYZ{FvB_)N(+K zgpkcy@i_{6QP zEibSC_Ex1*X}8;dnW({F5Q#)Q9&d4R@%c?UhdWsf4Gm0wYilbWvfJ%ZQBh2^x3||} zZhd_{e!8KdA$r_uwZ47(79rHu*5;8Wox_nVgTdhA#=LO?(O9`*J0eY8kHK>@mtL?Y4a z^`2_dB^=3;$z+H%a=HA`BFD$aLZJ}P92pt$V3RK4=zZ4J)fIEBR4SeKWH1;WEfN_S z+1c5Nk9_@q`jz7FXPz9%^7Hd!jxR4S-L4ZH9E^WlSy}n?>680y0pUniL_`F0e0X?h zHk;ijqSNW>>gpT@RVvlV$;q>tV+}{L@LT6UfBw)n@gRQx{#{&Le0O)Zy}jMk)WnRN z&E~4AD$jK361R6dyok%ab9s3g&yR|VGMmj0(`+_>n@Xkn_U+rl5dD&~tE-EmsGFM` zxm=DH{QUXz^P6-DN3!g8dvbCzKIPrJcSfTT&oP-yMMXsjq4M%_hv}oEqX?n2v^1;L zN{_6ptl-teV)5ze>BIBl&(cXrNp3HehP%g|gM$OHSd7mS2n1ziWj#GTA3uKl@Zkd; zmC0m(KY#wjCvIi87CnhFpYHFBhQ&ZE# z!~`9hnVC_kR1VW65=mcQ-~9YMQ(6l zu~@WPtzNG;o6Q1&K%r15l}h~a(6byJ9j&geo}ZtKL?VSkk(HI@{Jr2)ypF7Y6!P@} jJ(FJrK$BVp5f=ChdVQEGH*j zsPhV&{MTb_MLP2Rxur*+v++qVONuCB)6ZU{thsH=;$xC!1&l*{Epp^(ev zR@E3-dbPb-R5@g)7ewwB#f^;z$_mk3D?(&sBp`TeXPZu^iwQhtQ4vVphcn`lwW(C} zmAs#$pLrD(&Xe8^cfev>EX@a^lKuui|27maqNTW`r02;K3Wefc97Q6zm@^M^?u$es zu^4mcP-&9W!Gp^S3o}$Sxw$(oATZEqH#$0cOF0{sle7E!wK=r5y1KfymSI>!;7L=F zD?7hhLk7xwwHJ~JbUNlv=*g3bt@4@4l@+qTzXl{{ZK3SVpWEBp`T0QK0B`TExw*Sh z)b#X&C=|hv(c9}!L@SvT!HXowfw%Nqa8ROto*!q#&{suk>+7Dzx}aABEIIAQ9_i`nX=t$b1@@7bKRmvGzLS-erBpaZ zr}_Bt<7%-e(PI(B(JB(FT>E(^mb+Cvh`Tr1e?t+DA3A?4huWC z`YG59li({zy01348*HdvzO}X(H2~k)+6aqwL!&n*C%vEP6GLW#Q_|8LJ-@CkvZkz5 zw~?Kl-Jbpd#;|}uM#R{a1Yj58B&j z6&PqOpFb4!dTOdRS=m=&IWREr@#Du+otZ|Or#deB@Knzd6CM3^>EE6_gEYEGZMt)* zBXi^WrwJjs}g#DF$ICBG#Zo192y?} zkDX1S{G;%!iEY7RFvd3`A|h0K#{1D|G!BOY27}$&gu+A=OZ8td^jPddD&CN}C<;%>jjaV>QBd1gIyW#6Qp>9jV2Nj7}ZC$re P0W=`cACGJHjl23kUTwM} literal 0 HcmV?d00001 diff --git a/Tests/images/variation_adobe_older_harfbuzz_axes.png b/Tests/images/variation_adobe_older_harfbuzz_axes.png new file mode 100644 index 0000000000000000000000000000000000000000..9376c1d7b8fc6b11907267aec1a32ad73a0c45fd GIT binary patch literal 1448 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 z Date: Sun, 14 Jun 2020 14:39:48 +0200 Subject: [PATCH 189/262] add docs for ImageDraw2 based on ImageDraw, fix ImageDraw links --- docs/PIL.rst | 3 +- docs/reference/ImageDraw.rst | 38 ++++++++++---------- src/PIL/ImageDraw2.py | 70 +++++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index e7939554d7e..07cacf31f63 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -52,13 +52,12 @@ can be found here. :undoc-members: :show-inheritance: -.. intentionally skipped documenting this because it's not documented anywhere - :mod:`ImageDraw2` Module ------------------------ .. automodule:: PIL.ImageDraw2 :members: + :member-order: bysource :undoc-members: :show-inheritance: diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 782b0434b68..495a7d117cd 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -124,7 +124,7 @@ Example: Draw Multiline Text Functions --------- -.. py:class:: PIL.ImageDraw.Draw(im, mode=None) +.. py:method:: Draw(im, mode=None) Creates an object that can be used to draw in the given image. @@ -140,13 +140,13 @@ Functions Methods ------- -.. py:method:: PIL.ImageDraw.ImageDraw.getfont() +.. py:method:: ImageDraw.getfont() Get the current default font. :returns: An image font. -.. py:method:: PIL.ImageDraw.ImageDraw.arc(xy, start, end, fill=None, width=0) +.. py:method:: ImageDraw.arc(xy, start, end, fill=None, width=0) Draws an arc (a portion of a circle outline) between the start and end angles, inside the given bounding box. @@ -162,7 +162,7 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: PIL.ImageDraw.ImageDraw.bitmap(xy, bitmap, fill=None) +.. py:method:: ImageDraw.bitmap(xy, bitmap, fill=None) Draws a bitmap (mask) at the given position, using the current fill color for the non-zero portions. The bitmap should be a valid transparency mask @@ -173,7 +173,7 @@ Methods To paste pixel data into an image, use the :py:meth:`~PIL.Image.Image.paste` method on the image itself. -.. py:method:: PIL.ImageDraw.ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1) +.. py:method:: ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1) Same as :py:meth:`~PIL.ImageDraw.ImageDraw.arc`, but connects the end points with a straight line. @@ -187,7 +187,7 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: PIL.ImageDraw.ImageDraw.ellipse(xy, fill=None, outline=None, width=1) +.. py:method:: ImageDraw.ellipse(xy, fill=None, outline=None, width=1) Draws an ellipse inside the given bounding box. @@ -200,9 +200,9 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: PIL.ImageDraw.ImageDraw.line(xy, fill=None, width=0, joint=None) +.. py:method:: ImageDraw.line(xy, fill=None, width=0, joint=None) - Draws a line between the coordinates in the **xy** list. + Draws a line between the coordinates in the ``xy`` list. :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or numeric values like ``[x, y, x, y, ...]``. @@ -216,7 +216,7 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: PIL.ImageDraw.ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1) +.. py:method:: ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1) Same as arc, but also draws straight lines between the end points and the center of the bounding box. @@ -233,7 +233,7 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: PIL.ImageDraw.ImageDraw.point(xy, fill=None) +.. py:method:: ImageDraw.point(xy, fill=None) Draws points (individual pixels) at the given coordinates. @@ -241,7 +241,7 @@ Methods numeric values like ``[x, y, x, y, ...]``. :param fill: Color to use for the point. -.. py:method:: PIL.ImageDraw.ImageDraw.polygon(xy, fill=None, outline=None) +.. py:method:: ImageDraw.polygon(xy, fill=None, outline=None) Draws a polygon. @@ -254,7 +254,7 @@ Methods :param outline: Color to use for the outline. :param fill: Color to use for the fill. -.. py:method:: PIL.ImageDraw.ImageDraw.rectangle(xy, fill=None, outline=None, width=1) +.. py:method:: ImageDraw.rectangle(xy, fill=None, outline=None, width=1) Draws a rectangle. @@ -267,13 +267,13 @@ Methods .. versionadded:: 5.3.0 -.. py:method:: PIL.ImageDraw.ImageDraw.shape(shape, fill=None, outline=None) +.. py:method:: ImageDraw.shape(shape, fill=None, outline=None) .. warning:: This method is experimental. Draw a shape. -.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None) +.. py:method:: ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None, stroke_width=0, stroke_fill=None) Draws the string at the given position. @@ -325,7 +325,7 @@ Methods .. versionadded:: 6.2.0 -.. py:method:: PIL.ImageDraw.ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None) +.. py:method:: ImageDraw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=4, align="left", direction=None, features=None, language=None) Draws the string at the given position. @@ -362,7 +362,7 @@ Methods .. versionadded:: 6.0.0 -.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) +.. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) Return the size of the given string, in pixels. @@ -401,7 +401,7 @@ Methods .. versionadded:: 6.2.0 -.. py:method:: PIL.ImageDraw.ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) +.. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) Return the size of the given string, in pixels. @@ -439,7 +439,7 @@ Methods .. versionadded:: 6.2.0 -.. py:method:: PIL.ImageDraw.getdraw(im=None, hints=None) +.. py:method:: getdraw(im=None, hints=None) .. warning:: This method is experimental. @@ -450,7 +450,7 @@ Methods :param hints: An optional list of hints. :returns: A (drawing context, drawing resource factory) tuple. -.. py:method:: PIL.ImageDraw.floodfill(image, xy, value, border=None, thresh=0) +.. py:method:: floodfill(image, xy, value, border=None, thresh=0) .. warning:: This method is experimental. diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index f0b4698f3df..b14b68e3ee9 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -17,24 +17,34 @@ # -"""WCK-style drawing interface operations""" +""" +(Experimental) WCK-style drawing interface operations + +.. seealso:: :py:mod:`PIL.ImageDraw` +""" from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath class Pen: + """Stores an outline color and width.""" + def __init__(self, color, width=1, opacity=255): self.color = ImageColor.getrgb(color) self.width = width class Brush: + """Stores a fill color""" + def __init__(self, color, opacity=255): self.color = ImageColor.getrgb(color) class Font: + """Stores a TrueType font and color""" + def __init__(self, color, file, size=12): # FIXME: add support for bitmap fonts self.color = ImageColor.getrgb(color) @@ -42,6 +52,10 @@ def __init__(self, color, file, size=12): class Draw: + """ + (Experimental) WCK-style drawing interface + """ + def __init__(self, image, size=None, color=None): if not hasattr(image, "im"): image = Image.new(image, size, color) @@ -77,35 +91,89 @@ def render(self, op, xy, pen, brush=None): getattr(self.draw, op)(xy, fill=fill, outline=outline) def settransform(self, offset): + """Sets a transformation offset.""" (xoffset, yoffset) = offset self.transform = (1, 0, xoffset, 0, 1, yoffset) def arc(self, xy, start, end, *options): + """ + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc` + """ self.render("arc", xy, start, end, *options) def chord(self, xy, start, end, *options): + """ + Same as :py:meth:`~PIL.ImageDraw2.ImageDraw.arc`, but connects the end points + with a straight line. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` + """ self.render("chord", xy, start, end, *options) def ellipse(self, xy, *options): + """ + Draws an ellipse inside the given bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` + """ self.render("ellipse", xy, *options) def line(self, xy, *options): + """ + Draws a line between the coordinates in the ``xy`` list. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` + """ self.render("line", xy, *options) def pieslice(self, xy, start, end, *options): + """ + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice` + """ self.render("pieslice", xy, start, end, *options) def polygon(self, xy, *options): + """ + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon` + """ self.render("polygon", xy, *options) def rectangle(self, xy, *options): + """ + Draws a rectangle. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` + """ self.render("rectangle", xy, *options) def text(self, xy, text, font): + """ + Draws the string at the given position. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` + """ if self.transform: xy = ImagePath.Path(xy) xy.transform(self.transform) self.draw.text(xy, text, font=font.font, fill=font.color) def textsize(self, text, font): + """ + Return the size of the given string, in pixels. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize` + """ return self.draw.textsize(text, font=font.font) From eb150c5518ac25fa2e423e6ef471a00ddb1ab44d Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 14:46:14 +0200 Subject: [PATCH 190/262] sort docs index --- docs/reference/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 9f9e7142b02..df7a63b0429 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -7,8 +7,8 @@ Reference Image ImageChops - ImageColor ImageCms + ImageColor ImageDraw ImageEnhance ImageFile From 7d1f5ba91bb3fdeaf68e0f569ff224c6e1ca3c39 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 14 Jun 2020 23:08:21 +1000 Subject: [PATCH 191/262] Removed test skips for MinGW --- Tests/test_imagefont.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index bf7cda5e893..e20950ca6a2 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -13,7 +13,6 @@ assert_image_equal, assert_image_similar, assert_image_similar_tofile, - is_mingw, is_pypy, is_win32, skip_unless_feature, @@ -677,7 +676,6 @@ def _check_text(self, font, path, epsilon): else: raise - @pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test") def test_variation_set_by_name(self): font = self.get_font() @@ -702,7 +700,6 @@ def test_variation_set_by_name(self): font.set_variation_by_name(name) self._check_text(font, "Tests/images/variation_tiny_name.png", 40) - @pytest.mark.skipif(is_mingw(), reason="epsilon too high for meaningful test") def test_variation_set_by_axes(self): font = self.get_font() From 255bd0caef8118b909edb2a976caa8385f77c7c3 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 16:39:23 +0200 Subject: [PATCH 192/262] create docs section Internal Modules --- docs/PIL.rst | 9 ------- docs/reference/internal_design.rst | 2 +- docs/reference/internal_modules.rst | 38 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 docs/reference/internal_modules.rst diff --git a/docs/PIL.rst b/docs/PIL.rst index 07cacf31f63..d098e1bb58c 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -123,12 +123,3 @@ can be found here. :members: :undoc-members: :show-inheritance: - -:mod:`_binary` Module ---------------------- - -.. automodule:: PIL._binary - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/reference/internal_design.rst b/docs/reference/internal_design.rst index bbc9050cff0..5f911db51d1 100644 --- a/docs/reference/internal_design.rst +++ b/docs/reference/internal_design.rst @@ -7,4 +7,4 @@ Internal Reference Docs open_files limits block_allocator - + internal_modules diff --git a/docs/reference/internal_modules.rst b/docs/reference/internal_modules.rst new file mode 100644 index 00000000000..7a7967d231c --- /dev/null +++ b/docs/reference/internal_modules.rst @@ -0,0 +1,38 @@ +Internal Modules +================ + +:mod:`_binary` Module +--------------------- + +.. automodule:: PIL._binary + :members: + :undoc-members: + :show-inheritance: + +:mod:`_tkinter_finder` Module +----------------------------- + +.. automodule:: PIL._tkinter_finder + :members: + :undoc-members: + :show-inheritance: + +:mod:`_util` Module +------------------- + +.. automodule:: PIL._util + :members: + :undoc-members: + :show-inheritance: + +:mod:`_version` Module +---------------------- + +.. module:: PIL._version + +.. data:: __version__ + :annotation: + :type: str + + This is the master version number for Pillow, + all other uses reference this module. From d4c432dd2f87a6ae0f6f157ca14cf22b32db6b5c Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 16:40:28 +0200 Subject: [PATCH 193/262] add autodocs for UnidentifiedImageError --- docs/PIL.rst | 8 ++++++++ src/PIL/__init__.py | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/docs/PIL.rst b/docs/PIL.rst index d098e1bb58c..222322b0d53 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -4,6 +4,14 @@ PIL Package (autodoc of remaining modules) Reference for modules whose documentation has not yet been ported or written can be found here. +:mod:`PIL` Module +------------------------- + +.. py:module:: PIL + +.. autoexception:: UnidentifiedImageError + :show-inheritance: + :mod:`BdfFontFile` Module ------------------------- diff --git a/src/PIL/__init__.py b/src/PIL/__init__.py index 81b87e012cd..d225ed134fc 100644 --- a/src/PIL/__init__.py +++ b/src/PIL/__init__.py @@ -132,4 +132,8 @@ def __le__(self, other): class UnidentifiedImageError(OSError): + """ + Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified. + """ + pass From c40b0e54267d148fd6d1bb8523ec0f8fe8e44cf3 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 17:16:15 +0200 Subject: [PATCH 194/262] fix some docs links --- docs/reference/ExifTags.rst | 6 ++++-- docs/reference/ImageEnhance.rst | 11 ++++++----- docs/reference/ImageStat.rst | 8 ++++---- docs/reference/JpegPresets.rst | 4 ++-- docs/reference/TiffTags.rst | 19 +++++++++++-------- src/PIL/ExifTags.py | 14 ++++++-------- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/reference/ExifTags.rst b/docs/reference/ExifTags.rst index 9fc7cd13ba3..39fdab02c1c 100644 --- a/docs/reference/ExifTags.rst +++ b/docs/reference/ExifTags.rst @@ -7,7 +7,8 @@ The :py:mod:`ExifTags` module exposes two dictionaries which provide constants and clear-text names for various well-known EXIF tags. -.. py:class:: PIL.ExifTags.TAGS +.. py:data:: TAGS + :type: dict The TAG dictionary maps 16-bit integer EXIF tag enumerations to descriptive string names. For instance: @@ -16,7 +17,8 @@ provide constants and clear-text names for various well-known EXIF tags. >>> TAGS[0x010e] 'ImageDescription' -.. py:class:: PIL.ExifTags.GPSTAGS +.. py:data:: GPSTAGS + :type: dict The GPSTAGS dictionary maps 8-bit integer EXIF gps enumerations to descriptive string names. For instance: diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst index b172054b2e3..f98d8f78054 100644 --- a/docs/reference/ImageEnhance.rst +++ b/docs/reference/ImageEnhance.rst @@ -29,7 +29,8 @@ Classes All enhancement classes implement a common interface, containing a single method: -.. py:class:: PIL.ImageEnhance._Enhance +.. py:class:: _Enhance + .. py:method:: enhance(factor) Returns an enhanced image. @@ -40,7 +41,7 @@ method: etc), and higher values more. There are no restrictions on this value. -.. py:class:: PIL.ImageEnhance.Color(image) +.. py:class:: Color(image) Adjust image color balance. @@ -49,7 +50,7 @@ method: factor of 0.0 gives a black and white image. A factor of 1.0 gives the original image. -.. py:class:: PIL.ImageEnhance.Contrast(image) +.. py:class:: Contrast(image) Adjust image contrast. @@ -57,7 +58,7 @@ method: to the contrast control on a TV set. An enhancement factor of 0.0 gives a solid grey image. A factor of 1.0 gives the original image. -.. py:class:: PIL.ImageEnhance.Brightness(image) +.. py:class:: Brightness(image) Adjust image brightness. @@ -65,7 +66,7 @@ method: enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the original image. -.. py:class:: PIL.ImageEnhance.Sharpness(image) +.. py:class:: Sharpness(image) Adjust image sharpness. diff --git a/docs/reference/ImageStat.rst b/docs/reference/ImageStat.rst index 32f5917c1f9..e94c24aa44d 100644 --- a/docs/reference/ImageStat.rst +++ b/docs/reference/ImageStat.rst @@ -7,7 +7,7 @@ The :py:mod:`ImageStat` module calculates global statistics for an image, or for a region of an image. -.. py:class:: PIL.ImageStat.Stat(image_or_list, mask=None) +.. py:class:: Stat(image_or_list, mask=None) Calculate statistics for the given image. If a mask is included, only the regions covered by that mask are included in the @@ -22,13 +22,13 @@ for a region of an image. .. note:: - This relies on the :py:meth:`~PIL.Image.histogram` method, and + This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and simply returns the low and high bins used. This is correct for images with 8 bits per channel, but fails for other modes such as - ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.getextrema` to + ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to return per-band extrema for the image. This is more correct and efficient because, for non-8-bit modes, the histogram method uses - :py:meth:`~PIL.Image.getextrema` to determine the bins used. + :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. .. py:attribute:: count diff --git a/docs/reference/JpegPresets.rst b/docs/reference/JpegPresets.rst index bf93f891faa..0a091460152 100644 --- a/docs/reference/JpegPresets.rst +++ b/docs/reference/JpegPresets.rst @@ -6,6 +6,6 @@ .. automodule:: PIL.JpegPresets .. data:: presets - :annotation: + :type: dict - A dict of all supported presets. + A dictionary of all supported presets. diff --git a/docs/reference/TiffTags.rst b/docs/reference/TiffTags.rst index 3b261625a02..4161110bd99 100644 --- a/docs/reference/TiffTags.rst +++ b/docs/reference/TiffTags.rst @@ -10,8 +10,8 @@ metadata tag numbers, names, and type information. .. method:: lookup(tag) :param tag: Integer tag number - :returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible, - otherwise just populating the value and name from ``TAGS``. + :returns: Taginfo namedtuple, From the :py:data:`~PIL.TiffTags.TAGS_V2` info if possible, + otherwise just populating the value and name from :py:data:`~PIL.TiffTags.TAGS`. If the tag is not recognized, "unknown" is returned for the name .. versionadded:: 3.1.0 @@ -22,7 +22,7 @@ metadata tag numbers, names, and type information. :param value: Integer Tag Number :param name: Tag Name - :param type: Integer type from :py:attr:`PIL.TiffTags.TYPES` + :param type: Integer type from :py:data:`PIL.TiffTags.TYPES` :param length: Array length: 0 == variable, 1 == single value, n = fixed :param enum: Dict of name:integer value options for an enumeration @@ -33,15 +33,17 @@ metadata tag numbers, names, and type information. .. versionadded:: 3.0.0 -.. py:attribute:: PIL.TiffTags.TAGS_V2 +.. py:data:: PIL.TiffTags.TAGS_V2 + :type: dict The ``TAGS_V2`` dictionary maps 16-bit integer tag numbers to - :py:class:`PIL.TagTypes.TagInfo` tuples for metadata fields defined in the TIFF + :py:class:`PIL.TiffTags.TagInfo` tuples for metadata fields defined in the TIFF spec. .. versionadded:: 3.0.0 -.. py:attribute:: PIL.TiffTags.TAGS +.. py:data:: PIL.TiffTags.TAGS + :type: dict The ``TAGS`` dictionary maps 16-bit integer TIFF tag number to descriptive string names. For instance: @@ -50,10 +52,11 @@ metadata tag numbers, names, and type information. >>> TAGS[0x010e] 'ImageDescription' - This dictionary contains a superset of the tags in TAGS_V2, common + This dictionary contains a superset of the tags in :py:data:`~PIL.TiffTags.TAGS_V2`, common EXIF tags, and other well known metadata tags. -.. py:attribute:: PIL.TiffTags.TYPES +.. py:data:: PIL.TiffTags.TYPES + :type: dict The ``TYPES`` dictionary maps the TIFF type short integer to a human readable type name. diff --git a/src/PIL/ExifTags.py b/src/PIL/ExifTags.py index cecc3f24602..f1c037e5186 100644 --- a/src/PIL/ExifTags.py +++ b/src/PIL/ExifTags.py @@ -9,13 +9,11 @@ # See the README file for information on usage and redistribution. # -## -# This module provides constants and clear-text names for various -# well-known EXIF tags. -## +""" +This module provides constants and clear-text names for various +well-known EXIF tags. +""" -## -# Maps EXIF tags to tag names. TAGS = { # possibly incomplete @@ -280,9 +278,8 @@ 0xC74E: "OpcodeList3", 0xC761: "NoiseProfile", } +"""Maps EXIF tags to tag names.""" -## -# Maps EXIF GPS tags to tag names. GPSTAGS = { 0: "GPSVersionID", @@ -318,3 +315,4 @@ 30: "GPSDifferential", 31: "GPSHPositioningError", } +"""Maps EXIF GPS tags to tag names.""" From 12cd02bd2de96bf1b1d6e91b9de5bfe70d5985ca Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 18:21:06 +0200 Subject: [PATCH 195/262] use xfail for failing tests --- .ci/test.sh | 2 +- .github/workflows/test-windows.yml | 2 +- Tests/test_image_resample.py | 14 +++++++------- Tests/test_imagefont.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index 516581ff008..b0e65abc4fe 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,7 +2,7 @@ set -e -python -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests +python -m pytest -v -x -ra -W always --cov PIL --cov Tests --cov-report term Tests # Docs if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ] && [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7ae26b8834c..1989bc00c3d 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -113,7 +113,7 @@ jobs: - name: Test Pillow run: | path %GITHUB_WORKSPACE%\\winbuild\\build\\bin;%PATH% - python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests + python.exe -m pytest -vxra -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests shell: cmd - name: Prepare to upload errors diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 764a3ca4907..35eae128bc5 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -218,7 +218,7 @@ def test_box_filter_correct_range(self): assert_image_equal(im, ref) -class CoreResampleConsistencyTest: +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] @@ -253,7 +253,7 @@ def test_32f(self): self.run_case(self.make_case("F", 1.192093e-07)) -class CoreResampleAlphaCorrectTest: +class TestCoreResampleAlphaCorrect: def make_levels_case(self, mode): i = Image.new(mode, (256, 16)) px = i.load() @@ -274,7 +274,7 @@ def run_levels_case(self, i): len(used_colors), y ) - @pytest.mark.skip("Current implementation isn't precise enough") + @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)) @@ -283,7 +283,7 @@ def test_levels_rgba(self): self.run_levels_case(case.resize((512, 32), Image.BICUBIC)) self.run_levels_case(case.resize((512, 32), Image.LANCZOS)) - @pytest.mark.skip("Current implementation isn't precise enough") + @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)) @@ -329,7 +329,7 @@ def test_dirty_pixels_la(self): self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,)) -class CoreResamplePassesTest: +class TestCoreResamplePasses: @contextmanager def count(self, diff): count = Image.core.get_stats()["new_count"] @@ -372,7 +372,7 @@ def test_box_vertical(self): assert_image_similar(with_box, cropped, 0.1) -class CoreResampleCoefficientsTest: +class TestCoreResampleCoefficients: def test_reduce(self): test_color = 254 @@ -401,7 +401,7 @@ def test_nonzero_coefficients(self): assert histogram[0x100 * 3 + 0xFF] == 0x10000 -class CoreResampleBoxTest: +class TestCoreResampleBox: def test_wrong_arguments(self): im = hopper() for resample in ( diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index bd79f08e39b..65d749b14a1 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -443,7 +443,7 @@ def test_unicode_pilfont(self): with pytest.raises(UnicodeEncodeError): font.getsize("’") - @pytest.mark.skipif(is_pypy(), reason="failing on PyPy") + @pytest.mark.xfail(is_pypy(), reason="failing on PyPy with Raqm") def test_unicode_extended(self): # issue #3777 text = "A\u278A\U0001F12B" From fc92f56382d9335b942881e84c5a2645e5304e5c Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 20:08:27 +0200 Subject: [PATCH 196/262] replace skip_known_bad_test with xfail --- Tests/helper.py | 6 ------ Tests/test_file_palm.py | 11 +++-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index 7e8abc9c9a8..cdc5f4efe3f 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -165,12 +165,6 @@ def assert_tuple_approx_equal(actuals, targets, threshold, msg): assert value, msg + ": " + repr(actuals) + " != " + repr(targets) -def skip_known_bad_test(msg=None): - # Skip if PILLOW_RUN_KNOWN_BAD is not true in the environment. - if not os.environ.get("PILLOW_RUN_KNOWN_BAD", False): - pytest.skip(msg or "Known bad test") - - def skip_unless_feature(feature): reason = "%s not available" % feature return pytest.mark.skipif(not features.check(feature), reason=reason) diff --git a/Tests/test_file_palm.py b/Tests/test_file_palm.py index 38f6dccd967..25d194b628f 100644 --- a/Tests/test_file_palm.py +++ b/Tests/test_file_palm.py @@ -2,15 +2,10 @@ import subprocess import pytest + from PIL import Image -from .helper import ( - IMCONVERT, - assert_image_equal, - hopper, - imagemagick_available, - skip_known_bad_test, -) +from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available _roundtrip = imagemagick_available() @@ -62,13 +57,13 @@ def test_monochrome(tmp_path): roundtrip(tmp_path, mode) +@pytest.mark.xfail(reason="Palm P image is wrong") def test_p_mode(tmp_path): # Arrange mode = "P" # Act / Assert helper_save_as_palm(tmp_path, mode) - skip_known_bad_test("Palm P image is wrong") roundtrip(tmp_path, mode) From dc41a4ec21990ea45fc12e5b099a61c974ed8db3 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 20:16:00 +0200 Subject: [PATCH 197/262] use skip_unless_feature in more tests --- Tests/test_image_reduce.py | 6 ++---- Tests/test_imagegrab.py | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Tests/test_image_reduce.py b/Tests/test_image_reduce.py index 353d0def04e..0f92b87f83f 100644 --- a/Tests/test_image_reduce.py +++ b/Tests/test_image_reduce.py @@ -1,7 +1,7 @@ import pytest from PIL import Image, ImageMath, ImageMode -from .helper import convert_to_comparable +from .helper import convert_to_comparable, skip_unless_feature codecs = dir(Image.core) @@ -254,9 +254,7 @@ def test_mode_F(): compare_reduce_with_box(im, factor) -@pytest.mark.skipif( - "jpeg2k_decoder" not in codecs, reason="JPEG 2000 support not available" -) +@skip_unless_feature("jpg_2000") def test_jpeg2k(): with Image.open("Tests/images/test-card-lossless.jp2") as im: assert im.reduce(2).size == (320, 240) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 82e746fda97..3e1cfa87f25 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -4,7 +4,7 @@ import pytest from PIL import Image, ImageGrab -from .helper import assert_image +from .helper import assert_image, skip_unless_feature class TestImageGrab: @@ -22,7 +22,7 @@ def test_grab(self): im = ImageGrab.grab(bbox=(10, 20, 50, 80)) assert_image(im, im.mode, (40, 60)) - @pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB") + @skip_unless_feature("xcb") def test_grab_x11(self): try: if sys.platform not in ("win32", "darwin"): @@ -45,7 +45,7 @@ def test_grab_no_xcb(self): ImageGrab.grab(xdisplay="") assert str(e.value).startswith("Pillow was built without XCB support") - @pytest.mark.skipif(not Image.core.HAVE_XCB, reason="requires XCB") + @skip_unless_feature("xcb") def test_grab_invalid_xdisplay(self): with pytest.raises(OSError) as e: ImageGrab.grab(xdisplay="error.test:0.0") From e2e8db4fe8b7a34f06bde86462dc6e2b9ef2a05f Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 15 Jun 2020 10:16:18 +0300 Subject: [PATCH 198/262] Fix isort --- Tests/test_file_palm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/test_file_palm.py b/Tests/test_file_palm.py index 25d194b628f..e7afeef23ea 100644 --- a/Tests/test_file_palm.py +++ b/Tests/test_file_palm.py @@ -2,7 +2,6 @@ import subprocess import pytest - from PIL import Image from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available From 703a9a0eb5a3bc4328d49e132b6ee1388e35dc16 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 15 Jun 2020 14:15:53 +0200 Subject: [PATCH 199/262] add pytest -ra into setup.cfg --- .ci/test.sh | 2 +- .github/workflows/test-windows.yml | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index b0e65abc4fe..516581ff008 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,7 +2,7 @@ set -e -python -m pytest -v -x -ra -W always --cov PIL --cov Tests --cov-report term Tests +python -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests # Docs if [ "$TRAVIS_PYTHON_VERSION" == "3.8" ] && [ "$TRAVIS_CPU_ARCH" == "amd64" ]; then diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 1989bc00c3d..7ae26b8834c 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -113,7 +113,7 @@ jobs: - name: Test Pillow run: | path %GITHUB_WORKSPACE%\\winbuild\\build\\bin;%PATH% - python.exe -m pytest -vxra -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests + python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests shell: cmd - name: Prepare to upload errors diff --git a/setup.cfg b/setup.cfg index 30843b847ac..5593c29b4c2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,5 +9,5 @@ line_length = 88 multi_line_output = 3 [tool:pytest] -addopts = -rs +addopts = -ra testpaths = Tests From 5e8854b8dbcdd20b29356fbd7f1b99a983430953 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 15 Jun 2020 14:54:38 +0200 Subject: [PATCH 200/262] add note about overriding Image.show behaviour --- src/PIL/Image.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3d6da8b84bb..d75fcc6b009 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2157,8 +2157,10 @@ def seek(self, frame): def show(self, title=None, command=None): """ - Displays this image. This method is mainly intended for - debugging purposes. + Displays this image. This method is mainly intended for debugging purposes. + + This method calls :py:func:`PIL.ImageShow.show` internally. You can use + :py:func:`PIL.ImageShow.register` to override its default behaviour. The image is first saved to a temporary file. By default, it will be in PNG format. @@ -2170,8 +2172,7 @@ def show(self, title=None, command=None): On Windows, the image is opened with the standard PNG display utility. - :param title: Optional title to use for the image window, - where possible. + :param title: Optional title to use for the image window, where possible. """ if command is not None: From 448cc46b1ea81711ab0508e6e95574b5b3f57ea7 Mon Sep 17 00:00:00 2001 From: nulano Date: Tue, 16 Jun 2020 03:21:38 +0200 Subject: [PATCH 201/262] fix tcl tests --- .github/workflows/test-windows.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 7ae26b8834c..83dc5748b0b 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -52,6 +52,11 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} + - name: Set up TCL + if: "contains(matrix.python-version, 'pypy')" + run: Write-Host "::set-env name=TCL_LIBRARY::$env:pythonLocation\tcl\tcl8.5" + shell: pwsh + - name: Print build system information run: python .github/workflows/system-info.py From 18e974ae6f3ce36b7e27e2d4891bab08e57e6a0c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Jun 2020 07:54:00 +1000 Subject: [PATCH 202/262] Updated lcms2 to 2.11 --- 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 1b5f2e056e4..e46bdf56c08 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -156,7 +156,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.9**. + above uses liblcms2. Tested with **1.19** and **2.7-2.11**. * **libwebp** provides the WebP format. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0ba8a135c15..5bde823ca87 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -195,9 +195,9 @@ def cmd_msbuild( # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.10/lcms2-2.10.tar.gz", - "filename": "lcms2-2.10.tar.gz", - "dir": "lcms2-2.10", + "url": SF_MIRROR + "/project/lcms/lcms/2.11/lcms2-2.11.tar.gz", + "filename": "lcms2-2.11.tar.gz", + "dir": "lcms2-2.11", "patch": { r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always From 6ad98ba3c0c371c9bddf53b0939e7632ede8c2b7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2020 21:40:38 +1000 Subject: [PATCH 203/262] Do not ignore viewer if order is zero when registering --- Tests/test_imageshow.py | 12 +++++++----- src/PIL/ImageShow.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 64f15326b40..fddc73bd1be 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -17,19 +17,21 @@ def test_register(): ImageShow._viewers.pop() -def test_viewer_show(): +@pytest.mark.parametrize( + "order", [-1, 0], +) +def test_viewer_show(order): class TestViewer(ImageShow.Viewer): - methodCalled = False - def show_image(self, image, **options): self.methodCalled = True return True viewer = TestViewer() - ImageShow.register(viewer, -1) + ImageShow.register(viewer, order) for mode in ("1", "I;16", "LA", "RGB", "RGBA"): - with hopper() as im: + viewer.methodCalled = False + with hopper(mode) as im: assert ImageShow.show(im) assert viewer.methodCalled diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index fc50894236b..cd85e81b4a7 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -31,7 +31,7 @@ def register(viewer, order=1): pass # raised if viewer wasn't a class if order > 0: _viewers.append(viewer) - elif order < 0: + else: _viewers.insert(0, viewer) From f99e0b824bdef7b5ca92fda228c99f42c5bf7627 Mon Sep 17 00:00:00 2001 From: Kirill Kuzminykh Date: Thu, 18 Jun 2020 16:18:18 +0300 Subject: [PATCH 204/262] Replaced primitive "magic number" inside of JpegImagePlugin._accept() function by more correct version. --- Tests/test_file_jpeg.py | 20 ++++++++++++++++++++ src/PIL/JpegImagePlugin.py | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index afca875de7e..7702d35b532 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -706,6 +706,26 @@ def test_icc_after_SOF(self): with Image.open("Tests/images/icc-after-SOF.jpg") as im: assert im.info["icc_profile"] == b"profile" + def test_reading_not_whole_file_for_define_it_type(self): + size = 1024 ** 2 + buffer = BytesIO(b"\xFF" * size) # Many xFF bytes + buffer.max_pos = 0 + orig_read = buffer.read + + def read(n=-1): + res = orig_read(n) + buffer.max_pos = max(buffer.max_pos, buffer.tell()) + return res + + buffer.read = read + with pytest.raises(OSError): + Image.open(buffer) + + # Only small part of file has been read. + # The upper limit of max_pos (8Kb) was chosen experimentally + # and increased approximately twice. + assert 0 < buffer.max_pos < 8 * 1024 + @pytest.mark.skipif(not is_win32(), reason="Windows only") @skip_unless_feature("jpg") diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 89e70f0e989..c9b83c032b9 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -323,7 +323,8 @@ def DQT(self, marker): def _accept(prefix): - return prefix[0:1] == b"\377" + # Magic number was taken from https://en.wikipedia.org/wiki/JPEG + return prefix[0:3] == b"\xFF\xD8\xFF" ## From 84b8776bfcc85e671d7ce5eaccec12e833379b4d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2020 08:29:35 +1000 Subject: [PATCH 205/262] Updated libjpeg-turbo to 2.0.4 --- 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 0ba8a135c15..357d7dcb3e8 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -105,9 +105,9 @@ def cmd_msbuild( # dependencies, listed in order of compilation deps = { "libjpeg": { - "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", - "filename": "libjpeg-turbo-2.0.3.tar.gz", - "dir": "libjpeg-turbo-2.0.3", + "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.4/libjpeg-turbo-2.0.4.tar.gz", + "filename": "libjpeg-turbo-2.0.4.tar.gz", + "dir": "libjpeg-turbo-2.0.4", "build": [ cmd_cmake( [ From 3e9068a34564f33620b2a3b753d0b198af2a662b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2020 09:48:55 +1000 Subject: [PATCH 206/262] Decreased length of test image data --- Tests/test_file_jpeg.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 7702d35b532..25c1726c1d9 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -707,7 +707,7 @@ def test_icc_after_SOF(self): assert im.info["icc_profile"] == b"profile" def test_reading_not_whole_file_for_define_it_type(self): - size = 1024 ** 2 + size = 4097 buffer = BytesIO(b"\xFF" * size) # Many xFF bytes buffer.max_pos = 0 orig_read = buffer.read @@ -721,10 +721,8 @@ def read(n=-1): with pytest.raises(OSError): Image.open(buffer) - # Only small part of file has been read. - # The upper limit of max_pos (8Kb) was chosen experimentally - # and increased approximately twice. - assert 0 < buffer.max_pos < 8 * 1024 + # Assert the entire file has not been read + assert 0 < buffer.max_pos < size @pytest.mark.skipif(not is_win32(), reason="Windows only") From abbc890b205dca4040a6ac144cf17997f1ab0e9e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2020 09:51:48 +1000 Subject: [PATCH 207/262] Replaced OSError with more specific UnidentifiedImageError --- Tests/test_file_jpeg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 25c1726c1d9..9b7c4dceab6 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -3,7 +3,7 @@ from io import BytesIO import pytest -from PIL import ExifTags, Image, ImageFile, JpegImagePlugin +from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, UnidentifiedImageError from .helper import ( assert_image, @@ -718,7 +718,7 @@ def read(n=-1): return res buffer.read = read - with pytest.raises(OSError): + with pytest.raises(UnidentifiedImageError): Image.open(buffer) # Assert the entire file has not been read From 65742cfc9558eebb52e13087c2c06740478586d1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 20 Jun 2020 09:57:51 +1000 Subject: [PATCH 208/262] Renamed test --- Tests/test_file_jpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 9b7c4dceab6..5573086cb31 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -706,7 +706,7 @@ def test_icc_after_SOF(self): with Image.open("Tests/images/icc-after-SOF.jpg") as im: assert im.info["icc_profile"] == b"profile" - def test_reading_not_whole_file_for_define_it_type(self): + def test_jpeg_magic_number(self): size = 4097 buffer = BytesIO(b"\xFF" * size) # Many xFF bytes buffer.max_pos = 0 From 2155c16ae0e8736736b613dc700eaae7a14199d4 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 20 Jun 2020 12:00:30 +0100 Subject: [PATCH 209/262] improve warning wording Co-authored-by: Hugo van Kemenade --- 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 d75fcc6b009..c4f8380df0c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2177,7 +2177,7 @@ def show(self, title=None, command=None): if command is not None: warnings.warn( - "The command parameter was deprecated and will be removed in a future" + "The command parameter is deprecated and will be removed in a future" "release. Use a subclass of ImageShow.Viewer instead.", DeprecationWarning, ) From 2f3deef8c56d7b9199dba3881f09445085328ab9 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 20 Jun 2020 13:10:10 +0200 Subject: [PATCH 210/262] update wording for #4706 --- 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 c833ce8e658..57b7dcac7f6 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -29,8 +29,8 @@ def register(viewer, order=1): :param viewer: The viewer to be registered. :param order: - A negative integer to prepend this viewer to the list, - or a positive integer to append it. + Zero or a negative integer to prepend this viewer to the list, + a positive integer to append it. """ try: if issubclass(viewer, Viewer): From d728cd58754f6701ffb3487609eabe39602238f5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 May 2020 16:38:38 +1000 Subject: [PATCH 211/262] Allow libtiff to write COLORMAP tag --- Tests/test_file_libtiff.py | 13 +++++++++++++ src/PIL/TiffImagePlugin.py | 1 - src/PIL/TiffTags.py | 1 - src/encode.c | 23 +++++++++++++++++++++-- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 855a9ab3aa8..9c18eca269e 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -203,6 +203,7 @@ def test_additional_metadata(self, tmp_path): del core_items[tag] except KeyError: pass + del core_items[320] # colormap is special, tested below # Type codes: # 2: "ascii", @@ -479,6 +480,18 @@ def test_cmyk_save(self, tmp_path): with Image.open(out) as im2: assert_image_equal(im, im2) + def test_palette_save(self, tmp_path): + im = hopper("P") + out = str(tmp_path / "temp.tif") + + TiffImagePlugin.WRITE_LIBTIFF = True + im.save(out) + TiffImagePlugin.WRITE_LIBTIFF = False + + with Image.open(out) as reloaded: + # colormap/palette tag + assert len(reloaded.tag_v2[320]) == 768 + def xtest_bw_compression_w_rgb(self, tmp_path): """ This test passes, but when running all tests causes a failure due to output on stderr from the error thrown by libtiff. We need to diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index ee183ccbac7..6fc8cc8cf52 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1524,7 +1524,6 @@ def _save(im, fp, filename): # BITSPERSAMPLE, etc), passing arrays with a different length will result in # segfaults. Block these tags until we add extra validation. blocklist = [ - COLORMAP, REFERENCEBLACKWHITE, SAMPLEFORMAT, STRIPBYTECOUNTS, diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index 6cc9ff7f349..e1c1b701b40 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -483,7 +483,6 @@ def _populate(): 65537, } -LIBTIFF_CORE.remove(320) # Array of short, crashes LIBTIFF_CORE.remove(301) # Array of short, crashes LIBTIFF_CORE.remove(532) # Array of long, crashes diff --git a/src/encode.c b/src/encode.c index 03a39448d2f..d64f47d2b13 100644 --- a/src/encode.c +++ b/src/encode.c @@ -671,7 +671,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) // This list also exists in TiffTags.py const int core_tags[] = { 256, 257, 258, 259, 262, 263, 266, 269, 274, 277, 278, 280, 281, 340, - 341, 282, 283, 284, 286, 287, 296, 297, 321, 338, 32995, 32998, 32996, + 341, 282, 283, 284, 286, 287, 296, 297, 320, 321, 338, 32995, 32998, 32996, 339, 32997, 330, 531, 530, 65537 }; @@ -801,7 +801,26 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) TRACE(("Setting from Tuple: %d \n", key_int)); len = PyTuple_Size(value); - if (type == TIFF_SHORT) { + if (key_int == TIFFTAG_COLORMAP) { + int stride = 256; + if (len != 768) { + PyErr_SetString(PyExc_ValueError, "Requiring 768 items for for Colormap"); + return NULL; + } + UINT16 *av; + /* malloc check ok, calloc checks for overflow */ + av = calloc(len, sizeof(UINT16)); + if (av) { + for (i=0;istate, (ttag_t) key_int, + av, + av + stride, + av + stride * 2); + free(av); + } + } else if (type == TIFF_SHORT) { UINT16 *av; /* malloc check ok, calloc checks for overflow */ av = calloc(len, sizeof(UINT16)); From 8a51ad07fdcaac054b1d95077a2c29f85e923f1d Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 20 Jun 2020 22:41:04 +1000 Subject: [PATCH 212/262] Renamed variable Co-authored-by: Hugo van Kemenade --- src/PIL/TiffImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 43a2f7c406f..fe9cc5a1891 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1508,10 +1508,10 @@ def _save(im, fp, filename): # data orientation stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) ifd[ROWSPERSTRIP] = im.size[1] - stripByteCounts = stride * im.size[1] - if stripByteCounts >= 2 ** 16: + strip_byte_counts = stride * im.size[1] + if strip_byte_counts >= 2 ** 16: ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG - ifd[STRIPBYTECOUNTS] = stripByteCounts + ifd[STRIPBYTECOUNTS] = strip_byte_counts ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer # no compression by default: ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) From 34d77a757863efe62b2c226305ec75f467f6d207 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 10:46:27 +1000 Subject: [PATCH 213/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 566e055a418..b3b96bb9e09 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,24 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Change STRIPBYTECOUNTS to LONG if necessary when saving #4626 + [radarhere, hugovk] + +- Write JFIF header when saving JPEG #4639 + [radarhere] + +- Replaced tiff_jpeg with jpeg compression when saving TIFF images #4627 + [radarhere] + +- Writing TIFF tags: improved BYTE, added UNDEFINED #4605 + [radarhere] + +- Consider transparency when pasting text on an RGBA image #4566 + [radarhere] + +- Added method argument to single frame WebP saving #4547 + [radarhere] + - Use ImageFileDirectory_v2 in Image.Exif #4637 [radarhere] From c82483e35a37919df9700485aa752e8c5a38f28c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 17:03:15 +1000 Subject: [PATCH 214/262] Install NumPy with OpenBLAS --- .github/workflows/macos-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index 6cd9dadf3d1..76a3ef2b748 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -2,7 +2,7 @@ set -e -brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype +brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas PYTHONOPTIMIZE=0 pip install cffi pip install coverage @@ -11,6 +11,8 @@ pip install -U pytest pip install -U pytest-cov pip install pyroma pip install test-image-results + +echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg pip install numpy # extra test images From f7e47dffc4cd26ae7a37a8a8d2c387378cf746a7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 11:01:30 +1000 Subject: [PATCH 215/262] Added release notes for #4605 [ci skip] --- docs/releasenotes/7.2.0.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst index 904e9d5ab32..00baca474f6 100644 --- a/docs/releasenotes/7.2.0.rst +++ b/docs/releasenotes/7.2.0.rst @@ -27,3 +27,9 @@ Moved from the legacy :py:class:`PIL.TiffImagePlugin.ImageFileDirectory_v1` to :py:class:`PIL.Image.Exif`. This means that Exif RATIONALs and SIGNED_RATIONALs are now read as :py:class:`PIL.TiffImagePlugin.IFDRational`, instead of as a tuple with a numerator and a denominator. + +TIFF BYTE tags format +^^^^^^^^^^^^^^^^^^^^^ + +TIFF BYTE tags were previously read as a tuple containing a bytestring. They +are now read as just a single bytestring. From a324f4a466ab12d4c8cf0b95abe4a5bd79b1c7aa Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 12 Oct 2019 14:29:10 +0100 Subject: [PATCH 216/262] add version to features info block --- Tests/test_file_icns.py | 4 +-- Tests/test_file_jpeg.py | 4 +-- Tests/test_file_jpeg2k.py | 4 +-- Tests/test_file_png.py | 4 +-- Tests/test_imagecms.py | 4 +-- Tests/test_imagefont.py | 12 +++---- src/PIL/IcnsImagePlugin.py | 4 +-- src/PIL/features.py | 64 ++++++++++++++++++++++++++++++++------ 8 files changed, 73 insertions(+), 27 deletions(-) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index aeb146f7ec2..7bf7b72ec92 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -2,14 +2,14 @@ import sys import pytest -from PIL import IcnsImagePlugin, Image +from PIL import IcnsImagePlugin, Image, features from .helper import assert_image_equal, assert_image_similar # sample icon file TEST_FILE = "Tests/images/pillow.icns" -ENABLE_JPEG2K = hasattr(Image.core, "jp2klib_version") +ENABLE_JPEG2K = features.check_codec("jpg_2000") def test_sanity(): diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index ee05430276e..be8e21d5a10 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -3,7 +3,7 @@ from io import BytesIO import pytest -from PIL import ExifTags, Image, ImageFile, JpegImagePlugin +from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, features from .helper import ( assert_image, @@ -41,7 +41,7 @@ def gen_random_image(self, size, mode="RGB"): def test_sanity(self): # internal version number - assert re.search(r"\d+\.\d+$", Image.core.jpeglib_version) + assert re.search(r"\d+\.\d+$", features.version_codec("jpg")) with Image.open(TEST_FILE) as im: im.load() diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 7b8b7a04a45..07f8e8e052e 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -2,7 +2,7 @@ from io import BytesIO import pytest -from PIL import Image, ImageFile, Jpeg2KImagePlugin +from PIL import Image, ImageFile, Jpeg2KImagePlugin, features from .helper import ( assert_image_equal, @@ -35,7 +35,7 @@ def roundtrip(im, **options): def test_sanity(): # Internal version number - assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version) + assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("jpg_2000")) with Image.open("Tests/images/test-card-lossless.jp2") as im: px = im.load() diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index a44bdecf87e..9bd8507d9ac 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -3,7 +3,7 @@ from io import BytesIO import pytest -from PIL import Image, ImageFile, PngImagePlugin +from PIL import Image, ImageFile, PngImagePlugin, features from .helper import ( PillowLeakTestCase, @@ -73,7 +73,7 @@ def get_chunks(self, filename): def test_sanity(self, tmp_path): # internal version number - assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version) + assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib")) test_file = str(tmp_path / "temp.png") diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 921fdc369d3..95373121561 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -4,7 +4,7 @@ from io import BytesIO import pytest -from PIL import Image, ImageMode +from PIL import Image, ImageMode, features from .helper import assert_image, assert_image_equal, assert_image_similar, hopper @@ -46,7 +46,7 @@ def test_sanity(): assert list(map(type, v)) == [str, str, str, str] # internal version number - assert re.search(r"\d+\.\d+$", ImageCms.core.littlecms_version) + assert re.search(r"\d+\.\d+$", features.version_module("littlecms2")) skip_missing() i = ImageCms.profileToProfile(hopper(), SRGB, SRGB) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index b62fc2e23f3..9602a309955 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -7,7 +7,7 @@ from io import BytesIO import pytest -from PIL import Image, ImageDraw, ImageFont +from PIL import Image, ImageDraw, ImageFont, features from .helper import ( assert_image_equal, @@ -40,7 +40,7 @@ class TestImageFont: @classmethod def setup_class(self): - freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + freetype = distutils.version.StrictVersion(features.version_module("freetype2")) self.metrics = self.METRICS["Default"] for conditions, metrics in self.METRICS.items(): @@ -67,7 +67,7 @@ def get_font(self): ) def test_sanity(self): - assert re.search(r"\d+\.\d+\.\d+$", ImageFont.core.freetype2_version) + assert re.search(r"\d+\.\d+\.\d+$", features.version_module("freetype2")) def test_font_properties(self): ttf = self.get_font() @@ -619,7 +619,7 @@ def test_complex_font_settings(self): def test_variation_get(self): font = self.get_font() - freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + freetype = distutils.version.StrictVersion(features.version_module("freetype2")) if freetype < "2.9.1": with pytest.raises(NotImplementedError): font.get_variation_names() @@ -691,7 +691,7 @@ def _check_text(self, font, path, epsilon): def test_variation_set_by_name(self): font = self.get_font() - freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + freetype = distutils.version.StrictVersion(features.version_module("freetype2")) if freetype < "2.9.1": with pytest.raises(NotImplementedError): font.set_variation_by_name("Bold") @@ -715,7 +715,7 @@ def test_variation_set_by_name(self): def test_variation_set_by_axes(self): font = self.get_font() - freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + freetype = distutils.version.StrictVersion(features.version_module("freetype2")) if freetype < "2.9.1": with pytest.raises(NotImplementedError): font.set_variation_by_axes([100]) diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index c003926154e..9de7d8dfe3b 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -23,10 +23,10 @@ import sys import tempfile -from PIL import Image, ImageFile, PngImagePlugin +from PIL import Image, ImageFile, PngImagePlugin, features from PIL._binary import i8 -enable_jpeg2k = hasattr(Image.core, "jp2klib_version") +enable_jpeg2k = features.check_codec("jpg_2000") if enable_jpeg2k: from PIL import Jpeg2KImagePlugin diff --git a/src/PIL/features.py b/src/PIL/features.py index 33e89cf2441..e1823537e27 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -8,11 +8,11 @@ from . import Image modules = { - "pil": "PIL._imaging", - "tkinter": "PIL._tkinter_finder", - "freetype2": "PIL._imagingft", - "littlecms2": "PIL._imagingcms", - "webp": "PIL._webp", + "pil": ("PIL._imaging", None), + "tkinter": ("PIL._tkinter_finder", None), + "freetype2": ("PIL._imagingft", "freetype2"), + "littlecms2": ("PIL._imagingcms", "littlecms"), + "webp": ("PIL._webp", None), } @@ -27,7 +27,7 @@ def check_module(feature): if not (feature in modules): raise ValueError("Unknown module %s" % feature) - module = modules[feature] + module, lib = modules[feature] try: __import__(module) @@ -36,6 +36,20 @@ def check_module(feature): return False +def version_module(feature): + if not check_module(feature): + return None + + module, lib = modules[feature] + + if lib is None: + return None + + attr = lib + "_version" + + return getattr(__import__(module, fromlist=[attr]), attr) + + def get_supported_modules(): """ :returns: A list of all supported modules. @@ -43,7 +57,12 @@ def get_supported_modules(): return [f for f in modules if check_module(f)] -codecs = {"jpg": "jpeg", "jpg_2000": "jpeg2k", "zlib": "zip", "libtiff": "libtiff"} +codecs = { + "jpg": ("jpeg", "jpeglib"), + "jpg_2000": ("jpeg2k", "jp2klib"), + "zlib": ("zip", "zlib"), + "libtiff": ("libtiff", "libtiff"), +} def check_codec(feature): @@ -57,11 +76,25 @@ def check_codec(feature): if feature not in codecs: raise ValueError("Unknown codec %s" % feature) - codec = codecs[feature] + codec, lib = codecs[feature] return codec + "_encoder" in dir(Image.core) +def version_codec(feature): + if not check_codec(feature): + return None + + codec, lib = codecs[feature] + + version = getattr(Image.core, lib + "_version") + + if feature == "libtiff": + return version.split("\n")[0].split("Version ")[1] + + return version + + def get_supported_codecs(): """ :returns: A list of all supported codecs. @@ -125,6 +158,14 @@ def check(feature): return False +def version(feature): + if feature in modules: + return version_module(feature) + if feature in codecs: + return version_codec(feature) + return None + + def get_supported(): """ :returns: A list of all supported modules, features, and codecs. @@ -187,7 +228,12 @@ def pilinfo(out=None, supported_formats=True): ("xcb", "XCB (X protocol)"), ]: if check(name): - print("---", feature, "support ok", file=out) + v = version(name) + if v is not None: + support = "ok (version {})".format(v) + else: + support = "ok" + print("---", feature, "support", support, file=out) else: print("***", feature, "support not installed", file=out) print("-" * 68, file=out) From 6c1ff252d60954bb6ae8af767dae858afa159562 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 14 Jun 2020 05:35:43 +0200 Subject: [PATCH 217/262] check run-time version numbers where available, add docs --- docs/reference/features.rst | 18 ++++--- src/PIL/features.py | 87 ++++++++++++++++++++++++---------- src/_imaging.c | 9 ++++ src/_imagingcms.c | 4 +- src/_imagingft.c | 7 +++ src/_webp.c | 13 +++++ src/libImaging/QuantPngQuant.c | 9 ++++ src/libImaging/ZipEncode.c | 2 +- 8 files changed, 117 insertions(+), 32 deletions(-) diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 196f938ed69..47e9a6d630e 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -8,6 +8,7 @@ The :py:mod:`PIL.features` module can be used to detect which Pillow features ar .. autofunction:: PIL.features.pilinfo .. autofunction:: PIL.features.check +.. autofunction:: PIL.features.version .. autofunction:: PIL.features.get_supported Modules @@ -16,28 +17,31 @@ Modules Support for the following modules can be checked: * ``pil``: The Pillow core module, required for all functionality. -* ``tkinter``: Tkinter support. +* ``tkinter``: Tkinter support. Version number not available. * ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`. * ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`. * ``webp``: WebP image support. .. autofunction:: PIL.features.check_module +.. autofunction:: PIL.features.version_module .. autofunction:: PIL.features.get_supported_modules Codecs ------ -These are only checked during Pillow compilation. +Support for these is only checked during Pillow compilation. If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead. +Except for ``jpg``, the version number is checked at run-time. Support for the following codecs can be checked: -* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. +* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. Only compile time version number is available. * ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats. * ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG. * ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats. .. autofunction:: PIL.features.check_codec +.. autofunction:: PIL.features.version_codec .. autofunction:: PIL.features.get_supported_codecs Features @@ -45,16 +49,18 @@ Features Some of these are only checked during Pillow compilation. If the required library was uninstalled from the system, the relevant module may fail to load instead. +Feature version numbers are available only where stated. Support for the following features can be checked: -* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. +* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. Compile-time version number is available. * ``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`. -* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. +* ``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. .. autofunction:: PIL.features.check_feature +.. autofunction:: PIL.features.version_feature .. autofunction:: PIL.features.get_supported_features diff --git a/src/PIL/features.py b/src/PIL/features.py index e1823537e27..4f1bb0b8fd0 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -8,11 +8,11 @@ from . import Image modules = { - "pil": ("PIL._imaging", None), + "pil": ("PIL._imaging", "PILLOW_VERSION"), "tkinter": ("PIL._tkinter_finder", None), - "freetype2": ("PIL._imagingft", "freetype2"), - "littlecms2": ("PIL._imagingcms", "littlecms"), - "webp": ("PIL._webp", None), + "freetype2": ("PIL._imagingft", "freetype2_version"), + "littlecms2": ("PIL._imagingcms", "littlecms_version"), + "webp": ("PIL._webp", "webpdecoder_version"), } @@ -27,7 +27,7 @@ def check_module(feature): if not (feature in modules): raise ValueError("Unknown module %s" % feature) - module, lib = modules[feature] + module, ver = modules[feature] try: __import__(module) @@ -37,17 +37,21 @@ def check_module(feature): def version_module(feature): + """ + :param feature: The module to check for. + :returns: + The loaded version number as a string, or ``None`` if unknown or not available. + :raises ValueError: If the module is not defined in this version of Pillow. + """ if not check_module(feature): return None - module, lib = modules[feature] + module, ver = modules[feature] - if lib is None: + if ver is None: return None - attr = lib + "_version" - - return getattr(__import__(module, fromlist=[attr]), attr) + return getattr(__import__(module, fromlist=[ver]), ver) def get_supported_modules(): @@ -82,6 +86,13 @@ def check_codec(feature): def version_codec(feature): + """ + :param feature: The codec to check for. + :returns: + The version number as a string, or ``None`` if not available. + Checked at compile time for ``jpg``, run-time otherwise. + :raises ValueError: If the codec is not defined in this version of Pillow. + """ if not check_codec(feature): return None @@ -103,13 +114,13 @@ def get_supported_codecs(): features = { - "webp_anim": ("PIL._webp", "HAVE_WEBPANIM"), - "webp_mux": ("PIL._webp", "HAVE_WEBPMUX"), - "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"), - "raqm": ("PIL._imagingft", "HAVE_RAQM"), - "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"), - "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT"), - "xcb": ("PIL._imaging", "HAVE_XCB"), + "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None), + "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None), + "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None), + "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), + "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), + "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), + "xcb": ("PIL._imaging", "HAVE_XCB", None), } @@ -124,7 +135,7 @@ def check_feature(feature): if feature not in features: raise ValueError("Unknown feature %s" % feature) - module, flag = features[feature] + module, flag, ver = features[feature] try: imported_module = __import__(module, fromlist=["PIL"]) @@ -133,6 +144,23 @@ def check_feature(feature): return None +def version_feature(feature): + """ + :param feature: The feature to check for. + :returns: The version number as a string, or ``None`` if not available. + :raises ValueError: If the feature is not defined in this version of Pillow. + """ + if not check_feature(feature): + return None + + module, flag, ver = features[feature] + + if ver is None: + return None + + return getattr(__import__(module, fromlist=[ver]), ver) + + def get_supported_features(): """ :returns: A list of all supported features. @@ -142,9 +170,9 @@ def get_supported_features(): def check(feature): """ - :param feature: A module, feature, or codec name. + :param feature: A module, codec, or feature name. :returns: - ``True`` if the module, feature, or codec is available, + ``True`` if the module, codec, or feature is available, ``False`` or ``None`` otherwise. """ @@ -159,10 +187,18 @@ def check(feature): def version(feature): + """ + :param feature: + The module, codec, or feature to check for. + :returns: + The version number as a string, or ``None`` if unknown or not available. + """ if feature in modules: return version_module(feature) if feature in codecs: return version_codec(feature) + if feature in features: + return version_feature(feature) return None @@ -228,12 +264,15 @@ def pilinfo(out=None, supported_formats=True): ("xcb", "XCB (X protocol)"), ]: if check(name): - v = version(name) + if name == "jpg" and check_feature("libjpeg_turbo"): + v = "libjpeg-turbo " + version_feature("libjpeg_turbo") + else: + v = version(name) if v is not None: - support = "ok (version {})".format(v) + t = "compiled for" if name in ("pil", "jpg") else "loaded" + print("---", feature, "support ok,", t, "version", v, file=out) else: - support = "ok" - print("---", feature, "support", support, file=out) + print("---", feature, "support ok", file=out) else: print("***", feature, "support not installed", file=out) print("-" * 68, file=out) diff --git a/src/_imaging.c b/src/_imaging.c index 40bfbf2fe12..1ed5e8a4295 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -4168,12 +4168,21 @@ setup_module(PyObject* m) { #ifdef LIBJPEG_TURBO_VERSION PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_True); + #define tostr1(a) #a + #define tostr(a) tostr1(a) + PyDict_SetItemString(d, "libjpeg_turbo_version", PyUnicode_FromString(tostr(LIBJPEG_TURBO_VERSION))); + #undef tostr + #undef tostr1 #else PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_False); #endif #ifdef HAVE_LIBIMAGEQUANT PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_True); + { + extern const char* ImagingImageQuantVersion(void); + PyDict_SetItemString(d, "imagequant_version", PyUnicode_FromString(ImagingImageQuantVersion())); + } #else PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_False); #endif diff --git a/src/_imagingcms.c b/src/_imagingcms.c index 60b6b722884..7f23d596402 100644 --- a/src/_imagingcms.c +++ b/src/_imagingcms.c @@ -1608,6 +1608,7 @@ static int setup_module(PyObject* m) { PyObject *d; PyObject *v; + int vn; d = PyModule_GetDict(m); @@ -1622,7 +1623,8 @@ setup_module(PyObject* m) { d = PyModule_GetDict(m); - v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); + vn = cmsGetEncodedCMMversion(); + v = PyUnicode_FromFormat("%d.%d", vn / 100, vn % 100); PyDict_SetItemString(d, "littlecms_version", v); return 0; diff --git a/src/_imagingft.c b/src/_imagingft.c index e0ff7521c99..d7ff7ad281b 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -81,6 +81,7 @@ typedef struct { static PyTypeObject Font_Type; +typedef const char* (*t_raqm_version_string) (void); typedef bool (*t_raqm_version_atleast)(unsigned int major, unsigned int minor, unsigned int micro); @@ -112,6 +113,7 @@ typedef void (*t_raqm_destroy) (raqm_t *rq); typedef struct { void* raqm; int version; + t_raqm_version_string version_string; t_raqm_version_atleast version_atleast; t_raqm_create create; t_raqm_set_text set_text; @@ -173,6 +175,7 @@ setraqm(void) } #ifndef _WIN32 + p_raqm.version_string = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_string"); p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast"); p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create"); p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text"); @@ -206,6 +209,7 @@ setraqm(void) return 2; } #else + p_raqm.version_string = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_string"); p_raqm.version_atleast = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast"); p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create"); p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text"); @@ -1251,6 +1255,9 @@ setup_module(PyObject* m) { setraqm(); v = PyBool_FromLong(!!p_raqm.raqm); PyDict_SetItemString(d, "HAVE_RAQM", v); + if (p_raqm.version_string) { + PyDict_SetItemString(d, "raqm_version", PyUnicode_FromString(p_raqm.version_string())); + } return 0; } diff --git a/src/_webp.c b/src/_webp.c index c2b363cd097..468a9ff73c6 100644 --- a/src/_webp.c +++ b/src/_webp.c @@ -821,6 +821,16 @@ PyObject* WebPDecoderVersion_wrapper() { return Py_BuildValue("i", WebPGetDecoderVersion()); } +// Version as string +const char* +WebPDecoderVersion_str(void) +{ + static char version[20]; + int version_number = WebPGetDecoderVersion(); + sprintf(version, "%d.%d.%d", version_number >> 16, (version_number >> 8) % 0x100, version_number % 0x100); + return version; +} + /* * The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well. * Files that are valid with 0.3 are reported as being invalid. @@ -872,10 +882,13 @@ void addTransparencyFlagToModule(PyObject* m) { } static int setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); addMuxFlagToModule(m); addAnimFlagToModule(m); addTransparencyFlagToModule(m); + PyDict_SetItemString(d, "webpdecoder_version", PyUnicode_FromString(WebPDecoderVersion_str())); + #ifdef HAVE_WEBPANIM /* Ready object types */ if (PyType_Ready(&WebPAnimDecoder_Type) < 0 || diff --git a/src/libImaging/QuantPngQuant.c b/src/libImaging/QuantPngQuant.c index 753ceb02f25..7a23ec8c5a5 100644 --- a/src/libImaging/QuantPngQuant.c +++ b/src/libImaging/QuantPngQuant.c @@ -113,4 +113,13 @@ quantize_pngquant( return result; } +const char* +ImagingImageQuantVersion(void) +{ + static char version[20]; + int number = liq_version(); + sprintf(version, "%d.%d.%d", number / 10000, (number / 100) % 100, number % 100); + return version; +} + #endif diff --git a/src/libImaging/ZipEncode.c b/src/libImaging/ZipEncode.c index 0b443567874..84ccb14ea54 100644 --- a/src/libImaging/ZipEncode.c +++ b/src/libImaging/ZipEncode.c @@ -373,7 +373,7 @@ ImagingZipEncodeCleanup(ImagingCodecState state) { const char* ImagingZipVersion(void) { - return ZLIB_VERSION; + return zlibVersion(); } #endif From d5a6b2584e3e1a2dcc15c5adec291cd0390fb785 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 15 Jun 2020 15:32:30 +0200 Subject: [PATCH 218/262] add tests for version numbers --- Tests/test_features.py | 39 ++++++++++++++++++++++++++++++++++++++ Tests/test_file_libtiff.py | 6 +++++- Tests/test_file_webp.py | 4 +++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Tests/test_features.py b/Tests/test_features.py index 7cfa08071ba..1e769220484 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -1,4 +1,5 @@ import io +import re import pytest from PIL import features @@ -21,6 +22,27 @@ def test_check(): assert features.check_feature(feature) == features.check(feature) +def test_version(): + # Check the correctness of the convenience function + # and the format of version numbers + + def test(name, function): + version = features.version(name) + if not features.check(name): + assert version is None + else: + assert function(name) == version + if name != "PIL": + assert version is None or re.search(r"\d+(\.\d+)*$", version) + + for module in features.modules: + test(module, features.version_module) + for codec in features.codecs: + test(codec, features.version_codec) + for feature in features.features: + test(feature, features.version_feature) + + @skip_unless_feature("webp") def test_webp_transparency(): assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha() @@ -37,9 +59,22 @@ def test_webp_anim(): assert features.check("webp_anim") == _webp.HAVE_WEBPANIM +@skip_unless_feature("libjpeg_turbo") +def test_libjpeg_turbo_version(): + assert re.search(r"\d+\.\d+\.\d+$", features.version("libjpeg_turbo")) + + +@skip_unless_feature("libimagequant") +def test_libimagequant_version(): + assert re.search(r"\d+\.\d+\.\d+$", features.version("libimagequant")) + + def test_check_modules(): for feature in features.modules: assert features.check_module(feature) in [True, False] + + +def test_check_codecs(): for feature in features.codecs: assert features.check_codec(feature) in [True, False] @@ -64,6 +99,8 @@ def test_unsupported_codec(): # Act / Assert with pytest.raises(ValueError): features.check_codec(codec) + with pytest.raises(ValueError): + features.version_codec(codec) def test_unsupported_module(): @@ -72,6 +109,8 @@ def test_unsupported_module(): # Act / Assert with pytest.raises(ValueError): features.check_module(module) + with pytest.raises(ValueError): + features.version_module(module) def test_pilinfo(): diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 9d9e49289c1..c30eb54eb60 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -3,11 +3,12 @@ import itertools import logging import os +import re from collections import namedtuple from ctypes import c_float import pytest -from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags +from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features from .helper import ( assert_image_equal, @@ -47,6 +48,9 @@ def _assert_noerr(self, tmp_path, im): class TestFileLibTiff(LibTiffTestCase): + def test_version(self): + assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("libtiff")) + def test_g4_tiff(self, tmp_path): """Test the ordinary file path load path""" diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index f538b0ecf85..25a4bb8da0b 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,7 +1,8 @@ import io +import re import pytest -from PIL import Image, WebPImagePlugin +from PIL import Image, WebPImagePlugin, features from .helper import ( assert_image_similar, @@ -38,6 +39,7 @@ def setup_method(self): def test_version(self): _webp.WebPDecoderVersion() _webp.WebPDecoderBuggyAlpha() + assert re.search(r"\d+\.\d+\.\d+$", features.version_module("webp")) def test_read_rgb(self): """ From 659ce90af1b67151ad5089798bc2252239c345c1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 19:09:09 +1000 Subject: [PATCH 219/262] Fixed typo [ci skip] --- docs/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 203921c0b84..885fba4cd46 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -18,7 +18,7 @@ ImageFile.raise_ioerror .. deprecated:: 7.2.0 ``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror`` -is now deprecated and will be removed in a future released. Use +is now deprecated and will be removed in a future release. Use ``ImageFile.raise_oserror`` instead. PILLOW_VERSION constant From c76dfbaef597de34baefb9d16d0a174e2877b73e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 19:11:09 +1000 Subject: [PATCH 220/262] Added release notes for #4536 [ci skip] --- docs/releasenotes/7.2.0.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst index 00baca474f6..26a1464a4c0 100644 --- a/docs/releasenotes/7.2.0.rst +++ b/docs/releasenotes/7.2.0.rst @@ -33,3 +33,13 @@ TIFF BYTE tags format TIFF BYTE tags were previously read as a tuple containing a bytestring. They are now read as just a single bytestring. + +Deprecations +^^^^^^^^^^^^ + +ImageFile.raise_ioerror +~~~~~~~~~~~~~~~~~~~~~~~ + +``IOError`` was merged into ``OSError`` in Python 3.3. So, ``ImageFile.raise_ioerror`` +is now deprecated and will be removed in a future release. Use +``ImageFile.raise_oserror`` instead. From d4f490183838a03762521e1f8e4063dc4481f08a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 19:29:33 +1000 Subject: [PATCH 221/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b3b96bb9e09..96239a6e275 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -62,6 +62,9 @@ Changelog (Pillow) - Fix pickling WebP #4561 [hugovk, radarhere] +- Replace IOError and WindowsError aliases with OSError #4536 + [hugovk, radarhere] + 7.1.2 (2020-04-25) ------------------ From 2f0d4308076d9c59662c563d9024382b3972e1cf Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Sun, 21 Jun 2020 13:13:35 +0300 Subject: [PATCH 222/262] Fix exception causes all over the codebase --- docs/example/DdsImagePlugin.py | 8 +++---- src/PIL/BlpImagePlugin.py | 4 ++-- src/PIL/BmpImagePlugin.py | 4 ++-- src/PIL/EpsImagePlugin.py | 4 ++-- src/PIL/FpxImagePlugin.py | 4 ++-- src/PIL/GdImageFile.py | 4 ++-- src/PIL/GifImagePlugin.py | 4 ++-- src/PIL/ImImagePlugin.py | 8 +++---- src/PIL/Image.py | 40 +++++++++++++++++----------------- src/PIL/ImageCms.py | 32 +++++++++++++-------------- src/PIL/ImageFile.py | 10 ++++----- src/PIL/ImageFilter.py | 4 ++-- src/PIL/ImageFont.py | 12 +++++----- src/PIL/ImageMath.py | 8 +++---- src/PIL/ImagePalette.py | 4 ++-- src/PIL/ImageSequence.py | 8 +++---- src/PIL/IptcImagePlugin.py | 4 ++-- src/PIL/JpegImagePlugin.py | 24 ++++++++++---------- src/PIL/MicImagePlugin.py | 8 +++---- src/PIL/MspImagePlugin.py | 8 +++---- src/PIL/PcxImagePlugin.py | 4 ++-- src/PIL/PngImagePlugin.py | 18 ++++++++------- src/PIL/PsdImagePlugin.py | 4 ++-- src/PIL/SpiderImagePlugin.py | 4 ++-- src/PIL/TgaImagePlugin.py | 4 ++-- src/PIL/TiffImagePlugin.py | 12 +++++----- 26 files changed, 125 insertions(+), 123 deletions(-) diff --git a/docs/example/DdsImagePlugin.py b/docs/example/DdsImagePlugin.py index 45f63f1647d..1e36f093ade 100644 --- a/docs/example/DdsImagePlugin.py +++ b/docs/example/DdsImagePlugin.py @@ -249,8 +249,8 @@ class DXT1Decoder(ImageFile.PyDecoder): def decode(self, buffer): try: self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize)) - except struct.error: - raise OSError("Truncated DDS file") + except struct.error as e: + raise OSError("Truncated DDS file") from e return 0, 0 @@ -260,8 +260,8 @@ class DXT5Decoder(ImageFile.PyDecoder): def decode(self, buffer): try: self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize)) - except struct.error: - raise OSError("Truncated DDS file") + except struct.error as e: + raise OSError("Truncated DDS file") from e return 0, 0 diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index 5ccba37dbd8..cb8a08e2036 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -282,8 +282,8 @@ def decode(self, buffer): self.magic = self.fd.read(4) self._read_blp_header() self._load() - except struct.error: - raise OSError("Truncated Blp file") + except struct.error as e: + raise OSError("Truncated Blp file") from e return 0, 0 def _read_palette(self): diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 85e2350c5ad..e87f7b95e03 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -304,8 +304,8 @@ def _dib_save(im, fp, filename): def _save(im, fp, filename, bitmap_header=True): try: rawmode, bits, colors = SAVE[im.mode] - except KeyError: - raise OSError("cannot write mode %s as BMP" % im.mode) + except KeyError as e: + raise OSError("cannot write mode %s as BMP" % im.mode) from e info = im.encoderinfo diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index e27a57671e4..652dc489a6a 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -231,8 +231,8 @@ def _open(self): try: m = split.match(s) - except re.error: - raise SyntaxError("not an EPS file") + except re.error as e: + raise SyntaxError("not an EPS file") from e if m: k, v = m.group(1, 2) diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 81501e2442e..bbee9e24dda 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -59,8 +59,8 @@ def _open(self): try: self.ole = olefile.OleFileIO(self.fp) - except OSError: - raise SyntaxError("not an FPX file; invalid OLE file") + except OSError as e: + raise SyntaxError("not an FPX file; invalid OLE file") from e if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": raise SyntaxError("not an FPX file; bad root CLSID") diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index b3ab01a4ebd..9ee37386871 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -81,5 +81,5 @@ def open(fp, mode="r"): try: return GdImageFile(fp) - except SyntaxError: - raise UnidentifiedImageError("cannot identify this image file") + except SyntaxError as e: + raise UnidentifiedImageError("cannot identify this image file") from e diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9d360beaec2..ac214bb29de 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -130,9 +130,9 @@ def seek(self, frame): for f in range(self.__frame + 1, frame + 1): try: self._seek(f) - except EOFError: + except EOFError as e: self.seek(last_frame) - raise EOFError("no more images in GIF file") + raise EOFError("no more images in GIF file") from e def _seek(self, frame): diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 8b03f35da8c..d940899b03e 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -163,8 +163,8 @@ def _open(self): try: m = split.match(s) - except re.error: - raise SyntaxError("not an IM file") + except re.error as e: + raise SyntaxError("not an IM file") from e if m: @@ -341,8 +341,8 @@ def _save(im, fp, filename): try: image_type, rawmode = SAVE[im.mode] - except KeyError: - raise ValueError("Cannot save %s images as IM" % im.mode) + except KeyError as e: + raise ValueError("Cannot save %s images as IM" % im.mode) from e frames = im.encoderinfo.get("frames", 1) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9d94bce0ee4..210a0ff5e56 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -434,8 +434,8 @@ def _getdecoder(mode, decoder_name, args, extra=()): try: # get decoder decoder = getattr(core, decoder_name + "_decoder") - except AttributeError: - raise OSError("decoder %s not available" % decoder_name) + except AttributeError as e: + raise OSError("decoder %s not available" % decoder_name) from e return decoder(mode, *args + extra) @@ -457,8 +457,8 @@ def _getencoder(mode, encoder_name, args, extra=()): try: # get encoder encoder = getattr(core, encoder_name + "_encoder") - except AttributeError: - raise OSError("encoder %s not available" % encoder_name) + except AttributeError as e: + raise OSError("encoder %s not available" % encoder_name) from e return encoder(mode, *args + extra) @@ -971,10 +971,10 @@ def convert_transparency(m, v): if isinstance(t, tuple): try: t = trns_im.palette.getcolor(t) - except Exception: + except Exception as e: raise ValueError( "Couldn't allocate a palette color for transparency" - ) + ) from e trns_im.putpixel((0, 0), t) if mode in ("L", "RGB"): @@ -1027,8 +1027,8 @@ def convert_transparency(m, v): # normalize source image and try again im = self.im.convert(getmodebase(self.mode)) im = im.convert(mode, dither) - except KeyError: - raise ValueError("illegal conversion") + except KeyError as e: + raise ValueError("illegal conversion") from e new_im = self._new(im) if delete_trns: @@ -1625,16 +1625,16 @@ def putalpha(self, alpha): mode = getmodebase(self.mode) + "A" try: self.im.setmode(mode) - except (AttributeError, ValueError): + except (AttributeError, ValueError) as e: # do things the hard way im = self.im.convert(mode) if im.mode not in ("LA", "PA", "RGBA"): - raise ValueError # sanity check + raise ValueError from e # sanity check self.im = im self.pyaccess = None self.mode = self.im.mode - except (KeyError, ValueError): - raise ValueError("illegal image mode") + except (KeyError, ValueError) as e: + raise ValueError("illegal image mode") from e if self.mode in ("LA", "PA"): band = 1 @@ -2136,8 +2136,8 @@ def save(self, fp, format=None, **params): init() try: format = EXTENSION[ext] - except KeyError: - raise ValueError("unknown file extension: {}".format(ext)) + except KeyError as e: + raise ValueError("unknown file extension: {}".format(ext)) from e if format.upper() not in SAVE: init() @@ -2238,8 +2238,8 @@ def getchannel(self, channel): if isinstance(channel, str): try: channel = self.getbands().index(channel) - except ValueError: - raise ValueError('The image has no channel "{}"'.format(channel)) + except ValueError as e: + raise ValueError('The image has no channel "{}"'.format(channel)) from e return self._new(self.im.getband(channel)) @@ -2736,12 +2736,12 @@ def fromarray(obj, mode=None): if mode is None: try: typekey = (1, 1) + shape[2:], arr["typestr"] - except KeyError: - raise TypeError("Cannot handle this data type") + except KeyError as e: + raise TypeError("Cannot handle this data type") from e try: mode, rawmode = _fromarray_typemap[typekey] - except KeyError: - raise TypeError("Cannot handle this data type: %s, %s" % typekey) + except KeyError as e: + raise TypeError("Cannot handle this data type: %s, %s" % typekey) from e else: rawmode = mode if mode in ["1", "L", "I", "P", "F"]: diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 723e7ceb7fe..8b97c19a14a 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -369,7 +369,7 @@ def profileToProfile( else: imOut = transform.apply(im) except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v return imOut @@ -393,7 +393,7 @@ def getOpenProfile(profileFilename): try: return ImageCmsProfile(profileFilename) except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def buildTransform( @@ -474,7 +474,7 @@ def buildTransform( inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags ) except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def buildProofTransform( @@ -585,7 +585,7 @@ def buildProofTransform( flags, ) except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v buildTransformFromOpenProfiles = buildTransform @@ -640,7 +640,7 @@ def applyTransform(im, transform, inPlace=False): else: imOut = transform.apply(im) except (TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v return imOut @@ -682,15 +682,15 @@ def createProfile(colorSpace, colorTemp=-1): if colorSpace == "LAB": try: colorTemp = float(colorTemp) - except (TypeError, ValueError): + except (TypeError, ValueError) as e: raise PyCMSError( 'Color temperature must be numeric, "%s" not valid' % colorTemp - ) + ) from e try: return core.createProfile(colorSpace, colorTemp) except (TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getProfileName(profile): @@ -732,7 +732,7 @@ def getProfileName(profile): return "{} - {}\n".format(model, manufacturer) except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getProfileInfo(profile): @@ -772,7 +772,7 @@ def getProfileInfo(profile): return "\r\n\r\n".join(arr) + "\r\n\r\n" except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getProfileCopyright(profile): @@ -800,7 +800,7 @@ def getProfileCopyright(profile): profile = ImageCmsProfile(profile) return (profile.profile.copyright or "") + "\n" except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getProfileManufacturer(profile): @@ -828,7 +828,7 @@ def getProfileManufacturer(profile): profile = ImageCmsProfile(profile) return (profile.profile.manufacturer or "") + "\n" except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getProfileModel(profile): @@ -857,7 +857,7 @@ def getProfileModel(profile): profile = ImageCmsProfile(profile) return (profile.profile.model or "") + "\n" except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getProfileDescription(profile): @@ -886,7 +886,7 @@ def getProfileDescription(profile): profile = ImageCmsProfile(profile) return (profile.profile.profile_description or "") + "\n" except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def getDefaultIntent(profile): @@ -925,7 +925,7 @@ def getDefaultIntent(profile): profile = ImageCmsProfile(profile) return profile.profile.rendering_intent except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def isIntentSupported(profile, intent, direction): @@ -976,7 +976,7 @@ def isIntentSupported(profile, intent, direction): else: return -1 except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) + raise PyCMSError(v) from v def versions(): diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 9a780b4e04f..9c9e89e3f00 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -122,7 +122,7 @@ def __init__(self, fp=None, filename=None): EOFError, # got header but not the first frame struct.error, ) as v: - raise SyntaxError(v) + raise SyntaxError(v) from v if not self.mode or self.size[0] <= 0: raise SyntaxError("not identified by this driver") @@ -241,12 +241,12 @@ def load(self): while True: try: s = read(self.decodermaxblock) - except (IndexError, struct.error): + except (IndexError, struct.error) as e: # truncated png/gif if LOAD_TRUNCATED_IMAGES: break else: - raise OSError("image file is truncated") + raise OSError("image file is truncated") from e if not s: # truncated jpeg if LOAD_TRUNCATED_IMAGES: @@ -505,7 +505,7 @@ def _save(im, fp, tile, bufsize=0): try: fh = fp.fileno() fp.flush() - except (AttributeError, io.UnsupportedOperation): + except (AttributeError, io.UnsupportedOperation) as e: # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) @@ -522,7 +522,7 @@ def _save(im, fp, tile, bufsize=0): if s: break if s < 0: - raise OSError("encoder error %d when writing image file" % s) + raise OSError("encoder error %d when writing image file" % s) from e e.cleanup() else: # slight speedup: compress to real file object diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 6b0f5eb376b..3e61a6ca1da 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -411,10 +411,10 @@ def __init__(self, size, table, channels=3, target_mode=None, **kwargs): def _check_size(size): try: _, _, _ = size - except ValueError: + except ValueError as e: raise ValueError( "Size should be either an integer or a tuple of three integers." - ) + ) from e except TypeError: size = (size, size, size) size = [int(x) for x in size] diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 79c1617135e..6b7368c1b83 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -503,8 +503,8 @@ def get_variation_names(self): """ try: names = self.font.getvarnames() - except AttributeError: - raise NotImplementedError("FreeType 2.9.1 or greater is required") + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e return [name.replace(b"\x00", b"") for name in names] def set_variation_by_name(self, name): @@ -533,8 +533,8 @@ def get_variation_axes(self): """ try: axes = self.font.getvaraxes() - except AttributeError: - raise NotImplementedError("FreeType 2.9.1 or greater is required") + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e for axis in axes: axis["name"] = axis["name"].replace(b"\x00", b"") return axes @@ -546,8 +546,8 @@ def set_variation_by_axes(self, axes): """ try: self.font.setvaraxes(axes) - except AttributeError: - raise NotImplementedError("FreeType 2.9.1 or greater is required") + except AttributeError as e: + raise NotImplementedError("FreeType 2.9.1 or greater is required") from e class TransposedFont: diff --git a/src/PIL/ImageMath.py b/src/PIL/ImageMath.py index adbb940000e..9a2d0b78e30 100644 --- a/src/PIL/ImageMath.py +++ b/src/PIL/ImageMath.py @@ -57,8 +57,8 @@ def apply(self, op, im1, im2=None, mode=None): im1.load() try: op = getattr(_imagingmath, op + "_" + im1.mode) - except AttributeError: - raise TypeError("bad operand type for '%s'" % op) + except AttributeError as e: + raise TypeError("bad operand type for '%s'" % op) from e _imagingmath.unop(op, out.im.id, im1.im.id) else: # binary operation @@ -85,8 +85,8 @@ def apply(self, op, im1, im2=None, mode=None): im2.load() try: op = getattr(_imagingmath, op + "_" + im1.mode) - except AttributeError: - raise TypeError("bad operand type for '%s'" % op) + except AttributeError as e: + raise TypeError("bad operand type for '%s'" % op) from e _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) return _Operand(out) diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index e0d439c9841..5dba6176fc6 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -97,13 +97,13 @@ def getcolor(self, color): if isinstance(color, tuple): try: return self.colors[color] - except KeyError: + except KeyError as e: # allocate new color slot if isinstance(self.palette, bytes): self.palette = bytearray(self.palette) index = len(self.colors) if index >= 256: - raise ValueError("cannot allocate more than 256 colors") + raise ValueError("cannot allocate more than 256 colors") from e self.colors[color] = index self.palette[index] = color[0] self.palette[index + 256] = color[1] diff --git a/src/PIL/ImageSequence.py b/src/PIL/ImageSequence.py index 4e9f5c210b7..9df910a4330 100644 --- a/src/PIL/ImageSequence.py +++ b/src/PIL/ImageSequence.py @@ -38,8 +38,8 @@ def __getitem__(self, ix): try: self.im.seek(ix) return self.im - except EOFError: - raise IndexError # end of sequence + except EOFError as e: + raise IndexError from e # end of sequence def __iter__(self): return self @@ -49,8 +49,8 @@ def __next__(self): self.im.seek(self.position) self.position += 1 return self.im - except EOFError: - raise StopIteration + except EOFError as e: + raise StopIteration from e def all_frames(im, func=None): diff --git a/src/PIL/IptcImagePlugin.py b/src/PIL/IptcImagePlugin.py index b2f976dda0d..75e7b5a2a45 100644 --- a/src/PIL/IptcImagePlugin.py +++ b/src/PIL/IptcImagePlugin.py @@ -118,8 +118,8 @@ def _open(self): # compression try: compression = COMPRESSION[self.getint((3, 120))] - except KeyError: - raise OSError("Unknown IPTC image compression") + except KeyError as e: + raise OSError("Unknown IPTC image compression") from e # tile if tag == (8, 10): diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 89e70f0e989..449d9cde7bd 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -503,13 +503,13 @@ def _getmp(self): file_contents.seek(info.next) info.load(file_contents) mp = dict(info) - except Exception: - raise SyntaxError("malformed MP Index (unreadable directory)") + except Exception as e: + raise SyntaxError("malformed MP Index (unreadable directory)") from e # it's an error not to have a number of images try: quant = mp[0xB001] - except KeyError: - raise SyntaxError("malformed MP Index (no number of images)") + except KeyError as e: + raise SyntaxError("malformed MP Index (no number of images)") from e # get MP entries mpentries = [] try: @@ -545,8 +545,8 @@ def _getmp(self): mpentry["Attribute"] = mpentryattr mpentries.append(mpentry) mp[0xB002] = mpentries - except KeyError: - raise SyntaxError("malformed MP Index (bad MP Entry)") + except KeyError as e: + raise SyntaxError("malformed MP Index (bad MP Entry)") from e # Next we should try and parse the individual image unique ID list; # we don't because I've never seen this actually used in a real MPO # file and so can't test it. @@ -610,8 +610,8 @@ def _save(im, fp, filename): try: rawmode = RAWMODE[im.mode] - except KeyError: - raise OSError("cannot write mode %s as JPEG" % im.mode) + except KeyError as e: + raise OSError("cannot write mode %s as JPEG" % im.mode) from e info = im.encoderinfo @@ -663,8 +663,8 @@ def validate_qtables(qtables): for line in qtables.splitlines() for num in line.split("#", 1)[0].split() ] - except ValueError: - raise ValueError("Invalid quantization table") + except ValueError as e: + raise ValueError("Invalid quantization table") from e else: qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)] if isinstance(qtables, (tuple, list, dict)): @@ -679,8 +679,8 @@ def validate_qtables(qtables): if len(table) != 64: raise TypeError table = array.array("B", table) - except TypeError: - raise ValueError("Invalid quantization table") + except TypeError as e: + raise ValueError("Invalid quantization table") from e else: qtables[idx] = list(table) return qtables diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 1d7af7c7ac7..2aed2603080 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -46,8 +46,8 @@ def _open(self): try: self.ole = olefile.OleFileIO(self.fp) - except OSError: - raise SyntaxError("not an MIC file; invalid OLE file") + except OSError as e: + raise SyntaxError("not an MIC file; invalid OLE file") from e # find ACI subfiles with Image members (maybe not the # best way to identify MIC files, but what the... ;-) @@ -77,8 +77,8 @@ def seek(self, frame): return try: filename = self.images[frame] - except IndexError: - raise EOFError("no such frame") + except IndexError as e: + raise EOFError("no such frame") from e self.fp = self.ole.openstream(filename) diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 2b2937ecfcf..a729e7b49a4 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -116,8 +116,8 @@ def decode(self, buffer): rowmap = struct.unpack_from( "<%dH" % (self.state.ysize), self.fd.read(self.state.ysize * 2) ) - except struct.error: - raise OSError("Truncated MSP file in row map") + except struct.error as e: + raise OSError("Truncated MSP file in row map") from e for x, rowlen in enumerate(rowmap): try: @@ -142,8 +142,8 @@ def decode(self, buffer): img.write(row[idx : idx + runcount]) idx += runcount - except struct.error: - raise OSError("Corrupted MSP file in row %d" % x) + except struct.error as e: + raise OSError("Corrupted MSP file in row %d" % x) from e self.set_as_raw(img.getvalue(), ("1", 0, 1)) diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index 6cf10deb3f7..f7ae3bf70e1 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -131,8 +131,8 @@ def _save(im, fp, filename): try: version, bits, planes, rawmode = SAVE[im.mode] - except KeyError: - raise ValueError("Cannot save %s images as PCX" % im.mode) + except KeyError as e: + raise ValueError("Cannot save %s images as PCX" % im.mode) from e # bytes per plane stride = (im.size[0] * bits + 7) // 8 diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index f62bf8542c8..025e4ec26b4 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -168,8 +168,10 @@ def crc(self, cid, data): crc2 = i32(self.fp.read(4)) if crc1 != crc2: raise SyntaxError("broken PNG file (bad header checksum in %r)" % cid) - except struct.error: - raise SyntaxError("broken PNG file (incomplete checksum in %r)" % cid) + except struct.error as e: + raise SyntaxError( + "broken PNG file (incomplete checksum in %r)" % cid + ) from e def crc_skip(self, cid, data): """Read checksum. Used if the C module is not present""" @@ -186,8 +188,8 @@ def verify(self, endchunk=b"IEND"): while True: try: cid, pos, length = self.read() - except struct.error: - raise OSError("truncated PNG file") + except struct.error as e: + raise OSError("truncated PNG file") from e if cid == endchunk: break @@ -737,9 +739,9 @@ def seek(self, frame): for f in range(self.__frame + 1, frame + 1): try: self._seek(f) - except EOFError: + except EOFError as e: self.seek(last_frame) - raise EOFError("no more images in APNG file") + raise EOFError("no more images in APNG file") from e def _seek(self, frame, rewind=False): if frame == 0: @@ -1168,8 +1170,8 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False): # get the corresponding PNG mode try: rawmode, mode = _OUTMODES[mode] - except KeyError: - raise OSError("cannot write mode %s as PNG" % mode) + except KeyError as e: + raise OSError("cannot write mode %s as PNG" % mode) from e # # write minimal PNG file diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 044df443d75..1ff4c8624ea 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -144,8 +144,8 @@ def seek(self, layer): self.frame = layer self.fp = self.__fp return name, bbox - except IndexError: - raise EOFError("no such layer") + except IndexError as e: + raise EOFError("no such layer") from e def tell(self): # return layer number (0=image, 1..max=layers) diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index cbd31cf82ed..56aac298797 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -111,8 +111,8 @@ def _open(self): hdrlen = isSpiderHeader(t) if hdrlen == 0: raise SyntaxError("not a valid Spider file") - except struct.error: - raise SyntaxError("not a valid Spider file") + except struct.error as e: + raise SyntaxError("not a valid Spider file") from e h = (99,) + t # add 1 value : spider header index starts at 1 iform = int(h[5]) diff --git a/src/PIL/TgaImagePlugin.py b/src/PIL/TgaImagePlugin.py index fd71e545d62..566f0ac18dd 100644 --- a/src/PIL/TgaImagePlugin.py +++ b/src/PIL/TgaImagePlugin.py @@ -167,8 +167,8 @@ def _save(im, fp, filename): try: rawmode, bits, colormaptype, imagetype = SAVE[im.mode] - except KeyError: - raise OSError("cannot write mode %s as TGA" % im.mode) + except KeyError as e: + raise OSError("cannot write mode %s as TGA" % im.mode) from e if "rle" in im.encoderinfo: rle = im.encoderinfo["rle"] diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index bee05e6ed28..cb725d952ad 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1117,8 +1117,8 @@ def _load_libtiff(self): ) try: decoder.setimage(self.im, extents) - except ValueError: - raise OSError("Couldn't set the image") + except ValueError as e: + raise OSError("Couldn't set the image") from e close_self_fp = self._exclusive_fp and not self.is_animated if hasattr(self.fp, "getvalue"): @@ -1231,9 +1231,9 @@ def _setup(self): logger.debug("format key: {}".format(key)) try: self.mode, rawmode = OPEN_INFO[key] - except KeyError: + except KeyError as e: logger.debug("- unsupported format") - raise SyntaxError("unknown pixel mode") + raise SyntaxError("unknown pixel mode") from e logger.debug("- raw mode: {}".format(rawmode)) logger.debug("- pil mode: {}".format(self.mode)) @@ -1400,8 +1400,8 @@ def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] - except KeyError: - raise OSError("cannot write mode %s as TIFF" % im.mode) + except KeyError as e: + raise OSError("cannot write mode %s as TIFF" % im.mode) from e ifd = ImageFileDirectory_v2(prefix=prefix) From 34ba2ae139d46113ab7b1c683f3a6f6d5a8f89c7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 20:26:10 +1000 Subject: [PATCH 223/262] Removed comments suggesting users override functions --- src/PIL/Image.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 9d94bce0ee4..2fc37d9cee2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3139,11 +3139,10 @@ def register_encoder(name, encoder): # -------------------------------------------------------------------- -# Simple display support. User code may override this. +# Simple display support. def _show(image, **options): - # override me, as necessary _showxv(image, **options) From 29dbabd54473b584ca670d6e915b631b4fde7772 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 21 Jun 2020 11:35:47 +0100 Subject: [PATCH 224/262] improve wording Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/deprecations.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 508b1242bf6..08906a9b69e 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -12,8 +12,8 @@ Deprecated features Below are features which are considered deprecated. Where appropriate, a ``DeprecationWarning`` is issued. -Image.show -~~~~~~~~~~ +Image.show command parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. deprecated:: 7.2.0 From c15dda4308c1aa0d02c2ade7af528d2182b9a90c Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 21 Jun 2020 12:16:27 +0100 Subject: [PATCH 225/262] fix typo Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- 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 c4f8380df0c..051996c09ff 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2177,7 +2177,7 @@ def show(self, title=None, command=None): if command is not None: warnings.warn( - "The command parameter is deprecated and will be removed in a future" + "The command parameter is deprecated and will be removed in a future " "release. Use a subclass of ImageShow.Viewer instead.", DeprecationWarning, ) From 24672a2f7502e0ae0534bd0aadf17b2c3e200787 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 21 Jun 2020 18:07:10 +0100 Subject: [PATCH 226/262] simplify output Co-authored-by: Hugo van Kemenade --- src/PIL/features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/features.py b/src/PIL/features.py index 4f1bb0b8fd0..66b093350f5 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -270,7 +270,7 @@ def pilinfo(out=None, supported_formats=True): v = version(name) if v is not None: t = "compiled for" if name in ("pil", "jpg") else "loaded" - print("---", feature, "support ok,", t, "version", v, file=out) + print("---", feature, "support ok,", t, v, file=out) else: print("---", feature, "support ok", file=out) else: From 66eee05a37bb5674e7a9d24d2951e71ed773b8e2 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 21 Jun 2020 18:47:30 +0100 Subject: [PATCH 227/262] Apply suggestions from code review Co-authored-by: Hugo van Kemenade --- docs/PIL.rst | 2 +- src/PIL/FontFile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 222322b0d53..8f8cda5fb83 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -5,7 +5,7 @@ Reference for modules whose documentation has not yet been ported or written can be found here. :mod:`PIL` Module -------------------------- +----------------- .. py:module:: PIL diff --git a/src/PIL/FontFile.py b/src/PIL/FontFile.py index 4243b28b65b..3ebd90730bd 100644 --- a/src/PIL/FontFile.py +++ b/src/PIL/FontFile.py @@ -23,7 +23,7 @@ def puti16(fp, values): - """write network order (big-endian) 16-bit sequence""" + """Write network order (big-endian) 16-bit sequence""" for v in values: if v < 0: v += 65536 From 224ef2fadd1fffefa6b2ff0b98be2595c23b4188 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 05:11:02 +0200 Subject: [PATCH 228/262] require sphinx>=2.4 --- docs/conf.py | 2 +- requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index a0a3315c61a..d95ec2a4a86 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' +needs_sphinx = "2.4" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom diff --git a/requirements.txt b/requirements.txt index 14f934c9cc9..0e0d38cddce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,5 @@ pyflakes pyroma pytest pytest-cov +sphinx>=2.4 sphinx-rtd-theme From 6d2fe429c2f2786f455279ad6e119614091c2806 Mon Sep 17 00:00:00 2001 From: Kirill Kuzminykh Date: Mon, 22 Jun 2020 12:20:57 +0300 Subject: [PATCH 229/262] Reformat code of ``test_file_jpeg.py`. --- Tests/test_file_jpeg.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 74ea32b0b2e..1801cd8ff6d 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -3,7 +3,14 @@ from io import BytesIO import pytest -from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, UnidentifiedImageError, features +from PIL import ( + ExifTags, + Image, + ImageFile, + JpegImagePlugin, + UnidentifiedImageError, + features, +) from .helper import ( assert_image, From c4c1b51f459ba8804a7af13b142d7caea42725e0 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 22 Jun 2020 12:10:45 +0300 Subject: [PATCH 230/262] pre-commit: when hooks fail, run 'git diff' directly afterwards --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 9f310ca3a05..8005888d4d9 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = [testenv:lint] commands = - pre-commit run --all-files + pre-commit run --all-files --show-diff-on-failure check-manifest deps = pre-commit From 7e3556e5f640b63103d92b2f749f7aef6f3d5d44 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 22 Jun 2020 12:22:43 +0300 Subject: [PATCH 231/262] GHA: Force colour output of pre-commit --- .github/workflows/lint.yml | 4 +++- tox.ini | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6c5d81ac44d..64296b37789 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v2 - name: pip cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ~/.cache/pip key: lint-pip-${{ hashFiles('**/setup.py') }} @@ -46,4 +46,6 @@ jobs: - name: Lint run: tox -e lint + env: + PRE_COMMIT_COLOR: always diff --git a/tox.ini b/tox.ini index 8005888d4d9..aa6875374bb 100644 --- a/tox.ini +++ b/tox.ini @@ -30,3 +30,4 @@ deps = pre-commit check-manifest skip_install = true +passenv = PRE_COMMIT_COLOR From 519ce4b8db49b533e7ac833ff4363d0d0990b542 Mon Sep 17 00:00:00 2001 From: Hugo Date: Mon, 22 Jun 2020 12:42:02 +0300 Subject: [PATCH 232/262] GHA: Force colour output of pytest --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 30843b847ac..f9d47e243ff 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,5 +9,5 @@ line_length = 88 multi_line_output = 3 [tool:pytest] -addopts = -rs +addopts = -rs --color=yes testpaths = Tests From 96d1a8b41867e3459c77c190d8c01a4301d524ba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 00:25:59 +1000 Subject: [PATCH 233/262] Updated _open check to match _accept --- src/PIL/JpegImagePlugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index c9b83c032b9..6edaee7951b 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -338,10 +338,11 @@ class JpegImageFile(ImageFile.ImageFile): def _open(self): - s = self.fp.read(1) + s = self.fp.read(3) - if i8(s) != 255: + if s != b"\xFF\xD8\xFF": raise SyntaxError("not a JPEG file") + s = b"\xFF" # Create attributes self.bits = self.layers = 0 From 03d4f31a3453793bfb9fc068fe1dbd61ef74a3c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 08:14:02 +1000 Subject: [PATCH 234/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 96239a6e275..dad3e7d00b6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Updated JPEG magic number #4707 + [Cykooz, radarhere] + - Change STRIPBYTECOUNTS to LONG if necessary when saving #4626 [radarhere, hugovk] From 262e2aaabb691458d630eb60b70bc8ce4e7ada3a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 08:17:26 +1000 Subject: [PATCH 235/262] Updated harfbuzz to 2.6.8 --- 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 beafda4a465..553ed636530 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -251,9 +251,9 @@ def cmd_msbuild( "libs": [r"*.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.7.zip", - "filename": "harfbuzz-2.6.7.zip", - "dir": "harfbuzz-2.6.7", + "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.8.zip", + "filename": "harfbuzz-2.6.8.zip", + "dir": "harfbuzz-2.6.8", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 9b6fdd719f258c5a382e2cbf5f110f172f6542bd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 17:41:13 +1000 Subject: [PATCH 236/262] Call _accept instead of duplicating code --- src/PIL/BmpImagePlugin.py | 2 +- src/PIL/DcxImagePlugin.py | 2 +- src/PIL/FliImagePlugin.py | 4 ++-- src/PIL/GifImagePlugin.py | 2 +- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MspImagePlugin.py | 2 +- src/PIL/PixarImagePlugin.py | 2 +- src/PIL/PngImagePlugin.py | 2 +- src/PIL/PsdImagePlugin.py | 2 +- src/PIL/SgiImagePlugin.py | 3 +-- src/PIL/SunImagePlugin.py | 2 +- 11 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 85e2350c5ad..a1ed80aee0b 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -263,7 +263,7 @@ def _open(self): # read 14 bytes: magic number, filesize, reserved, header final offset head_data = self.fp.read(14) # choke if the file does not have the required magic bytes - if head_data[0:2] != b"BM": + if not _accept(head_data[0:2]): raise SyntaxError("Not a BMP file") # read the start position of the BMP image data (u32) offset = i32(head_data[10:14]) diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index a12d9195b7a..de21db8f082 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -46,7 +46,7 @@ def _open(self): # Header s = self.fp.read(4) - if i32(s) != MAGIC: + if not _accept(s): raise SyntaxError("not a DCX file") # Component directory diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 98909456990..f09d62ce3ad 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -42,9 +42,8 @@ def _open(self): # HEAD s = self.fp.read(128) - magic = i16(s[4:6]) if not ( - magic in [0xAF11, 0xAF12] + _accept(s) and i16(s[14:16]) in [0, 3] # flags and s[20:22] == b"\x00\x00" # reserved ): @@ -60,6 +59,7 @@ def _open(self): # animation speed duration = i32(s[16:20]) + magic = i16(s[4:6]) if magic == 0xAF11: duration = (duration * 1000) // 70 self.info["duration"] = duration diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9d360beaec2..6fd6182ab19 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -63,7 +63,7 @@ def _open(self): # Screen s = self.fp.read(13) - if s[:6] not in [b"GIF87a", b"GIF89a"]: + if not _accept(s): raise SyntaxError("not a GIF file") self.info["version"] = s[:6] diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 6edaee7951b..f846569b2ef 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -340,7 +340,7 @@ def _open(self): s = self.fp.read(3) - if s != b"\xFF\xD8\xFF": + if not _accept(s): raise SyntaxError("not a JPEG file") s = b"\xFF" diff --git a/src/PIL/MspImagePlugin.py b/src/PIL/MspImagePlugin.py index 2b2937ecfcf..26acdd8983d 100644 --- a/src/PIL/MspImagePlugin.py +++ b/src/PIL/MspImagePlugin.py @@ -51,7 +51,7 @@ def _open(self): # Header s = self.fp.read(32) - if s[:4] not in [b"DanM", b"LinS"]: + if not _accept(s): raise SyntaxError("not an MSP file") # Header checksum diff --git a/src/PIL/PixarImagePlugin.py b/src/PIL/PixarImagePlugin.py index 5ea32ba89be..91f0314b515 100644 --- a/src/PIL/PixarImagePlugin.py +++ b/src/PIL/PixarImagePlugin.py @@ -43,7 +43,7 @@ def _open(self): # assuming a 4-byte magic label s = self.fp.read(4) - if s != b"\200\350\000\000": + if not _accept(s): raise SyntaxError("not a PIXAR file") # read rest of header diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index f62bf8542c8..51b78c7dea9 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -633,7 +633,7 @@ class PngImageFile(ImageFile.ImageFile): def _open(self): - if self.fp.read(8) != _MAGIC: + if not _accept(self.fp.read(8)): raise SyntaxError("not a PNG file") self.__fp = self.fp self.__frame = 0 diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 044df443d75..89e55a4ce69 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -61,7 +61,7 @@ def _open(self): # header s = read(26) - if s[:4] != b"8BPS" or i16(s[4:]) != 1: + if not _accept(s) or i16(s[4:]) != 1: raise SyntaxError("not a PSD file") psd_bits = i16(s[22:]) diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index ddd3de379aa..ec9855e775b 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -58,8 +58,7 @@ def _open(self): headlen = 512 s = self.fp.read(headlen) - # magic number : 474 - if i16(s) != 474: + if not _accept(s): raise ValueError("Not an SGI image file") # compression : verbatim or RLE diff --git a/src/PIL/SunImagePlugin.py b/src/PIL/SunImagePlugin.py index fd7ca8a403e..d99884293ae 100644 --- a/src/PIL/SunImagePlugin.py +++ b/src/PIL/SunImagePlugin.py @@ -53,7 +53,7 @@ def _open(self): # HEAD s = self.fp.read(32) - if i32(s) != 0x59A66A95: + if not _accept(s): raise SyntaxError("not an SUN raster file") offset = 32 From 6c2d575f9bf05a877a7c33e19ae9be14a1c73a71 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 23 Jun 2020 18:09:12 +1000 Subject: [PATCH 237/262] Simplified passing of data to _accept Co-authored-by: Hugo van Kemenade --- src/PIL/BmpImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index a1ed80aee0b..7bd5a0308b3 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -263,7 +263,7 @@ def _open(self): # read 14 bytes: magic number, filesize, reserved, header final offset head_data = self.fp.read(14) # choke if the file does not have the required magic bytes - if not _accept(head_data[0:2]): + if not _accept(head_data): raise SyntaxError("Not a BMP file") # read the start position of the BMP image data (u32) offset = i32(head_data[10:14]) From c1fe0b4e0c4319926c3322ebf0ea60dd52b9c539 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 19:17:00 +1000 Subject: [PATCH 238/262] Use hypot function --- .../imagedraw_wide_line_larger_than_int.png | Bin 0 -> 557 bytes Tests/test_imagedraw.py | 13 +++++++++++++ src/libImaging/Draw.c | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Tests/images/imagedraw_wide_line_larger_than_int.png diff --git a/Tests/images/imagedraw_wide_line_larger_than_int.png b/Tests/images/imagedraw_wide_line_larger_than_int.png new file mode 100644 index 0000000000000000000000000000000000000000..68073ce48276fb22191fbe03612f32955ec9b48d GIT binary patch literal 557 zcmV+|0@D47P)Nkl!vb;ZKDS8psldkw(Ct=AYVe0mMT!lBnlEWCLQ#=@1?cr5&QnZUw{ zmlZ5Lcp1XNyq7I3Y;-94y*< zg~Fn-S2QeIdIiLynO96K+IWS Date: Tue, 23 Jun 2020 18:20:43 +1000 Subject: [PATCH 239/262] Replaced assert_image_similar with assert_image_similar_tofile --- Tests/test_imagedraw.py | 74 ++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 9aac1a0c8e4..56b189ecd2d 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -5,7 +5,7 @@ from .helper import ( assert_image_equal, - assert_image_similar, + assert_image_similar_tofile, hopper, skip_unless_feature, ) @@ -71,7 +71,7 @@ def helper_arc(bbox, start, end): draw.arc(bbox, start, end) # Assert - assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_arc.png", 1) def test_arc1(): @@ -110,20 +110,19 @@ def test_arc_no_loops(): draw.arc(BBOX1, start=start, end=end) # Assert - assert_image_similar(im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_no_loops.png", 1) def test_arc_width(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width.png" # Act draw.arc(BBOX1, 10, 260, width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width.png", 1) def test_arc_width_pieslice_large(): @@ -131,26 +130,24 @@ def test_arc_width_pieslice_large(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width_pieslice.png" # Act draw.arc(BBOX1, 10, 260, fill="yellow", width=100) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_pieslice.png", 1) def test_arc_width_fill(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_arc_width_fill.png" # Act draw.arc(BBOX1, 10, 260, fill="yellow", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_fill.png", 1) def test_arc_width_non_whole_angle(): @@ -163,7 +160,7 @@ def test_arc_width_non_whole_angle(): draw.arc(BBOX1, 10, 259.5, width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, expected, 1) def test_bitmap(): @@ -190,7 +187,7 @@ def helper_chord(mode, bbox, start, end): draw.chord(bbox, start, end, fill="red", outline="yellow") # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, expected, 1) def test_chord1(): @@ -209,26 +206,24 @@ def test_chord_width(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_chord_width.png" # Act draw.chord(BBOX1, 10, 260, outline="yellow", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width.png", 1) def test_chord_width_fill(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_chord_width_fill.png" # Act draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width_fill.png", 1) def test_chord_zero_width(): @@ -254,7 +249,7 @@ def helper_ellipse(mode, bbox): draw.ellipse(bbox, fill="green", outline="blue") # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, expected, 1) def test_ellipse1(): @@ -276,8 +271,8 @@ def test_ellipse_translucent(): draw.ellipse(BBOX1, fill=(0, 255, 0, 127)) # Assert - expected = Image.open("Tests/images/imagedraw_ellipse_translucent.png") - assert_image_similar(im, expected, 1) + expected = "Tests/images/imagedraw_ellipse_translucent.png" + assert_image_similar_tofile(im, expected, 1) def test_ellipse_edge(): @@ -289,7 +284,7 @@ def test_ellipse_edge(): draw.ellipse(((0, 0), (W - 1, H)), fill="white") # Assert - assert_image_similar(im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1) def test_ellipse_symmetric(): @@ -304,39 +299,36 @@ def test_ellipse_width(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_width.png" # Act draw.ellipse(BBOX1, outline="blue", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width.png", 1) def test_ellipse_width_large(): # Arrange im = Image.new("RGB", (500, 500)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_width_large.png" # Act draw.ellipse((25, 25, 475, 475), outline="blue", width=75) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_large.png", 1) def test_ellipse_width_fill(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_ellipse_width_fill.png" # Act draw.ellipse(BBOX1, fill="green", outline="blue", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_fill.png", 1) def test_ellipse_zero_width(): @@ -423,7 +415,7 @@ def helper_pieslice(bbox, start, end): draw.pieslice(bbox, start, end, fill="white", outline="blue") # Assert - assert_image_similar(im, Image.open("Tests/images/imagedraw_pieslice.png"), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice.png", 1) def test_pieslice1(): @@ -440,13 +432,12 @@ def test_pieslice_width(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_pieslice_width.png" # Act draw.pieslice(BBOX1, 10, 260, outline="blue", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice_width.png", 1) def test_pieslice_width_fill(): @@ -459,7 +450,7 @@ def test_pieslice_width_fill(): draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, expected, 1) def test_pieslice_zero_width(): @@ -571,13 +562,12 @@ def test_big_rectangle(): im = Image.new("RGB", (W, H)) bbox = [(-1, -1), (W + 1, H + 1)] draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_big_rectangle.png" # Act draw.rectangle(bbox, fill="orange") # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_big_rectangle.png", 1) def test_rectangle_width(): @@ -878,13 +868,12 @@ def test_wide_line_dot(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_wide_line_dot.png" # Act draw.line([(50, 50), (50, 50)], width=3) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, "Tests/images/imagedraw_wide_line_dot.png", 1) def test_wide_line_larger_than_int(): @@ -897,7 +886,7 @@ def test_wide_line_larger_than_int(): draw.line([(0, 0), (32768, 32768)], width=3) # Assert - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, expected, 1) @pytest.mark.parametrize( @@ -984,13 +973,12 @@ def test_wide_line_larger_than_int(): def test_line_joint(xy): im = Image.new("RGB", (500, 325)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_line_joint_curve.png" # Act draw.line(xy, GRAY, 50, "curve") # Assert - assert_image_similar(im, Image.open(expected), 3) + assert_image_similar_tofile(im, "Tests/images/imagedraw_line_joint_curve.png", 3) def test_textsize_empty_string(): @@ -1031,8 +1019,8 @@ def test_stroke(): draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill) # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1 + assert_image_similar_tofile( + im, "Tests/images/imagedraw_stroke_" + suffix + ".png", 3.1 ) @@ -1047,9 +1035,7 @@ def test_stroke_descender(): draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0") # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_stroke_descender.png"), 6.76 - ) + assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_descender.png", 6.76) @skip_unless_feature("freetype2") @@ -1065,9 +1051,7 @@ def test_stroke_multiline(): ) # Assert - assert_image_similar( - im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3 - ) + assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_multiline.png", 3.3) def test_same_color_outline(): @@ -1106,4 +1090,4 @@ def test_same_color_outline(): expected = "Tests/images/imagedraw_outline_{}_{}.png".format( operation, mode ) - assert_image_similar(im, Image.open(expected), 1) + assert_image_similar_tofile(im, expected, 1) From 824f2930269bb7001642248582692b9178fe3f22 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 19:57:02 +1000 Subject: [PATCH 240/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index dad3e7d00b6..c79b9d1efda 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Deprecate Image.show(command="...") #4646 + [nulano, hugovk, radarhere] + - Updated JPEG magic number #4707 [Cykooz, radarhere] From d7b812fcb2c942b36787837ca165ff2473defd32 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Jun 2020 21:50:23 +1000 Subject: [PATCH 241/262] Added release notes for #4646 [ci skip] --- docs/releasenotes/7.2.0.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst index 26a1464a4c0..6b91a1b0188 100644 --- a/docs/releasenotes/7.2.0.rst +++ b/docs/releasenotes/7.2.0.rst @@ -37,6 +37,12 @@ are now read as just a single bytestring. Deprecations ^^^^^^^^^^^^ +Image.show command parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``command`` parameter was deprecated and will be removed in a future release. +Use a subclass of :py:class:`PIL.ImageShow.Viewer` instead. + ImageFile.raise_ioerror ~~~~~~~~~~~~~~~~~~~~~~~ From ee06255ff0ee64f8d3d1062c75e455973cc9d2fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Jun 2020 22:17:18 +1000 Subject: [PATCH 242/262] Deprecated _showxv --- Tests/test_image.py | 18 +++++++++++++++++- docs/deprecations.rst | 9 +++++++++ docs/releasenotes/7.2.0.rst | 7 +++++++ src/PIL/Image.py | 9 +++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index f284f89a740..be67f594798 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -5,7 +5,7 @@ import PIL import pytest -from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError +from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError from .helper import ( assert_image_equal, @@ -585,6 +585,22 @@ def test_p_from_rgb_rgba(self): expected = Image.new(mode, (100, 100), color) assert_image_equal(im.convert(mode), expected) + def test_showxv_deprecation(self): + class TestViewer(ImageShow.Viewer): + def show_image(self, image, **options): + return True + + viewer = TestViewer() + ImageShow.register(viewer, -1) + + im = Image.new("RGB", (50, 50), "white") + + with pytest.warns(DeprecationWarning): + Image._showxv(im) + + # Restore original state + ImageShow._viewers.pop(0) + def test_no_resource_warning_on_save(self, tmp_path): # https://github.com/python-pillow/Pillow/issues/835 # Arrange diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 38d2143c47e..f78842ac101 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -20,6 +20,15 @@ Image.show command parameter The ``command`` parameter was deprecated and will be removed in a future release. Use a subclass of ``ImageShow.Viewer`` instead. +Image._showxv +~~~~~~~~~~~~~ + +.. deprecated:: 7.2.0 + +``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show` +instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add +a custom :py:class:`~PIL.ImageShow.Viewer` class. + ImageFile.raise_ioerror ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/releasenotes/7.2.0.rst b/docs/releasenotes/7.2.0.rst index 6b91a1b0188..ff1b7c9e764 100644 --- a/docs/releasenotes/7.2.0.rst +++ b/docs/releasenotes/7.2.0.rst @@ -43,6 +43,13 @@ Image.show command parameter The ``command`` parameter was deprecated and will be removed in a future release. Use a subclass of :py:class:`PIL.ImageShow.Viewer` instead. +Image._showxv +~~~~~~~~~~~~~ + +``Image._showxv`` has been deprecated. Use :py:meth:`~PIL.Image.Image.show` +instead. If custom behaviour is required, use :py:meth:`~PIL.ImageShow.register` to add +a custom :py:class:`~PIL.ImageShow.Viewer` class. + ImageFile.raise_ioerror ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 02c73bc49ae..7a2ae02d692 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3150,12 +3150,21 @@ def register_encoder(name, encoder): def _show(image, **options): + options["_internal_pillow"] = True _showxv(image, **options) def _showxv(image, title=None, **options): from . import ImageShow + if "_internal_pillow" in options: + del options["_internal_pillow"] + else: + warnings.warn( + "_showxv is deprecated and will be removed in a future release. " + "Use Image.show instead.", + DeprecationWarning, + ) ImageShow.show(image, title, **options) From bd466c41c141472f69318c46a7b3ed74d3f1b45d Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 24 Jun 2020 10:11:16 +0300 Subject: [PATCH 243/262] Fix setting disposal --- src/PIL/PngImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 51b78c7dea9..32ba68cd567 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1036,7 +1036,7 @@ def _write_multiple_frames(im, fp, chunk, rawmode): 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 + prev_disposal = APNG_DISPOSE_OP_BACKGROUND if prev_disposal == APNG_DISPOSE_OP_BACKGROUND: base_im = previous["im"] From 685c0439b8ef3c0f9f7be4837a751d22f07cbb87 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 26 Jun 2020 18:47:34 +1000 Subject: [PATCH 244/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c79b9d1efda..c5b55a52475 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Deprecated _showxv #4714 + [radarhere] + - Deprecate Image.show(command="...") #4646 [nulano, hugovk, radarhere] From d641bdc504a943b21eaa770e3a549dd6b33d15af Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 27 Jun 2020 14:05:34 +0300 Subject: [PATCH 245/262] Fix isort --- Tests/test_imagegrab.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index a1453a07ecb..ae1277cedbc 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -4,6 +4,7 @@ import pytest from PIL import Image, ImageGrab + from .helper import assert_image, assert_image_equal_tofile, skip_unless_feature From e4210eb8d776bdbe57a17bb4a256e64d4367dcc0 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 06:43:14 +0200 Subject: [PATCH 246/262] fix ImageFile references (cherry picked from commit 6ac071782f820fa59acc91ff0fe0a697fc5f8cbe) --- docs/reference/ImageFile.rst | 17 ++++++++++++----- src/PIL/ImageFile.py | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst index d93dfb3a3df..6da40228359 100644 --- a/docs/reference/ImageFile.rst +++ b/docs/reference/ImageFile.rst @@ -34,14 +34,21 @@ Example: Parse an image im.save("copy.jpg") -:py:class:`~PIL.ImageFile.Parser` ---------------------------------- +Classes +------- .. autoclass:: PIL.ImageFile.Parser() :members: -:py:class:`~PIL.ImageFile.PyDecoder` ------------------------------------- - .. autoclass:: PIL.ImageFile.PyDecoder() :members: + +.. autoclass:: PIL.ImageFile.ImageFile() + :member-order: bysource + :members: + :undoc-members: + :show-inheritance: + +.. autoclass:: PIL.ImageFile.StubImageFile() + :members: + :show-inheritance: diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 9a780b4e04f..b431fbd1cab 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -85,7 +85,7 @@ def _tilesort(t): class ImageFile(Image.Image): - "Base class for image file format handlers." + """Base class for image file format handlers.""" def __init__(self, fp=None, filename=None): super().__init__() From 471f24f660c7d98023f0458853fc5548431d48b9 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 06:43:29 +0200 Subject: [PATCH 247/262] fix PyCMSError references (cherry picked from commit 63d0fb4f7019a8423bb5c62ea7b225f437f823cf) --- docs/reference/ImageCms.rst | 27 ++++++++++-- src/PIL/ImageCms.py | 87 +++++++++++++++++++------------------ 2 files changed, 69 insertions(+), 45 deletions(-) diff --git a/docs/reference/ImageCms.rst b/docs/reference/ImageCms.rst index 67c58176565..30690da345b 100644 --- a/docs/reference/ImageCms.rst +++ b/docs/reference/ImageCms.rst @@ -8,9 +8,30 @@ The :py:mod:`ImageCms` module provides color profile management support using the LittleCMS2 color management engine, based on Kevin Cazabon's PyCMS library. -.. automodule:: PIL.ImageCms - :members: - :noindex: +.. autoclass:: ImageCmsTransform +.. autoexception:: PyCMSError + +Functions +--------- + +.. autofunction:: applyTransform +.. autofunction:: buildProofTransform +.. autofunction:: buildProofTransformFromOpenProfiles +.. autofunction:: buildTransform +.. autofunction:: buildTransformFromOpenProfiles +.. autofunction:: createProfile +.. autofunction:: getDefaultIntent +.. autofunction:: getOpenProfile +.. autofunction:: getProfileCopyright +.. autofunction:: getProfileDescription +.. autofunction:: getProfileInfo +.. autofunction:: getProfileManufacturer +.. autofunction:: getProfileModel +.. autofunction:: getProfileName +.. autofunction:: get_display_profile +.. autofunction:: isIntentSupported +.. autofunction:: profileToProfile +.. autofunction:: versions CmsProfile ---------- diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index 723e7ceb7fe..93f345907ce 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -295,11 +295,12 @@ def profileToProfile( ``inputProfile`` to ``outputProfile``. If the input or output profiles specified are not valid filenames, a - ``PyCMSError`` will be raised. If ``inPlace`` is ``True`` and - ``outputMode != im.mode``, a ``PyCMSError`` will be raised. If an error - occurs during application of the profiles, a ``PyCMSError`` will be raised. + :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and + ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. + If an error occurs during application of the profiles, + a :exc:`PyCMSError` will be raised. If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), - a ``PyCMSError`` will be raised. + a :exc:`PyCMSError` will be raised. This function applies an ICC transformation to im from ``inputProfile``'s color space to ``outputProfile``'s color space using the specified rendering @@ -381,8 +382,8 @@ def getOpenProfile(profileFilename): The PyCMSProfile object can be passed back into pyCMS for use in creating transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). - If ``profileFilename`` is not a valid filename for an ICC profile, a ``PyCMSError`` - will be raised. + If ``profileFilename`` is not a valid filename for an ICC profile, + a :exc:`PyCMSError` will be raised. :param profileFilename: String, as a valid filename path to the ICC profile you wish to open, or a file-like object. @@ -410,11 +411,11 @@ def buildTransform( image. If the input or output profiles specified are not valid filenames, a - ``PyCMSError`` will be raised. If an error occurs during creation of the - transform, a ``PyCMSError`` will be raised. + :exc:`PyCMSError` will be raised. If an error occurs during creation + of the transform, a :exc:`PyCMSError` will be raised. If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a ``PyCMSError`` will be raised. + (or by pyCMS), a :exc:`PyCMSError` will be raised. This function builds and returns an ICC transform from the ``inputProfile`` to the ``outputProfile`` using the ``renderingIntent`` to determine what to do @@ -493,13 +494,13 @@ def buildProofTransform( obtained on the ``proofProfile`` device. If the input, output, or proof profiles specified are not valid - filenames, a ``PyCMSError`` will be raised. + filenames, a :exc:`PyCMSError` will be raised. - If an error occurs during creation of the transform, a ``PyCMSError`` - will be raised. + If an error occurs during creation of the transform, + a :exc:`PyCMSError` will be raised. If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a ``PyCMSError`` will be raised. + (or by pyCMS), a :exc:`PyCMSError` will be raised. This function builds and returns an ICC transform from the ``inputProfile`` to the ``outputProfile``, but tries to simulate the result that would be @@ -596,17 +597,17 @@ def applyTransform(im, transform, inPlace=False): """ (pyCMS) Applies a transform to a given image. - If ``im.mode != transform.inMode``, a ``PyCMSError`` is raised. + If ``im.mode != transform.inMode``, a :exc:`PyCMSError` is raised. If ``inPlace`` is ``True`` and ``transform.inMode != transform.outMode``, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. If ``im.mode``, ``transform.inMode`` or ``transform.outMode`` is not supported by pyCMSdll or the profiles you used for the transform, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. - If an error occurs while the transform is being applied, a ``PyCMSError`` - is raised. + If an error occurs while the transform is being applied, + a :exc:`PyCMSError` is raised. This function applies a pre-calculated transform (from ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) @@ -649,12 +650,14 @@ def createProfile(colorSpace, colorTemp=-1): """ (pyCMS) Creates a profile. - If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, a ``PyCMSError`` is raised. + If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, + a :exc:`PyCMSError` is raised. - If using LAB and ``colorTemp`` is not a positive integer, a ``PyCMSError`` is - raised. + If using LAB and ``colorTemp`` is not a positive integer, + a :exc:`PyCMSError` is raised. - If an error occurs while creating the profile, a ``PyCMSError`` is raised. + If an error occurs while creating the profile, + a :exc:`PyCMSError` is raised. Use this function to create common profiles on-the-fly instead of having to supply a profile on disk and knowing the path to it. It @@ -699,8 +702,8 @@ def getProfileName(profile): (pyCMS) Gets the internal product name for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a ``PyCMSError`` is raised If an error occurs while trying to obtain the - name tag, a ``PyCMSError`` is raised. + a :exc:`PyCMSError` is raised If an error occurs while trying + to obtain the name tag, a :exc:`PyCMSError` is raised. Use this function to obtain the INTERNAL name of the profile (stored in an ICC tag in the profile itself), usually the one used when the @@ -740,10 +743,10 @@ def getProfileInfo(profile): (pyCMS) Gets the internal product information for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a ``PyCMSError`` is raised. + a :exc:`PyCMSError` is raised. - If an error occurs while trying to obtain the info tag, a ``PyCMSError`` - is raised. + If an error occurs while trying to obtain the info tag, + a :exc:`PyCMSError` is raised. Use this function to obtain the information stored in the profile's info tag. This often contains details about the profile, and how it @@ -780,10 +783,10 @@ def getProfileCopyright(profile): (pyCMS) Gets the copyright for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. - If an error occurs while trying to obtain the copyright tag, a ``PyCMSError`` - is raised. + If an error occurs while trying to obtain the copyright tag, + a :exc:`PyCMSError` is raised. Use this function to obtain the information stored in the profile's copyright tag. @@ -808,10 +811,10 @@ def getProfileManufacturer(profile): (pyCMS) Gets the manufacturer for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. If an error occurs while trying to obtain the manufacturer tag, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. Use this function to obtain the information stored in the profile's manufacturer tag. @@ -836,10 +839,10 @@ def getProfileModel(profile): (pyCMS) Gets the model for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. - If an error occurs while trying to obtain the model tag, a ``PyCMSError`` - is raised. + If an error occurs while trying to obtain the model tag, + a :exc:`PyCMSError` is raised. Use this function to obtain the information stored in the profile's model tag. @@ -865,10 +868,10 @@ def getProfileDescription(profile): (pyCMS) Gets the description for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. - If an error occurs while trying to obtain the description tag, a ``PyCMSError`` - is raised. + If an error occurs while trying to obtain the description tag, + a :exc:`PyCMSError` is raised. Use this function to obtain the information stored in the profile's description tag. @@ -894,10 +897,10 @@ def getDefaultIntent(profile): (pyCMS) Gets the default intent name for the given profile. If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. If an error occurs while trying to obtain the default intent, a - ``PyCMSError`` is raised. + :exc:`PyCMSError` is raised. Use this function to determine the default (and usually best optimized) rendering intent for this profile. Most profiles support multiple @@ -940,8 +943,8 @@ def isIntentSupported(profile, intent, direction): be used for others. Some profiles can only be used for certain rendering intents, so it's best to either verify this before trying to create a transform with them (using this function), or catch the - potential ``PyCMSError`` that will occur if they don't support the modes - you select. + potential :exc:`PyCMSError` that will occur if they don't + support the modes you select. :param profile: EITHER a valid CmsProfile object, OR a string of the filename of an ICC profile. From 2761a02d130c53a1731439e571016ae286ba093d Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 05:52:50 +0200 Subject: [PATCH 248/262] fix module references (cherry picked from commit b077850baa60e413534defeab997a9b574daaa6e) --- docs/PIL.rst | 48 ++++---- docs/porting.rst | 2 +- docs/reference/ExifTags.rst | 6 +- docs/reference/Image.rst | 4 +- docs/reference/ImageChops.rst | 8 +- docs/reference/ImageCms.rst | 6 +- docs/reference/ImageColor.rst | 6 +- docs/reference/ImageDraw.rst | 6 +- docs/reference/ImageEnhance.rst | 6 +- docs/reference/ImageFile.rst | 6 +- docs/reference/ImageFilter.rst | 6 +- docs/reference/ImageFont.rst | 6 +- docs/reference/ImageGrab.rst | 6 +- docs/reference/ImageMath.rst | 6 +- docs/reference/ImageMorph.rst | 6 +- docs/reference/ImageOps.rst | 6 +- docs/reference/ImagePalette.rst | 6 +- docs/reference/ImagePath.rst | 6 +- docs/reference/ImageQt.rst | 6 +- docs/reference/ImageSequence.rst | 6 +- docs/reference/ImageShow.rst | 6 +- docs/reference/ImageStat.rst | 6 +- docs/reference/ImageTk.rst | 6 +- docs/reference/ImageWin.rst | 6 +- docs/reference/JpegPresets.rst | 4 +- docs/reference/PSDraw.rst | 6 +- docs/reference/PyAccess.rst | 6 +- docs/reference/TiffTags.rst | 6 +- docs/reference/features.rst | 4 +- docs/reference/internal_modules.rst | 16 +-- docs/reference/plugins.rst | 164 ++++++++++++++-------------- 31 files changed, 194 insertions(+), 194 deletions(-) diff --git a/docs/PIL.rst b/docs/PIL.rst index 8f8cda5fb83..4b10184fafc 100644 --- a/docs/PIL.rst +++ b/docs/PIL.rst @@ -12,56 +12,56 @@ can be found here. .. autoexception:: UnidentifiedImageError :show-inheritance: -:mod:`BdfFontFile` Module -------------------------- +:mod:`~PIL.BdfFontFile` Module +------------------------------ .. automodule:: PIL.BdfFontFile :members: :undoc-members: :show-inheritance: -:mod:`ContainerIO` Module -------------------------- +:mod:`~PIL.ContainerIO` Module +------------------------------ .. automodule:: PIL.ContainerIO :members: :undoc-members: :show-inheritance: -:mod:`FontFile` Module ----------------------- +:mod:`~PIL.FontFile` Module +--------------------------- .. automodule:: PIL.FontFile :members: :undoc-members: :show-inheritance: -:mod:`GdImageFile` Module -------------------------- +:mod:`~PIL.GdImageFile` Module +------------------------------ .. automodule:: PIL.GdImageFile :members: :undoc-members: :show-inheritance: -:mod:`GimpGradientFile` Module ------------------------------- +:mod:`~PIL.GimpGradientFile` Module +----------------------------------- .. automodule:: PIL.GimpGradientFile :members: :undoc-members: :show-inheritance: -:mod:`GimpPaletteFile` Module ------------------------------ +:mod:`~PIL.GimpPaletteFile` Module +---------------------------------- .. automodule:: PIL.GimpPaletteFile :members: :undoc-members: :show-inheritance: -:mod:`ImageDraw2` Module ------------------------- +:mod:`~PIL.ImageDraw2` Module +----------------------------- .. automodule:: PIL.ImageDraw2 :members: @@ -69,24 +69,24 @@ can be found here. :undoc-members: :show-inheritance: -:mod:`ImageTransform` Module ----------------------------- +:mod:`~PIL.ImageTransform` Module +--------------------------------- .. automodule:: PIL.ImageTransform :members: :undoc-members: :show-inheritance: -:mod:`PaletteFile` Module -------------------------- +:mod:`~PIL.PaletteFile` Module +------------------------------ .. automodule:: PIL.PaletteFile :members: :undoc-members: :show-inheritance: -:mod:`PcfFontFile` Module -------------------------- +:mod:`~PIL.PcfFontFile` Module +------------------------------ .. automodule:: PIL.PcfFontFile :members: @@ -116,16 +116,16 @@ can be found here. :show-inheritance: -:mod:`TarIO` Module -------------------- +:mod:`~PIL.TarIO` Module +------------------------ .. automodule:: PIL.TarIO :members: :undoc-members: :show-inheritance: -:mod:`WalImageFile` Module --------------------------- +:mod:`~PIL.WalImageFile` Module +------------------------------- .. automodule:: PIL.WalImageFile :members: diff --git a/docs/porting.rst b/docs/porting.rst index 3b14cde9d7d..d962e93300b 100644 --- a/docs/porting.rst +++ b/docs/porting.rst @@ -19,7 +19,7 @@ to this:: from PIL import Image -The :py:mod:`_imaging` module has been moved. You can now import it like this:: +The :py:mod:`~PIL._imaging` module has been moved. You can now import it like this:: from PIL.Image import core as _imaging diff --git a/docs/reference/ExifTags.rst b/docs/reference/ExifTags.rst index 39fdab02c1c..4567d4d3e79 100644 --- a/docs/reference/ExifTags.rst +++ b/docs/reference/ExifTags.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ExifTags .. py:currentmodule:: PIL.ExifTags -:py:mod:`ExifTags` Module -========================== +:py:mod:`~PIL.ExifTags` Module +============================== -The :py:mod:`ExifTags` module exposes two dictionaries which +The :py:mod:`~PIL.ExifTags` module exposes two dictionaries which provide constants and clear-text names for various well-known EXIF tags. .. py:data:: TAGS diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 216fa1196e4..469774a8d51 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.Image .. py:currentmodule:: PIL.Image -:py:mod:`Image` Module -====================== +:py:mod:`~PIL.Image` Module +=========================== The :py:mod:`~PIL.Image` module provides a class with the same name which is used to represent a PIL image. The module also provides a number of factory diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst index fb742254903..772d9c98363 100644 --- a/docs/reference/ImageChops.rst +++ b/docs/reference/ImageChops.rst @@ -1,15 +1,15 @@ .. py:module:: PIL.ImageChops .. py:currentmodule:: PIL.ImageChops -:py:mod:`ImageChops` ("Channel Operations") Module -================================================== +:py:mod:`~PIL.ImageChops` ("Channel Operations") Module +======================================================= -The :py:mod:`ImageChops` module contains a number of arithmetical image +The :py:mod:`~PIL.ImageChops` module contains a number of arithmetical image operations, called channel operations (“chops”). These can be used for various purposes, including special effects, image compositions, algorithmic painting, and more. -For more pre-made operations, see :py:mod:`ImageOps`. +For more pre-made operations, see :py:mod:`~PIL.ImageOps`. At this time, most channel operations are only implemented for 8-bit images (e.g. “L” and “RGB”). diff --git a/docs/reference/ImageCms.rst b/docs/reference/ImageCms.rst index 67c58176565..8c11e1476fe 100644 --- a/docs/reference/ImageCms.rst +++ b/docs/reference/ImageCms.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageCms .. py:currentmodule:: PIL.ImageCms -:py:mod:`ImageCms` Module -========================= +:py:mod:`~PIL.ImageCms` Module +============================== -The :py:mod:`ImageCms` module provides color profile management +The :py:mod:`~PIL.ImageCms` module provides color profile management support using the LittleCMS2 color management engine, based on Kevin Cazabon's PyCMS library. diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst index 187306f1b77..e32a77b54d5 100644 --- a/docs/reference/ImageColor.rst +++ b/docs/reference/ImageColor.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageColor .. py:currentmodule:: PIL.ImageColor -:py:mod:`ImageColor` Module -=========================== +:py:mod:`~PIL.ImageColor` Module +================================ -The :py:mod:`ImageColor` module contains color tables and converters from +The :py:mod:`~PIL.ImageColor` module contains color tables and converters from CSS3-style color specifiers to RGB tuples. This module is used by :py:meth:`PIL.Image.new` and the :py:mod:`~PIL.ImageDraw` module, among others. diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 495a7d117cd..fd74b69c1db 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageDraw .. py:currentmodule:: PIL.ImageDraw -:py:mod:`ImageDraw` Module -========================== +:py:mod:`~PIL.ImageDraw` Module +=============================== -The :py:mod:`ImageDraw` module provides simple 2D graphics for +The :py:mod:`~PIL.ImageDraw` module provides simple 2D graphics for :py:class:`~PIL.Image.Image` objects. You can use this module to create new images, annotate or retouch existing images, and to generate graphics on the fly for web use. diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst index f98d8f78054..742cf006879 100644 --- a/docs/reference/ImageEnhance.rst +++ b/docs/reference/ImageEnhance.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageEnhance .. py:currentmodule:: PIL.ImageEnhance -:py:mod:`ImageEnhance` Module -============================= +:py:mod:`~PIL.ImageEnhance` Module +================================== -The :py:mod:`ImageEnhance` module contains a number of classes that can be used +The :py:mod:`~PIL.ImageEnhance` module contains a number of classes that can be used for image enhancement. Example: Vary the sharpness of an image diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst index d93dfb3a3df..82a84dbdeaa 100644 --- a/docs/reference/ImageFile.rst +++ b/docs/reference/ImageFile.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageFile .. py:currentmodule:: PIL.ImageFile -:py:mod:`ImageFile` Module -========================== +:py:mod:`~PIL.ImageFile` Module +=============================== -The :py:mod:`ImageFile` module provides support functions for the image open +The :py:mod:`~PIL.ImageFile` module provides support functions for the image open and save functions. In addition, it provides a :py:class:`Parser` class which can be used to decode diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst index 52a7d750033..1b93c53e771 100644 --- a/docs/reference/ImageFilter.rst +++ b/docs/reference/ImageFilter.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageFilter .. py:currentmodule:: PIL.ImageFilter -:py:mod:`ImageFilter` Module -============================ +:py:mod:`~PIL.ImageFilter` Module +================================= -The :py:mod:`ImageFilter` module contains definitions for a pre-defined set of +The :py:mod:`~PIL.ImageFilter` module contains definitions for a pre-defined set of filters, which can be be used with the :py:meth:`Image.filter() ` method. diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index 1aa22aa510a..b3c3cce578a 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageFont .. py:currentmodule:: PIL.ImageFont -:py:mod:`ImageFont` Module -========================== +:py:mod:`~PIL.ImageFont` Module +=============================== -The :py:mod:`ImageFont` module defines a class with the same name. Instances of +The :py:mod:`~PIL.ImageFont` module defines a class with the same name. Instances of this class store bitmap fonts, and are used with the :py:meth:`PIL.ImageDraw.Draw.text` method. diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index 943fdf69be7..a9427be9c42 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageGrab .. py:currentmodule:: PIL.ImageGrab -:py:mod:`ImageGrab` Module -========================== +:py:mod:`~PIL.ImageGrab` Module +=============================== -The :py:mod:`ImageGrab` module can be used to copy the contents of the screen +The :py:mod:`~PIL.ImageGrab` module can be used to copy the contents of the screen or the clipboard to a PIL image memory. .. versionadded:: 1.1.3 diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst index ca30244d1d5..45b9200d123 100644 --- a/docs/reference/ImageMath.rst +++ b/docs/reference/ImageMath.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageMath .. py:currentmodule:: PIL.ImageMath -:py:mod:`ImageMath` Module -========================== +:py:mod:`~PIL.ImageMath` Module +=============================== -The :py:mod:`ImageMath` module can be used to evaluate “image expressions”. The +The :py:mod:`~PIL.ImageMath` module can be used to evaluate “image expressions”. The module provides a single :py:meth:`~PIL.ImageMath.eval` function, which takes an expression string and one or more images. diff --git a/docs/reference/ImageMorph.rst b/docs/reference/ImageMorph.rst index be9d59348a8..d4522a06ae3 100644 --- a/docs/reference/ImageMorph.rst +++ b/docs/reference/ImageMorph.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageMorph .. py:currentmodule:: PIL.ImageMorph -:py:mod:`ImageMorph` Module -=========================== +:py:mod:`~PIL.ImageMorph` Module +================================ -The :py:mod:`ImageMorph` module provides morphology operations on images. +The :py:mod:`~PIL.ImageMorph` module provides morphology operations on images. .. automodule:: PIL.ImageMorph :members: diff --git a/docs/reference/ImageOps.rst b/docs/reference/ImageOps.rst index 1c86d168ff0..9a16d6625e7 100644 --- a/docs/reference/ImageOps.rst +++ b/docs/reference/ImageOps.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageOps .. py:currentmodule:: PIL.ImageOps -:py:mod:`ImageOps` Module -========================== +:py:mod:`~PIL.ImageOps` Module +============================== -The :py:mod:`ImageOps` module contains a number of ‘ready-made’ image +The :py:mod:`~PIL.ImageOps` module contains a number of ‘ready-made’ image processing operations. This module is somewhat experimental, and most operators only work on L and RGB images. diff --git a/docs/reference/ImagePalette.rst b/docs/reference/ImagePalette.rst index 15b8aed8f05..f14c1c3a446 100644 --- a/docs/reference/ImagePalette.rst +++ b/docs/reference/ImagePalette.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImagePalette .. py:currentmodule:: PIL.ImagePalette -:py:mod:`ImagePalette` Module -============================= +:py:mod:`~PIL.ImagePalette` Module +================================== -The :py:mod:`ImagePalette` module contains a class of the same name to +The :py:mod:`~PIL.ImagePalette` module contains a class of the same name to represent the color palette of palette mapped images. .. note:: diff --git a/docs/reference/ImagePath.rst b/docs/reference/ImagePath.rst index 5ab350ef381..21a202b5e9b 100644 --- a/docs/reference/ImagePath.rst +++ b/docs/reference/ImagePath.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImagePath .. py:currentmodule:: PIL.ImagePath -:py:mod:`ImagePath` Module -========================== +:py:mod:`~PIL.ImagePath` Module +=============================== -The :py:mod:`ImagePath` module is used to store and manipulate 2-dimensional +The :py:mod:`~PIL.ImagePath` module is used to store and manipulate 2-dimensional vector data. Path objects can be passed to the methods on the :py:mod:`~PIL.ImageDraw` module. diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst index 7dd7084dbfa..887eab9ba73 100644 --- a/docs/reference/ImageQt.rst +++ b/docs/reference/ImageQt.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageQt .. py:currentmodule:: PIL.ImageQt -:py:mod:`ImageQt` Module -======================== +:py:mod:`~PIL.ImageQt` Module +============================= -The :py:mod:`ImageQt` module contains support for creating PyQt5 or PySide2 QImage +The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt5 or PySide2 QImage objects from PIL images. .. versionadded:: 1.1.6 diff --git a/docs/reference/ImageSequence.rst b/docs/reference/ImageSequence.rst index 353e8099ef4..ae93fa47df5 100644 --- a/docs/reference/ImageSequence.rst +++ b/docs/reference/ImageSequence.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageSequence .. py:currentmodule:: PIL.ImageSequence -:py:mod:`ImageSequence` Module -============================== +:py:mod:`~PIL.ImageSequence` Module +=================================== -The :py:mod:`ImageSequence` module contains a wrapper class that lets you +The :py:mod:`~PIL.ImageSequence` module contains a wrapper class that lets you iterate over the frames of an image sequence. Extracting frames from an animation diff --git a/docs/reference/ImageShow.rst b/docs/reference/ImageShow.rst index 0b012d856db..a30a6caedb9 100644 --- a/docs/reference/ImageShow.rst +++ b/docs/reference/ImageShow.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageShow .. py:currentmodule:: PIL.ImageShow -:py:mod:`ImageShow` Module -========================== +:py:mod:`~PIL.ImageShow` Module +=============================== -The :py:mod:`ImageShow` Module is used to display images. +The :py:mod:`~PIL.ImageShow` Module is used to display images. All default viewers convert the image to be shown to PNG format. .. autofunction:: PIL.ImageShow.show diff --git a/docs/reference/ImageStat.rst b/docs/reference/ImageStat.rst index e94c24aa44d..5bb73529635 100644 --- a/docs/reference/ImageStat.rst +++ b/docs/reference/ImageStat.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageStat .. py:currentmodule:: PIL.ImageStat -:py:mod:`ImageStat` Module -========================== +:py:mod:`~PIL.ImageStat` Module +=============================== -The :py:mod:`ImageStat` module calculates global statistics for an image, or +The :py:mod:`~PIL.ImageStat` module calculates global statistics for an image, or for a region of an image. .. py:class:: Stat(image_or_list, mask=None) diff --git a/docs/reference/ImageTk.rst b/docs/reference/ImageTk.rst index 7ee4af02980..134ef565188 100644 --- a/docs/reference/ImageTk.rst +++ b/docs/reference/ImageTk.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageTk .. py:currentmodule:: PIL.ImageTk -:py:mod:`ImageTk` Module -======================== +:py:mod:`~PIL.ImageTk` Module +============================= -The :py:mod:`ImageTk` module contains support to create and modify Tkinter +The :py:mod:`~PIL.ImageTk` module contains support to create and modify Tkinter BitmapImage and PhotoImage objects from PIL images. For examples, see the demo programs in the Scripts directory. diff --git a/docs/reference/ImageWin.rst b/docs/reference/ImageWin.rst index ff3d6a7fc69..2ee3cadb70b 100644 --- a/docs/reference/ImageWin.rst +++ b/docs/reference/ImageWin.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.ImageWin .. py:currentmodule:: PIL.ImageWin -:py:mod:`ImageWin` Module (Windows-only) -======================================== +:py:mod:`~PIL.ImageWin` Module (Windows-only) +============================================= -The :py:mod:`ImageWin` module contains support to create and display images on +The :py:mod:`~PIL.ImageWin` module contains support to create and display images on Windows. ImageWin can be used with PythonWin and other user interface toolkits that diff --git a/docs/reference/JpegPresets.rst b/docs/reference/JpegPresets.rst index 0a091460152..aafae44cf4a 100644 --- a/docs/reference/JpegPresets.rst +++ b/docs/reference/JpegPresets.rst @@ -1,7 +1,7 @@ .. py:currentmodule:: PIL.JpegPresets -:py:mod:`JpegPresets` Module -============================ +:py:mod:`~PIL.JpegPresets` Module +================================= .. automodule:: PIL.JpegPresets diff --git a/docs/reference/PSDraw.rst b/docs/reference/PSDraw.rst index 2b5b9b340b3..95838581884 100644 --- a/docs/reference/PSDraw.rst +++ b/docs/reference/PSDraw.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.PSDraw .. py:currentmodule:: PIL.PSDraw -:py:mod:`PSDraw` Module -======================= +:py:mod:`~PIL.PSDraw` Module +============================ -The :py:mod:`PSDraw` module provides simple print support for Postscript +The :py:mod:`~PIL.PSDraw` module provides simple print support for Postscript printers. You can print text, graphics and images through this module. .. autoclass:: PIL.PSDraw.PSDraw diff --git a/docs/reference/PyAccess.rst b/docs/reference/PyAccess.rst index e00741c4323..486c9fc2103 100644 --- a/docs/reference/PyAccess.rst +++ b/docs/reference/PyAccess.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.PyAccess .. py:currentmodule:: PIL.PyAccess -:py:mod:`PyAccess` Module -========================= +:py:mod:`~PIL.PyAccess` Module +============================== -The :py:mod:`PyAccess` module provides a CFFI/Python implementation of the :ref:`PixelAccess`. This implementation is far faster on PyPy than the PixelAccess version. +The :py:mod:`~PIL.PyAccess` module provides a CFFI/Python implementation of the :ref:`PixelAccess`. This implementation is far faster on PyPy than the PixelAccess version. .. note:: Accessing individual pixels is fairly slow. If you are looping over all of the pixels in an image, there is likely diff --git a/docs/reference/TiffTags.rst b/docs/reference/TiffTags.rst index 4161110bd99..a53788a9feb 100644 --- a/docs/reference/TiffTags.rst +++ b/docs/reference/TiffTags.rst @@ -1,10 +1,10 @@ .. py:module:: PIL.TiffTags .. py:currentmodule:: PIL.TiffTags -:py:mod:`TiffTags` Module -========================= +:py:mod:`~PIL.TiffTags` Module +============================== -The :py:mod:`TiffTags` module exposes many of the standard TIFF +The :py:mod:`~PIL.TiffTags` module exposes many of the standard TIFF metadata tag numbers, names, and type information. .. method:: lookup(tag) diff --git a/docs/reference/features.rst b/docs/reference/features.rst index 47e9a6d630e..dd218fa0e70 100644 --- a/docs/reference/features.rst +++ b/docs/reference/features.rst @@ -1,8 +1,8 @@ .. py:module:: PIL.features .. py:currentmodule:: PIL.features -:py:mod:`features` Module -========================== +:py:mod:`~PIL.features` Module +============================== The :py:mod:`PIL.features` module can be used to detect which Pillow features are available on your system. diff --git a/docs/reference/internal_modules.rst b/docs/reference/internal_modules.rst index 7a7967d231c..288a049ee0b 100644 --- a/docs/reference/internal_modules.rst +++ b/docs/reference/internal_modules.rst @@ -1,32 +1,32 @@ Internal Modules ================ -:mod:`_binary` Module ---------------------- +:mod:`~PIL._binary` Module +-------------------------- .. automodule:: PIL._binary :members: :undoc-members: :show-inheritance: -:mod:`_tkinter_finder` Module ------------------------------ +:mod:`~PIL._tkinter_finder` Module +---------------------------------- .. automodule:: PIL._tkinter_finder :members: :undoc-members: :show-inheritance: -:mod:`_util` Module -------------------- +:mod:`~PIL._util` Module +------------------------ .. automodule:: PIL._util :members: :undoc-members: :show-inheritance: -:mod:`_version` Module ----------------------- +:mod:`~PIL._version` Module +--------------------------- .. module:: PIL._version diff --git a/docs/reference/plugins.rst b/docs/reference/plugins.rst index cc0742fde44..ef080b6dbd8 100644 --- a/docs/reference/plugins.rst +++ b/docs/reference/plugins.rst @@ -1,232 +1,232 @@ Plugin reference ================ -:mod:`BmpImagePlugin` Module ----------------------------- +:mod:`~PIL.BmpImagePlugin` Module +--------------------------------- .. automodule:: PIL.BmpImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`BufrStubImagePlugin` Module ---------------------------------- +:mod:`~PIL.BufrStubImagePlugin` Module +-------------------------------------- .. automodule:: PIL.BufrStubImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`CurImagePlugin` Module ----------------------------- +:mod:`~PIL.CurImagePlugin` Module +--------------------------------- .. automodule:: PIL.CurImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`DcxImagePlugin` Module ----------------------------- +:mod:`~PIL.DcxImagePlugin` Module +--------------------------------- .. automodule:: PIL.DcxImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`EpsImagePlugin` Module ----------------------------- +:mod:`~PIL.EpsImagePlugin` Module +--------------------------------- .. automodule:: PIL.EpsImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`FitsStubImagePlugin` Module ---------------------------------- +:mod:`~PIL.FitsStubImagePlugin` Module +-------------------------------------- .. automodule:: PIL.FitsStubImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`FliImagePlugin` Module ----------------------------- +:mod:`~PIL.FliImagePlugin` Module +--------------------------------- .. automodule:: PIL.FliImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`FpxImagePlugin` Module ----------------------------- +:mod:`~PIL.FpxImagePlugin` Module +--------------------------------- .. automodule:: PIL.FpxImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`GbrImagePlugin` Module ----------------------------- +:mod:`~PIL.GbrImagePlugin` Module +--------------------------------- .. automodule:: PIL.GbrImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`GifImagePlugin` Module ----------------------------- +:mod:`~PIL.GifImagePlugin` Module +--------------------------------- .. automodule:: PIL.GifImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`GribStubImagePlugin` Module ---------------------------------- +:mod:`~PIL.GribStubImagePlugin` Module +-------------------------------------- .. automodule:: PIL.GribStubImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`Hdf5StubImagePlugin` Module ---------------------------------- +:mod:`~PIL.Hdf5StubImagePlugin` Module +-------------------------------------- .. automodule:: PIL.Hdf5StubImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`IcnsImagePlugin` Module ------------------------------ +:mod:`~PIL.IcnsImagePlugin` Module +---------------------------------- .. automodule:: PIL.IcnsImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`IcoImagePlugin` Module ----------------------------- +:mod:`~PIL.IcoImagePlugin` Module +--------------------------------- .. automodule:: PIL.IcoImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`ImImagePlugin` Module ---------------------------- +:mod:`~PIL.ImImagePlugin` Module +-------------------------------- .. automodule:: PIL.ImImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`ImtImagePlugin` Module ----------------------------- +:mod:`~PIL.ImtImagePlugin` Module +--------------------------------- .. automodule:: PIL.ImtImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`IptcImagePlugin` Module ------------------------------ +:mod:`~PIL.IptcImagePlugin` Module +---------------------------------- .. automodule:: PIL.IptcImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`JpegImagePlugin` Module ------------------------------ +:mod:`~PIL.JpegImagePlugin` Module +---------------------------------- .. automodule:: PIL.JpegImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`Jpeg2KImagePlugin` Module -------------------------------- +:mod:`~PIL.Jpeg2KImagePlugin` Module +------------------------------------ .. automodule:: PIL.Jpeg2KImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`McIdasImagePlugin` Module -------------------------------- +:mod:`~PIL.McIdasImagePlugin` Module +------------------------------------ .. automodule:: PIL.McIdasImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`MicImagePlugin` Module ----------------------------- +:mod:`~PIL.MicImagePlugin` Module +--------------------------------- .. automodule:: PIL.MicImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`MpegImagePlugin` Module ------------------------------ +:mod:`~PIL.MpegImagePlugin` Module +---------------------------------- .. automodule:: PIL.MpegImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`MspImagePlugin` Module ----------------------------- +:mod:`~PIL.MspImagePlugin` Module +--------------------------------- .. automodule:: PIL.MspImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PalmImagePlugin` Module ------------------------------ +:mod:`~PIL.PalmImagePlugin` Module +---------------------------------- .. automodule:: PIL.PalmImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PcdImagePlugin` Module ----------------------------- +:mod:`~PIL.PcdImagePlugin` Module +--------------------------------- .. automodule:: PIL.PcdImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PcxImagePlugin` Module ----------------------------- +:mod:`~PIL.PcxImagePlugin` Module +--------------------------------- .. automodule:: PIL.PcxImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PdfImagePlugin` Module ----------------------------- +:mod:`~PIL.PdfImagePlugin` Module +--------------------------------- .. automodule:: PIL.PdfImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PixarImagePlugin` Module ------------------------------- +:mod:`~PIL.PixarImagePlugin` Module +----------------------------------- .. automodule:: PIL.PixarImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PngImagePlugin` Module ----------------------------- +:mod:`~PIL.PngImagePlugin` Module +--------------------------------- .. automodule:: PIL.PngImagePlugin :members: ChunkStream, PngStream, getchunks, is_cid, putchunk @@ -245,96 +245,96 @@ Plugin reference :show-inheritance: -:mod:`PpmImagePlugin` Module ----------------------------- +:mod:`~PIL.PpmImagePlugin` Module +--------------------------------- .. automodule:: PIL.PpmImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`PsdImagePlugin` Module ----------------------------- +:mod:`~PIL.PsdImagePlugin` Module +--------------------------------- .. automodule:: PIL.PsdImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`SgiImagePlugin` Module ----------------------------- +:mod:`~PIL.SgiImagePlugin` Module +--------------------------------- .. automodule:: PIL.SgiImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`SpiderImagePlugin` Module -------------------------------- +:mod:`~PIL.SpiderImagePlugin` Module +------------------------------------ .. automodule:: PIL.SpiderImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`SunImagePlugin` Module ----------------------------- +:mod:`~PIL.SunImagePlugin` Module +--------------------------------- .. automodule:: PIL.SunImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`TgaImagePlugin` Module ----------------------------- +:mod:`~PIL.TgaImagePlugin` Module +--------------------------------- .. automodule:: PIL.TgaImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`TiffImagePlugin` Module ------------------------------ +:mod:`~PIL.TiffImagePlugin` Module +---------------------------------- .. automodule:: PIL.TiffImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`WebPImagePlugin` Module ------------------------------ +:mod:`~PIL.WebPImagePlugin` Module +---------------------------------- .. automodule:: PIL.WebPImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`WmfImagePlugin` Module ----------------------------- +:mod:`~PIL.WmfImagePlugin` Module +--------------------------------- .. automodule:: PIL.WmfImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`XVThumbImagePlugin` Module --------------------------------- +:mod:`~PIL.XVThumbImagePlugin` Module +------------------------------------- .. automodule:: PIL.XVThumbImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`XbmImagePlugin` Module ----------------------------- +:mod:`~PIL.XbmImagePlugin` Module +--------------------------------- .. automodule:: PIL.XbmImagePlugin :members: :undoc-members: :show-inheritance: -:mod:`XpmImagePlugin` Module ----------------------------- +:mod:`~PIL.XpmImagePlugin` Module +--------------------------------- .. automodule:: PIL.XpmImagePlugin :members: From bd2c705606069cb29d466768f306a3a621c949e8 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 07:14:07 +0200 Subject: [PATCH 249/262] fix CmsProfile type references (cherry picked from commit 61966951562ac9c7f1dd7764d913c9166f642e45) --- docs/reference/ImageCms.rst | 139 +++++++++++++++--------------------- 1 file changed, 57 insertions(+), 82 deletions(-) diff --git a/docs/reference/ImageCms.rst b/docs/reference/ImageCms.rst index 67c58176565..01682137b7d 100644 --- a/docs/reference/ImageCms.rst +++ b/docs/reference/ImageCms.rst @@ -25,31 +25,30 @@ can be easily displayed in a chromaticity diagram, for example). .. py:class:: CmsProfile .. py:attribute:: creation_date + :type: Optional[datetime.datetime] Date and time this profile was first created (see 7.2.1 of ICC.1:2010). - :type: :py:class:`datetime.datetime` or ``None`` - .. py:attribute:: version + :type: float The version number of the ICC standard that this profile follows (e.g. ``2.0``). - :type: :py:class:`float` - .. py:attribute:: icc_version + :type: int Same as ``version``, but in encoded format (see 7.2.4 of ICC.1:2010). .. py:attribute:: device_class + :type: str 4-character string identifying the profile class. One of ``scnr``, ``mntr``, ``prtr``, ``link``, ``spac``, ``abst``, ``nmcl`` (see 7.2.5 of ICC.1:2010 for details). - :type: :py:class:`string` - .. py:attribute:: xcolor_space + :type: str 4-character string (padded with whitespace) identifying the color space, e.g. ``XYZ␣``, ``RGB␣`` or ``CMYK`` (see 7.2.6 of @@ -59,9 +58,8 @@ can be easily displayed in a chromaticity diagram, for example). interpreted (non-padded) variant of this (but can be empty on unknown input). - :type: :py:class:`string` - .. py:attribute:: connection_space + :type: str 4-character string (padded with whitespace) identifying the color space on the B-side of the transform (see 7.2.7 of ICC.1:2010 for @@ -70,42 +68,37 @@ can be easily displayed in a chromaticity diagram, for example). Note that the deprecated attribute ``pcs`` contains an interpreted (non-padded) variant of this (but can be empty on unknown input). - :type: :py:class:`string` - .. py:attribute:: header_flags + :type: int The encoded header flags of the profile (see 7.2.11 of ICC.1:2010 for details). - :type: :py:class:`int` - .. py:attribute:: header_manufacturer + :type: str 4-character string (padded with whitespace) identifying the device manufacturer, which shall match the signature contained in the appropriate section of the ICC signature registry found at www.color.org (see 7.2.12 of ICC.1:2010). - :type: :py:class:`string` - .. py:attribute:: header_model + :type: str 4-character string (padded with whitespace) identifying the device model, which shall match the signature contained in the appropriate section of the ICC signature registry found at www.color.org (see 7.2.13 of ICC.1:2010). - :type: :py:class:`string` - .. py:attribute:: attributes + :type: int Flags used to identify attributes unique to the particular device setup for which the profile is applicable (see 7.2.14 of ICC.1:2010 for details). - :type: :py:class:`int` - .. py:attribute:: rendering_intent + :type: int The rendering intent to use when combining this profile with another profile (usually overridden at run-time, but provided here @@ -114,84 +107,82 @@ can be easily displayed in a chromaticity diagram, for example). One of ``ImageCms.INTENT_ABSOLUTE_COLORIMETRIC``, ``ImageCms.INTENT_PERCEPTUAL``, ``ImageCms.INTENT_RELATIVE_COLORIMETRIC`` and ``ImageCms.INTENT_SATURATION``. - :type: :py:class:`int` - .. py:attribute:: profile_id + :type: bytes A sequence of 16 bytes identifying the profile (via a specially constructed MD5 sum), or 16 binary zeroes if the profile ID has not been calculated (see 7.2.18 of ICC.1:2010). - :type: :py:class:`bytes` - .. py:attribute:: copyright + :type: Optional[str] The text copyright information for the profile (see 9.2.21 of ICC.1:2010). - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: manufacturer + :type: Optional[str] The (English) display string for the device manufacturer (see 9.2.22 of ICC.1:2010). - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: model + :type: Optional[str] The (English) display string for the device model of the device for which this profile is created (see 9.2.23 of ICC.1:2010). - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: profile_description + :type: Optional[str] The (English) display string for the profile description (see 9.2.41 of ICC.1:2010). - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: target + :type: Optional[str] The name of the registered characterization data set, or the measurement data for a characterization target (see 9.2.14 of ICC.1:2010). - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: red_colorant + :type: Optional[tuple[tuple[float]]] The first column in the matrix used in matrix/TRC transforms (see 9.2.44 of ICC.1:2010). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: green_colorant + :type: Optional[tuple[tuple[float]]] The second column in the matrix used in matrix/TRC transforms (see 9.2.30 of ICC.1:2010). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: blue_colorant + :type: Optional[tuple[tuple[float]]] The third column in the matrix used in matrix/TRC transforms (see 9.2.4 of ICC.1:2010). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: luminance + :type: Optional[tuple[tuple[float]]] The absolute luminance of emissive devices in candelas per square metre as described by the Y channel (see 9.2.32 of ICC.1:2010). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: chromaticity + :type: Optional[tuple[tuple[float]]] The data of the phosphor/colorant chromaticity set used (red, green and blue channels, see 9.2.16 of ICC.1:2010). - :type: ``((x, y, Y), (x, y, Y), (x, y, Y))`` or ``None`` + The value is in the format ``((x, y, Y), (x, y, Y), (x, y, Y))``, if available. .. py:attribute:: chromatic_adaption + :type: tuple[tuple[float]] The chromatic adaption matrix converts a color measured using the actual illumination conditions and relative to the actual adopted @@ -199,58 +190,52 @@ can be easily displayed in a chromaticity diagram, for example). complete adaptation from the actual adopted white chromaticity to the PCS adopted white chromaticity (see 9.2.15 of ICC.1:2010). - Two matrices are returned, one in (X, Y, Z) space and one in (x, y, Y) space. - - :type: 2-tuple of 3-tuple, the first with (X, Y, Z) and the second with (x, y, Y) values + Two 3-tuples of floats are returned in a 2-tuple, + one in (X, Y, Z) space and one in (x, y, Y) space. .. py:attribute:: colorant_table + :type: list[str] This tag identifies the colorants used in the profile by a unique name and set of PCSXYZ or PCSLAB values (see 9.2.19 of ICC.1:2010). - :type: list of strings - .. py:attribute:: colorant_table_out + :type: list[str] This tag identifies the colorants used in the profile by a unique name and set of PCSLAB values (for DeviceLink profiles only, see 9.2.19 of ICC.1:2010). - :type: list of strings - .. py:attribute:: colorimetric_intent + :type: Optional[str] 4-character string (padded with whitespace) identifying the image state of PCS colorimetry produced using the colorimetric intent transforms (see 9.2.20 of ICC.1:2010 for details). - :type: :py:class:`string` or ``None`` - .. py:attribute:: perceptual_rendering_intent_gamut + :type: Optional[str] 4-character string (padded with whitespace) identifying the (one) standard reference medium gamut (see 9.2.37 of ICC.1:2010 for details). - :type: :py:class:`string` or ``None`` - .. py:attribute:: saturation_rendering_intent_gamut + :type: Optional[str] 4-character string (padded with whitespace) identifying the (one) standard reference medium gamut (see 9.2.37 of ICC.1:2010 for details). - :type: :py:class:`string` or ``None`` - .. py:attribute:: technology + :type: Optional[str] 4-character string (padded with whitespace) identifying the device technology (see 9.2.47 of ICC.1:2010 for details). - :type: :py:class:`string` or ``None`` - .. py:attribute:: media_black_point + :type: Optional[tuple[tuple[float]]] This tag specifies the media black point and is used for generating absolute colorimetry. @@ -258,57 +243,57 @@ can be easily displayed in a chromaticity diagram, for example). This tag was available in ICC 3.2, but it is removed from version 4. - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: media_white_point_temperature + :type: Optional[float] Calculates the white point temperature (see the LCMS documentation for more information). - :type: :py:class:`float` or ``None`` - .. py:attribute:: viewing_condition + :type: Optional[str] The (English) display string for the viewing conditions (see 9.2.48 of ICC.1:2010). - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: screening_description + :type: Optional[str] The (English) display string for the screening conditions. This tag was available in ICC 3.2, but it is removed from version 4. - :type: :py:class:`unicode` or ``None`` - .. py:attribute:: red_primary + :type: Optional[tuple[tuple[float]]] The XYZ-transformed of the RGB primary color red (1, 0, 0). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: green_primary + :type: Optional[tuple[tuple[float]]] The XYZ-transformed of the RGB primary color green (0, 1, 0). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: blue_primary + :type: Optional[tuple[tuple[float]]] The XYZ-transformed of the RGB primary color blue (0, 0, 1). - :type: ``((X, Y, Z), (x, y, Y))`` or ``None`` + The value is in the format ``((X, Y, Z), (x, y, Y))``, if available. .. py:attribute:: is_matrix_shaper + :type: bool True if this profile is implemented as a matrix shaper (see documentation on LCMS). - :type: :py:class:`bool` - .. py:attribute:: clut + :type: dict[tuple[bool]] Returns a dictionary of all supported intents and directions for the CLUT model. @@ -326,9 +311,8 @@ can be easily displayed in a chromaticity diagram, for example). The elements of the tuple are booleans. If the value is ``True``, that intent is supported for that direction. - :type: :py:class:`dict` of boolean 3-tuples - .. py:attribute:: intent_supported + :type: dict[tuple[bool]] Returns a dictionary of all supported intents and directions. @@ -345,53 +329,46 @@ can be easily displayed in a chromaticity diagram, for example). The elements of the tuple are booleans. If the value is ``True``, that intent is supported for that direction. - :type: :py:class:`dict` of boolean 3-tuples - .. py:attribute:: color_space + :type: str Deprecated but retained for backwards compatibility. Interpreted value of :py:attr:`.xcolor_space`. May be the empty string if value could not be decoded. - :type: :py:class:`string` - .. py:attribute:: pcs + :type: str Deprecated but retained for backwards compatibility. Interpreted value of :py:attr:`.connection_space`. May be the empty string if value could not be decoded. - :type: :py:class:`string` - .. py:attribute:: product_model + :type: str Deprecated but retained for backwards compatibility. ASCII-encoded value of :py:attr:`.model`. - :type: :py:class:`string` - .. py:attribute:: product_manufacturer + :type: str Deprecated but retained for backwards compatibility. ASCII-encoded value of :py:attr:`.manufacturer`. - :type: :py:class:`string` - .. py:attribute:: product_copyright + :type: str Deprecated but retained for backwards compatibility. ASCII-encoded value of :py:attr:`.copyright`. - :type: :py:class:`string` - .. py:attribute:: product_description + :type: str Deprecated but retained for backwards compatibility. ASCII-encoded value of :py:attr:`.profile_description`. - :type: :py:class:`string` - .. py:attribute:: product_desc + :type: str Deprecated but retained for backwards compatibility. ASCII-encoded value of :py:attr:`.profile_description`. @@ -401,8 +378,6 @@ can be easily displayed in a chromaticity diagram, for example). depending on the value of the description, copyright, manufacturer and model fields). - :type: :py:class:`string` - There is one function defined on the class: .. py:method:: is_intent_supported(intent, direction) From 5e4c3ae5542437fd13ae5f022672c379ad75c1bd Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 07:22:13 +0200 Subject: [PATCH 250/262] fix ImageMath creating false index entries (cherry picked from commit eebecba3c20cde0aca126eaa081ebe8a49f7c659) --- docs/reference/ImageMath.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst index ca30244d1d5..4425d03ad00 100644 --- a/docs/reference/ImageMath.rst +++ b/docs/reference/ImageMath.rst @@ -98,20 +98,24 @@ These functions are applied to each individual pixel. .. py:currentmodule:: None .. py:function:: abs(image) + :noindex: Absolute value. .. py:function:: convert(image, mode) + :noindex: Convert image to the given mode. The mode must be given as a string constant. .. py:function:: float(image) + :noindex: Convert image to 32-bit floating point. This is equivalent to convert(image, “F”). .. py:function:: int(image) + :noindex: Convert image to 32-bit integer. This is equivalent to convert(image, “I”). @@ -119,9 +123,11 @@ These functions are applied to each individual pixel. integers if necessary to get a correct result. .. py:function:: max(image1, image2) + :noindex: Maximum value. .. py:function:: min(image1, image2) + :noindex: Minimum value. From 8b005dfe333e0fb7c71119e734dd3362b2fef014 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 08:59:57 +0200 Subject: [PATCH 251/262] fix base Image attribute references (cherry picked from commit 07cc74d38bb1a1309e872d47a4a2d08bd97e9423) --- docs/reference/Image.rst | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 216fa1196e4..641b14c579f 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -260,57 +260,51 @@ Attributes Instances of the :py:class:`Image` class have the following attributes: -.. py:attribute:: filename +.. py:attribute:: Image.filename + :type: str The filename or path of the source file. Only images created with the factory function ``open`` have a filename attribute. If the input is a file like object, the filename attribute is set to an empty string. - :type: :py:class:`string` - -.. py:attribute:: format +.. py:attribute:: Image.format + :type: Optional[str] The file format of the source file. For images created by the library itself (via a factory function, or by running a method on an existing image), this attribute is set to ``None``. - :type: :py:class:`string` or ``None`` - -.. py:attribute:: mode +.. py:attribute:: Image.mode + :type: str Image mode. This is a string specifying the pixel format used by the image. Typical values are “1”, “L”, “RGB”, or “CMYK.” See :ref:`concept-modes` for a full list. - :type: :py:class:`string` - -.. py:attribute:: size +.. py:attribute:: Image.size + :type: tuple[int] Image size, in pixels. The size is given as a 2-tuple (width, height). - :type: ``(width, height)`` - -.. py:attribute:: width +.. py:attribute:: Image.width + :type: int Image width, in pixels. - :type: :py:class:`int` - -.. py:attribute:: height +.. py:attribute:: Image.height + :type: int Image height, in pixels. - :type: :py:class:`int` - -.. py:attribute:: palette +.. py:attribute:: Image.palette + :type: Optional[PIL.ImagePalette.ImagePalette] Colour palette table, if any. If mode is "P" or "PA", this should be an instance of the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should be set to ``None``. - :type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None`` - -.. py:attribute:: info +.. py:attribute:: Image.info + :type: dict A dictionary holding data associated with the image. This dictionary is used by file handlers to pass on various non-image information read from @@ -322,5 +316,3 @@ Instances of the :py:class:`Image` class have the following attributes: keep a reference to the info dictionary returned from the open method. Unless noted elsewhere, this dictionary does not affect saving files. - - :type: :py:class:`dict` From 3342270947b6dd8442eaf9e14e7c34f9c017f507 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 08:45:18 +0200 Subject: [PATCH 252/262] fix Image constants references (cherry picked from commit 5511111f3b36890a64ec7b44368996f72ab4d876) --- docs/handbook/concepts.rst | 58 +++++++++------- docs/reference/Image.rst | 133 +++++++++++++++++++++++++++++++++++- docs/releasenotes/2.7.0.rst | 68 +++++++++--------- docs/releasenotes/4.2.0.rst | 2 +- src/PIL/Image.py | 70 +++++++++---------- 5 files changed, 235 insertions(+), 96 deletions(-) diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst index e4a720a0883..f62e4b1769e 100644 --- a/docs/handbook/concepts.rst +++ b/docs/handbook/concepts.rst @@ -121,39 +121,47 @@ Filters For geometry operations that may map multiple input pixels to a single output pixel, the Python Imaging Library provides different resampling *filters*. -``NEAREST`` +.. py:currentmodule:: PIL.Image + +.. data:: NEAREST + Pick one nearest pixel from the input image. Ignore all other input pixels. -``BOX`` +.. data:: BOX + Each pixel of source image contributes to one pixel of the destination image with identical weights. - For upscaling is equivalent of ``NEAREST``. + For upscaling is equivalent of :data:`NEAREST`. This filter can only be used with the :py:meth:`~PIL.Image.Image.resize` and :py:meth:`~PIL.Image.Image.thumbnail` methods. .. versionadded:: 3.4.0 -``BILINEAR`` +.. data:: BILINEAR + For resize calculate the output pixel value using linear interpolation on all pixels that may contribute to the output value. For other transformations linear interpolation over a 2x2 environment in the input image is used. -``HAMMING`` - Produces a sharper image than ``BILINEAR``, doesn't have dislocations - on local level like with ``BOX``. +.. data:: HAMMING + + Produces a sharper image than :data:`BILINEAR`, doesn't have dislocations + on local level like with :data:`BOX`. This filter can only be used with the :py:meth:`~PIL.Image.Image.resize` and :py:meth:`~PIL.Image.Image.thumbnail` methods. .. versionadded:: 3.4.0 -``BICUBIC`` +.. data:: BICUBIC + For resize calculate the output pixel value using cubic interpolation on all pixels that may contribute to the output value. For other transformations cubic interpolation over a 4x4 environment in the input image is used. -``LANCZOS`` +.. data:: LANCZOS + Calculate the output pixel value using a high-quality Lanczos filter (a truncated sinc) on all pixels that may contribute to the output value. This filter can only be used with the :py:meth:`~PIL.Image.Image.resize` @@ -165,19 +173,19 @@ pixel, the Python Imaging Library provides different resampling *filters*. Filters comparison table ~~~~~~~~~~~~~~~~~~~~~~~~ -+------------+-------------+-----------+-------------+ -| Filter | Downscaling | Upscaling | Performance | -| | quality | quality | | -+============+=============+===========+=============+ -|``NEAREST`` | | | ⭐⭐⭐⭐⭐ | -+------------+-------------+-----------+-------------+ -|``BOX`` | ⭐ | | ⭐⭐⭐⭐ | -+------------+-------------+-----------+-------------+ -|``BILINEAR``| ⭐ | ⭐ | ⭐⭐⭐ | -+------------+-------------+-----------+-------------+ -|``HAMMING`` | ⭐⭐ | | ⭐⭐⭐ | -+------------+-------------+-----------+-------------+ -|``BICUBIC`` | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | -+------------+-------------+-----------+-------------+ -|``LANCZOS`` | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐ | -+------------+-------------+-----------+-------------+ ++----------------+-------------+-----------+-------------+ +| Filter | Downscaling | Upscaling | Performance | +| | quality | quality | | ++================+=============+===========+=============+ +|:data:`NEAREST` | | | ⭐⭐⭐⭐⭐ | ++----------------+-------------+-----------+-------------+ +|:data:`BOX` | ⭐ | | ⭐⭐⭐⭐ | ++----------------+-------------+-----------+-------------+ +|:data:`BILINEAR`| ⭐ | ⭐ | ⭐⭐⭐ | ++----------------+-------------+-----------+-------------+ +|:data:`HAMMING` | ⭐⭐ | | ⭐⭐⭐ | ++----------------+-------------+-----------+-------------+ +|:data:`BICUBIC` | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ++----------------+-------------+-----------+-------------+ +|:data:`LANCZOS` | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐ | ++----------------+-------------+-----------+-------------+ diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 216fa1196e4..7a3a58442ea 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -234,7 +234,7 @@ 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 ``Image.FLIP_LEFT_RIGHT`` method. +This flips the input image by using the :data:`FLIP_LEFT_RIGHT` method. .. code-block:: python @@ -324,3 +324,134 @@ Instances of the :py:class:`Image` class have the following attributes: Unless noted elsewhere, this dictionary does not affect saving files. :type: :py:class:`dict` + +Constants +--------- + +.. data:: NONE + +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 + +Transform methods +^^^^^^^^^^^^^^^^^ + +Used to specify the :meth:`Image.transform` method to use. + +.. data:: AFFINE + + Affine transform + +.. data:: EXTENT + + Cut out a rectangular subregion + +.. data:: PERSPECTIVE + + Perspective transform + +.. data:: QUAD + + Map a quadrilateral to a rectangle + +.. data:: MESH + + 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: + +Some filters are also available under the following names for backwards compatibility: + +.. data:: NONE + :noindex: + :value: NEAREST +.. data:: LINEAR + :value: BILINEAR +.. data:: CUBIC + :value: BICUBIC +.. data:: ANTIALIAS + :value: LANCZOS + +Dither modes +^^^^^^^^^^^^ + +Used to specify the dithering method to use for the +:meth:`~Image.convert` and :meth:`~Image.quantize` methods. + +.. data:: NONE + :noindex: + + No dither + +.. comment: (not implemented) + .. data:: ORDERED + .. data:: RASTERIZE + +.. data:: FLOYDSTEINBERG + + Floyd-Steinberg dither + +Palettes +^^^^^^^^ + +Used to specify the pallete to use for the :meth:`~Image.convert` method. + +.. data:: WEB +.. data:: ADAPTIVE + +Quantization methods +^^^^^^^^^^^^^^^^^^^^ + +Used to specify the quantization method to use for the :meth:`~Image.quantize` method. + +.. data:: MEDIANCUT + + Median cut + +.. data:: MAXCOVERAGE + + Maximum coverage + +.. data:: FASTOCTREE + + Fast octree + +.. data:: LIBIMAGEQUANT + + libimagequant + + Check support using :py:func:`PIL.features.check_feature` + with ``feature="libimagequant"``. + +.. comment: These are not referenced anywhere? + Categories + ^^^^^^^^^^ + .. data:: NORMAL + .. data:: SEQUENCE + .. data:: CONTAINER diff --git a/docs/releasenotes/2.7.0.rst b/docs/releasenotes/2.7.0.rst index 931f9fd1e9f..03000528f88 100644 --- a/docs/releasenotes/2.7.0.rst +++ b/docs/releasenotes/2.7.0.rst @@ -29,53 +29,53 @@ Image resizing filters Image resizing methods :py:meth:`~PIL.Image.Image.resize` and :py:meth:`~PIL.Image.Image.thumbnail` take a ``resample`` argument, which tells which filter should be used for resampling. Possible values are: -:py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, -:py:attr:`PIL.Image.BICUBIC` and :py:attr:`PIL.Image.ANTIALIAS`. +:py:data:`PIL.Image.NEAREST`, :py:data:`PIL.Image.BILINEAR`, +:py:data:`PIL.Image.BICUBIC` and :py:data:`PIL.Image.ANTIALIAS`. Almost all of them were changed in this version. Bicubic and bilinear downscaling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -From the beginning :py:attr:`~PIL.Image.BILINEAR` and -:py:attr:`~PIL.Image.BICUBIC` filters were based on affine transformations +From the beginning :py:data:`~PIL.Image.BILINEAR` and +:py:data:`~PIL.Image.BICUBIC` filters were based on affine transformations and used a fixed number of pixels from the source image for every destination -pixel (2x2 pixels for :py:attr:`~PIL.Image.BILINEAR` and 4x4 for -:py:attr:`~PIL.Image.BICUBIC`). This gave an unsatisfactory result for +pixel (2x2 pixels for :py:data:`~PIL.Image.BILINEAR` and 4x4 for +:py:data:`~PIL.Image.BICUBIC`). This gave an unsatisfactory result for downscaling. At the same time, a high quality convolutions-based algorithm with -flexible kernel was used for :py:attr:`~PIL.Image.ANTIALIAS` filter. +flexible kernel was used for :py:data:`~PIL.Image.ANTIALIAS` filter. Starting from Pillow 2.7.0, a high quality convolutions-based algorithm is used for all of these three filters. If you have previously used any tricks to maintain quality when downscaling with -:py:attr:`~PIL.Image.BILINEAR` and :py:attr:`~PIL.Image.BICUBIC` filters +:py:data:`~PIL.Image.BILINEAR` and :py:data:`~PIL.Image.BICUBIC` filters (for example, reducing within several steps), they are unnecessary now. Antialias renamed to Lanczos ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A new :py:attr:`PIL.Image.LANCZOS` constant was added instead of -:py:attr:`~PIL.Image.ANTIALIAS`. +A new :py:data:`PIL.Image.LANCZOS` constant was added instead of +:py:data:`~PIL.Image.ANTIALIAS`. -When :py:attr:`~PIL.Image.ANTIALIAS` was initially added, it was the only +When :py:data:`~PIL.Image.ANTIALIAS` was initially added, it was the only high-quality filter based on convolutions. It's name was supposed to reflect this. Starting from Pillow 2.7.0 all resize method are based on convolutions. All of them are antialias from now on. And the real name of the -:py:attr:`~PIL.Image.ANTIALIAS` filter is Lanczos filter. +:py:data:`~PIL.Image.ANTIALIAS` filter is Lanczos filter. -The :py:attr:`~PIL.Image.ANTIALIAS` constant is left for backward compatibility -and is an alias for :py:attr:`~PIL.Image.LANCZOS`. +The :py:data:`~PIL.Image.ANTIALIAS` constant is left for backward compatibility +and is an alias for :py:data:`~PIL.Image.LANCZOS`. Lanczos upscaling quality ^^^^^^^^^^^^^^^^^^^^^^^^^ -The image upscaling quality with :py:attr:`~PIL.Image.LANCZOS` filter was -almost the same as :py:attr:`~PIL.Image.BILINEAR` due to bug. This has been fixed. +The image upscaling quality with :py:data:`~PIL.Image.LANCZOS` filter was +almost the same as :py:data:`~PIL.Image.BILINEAR` due to bug. This has been fixed. Bicubic upscaling quality ^^^^^^^^^^^^^^^^^^^^^^^^^ -The :py:attr:`~PIL.Image.BICUBIC` filter for affine transformations produced +The :py:data:`~PIL.Image.BICUBIC` filter for affine transformations produced sharp, slightly pixelated image for upscaling. Bicubic for convolutions is more soft. @@ -84,42 +84,42 @@ Resize performance In most cases, convolution is more a expensive algorithm for downscaling because it takes into account all the pixels of source image. Therefore -:py:attr:`~PIL.Image.BILINEAR` and :py:attr:`~PIL.Image.BICUBIC` filters' +:py:data:`~PIL.Image.BILINEAR` and :py:data:`~PIL.Image.BICUBIC` filters' performance can be lower than before. On the other hand the quality of -:py:attr:`~PIL.Image.BILINEAR` and :py:attr:`~PIL.Image.BICUBIC` was close to -:py:attr:`~PIL.Image.NEAREST`. So if such quality is suitable for your tasks -you can switch to :py:attr:`~PIL.Image.NEAREST` filter for downscaling, +:py:data:`~PIL.Image.BILINEAR` and :py:data:`~PIL.Image.BICUBIC` was close to +:py:data:`~PIL.Image.NEAREST`. So if such quality is suitable for your tasks +you can switch to :py:data:`~PIL.Image.NEAREST` filter for downscaling, which will give a huge improvement in performance. At the same time performance of convolution resampling for downscaling has been improved by around a factor of two compared to the previous version. -The upscaling performance of the :py:attr:`~PIL.Image.LANCZOS` filter has -remained the same. For :py:attr:`~PIL.Image.BILINEAR` filter it has improved by -1.5 times and for :py:attr:`~PIL.Image.BICUBIC` by four times. +The upscaling performance of the :py:data:`~PIL.Image.LANCZOS` filter has +remained the same. For :py:data:`~PIL.Image.BILINEAR` filter it has improved by +1.5 times and for :py:data:`~PIL.Image.BICUBIC` by four times. Default filter for thumbnails ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In Pillow 2.5 the default filter for :py:meth:`~PIL.Image.Image.thumbnail` was -changed from :py:attr:`~PIL.Image.NEAREST` to :py:attr:`~PIL.Image.ANTIALIAS`. +changed from :py:data:`~PIL.Image.NEAREST` to :py:data:`~PIL.Image.ANTIALIAS`. Antialias was chosen because all the other filters gave poor quality for -reduction. Starting from Pillow 2.7.0, :py:attr:`~PIL.Image.ANTIALIAS` has been -replaced with :py:attr:`~PIL.Image.BICUBIC`, because it's faster and -:py:attr:`~PIL.Image.ANTIALIAS` doesn't give any advantages after +reduction. Starting from Pillow 2.7.0, :py:data:`~PIL.Image.ANTIALIAS` has been +replaced with :py:data:`~PIL.Image.BICUBIC`, because it's faster and +:py:data:`~PIL.Image.ANTIALIAS` doesn't give any advantages after downscaling with libjpeg, which uses supersampling internally, not convolutions. Image transposition ------------------- -A new method :py:attr:`PIL.Image.TRANSPOSE` has been added for the +A new method :py:data:`PIL.Image.TRANSPOSE` has been added for the :py:meth:`~PIL.Image.Image.transpose` operation in addition to -:py:attr:`~PIL.Image.FLIP_LEFT_RIGHT`, :py:attr:`~PIL.Image.FLIP_TOP_BOTTOM`, -:py:attr:`~PIL.Image.ROTATE_90`, :py:attr:`~PIL.Image.ROTATE_180`, -:py:attr:`~PIL.Image.ROTATE_270`. :py:attr:`~PIL.Image.TRANSPOSE` is an algebra +: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:attr:`~PIL.Image.ROTATE_90`, :py:attr:`~PIL.Image.ROTATE_270` -and :py:attr:`~PIL.Image.TRANSPOSE` has been significantly improved for large +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. Gaussian blur and unsharp mask diff --git a/docs/releasenotes/4.2.0.rst b/docs/releasenotes/4.2.0.rst index e07fd90716d..906eeab8d3a 100644 --- a/docs/releasenotes/4.2.0.rst +++ b/docs/releasenotes/4.2.0.rst @@ -27,7 +27,7 @@ New DecompressionBomb Warning :py:meth:`PIL.Image.Image.crop` now may raise a DecompressionBomb warning if the crop region enlarges the image over the threshold -specified by :py:attr:`PIL.Image.MAX_PIXELS`. +specified by :py:data:`PIL.Image.MAX_PIXELS`. Removed Deprecated Items ======================== diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 7a2ae02d692..632818e73f8 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -876,7 +876,7 @@ def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256): The default method of converting a greyscale ("L") or "RGB" image into a bilevel (mode "1") image uses Floyd-Steinberg dither to approximate the original image luminosity levels. If - dither is NONE, all values larger than 128 are set to 255 (white), + dither is :data:`NONE`, all values larger than 128 are set to 255 (white), all other values to 0 (black). To use other thresholds, use the :py:meth:`~PIL.Image.Image.point` method. @@ -889,11 +889,11 @@ def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256): should be 4- or 12-tuple containing floating point values. :param dither: Dithering method, used when converting from mode "RGB" to "P" or from "RGB" or "L" to "1". - Available methods are NONE or FLOYDSTEINBERG (default). + Available methods are :data:`NONE` or :data:`FLOYDSTEINBERG` (default). Note that this is not used when **matrix** is supplied. :param palette: Palette to use when converting from mode "RGB" - to "P". Available palettes are WEB or ADAPTIVE. - :param colors: Number of colors to use for the ADAPTIVE palette. + to "P". Available palettes are :data:`WEB` or :data:`ADAPTIVE`. + :param colors: Number of colors to use for the :data:`ADAPTIVE` palette. Defaults to 256. :rtype: :py:class:`~PIL.Image.Image` :returns: An :py:class:`~PIL.Image.Image` object. @@ -1051,10 +1051,10 @@ def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1): of colors. :param colors: The desired number of colors, <= 256 - :param method: ``Image.MEDIANCUT=0`` (median cut), - ``Image.MAXCOVERAGE=1`` (maximum coverage), - ``Image.FASTOCTREE=2`` (fast octree), - ``Image.LIBIMAGEQUANT=3`` (libimagequant; check support using + :param method: :data:`MEDIANCUT` (median cut), + :data:`MAXCOVERAGE` (maximum coverage), + :data:`FASTOCTREE` (fast octree), + :data:`LIBIMAGEQUANT` (libimagequant; check support using :py:func:`PIL.features.check_feature` with ``feature="libimagequant"``). :param kmeans: Integer @@ -1062,7 +1062,7 @@ def quantize(self, colors=256, method=None, kmeans=0, palette=None, dither=1): :py:class:`PIL.Image.Image`. :param dither: Dithering method, used when converting from mode "RGB" to "P" or from "RGB" or "L" to "1". - Available methods are NONE or FLOYDSTEINBERG (default). + Available methods are :data:`NONE` or :data:`FLOYDSTEINBERG` (default). Default: 1 (legacy setting) :returns: A new image @@ -1842,12 +1842,12 @@ def resize(self, size, resample=BICUBIC, box=None, reducing_gap=None): :param size: The requested size in pixels, as a 2-tuple: (width, height). :param resample: An optional resampling filter. This can be - one of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BOX`, - :py:attr:`PIL.Image.BILINEAR`, :py:attr:`PIL.Image.HAMMING`, - :py:attr:`PIL.Image.BICUBIC` or :py:attr:`PIL.Image.LANCZOS`. - Default filter is :py:attr:`PIL.Image.BICUBIC`. + 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`. + Default filter is :py:data:`PIL.Image.BICUBIC`. If the image has mode "1" or "P", it is - always set to :py:attr:`PIL.Image.NEAREST`. + always set to :py:data:`PIL.Image.NEAREST`. See: :ref:`concept-filters`. :param box: An optional 4-tuple of floats providing the source image region to be scaled. @@ -1977,12 +1977,12 @@ def rotate( :param angle: In degrees counter clockwise. :param resample: An optional resampling filter. This can be - one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), - :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:attr:`PIL.Image.BICUBIC` + one of :py:data:`PIL.Image.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.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:attr:`PIL.Image.NEAREST`. See :ref:`concept-filters`. + set to :py:data:`PIL.Image.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 @@ -2274,10 +2274,10 @@ 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:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, - :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.LANCZOS`. - If omitted, it defaults to :py:attr:`PIL.Image.BICUBIC`. - (was :py:attr:`PIL.Image.NEAREST` prior to version 2.5.0). + of :py:data:`PIL.Image.NEAREST`, :py:data:`PIL.Image.BILINEAR`, + :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). See: :ref:`concept-filters`. :param reducing_gap: Apply optimization by resizing the image in two steps. First, reducing the image by integer times @@ -2341,11 +2341,11 @@ def transform( :param size: The output size. :param method: The transformation method. This is one of - :py:attr:`PIL.Image.EXTENT` (cut out a rectangular subregion), - :py:attr:`PIL.Image.AFFINE` (affine transform), - :py:attr:`PIL.Image.PERSPECTIVE` (perspective transform), - :py:attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or - :py:attr:`PIL.Image.MESH` (map a number of source quadrilaterals + :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 in one operation). It may also be an :py:class:`~PIL.Image.ImageTransformHandler` @@ -2365,11 +2365,11 @@ def getdata(self): return method, data :param data: Extra data to the transformation method. :param resample: Optional resampling filter. It can be one of - :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), - :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline + :py:data:`PIL.Image.NEAREST` (use nearest neighbour), + :py:data:`PIL.Image.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:attr:`PIL.Image.NEAREST`. + has mode "1" or "P", it is set to :py:data:`PIL.Image.NEAREST`. See: :ref:`concept-filters`. :param fill: If **method** is an :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of @@ -2493,10 +2493,10 @@ def transpose(self, method): """ Transpose image (flip or rotate in 90 degree steps) - :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, - :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, - :py:attr:`PIL.Image.ROTATE_180`, :py:attr:`PIL.Image.ROTATE_270`, - :py:attr:`PIL.Image.TRANSPOSE` or :py:attr:`PIL.Image.TRANSVERSE`. + :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`. :returns: Returns a flipped or rotated copy of this image. """ From 19dd5cbfab75f6d9e74349a611140a5aa2fcd85e Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 09:00:17 +0200 Subject: [PATCH 253/262] fix some function references (cherry picked from commit 9fb582940d577857d9034e0bf0c5cf5630c2d42e) --- docs/handbook/image-file-formats.rst | 22 +++++++++++----------- src/PIL/ImageDraw2.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 40db9fe2b5d..6bcff713560 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -8,7 +8,7 @@ Over 30 different file formats can be identified and read by the library. Write support is less extensive, but most common interchange and presentation formats are supported. -The :py:meth:`~PIL.Image.Image.open` function identifies files from their +The :py:meth:`~PIL.Image.open` function identifies files from their contents, not their names, but the :py:meth:`~PIL.Image.Image.save` method looks at the name to determine which format to use, unless the format is given explicitly. @@ -25,7 +25,7 @@ Pillow reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P` or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding is not supported. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **compression** @@ -74,7 +74,7 @@ are used or GIF89a is already in use. Note that GIF files are always read as grayscale (``L``) or palette mode (``P``) images. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **background** @@ -203,7 +203,7 @@ ICNS Pillow reads and (macOS only) writes macOS ``.icns`` files. By default, the largest available icon is read, though you can override this by setting the :py:attr:`~PIL.Image.Image.size` property before calling -:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.Image.open` method +:py:meth:`~PIL.Image.Image.load`. The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` property: **sizes** @@ -257,7 +257,7 @@ Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of their original size while loading them. -The :py:meth:`~PIL.Image.Image.open` method may set the following +The :py:meth:`~PIL.Image.open` method may set the following :py:attr:`~PIL.Image.Image.info` properties if available: **jfif** @@ -697,7 +697,7 @@ Pillow also reads SPIDER stack files containing sequences of SPIDER images. The :py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL.Image.Image.tell` methods are supported, and random access is allowed. -The :py:meth:`~PIL.Image.Image.open` method sets the following attributes: +The :py:meth:`~PIL.Image.open` method sets the following attributes: **format** Set to ``SPIDER`` @@ -750,7 +750,7 @@ uncompressed files. support for reading Packbits, LZW and JPEG compressed TIFFs without using libtiff. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **compression** @@ -1021,7 +1021,7 @@ FLI, FLC Pillow reads Autodesk FLI and FLC animations. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **duration** @@ -1054,7 +1054,7 @@ GBR The GBR decoder reads GIMP brush files, version 1 and 2. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **comment** @@ -1069,7 +1069,7 @@ GD Pillow reads uncompressed GD2 files. Note that you must use :py:func:`PIL.GdImageFile.open` to read such a file. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **transparency** @@ -1185,7 +1185,7 @@ XPM Pillow reads X pixmap files (mode ``P``) with 256 colors or less. -The :py:meth:`~PIL.Image.Image.open` method sets the following +The :py:meth:`~PIL.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: **transparency** diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index b14b68e3ee9..1f63110fd26 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -106,7 +106,7 @@ def arc(self, xy, start, end, *options): def chord(self, xy, start, end, *options): """ - Same as :py:meth:`~PIL.ImageDraw2.ImageDraw.arc`, but connects the end points + Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points with a straight line. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` From 4f1ee7a881f2ce08ae8f37a0d49241050f2d55b7 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 22 Jun 2020 10:55:54 +0200 Subject: [PATCH 254/262] add missing and sort Image functions (cherry picked from commit f31c786aa6dadfcd93596887bb67b1d9a776f8c6) --- docs/reference/Image.rst | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 216fa1196e4..649c9a1851e 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -76,9 +76,16 @@ Constructing images .. autofunction:: new .. autofunction:: fromarray .. autofunction:: frombytes -.. autofunction:: fromstring .. autofunction:: frombuffer +Generating images +^^^^^^^^^^^^^^^^^ + +.. autofunction:: effect_mandelbrot +.. autofunction:: effect_noise +.. autofunction:: linear_gradient +.. autofunction:: radial_gradient + Registering plugins ^^^^^^^^^^^^^^^^^^^ @@ -88,12 +95,14 @@ Registering plugins ignore them. .. autofunction:: register_open -.. autofunction:: register_decoder .. autofunction:: register_mime .. autofunction:: register_save -.. autofunction:: register_encoder +.. autofunction:: register_save_all .. autofunction:: register_extension - +.. autofunction:: register_extensions +.. autofunction:: registered_extensions +.. autofunction:: register_decoder +.. autofunction:: register_encoder The Image Class --------------- @@ -140,6 +149,8 @@ This crops the input image with the provided coordinates: .. automethod:: PIL.Image.Image.draft +.. automethod:: PIL.Image.Image.effect_spread +.. automethod:: PIL.Image.Image.entropy .. automethod:: PIL.Image.Image.filter This blurs the input image using a filter from the ``ImageFilter`` module: @@ -176,12 +187,14 @@ This helps to get the bounding box coordinates of the input image: print(im.getbbox()) # Returns four coordinates in the format (left, upper, right, lower) +.. automethod:: PIL.Image.Image.getchannel .. automethod:: PIL.Image.Image.getcolors .. automethod:: PIL.Image.Image.getdata -.. automethod:: PIL.Image.Image.getextrema .. automethod:: PIL.Image.Image.getexif +.. automethod:: PIL.Image.Image.getextrema .. automethod:: PIL.Image.Image.getpalette .. automethod:: PIL.Image.Image.getpixel +.. automethod:: PIL.Image.Image.getprojection .. automethod:: PIL.Image.Image.histogram .. automethod:: PIL.Image.Image.offset .. automethod:: PIL.Image.Image.paste @@ -191,6 +204,8 @@ This helps to get the bounding box coordinates of the input image: .. automethod:: PIL.Image.Image.putpalette .. automethod:: PIL.Image.Image.putpixel .. automethod:: PIL.Image.Image.quantize +.. automethod:: PIL.Image.Image.reduce +.. automethod:: PIL.Image.Image.remap_palette .. automethod:: PIL.Image.Image.resize This resizes the given image from ``(width, height)`` to ``(width/2, height/2)``: @@ -205,7 +220,6 @@ This resizes the given image from ``(width, height)`` to ``(width/2, height/2)`` (width, height) = (im.width // 2, im.height // 2) im_resized = im.resize((width, height)) -.. automethod:: PIL.Image.Image.remap_palette .. automethod:: PIL.Image.Image.rotate This rotates the input image by ``theta`` degrees counter clockwise: @@ -225,7 +239,6 @@ This rotates the input image by ``theta`` degrees counter clockwise: .. automethod:: PIL.Image.Image.seek .. automethod:: PIL.Image.Image.show .. automethod:: PIL.Image.Image.split -.. automethod:: PIL.Image.Image.getchannel .. automethod:: PIL.Image.Image.tell .. automethod:: PIL.Image.Image.thumbnail .. automethod:: PIL.Image.Image.tobitmap From 1e8d418f4266ddd1a14e024f0997c76b8dc1ca9c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 28 Jun 2020 17:24:27 +1000 Subject: [PATCH 255/262] Fixed ICNS file pointer saving --- Tests/test_file_icns.py | 13 +++++++++++++ src/PIL/IcnsImagePlugin.py | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 7bf7b72ec92..05feedb1aa9 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -55,6 +55,19 @@ def test_save_append_images(tmp_path): assert_image_equal(reread, provided_im) +@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS") +def test_save_fp(): + fp = io.BytesIO() + + with Image.open(TEST_FILE) as im: + im.save(fp, format="ICNS") + + with Image.open(fp) as reread: + assert reread.mode == "RGBA" + assert reread.size == (1024, 1024) + assert reread.format == "ICNS" + + def test_sizes(): # Check that we can load all of the sizes, and that the final pixel # dimensions are as expected diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index 9de7d8dfe3b..7023855ba11 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -337,6 +337,10 @@ def _save(im, fp, filename): # iconutil -c icns -o {} {} + fp_only = not filename + if fp_only: + f, filename = tempfile.mkstemp(".icns") + os.close(f) convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] convert_proc = subprocess.Popen( convert_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL @@ -349,6 +353,10 @@ def _save(im, fp, filename): if retcode: raise subprocess.CalledProcessError(retcode, convert_cmd) + if fp_only: + with open(filename, "rb") as f: + fp.write(f.read()) + Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns") Image.register_extension(IcnsImageFile.format, ".icns") From cdf4936c07a48372f9120afb5a83ed426db0f14a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Jun 2020 21:20:57 +1000 Subject: [PATCH 256/262] Fixed loading non-RGBA mode images with dispose background --- .../images/apng/dispose_op_background_p_mode.png | Bin 0 -> 1239 bytes Tests/test_file_apng.py | 7 +++++++ src/PIL/PngImagePlugin.py | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 Tests/images/apng/dispose_op_background_p_mode.png diff --git a/Tests/images/apng/dispose_op_background_p_mode.png b/Tests/images/apng/dispose_op_background_p_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..e5fb4784d260cd9fb1556011c97eb070e2d0a351 GIT binary patch literal 1239 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!3-pya`gK$FfcO&_=LFr2l7Y3Xb6nd5D@R2 z8Op%G$WRjG7d+DQ!q1gAU4iL_smkWTDoTrOpNX4AD2N^-4O$z_l z=Xp4Q6#%*K*%TZY80#+vYzA_q(jdBlOh}OfVn6`%3r2 Date: Mon, 29 Jun 2020 22:02:01 +1000 Subject: [PATCH 257/262] Added disposal test --- Tests/test_file_apng.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index deb043fdd4c..1fd7d8c3d22 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -494,6 +494,26 @@ def test_apng_save_disposal(tmp_path): assert im.getpixel((64, 32)) == (0, 255, 0, 255) +def test_apng_save_disposal_previous(tmp_path): + test_file = str(tmp_path / "temp.png") + size = (128, 64) + transparent = Image.new("RGBA", size, (0, 0, 0, 0)) + red = Image.new("RGBA", size, (255, 0, 0, 255)) + green = Image.new("RGBA", size, (0, 255, 0, 255)) + + # test APNG_DISPOSE_OP_NONE + transparent.save( + test_file, + save_all=True, + append_images=[red, green], + disposal=PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS, + ) + 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) + + def test_apng_save_blend(tmp_path): test_file = str(tmp_path / "temp.png") size = (128, 64) From e1ae9a50cb002f5d5613c359d5e008ba5485d9d4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 29 Jun 2020 22:14:40 +1000 Subject: [PATCH 258/262] Do not convert I;16 image when format is PNG --- src/PIL/ImageShow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 57b7dcac7f6..3ffb4d63254 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -71,7 +71,8 @@ def show(self, image, **options): # save temporary image to disk if not ( - image.mode in ("1", "RGBA") or (self.format == "PNG" and image.mode == "LA") + image.mode in ("1", "RGBA") + or (self.format == "PNG" and image.mode in ("I;16", "LA")) ): base = Image.getmodebase(image.mode) if image.mode != base: From 15f1e183d613cdf3c2a57f6fc2073fbf1ce8ecb7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Jun 2020 00:33:51 +1000 Subject: [PATCH 259/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c5b55a52475..151ca8a3d1b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Fixed loading non-RGBA mode APNGs with dispose background #4742 + [radarhere] + - Deprecated _showxv #4714 [radarhere] From 3648332edca5a7c152c81e3f8ee81dd08fd39bcf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Jun 2020 08:53:08 +1000 Subject: [PATCH 260/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 151ca8a3d1b..23c115a114f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 7.2.0 (unreleased) ------------------ +- Do not convert I;16 images when showing PNGs #4744 + [radarhere] + +- Fixed ICNS file pointer saving #4741 + [radarhere] + - Fixed loading non-RGBA mode APNGs with dispose background #4742 [radarhere] From 17b14f81fc103c822232101b143c85af12404e58 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Jun 2020 17:46:57 +1000 Subject: [PATCH 261/262] 7.2.0 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 eeee6a1f50a..035deeba7ce 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "7.2.0.dev0" +__version__ = "7.2.0" From 2bd74943fb9f320def6c066e732b701d1c15f677 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 30 Jun 2020 17:50:35 +1000 Subject: [PATCH 262/262] Updated CHANGES.rst [ci skip] --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 23c115a114f..c63f05e6a32 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,7 +2,7 @@ Changelog (Pillow) ================== -7.2.0 (unreleased) +7.2.0 (2020-07-01) ------------------ - Do not convert I;16 images when showing PNGs #4744