From fdbb1870d44b2a831d37b0545f5bdba2d147518e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 08:52:46 -0500 Subject: [PATCH 001/326] CI: Add a cygwin run to GitHub Actions. Requested for the _imagingtk fix. --- .github/workflows/test-cygwin.yml | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/test-cygwin.yml diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml new file mode 100644 index 00000000000..4275e65a67b --- /dev/null +++ b/.github/workflows/test-cygwin.yml @@ -0,0 +1,48 @@ +name: Test Cygwin + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: windows-2019 + name: Cygwin Python 3.8 + + steps: + - name: Checkout Pillow + uses: actions/checkout@v2 + - name: Set up Cygwin + uses: egor-tensin/setup-cygwin@v3 + with: + platform: x64 + packages: > + python38-devel python38-tkinter libfreetype-devel + libimagequant-devel libjpeg-devel liblcms2-devel + libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel + libxcb-devel python38-olefile python38-pathlib + python38-numpy netpbm ImageMagick jpeg xorg-server-extra + xorg-server-common xinit python38-setuptools + python38-wheel + install-dir: 'C:\tools\cygwin' + - name: Clean up path + uses: egor-tensin/cleanup-path@v2 + with: + dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Build Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 setup.py bdist_wheel + - name: Install Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + - name: Test Pillow + shell: 'C:\tools\cygwin\bin\dash.exe' + run: | + /usr/bin/python3.8 selftest.py + xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 67bef87a070af4e239707de64670ac833031e46c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 18:19:22 -0500 Subject: [PATCH 002/326] CI: Specify where the command line goes in the shell. I think this is how specifying the shell works. The documentation isn't terribly clear. --- .github/workflows/test-cygwin.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 4275e65a67b..c22fe2bec70 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -34,15 +34,15 @@ jobs: with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - name: Build Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 setup.py bdist_wheel - name: Install Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl - name: Test Pillow - shell: 'C:\tools\cygwin\bin\dash.exe' + shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | /usr/bin/python3.8 selftest.py xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From b798989f33b4ef275360cb491d0c555079fd899e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:08:28 -0500 Subject: [PATCH 003/326] CI: Make sure all python requirements are installed in Cygwin CI. For some reason wheel wasn't installed properly. --- .github/workflows/test-cygwin.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c22fe2bec70..cb0c84ef53c 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -21,18 +21,24 @@ jobs: with: platform: x64 packages: > - python38-devel python38-tkinter libfreetype-devel - libimagequant-devel libjpeg-devel liblcms2-devel - libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel - libxcb-devel python38-olefile python38-pathlib - python38-numpy netpbm ImageMagick jpeg xorg-server-extra - xorg-server-common xinit python38-setuptools - python38-wheel + libfreetype-devel libimagequant-devel libjpeg-devel + liblcms2-devel libopenjp2-devel libraqm-devel + libtiff-devel libwebp-devel libxcb-devel netpbm + ImageMagick jpeg xorg-server-extra xorg-server-common + xinit python38-setuptools python38-devel python38-tkinter + python38-wheel python38-pip python38-olefile + python38-numpy python38-pytest python38-sphinx + python38-packaging python38-cffi install-dir: 'C:\tools\cygwin' - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + - name: Ensure python dependencies installed + shell: 'C:\tools\cygwin\bin\dash.exe {0}' + run: | + /usr/bin/python3.8 -m pip install pip wheel setuptools + /usr/bin/python3.8 -m pip install -r requirements.txt - name: Build Pillow shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | From a4495decf878298abb8b379251c6cf98aa662736 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 19:57:44 -0500 Subject: [PATCH 004/326] CI: Stop using dash so the DOS line endings don't throw things off. --- .github/workflows/test-cygwin.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index cb0c84ef53c..904658f80a2 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -29,26 +29,23 @@ jobs: python38-wheel python38-pip python38-olefile python38-numpy python38-pytest python38-sphinx python38-packaging python38-cffi + libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel install-dir: 'C:\tools\cygwin' - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - name: Ensure python dependencies installed - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 -m pip install pip wheel setuptools - /usr/bin/python3.8 -m pip install -r requirements.txt + C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools + C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt - name: Build Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 setup.py bdist_wheel + C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + C:\tools\cygwin\bin\python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl - name: Test Pillow - shell: 'C:\tools\cygwin\bin\dash.exe {0}' run: | - /usr/bin/python3.8 selftest.py - xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\python3.8 selftest.py + C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 8ebac45f24fa53bcdb148b418222b41db0455626 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:14:41 -0500 Subject: [PATCH 005/326] CI: Explicitly use dash to get shell globbing. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 904658f80a2..c1404b2c68b 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow run: | - C:\tools\cygwin\bin\python3.8 -m pip install Pillow-*-cp38-cp38-cygwin_*_x86_64.whl + C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install *.whl' - name: Test Pillow run: | C:\tools\cygwin\bin\python3.8 selftest.py From cd087c600775c52af65a1a3ea6ff9818718f63a9 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:20:47 -0500 Subject: [PATCH 006/326] CI: Fix the path to the build wheels. setup.py bdist_wheel goes to dist/*.whl pip wheel goes to *.whl --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c1404b2c68b..ff496443b00 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel - name: Install Pillow run: | - C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install *.whl' + C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - name: Test Pillow run: | C:\tools\cygwin\bin\python3.8 selftest.py From 1daaf9273ceb94fd15e84cbe4f9f095ed7f6669c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 10 Dec 2021 21:53:43 -0500 Subject: [PATCH 007/326] CI: Split the config check from the actual test. I'm using selftest.py to check whether I've installed everything. Pytest actually finds and runs the tests. For some reason that wasn't running earlier. --- .github/workflows/test-cygwin.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index ff496443b00..94711051441 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -45,7 +45,9 @@ jobs: - name: Install Pillow run: | C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - - name: Test Pillow + - name: Check Pillow configuration run: | C:\tools\cygwin\bin\python3.8 selftest.py + - name: Test Pillow + run: | C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 9ec4034bff2f03bac37ac0ec0e60f4c0bbaf3313 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 08:26:24 -0500 Subject: [PATCH 008/326] CI: Incorporate suggestions from PR. Upload coverage information, add Cygwin to the list of systems with CI, space out entries. --- .github/workflows/test-cygwin.yml | 28 ++++++++++++++++++++-------- docs/installation.rst | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 94711051441..d7c74e7bc65 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -1,12 +1,6 @@ name: Test Cygwin -on: - push: - branches: - - main - pull_request: - branches: - - main +on: [push, pull_request, workflow_dispatch] jobs: build: @@ -16,6 +10,7 @@ jobs: steps: - name: Checkout Pillow uses: actions/checkout@v2 + - name: Set up Cygwin uses: egor-tensin/setup-cygwin@v3 with: @@ -31,23 +26,40 @@ jobs: python38-packaging python38-cffi libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel install-dir: 'C:\tools\cygwin' + - name: Clean up path uses: egor-tensin/cleanup-path@v2 with: dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' - - name: Ensure python dependencies installed + + - name: Build system information + run: C:\tools\cygwin\bin\python3.8 .github/workflows/system-info.py + + - name: Ensure Python dependencies are installed run: | C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt + - name: Build Pillow run: | C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel + - name: Install Pillow run: | C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' + - name: Check Pillow configuration run: | C:\tools\cygwin\bin\python3.8 selftest.py + - name: Test Pillow run: | C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + + - name: After success + run: | + C:\tools\cygwin\bin\dash .ci/after_success.sh + + - name: Upload coverage + run: | + C:\tools\cygwin\bin\bash -c 'bash <(curl -s https://codecov.io/bash) -F' diff --git a/docs/installation.rst b/docs/installation.rst index 1fb6897d279..3e5123c7a53 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,6 +460,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| Cygwin | 3.8 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | From 407abbfa186d545cd2dc4850c0f6f4d29032c386 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:12:31 -0500 Subject: [PATCH 009/326] CI: Try to get tests fully working The actual test step wasn't running, so try to run that as a shell script rather than an executable. Also get more of the dependencies installed. --- .github/workflows/test-cygwin.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d7c74e7bc65..24bd6a2be20 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -23,8 +23,7 @@ jobs: xinit python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy python38-pytest python38-sphinx - python38-packaging python38-cffi - libfribidi-devel zlib-devel libX11-xcb-devel tcl-tk-devel + python38-packaging python38-cffi python38-requests install-dir: 'C:\tools\cygwin' - name: Clean up path @@ -54,11 +53,11 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh - name: After success run: | - C:\tools\cygwin\bin\dash .ci/after_success.sh + C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage run: | From 4cfb1a854f531afdab4c0c4bb7a4fc6fc754e69a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:13:56 -0500 Subject: [PATCH 010/326] CI: Use the new GitHub Action for codecov The old bash downloader will be removed soon. --- .github/workflows/test-cygwin.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 24bd6a2be20..8bb3e4cdd4d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,5 +60,8 @@ jobs: C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage - run: | - C:\tools\cygwin\bin\bash -c 'bash <(curl -s https://codecov.io/bash) -F' + uses: codecov/codecov-actions@v2 + with: + file: ./coverage.xml + flags: GHA_Cygwin + name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} From 90cf149cbe9449934767eba376f690d37b1cc53a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:17:28 -0500 Subject: [PATCH 011/326] CI: Fix the spelling on the codecov repository. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 8bb3e4cdd4d..6215b72f8f5 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,7 +60,7 @@ jobs: C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-actions@v2 + uses: codecov/codecov-action@v2 with: file: ./coverage.xml flags: GHA_Cygwin From 7fd3e9977e7dd0c04e4e72fdc65f59253f0e6281 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 11 Dec 2021 15:59:45 -0500 Subject: [PATCH 012/326] CI: Make sure correct version of python is used for testing. Cygwin is trying to use the highest-available Python version. One of the Python packages has scripts in /usr/bin that should be in the python39- subpackage. --- .ci/test.sh | 4 ++-- .github/workflows/test-cygwin.yml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index 8ff7c5f6483..b0f019d6607 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,6 +2,6 @@ set -e -python3 -c "from PIL import Image" +python3$1 -c "from PIL import Image" -python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE +python3$1 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6215b72f8f5..bbe5eccee72 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,8 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + C:\tools\cygwin\bin\dash -c "mv .ci/test.sh .ci/test.sh.dos && /bin/tr -d '\r' <.ci/test.sh.dos >test.sh" + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success run: | From 1c25d95d5abf3ae8b4de6b4e55d42c8f8593cf80 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 09:55:24 -0500 Subject: [PATCH 013/326] CI: Get cygwin tests running They segfault in one of the NumPy tests, but they run. --- .github/workflows/test-cygwin.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index bbe5eccee72..d3ab0a36149 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,9 @@ jobs: - name: Test Pillow run: | - C:\tools\cygwin\bin\dash -c "mv .ci/test.sh .ci/test.sh.dos && /bin/tr -d '\r' <.ci/test.sh.dos >test.sh" + C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos + C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" + C:\tools\cygwin\bin\chmod u+x .ci/test.sh C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success @@ -66,3 +68,10 @@ jobs: file: ./coverage.xml flags: GHA_Cygwin name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} + + - name: After failure + if: failure() + run: | + C:\tools\cygwin\bin\uname -a + C:\tools\cygwin\bin\python3.8 -m pip list + C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow From b9fee08c5911dcdb38d320260589cce3229c5c72 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 18 Dec 2021 10:00:14 -0500 Subject: [PATCH 014/326] TST: Parametrize numpy roundtrip to find failing case Segfaults are annoying to debug. --- .github/workflows/test-cygwin.yml | 2 ++ Tests/test_numpy.py | 13 ++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d3ab0a36149..b6a305b7151 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -38,6 +38,7 @@ jobs: run: | C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt + C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked - name: Build Pillow run: | @@ -56,6 +57,7 @@ jobs: C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" C:\tools\cygwin\bin\chmod u+x .ci/test.sh + C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index def7adf3f02..936474fe8d2 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -189,8 +189,9 @@ def test_putdata(): assert len(im.getdata()) == len(arr) -def test_roundtrip_eye(): - for dtype in ( +@pytest.mark.parametrize( + "dtype", + ( bool, numpy.bool8, numpy.int8, @@ -202,9 +203,11 @@ def test_roundtrip_eye(): float, numpy.float32, numpy.float64, - ): - arr = numpy.eye(10, dtype=dtype) - numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) + ), +) +def test_roundtrip_eye(dtype): + arr = numpy.eye(10, dtype=dtype) + numpy.testing.assert_array_equal(arr, numpy.array(Image.fromarray(arr))) def test_zero_size(): From 77c8a07f1c6f5330146a31023de06d6f541e4735 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Thu, 16 Dec 2021 12:28:30 -0500 Subject: [PATCH 015/326] DOC: Rearrange the Cygwin entries on the support matrix. From a suggestion on the PR, make it clearer that Cygwin runs on Windows. Also record the Cygwin versions in the check against Python versions. --- docs/installation.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 3e5123c7a53..d77e2742f2c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -460,8 +460,6 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | CentOS Stream 8 | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Cygwin | 3.8 | x86-64 | -+----------------------------------+----------------------------+---------------------+ | Debian 10 Buster | 3.7 | x86 | +----------------------------------+----------------------------+---------------------+ | Fedora 34 | 3.9 | x86-64 | @@ -482,6 +480,8 @@ These platforms are built and tested for every change. | Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 | | +----------------------------+---------------------+ | | 3.9/MinGW | x86, x86-64 | +| +----------------------------+---------------------+ +| | 3.8/Cygwin | x86-64 | +----------------------------------+----------------------------+---------------------+ @@ -560,6 +560,8 @@ These platforms have been reported to work at the versions mentioned. +----------------------------------+---------------------------+------------------+--------------+ | Windows 10 | 3.7 | 7.1.0 |x86-64 | +----------------------------------+---------------------------+------------------+--------------+ +| Windows 10/Cygwin 3.3 | 3.6, 3.7, 3.8, 3.9 | 8.4.0 |x86-64 | ++----------------------------------+---------------------------+------------------+--------------+ | Windows 8.1 Pro | 2.6, 2.7, 3.2, 3.3, 3.4 | 2.4.0 |x86,x86-64 | +----------------------------------+---------------------------+------------------+--------------+ | Windows 8 Pro | 2.6, 2.7, 3.2, 3.3, 3.4a3 | 2.2.0 |x86,x86-64 | From 7ad8fdb67796300ab52e97e432bfe34d17860ad7 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:34:30 -0500 Subject: [PATCH 016/326] CI: Get all the tests running on Cygwin. --- .github/workflows/test-cygwin.yml | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b6a305b7151..f43460293b8 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -18,12 +18,13 @@ jobs: packages: > libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel - libtiff-devel libwebp-devel libxcb-devel netpbm - ImageMagick jpeg xorg-server-extra xorg-server-common - xinit python38-setuptools python38-devel python38-tkinter - python38-wheel python38-pip python38-olefile - python38-numpy python38-pytest python38-sphinx - python38-packaging python38-cffi python38-requests + libtiff-devel libwebp-devel libxcb-devel libpng-devel + netpbm ImageMagick jpeg xorg-server-extra + xorg-server-common xinit subversion python38-setuptools + python38-devel python38-tkinter python38-wheel + python38-pip python38-olefile python38-numpy + python38-pytest python38-sphinx python38-packaging + python38-cffi python38-requests install-dir: 'C:\tools\cygwin' - name: Clean up path @@ -40,6 +41,12 @@ jobs: C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked + - name: Download extra test images + run: | + C:\tools\cygwin\bin\dash -c "tr -d '\r' install_extra_test_images.sh" + C:\tools\cygwin\bin\dash -c "mv install_extra_test_images.sh depends/install_extra_test_images.sh" + C:\tools\cygwin\bin\dash -c "cd depends; dash install_extra_test_images.sh" + - name: Build Pillow run: | C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel @@ -57,11 +64,16 @@ jobs: C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" C:\tools\cygwin\bin\chmod u+x .ci/test.sh + C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 --ignore=Tests/test_numpy.py + + - name: Test the possibly-segfaulting NumPy tests separately + run: | C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 - name: After success run: | + C:\tools\cygwin\bin\mv .ci/after_success.sh .ci/after_success.sh.dos + C:\tools\cygwin\bin\dash -c "tr -d '\r' <.ci/after_success.sh.dos >.ci/after_success.sh" C:\tools\cygwin\bin\bash .ci/after_success.sh - name: Upload coverage From a23131c22e18d51b25398334f8f1e7b07a90d8ac Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 7 Jan 2022 14:49:45 -0500 Subject: [PATCH 017/326] CI: Stop trying to run the netpbm tests. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f43460293b8..10b83fe880a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -19,7 +19,7 @@ jobs: libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libpng-devel - netpbm ImageMagick jpeg xorg-server-extra + ImageMagick jpeg xorg-server-extra xorg-server-common xinit subversion python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy From 4e7e70fd7982c6cf478b20bb1d11e9140283dccc Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 8 Jan 2022 10:08:18 -0500 Subject: [PATCH 018/326] CI: Install netpbm and dependencies on Cygwin CI. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 10b83fe880a..162522c0aa3 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -19,7 +19,7 @@ jobs: libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libpng-devel - ImageMagick jpeg xorg-server-extra + ImageMagick jpeg netpbm perl xorg-server-extra xorg-server-common xinit subversion python38-setuptools python38-devel python38-tkinter python38-wheel python38-pip python38-olefile python38-numpy From e1c27358ba72a2a9f797e2eec81f90d9676c484c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 14:44:16 +1100 Subject: [PATCH 019/326] Switched to windows-latest --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 162522c0aa3..ca18309be98 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -4,7 +4,7 @@ on: [push, pull_request, workflow_dispatch] jobs: build: - runs-on: windows-2019 + runs-on: windows-latest name: Cygwin Python 3.8 steps: From 629340654c72f67baa8bedd61d9e38141c0222df Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:31:20 +1100 Subject: [PATCH 020/326] Switched to cygwin/cygwin-install-action --- .ci/after_success.sh | 4 +- .github/workflows/test-cygwin.yml | 63 +++++++++++-------------------- 2 files changed, 23 insertions(+), 44 deletions(-) diff --git a/.ci/after_success.sh b/.ci/after_success.sh index 53832c573dd..23a6fcd4d45 100755 --- a/.ci/after_success.sh +++ b/.ci/after_success.sh @@ -3,7 +3,7 @@ # gather the coverage data python3 -m pip install codecov if [[ $MATRIX_DOCKER ]]; then - coverage xml --ignore-errors + python3 -m coverage xml --ignore-errors else - coverage xml + python3 -m coverage xml fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index ca18309be98..022ef480848 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -8,73 +8,52 @@ jobs: name: Cygwin Python 3.8 steps: + - name: Fix line endings + run: | + git config --global core.autocrlf input + - name: Checkout Pillow uses: actions/checkout@v2 - - name: Set up Cygwin - uses: egor-tensin/setup-cygwin@v3 + - name: Install Cygwin + uses: cygwin/cygwin-install-action@v1 with: - platform: x64 + platform: x86_64 packages: > - libfreetype-devel libimagequant-devel libjpeg-devel - liblcms2-devel libopenjp2-devel libraqm-devel - libtiff-devel libwebp-devel libxcb-devel libpng-devel - ImageMagick jpeg netpbm perl xorg-server-extra - xorg-server-common xinit subversion python38-setuptools - python38-devel python38-tkinter python38-wheel - python38-pip python38-olefile python38-numpy - python38-pytest python38-sphinx python38-packaging - python38-cffi python38-requests - install-dir: 'C:\tools\cygwin' - - - name: Clean up path - uses: egor-tensin/cleanup-path@v2 - with: - dirs: 'C:\tools\cygwin\bin;C:\tools\cygwin\lib\lapack' + ImageMagick python38-cffi python38-numpy python38-sip + python38-devel python38-tkinter ghostscript libfreetype-devel + libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel + libraqm-devel libtiff-devel libwebp-devel libxcb-devel + libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information - run: C:\tools\cygwin\bin\python3.8 .github/workflows/system-info.py + run: | + bash.exe -c "python3 .github/workflows/system-info.py" - name: Ensure Python dependencies are installed run: | - C:\tools\cygwin\bin\python3.8 -m pip install pip wheel setuptools - C:\tools\cygwin\bin\python3.8 -m pip install -r requirements.txt - C:\tools\cygwin\bin\python3.8 -m pip install pytest-forked + bash.exe -c "python3 -m pip install pip wheel setuptools" + bash.exe -c "python3 -m pip install -r requirements.txt" - name: Download extra test images run: | - C:\tools\cygwin\bin\dash -c "tr -d '\r' install_extra_test_images.sh" - C:\tools\cygwin\bin\dash -c "mv install_extra_test_images.sh depends/install_extra_test_images.sh" - C:\tools\cygwin\bin\dash -c "cd depends; dash install_extra_test_images.sh" + bash.exe -c "cd depends; dash install_extra_test_images.sh" - name: Build Pillow run: | - C:\tools\cygwin\bin\python3.8 setup.py bdist_wheel + bash.exe -c "python3 setup.py bdist_wheel" - name: Install Pillow run: | - C:\tools\cygwin\bin\dash -c '/usr/bin/python3.8 -m pip install dist/*.whl' - - - name: Check Pillow configuration - run: | - C:\tools\cygwin\bin\python3.8 selftest.py + bash.exe -c "python3 -m pip install dist/*.whl" - name: Test Pillow run: | - C:\tools\cygwin\bin\mv .ci/test.sh .ci/test.sh.dos - C:\tools\cygwin\bin\dash -c "/bin/tr -d '\r' <.ci/test.sh.dos >.ci/test.sh" - C:\tools\cygwin\bin\chmod u+x .ci/test.sh - C:\tools\cygwin\bin\dash /usr/bin/xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh .8 --ignore=Tests/test_numpy.py - - - name: Test the possibly-segfaulting NumPy tests separately - run: | - C:\tools\cygwin\bin\python3.8 -m pytest --forked Tests/test_numpy.py -k roundtrip + bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh - name: After success run: | - C:\tools\cygwin\bin\mv .ci/after_success.sh .ci/after_success.sh.dos - C:\tools\cygwin\bin\dash -c "tr -d '\r' <.ci/after_success.sh.dos >.ci/after_success.sh" - C:\tools\cygwin\bin\bash .ci/after_success.sh + bash.exe .ci/after_success.sh - name: Upload coverage uses: codecov/codecov-action@v2 From 728bc7d7789aeba50a6720282bf9b90da5389693 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:05:54 +1100 Subject: [PATCH 021/326] Added matrix to test Python 3.7 and 32-bit --- .github/workflows/test-cygwin.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 022ef480848..77d55682ffe 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -5,7 +5,15 @@ on: [push, pull_request, workflow_dispatch] jobs: build: runs-on: windows-latest - name: Cygwin Python 3.8 + strategy: + fail-fast: false + matrix: + python-minor-version: [7, 8] + architecture: ["x86", "x86_64"] + + timeout-minutes: 30 + + name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} steps: - name: Fix line endings @@ -18,12 +26,15 @@ jobs: - name: Install Cygwin uses: cygwin/cygwin-install-action@v1 with: - platform: x86_64 + platform: ${{ matrix.architecture }} packages: > - ImageMagick python38-cffi python38-numpy python38-sip - python38-devel python38-tkinter ghostscript libfreetype-devel - libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel - libraqm-devel libtiff-devel libwebp-devel libxcb-devel + ImageMagick python3${{ matrix.python-minor-version }}-cffi + python3${{ matrix.python-minor-version }}-devel + python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-sip + python3${{ matrix.python-minor-version }}-tkinter ghostscript + libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel + libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information @@ -60,7 +71,7 @@ jobs: with: file: ./coverage.xml flags: GHA_Cygwin - name: ${{ runner.os }} Cygwin Python 3.8 ${{ matrix.architecture }} + name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} - name: After failure if: failure() From 4be3b760f2de24770823f1f9d592618b197c116e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:26:38 +1100 Subject: [PATCH 022/326] Connected Cygwin jobs to mergify --- .github/mergify.yml | 1 + .github/workflows/test-cygwin.yml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.github/mergify.yml b/.github/mergify.yml index 8b289bda671..8dfa07f4ec5 100644 --- a/.github/mergify.yml +++ b/.github/mergify.yml @@ -8,6 +8,7 @@ pull_request_rules: - status-success=Docker Test Successful - status-success=Windows Test Successful - status-success=MinGW Test Successful + - status-success=Cygwin Test Successful - status-success=continuous-integration/appveyor/pr actions: merge: diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 77d55682ffe..b78e1b231f0 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -79,3 +79,11 @@ jobs: C:\tools\cygwin\bin\uname -a C:\tools\cygwin\bin\python3.8 -m pip list C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow + + success: + needs: build + runs-on: ubuntu-latest + name: Cygwin Test Successful + steps: + - name: Success + run: echo Cygwin Test Successful From 8d2c56ec1f85bec979bad2178309c8c9491bc1c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:09:49 +1100 Subject: [PATCH 023/326] Upload errors on failure --- .github/workflows/test-cygwin.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b78e1b231f0..f4fbfd1f869 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -62,6 +62,18 @@ jobs: run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh + - name: Prepare to upload errors + if: failure() + run: | + mkdir -p Tests/errors + + - name: Upload errors + uses: actions/upload-artifact@v2 + if: failure() + with: + name: errors + path: Tests/errors + - name: After success run: | bash.exe .ci/after_success.sh @@ -73,13 +85,6 @@ jobs: flags: GHA_Cygwin name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} - - name: After failure - if: failure() - run: | - C:\tools\cygwin\bin\uname -a - C:\tools\cygwin\bin\python3.8 -m pip list - C:\tools\cygwin\bin\python3.8 -m pip show --files Pillow - success: needs: build runs-on: ubuntu-latest From 05637393a62a79ce2e58e7458667cb8e8845a45a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 2 Feb 2022 15:14:31 +1100 Subject: [PATCH 024/326] Install Pillow directly --- .ci/build_cygwin.sh | 8 +++++ .ci/install.sh | 55 ++++++++++++++++++------------- .github/workflows/test-cygwin.yml | 19 +++-------- 3 files changed, 45 insertions(+), 37 deletions(-) create mode 100644 .ci/build_cygwin.sh diff --git a/.ci/build_cygwin.sh b/.ci/build_cygwin.sh new file mode 100644 index 00000000000..d621a53364f --- /dev/null +++ b/.ci/build_cygwin.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +python3 -m coverage erase +make clean +CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . +python3 selftest.py diff --git a/.ci/install.sh b/.ci/install.sh index efc57a6414a..ec427ff27d4 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -13,17 +13,20 @@ aptget_update() return 1 fi } -aptget_update || aptget_update retry || aptget_update retry +if [[ $(uname) != CYGWIN* ]]; then + aptget_update || aptget_update retry || aptget_update retry +fi set -e -sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ - ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ - cmake meson imagemagick libharfbuzz-dev libfribidi-dev +if [[ $(uname) != CYGWIN* ]]; then + sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ + ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ + cmake meson imagemagick libharfbuzz-dev libfribidi-dev + python3 -m pip install --upgrade pip +fi -python3 -m pip install --upgrade pip python3 -m pip install --upgrade wheel -PYTHONOPTIMIZE=0 python3 -m pip install cffi python3 -m pip install coverage python3 -m pip install defusedxml python3 -m pip install olefile @@ -32,24 +35,30 @@ python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma python3 -m pip install test-image-results -python3 -m pip install numpy - -# PyQt5 doesn't support PyPy3 -if [[ $GHA_PYTHON_VERSION == 3.* ]]; then - # arm64, ppc64le, s390x CPUs: - # "ERROR: Could not find a version that satisfies the requirement pyqt5" - sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools - python3 -m pip install pyqt5 -fi -# webp -pushd depends && ./install_webp.sh && popd +if [[ $(uname) != CYGWIN* ]]; then + PYTHONOPTIMIZE=0 python3 -m pip install cffi + python3 -m pip install numpy -# libimagequant -pushd depends && ./install_imagequant.sh && popd + # PyQt5 doesn't support PyPy3 + if [[ $GHA_PYTHON_VERSION == 3.* ]]; then + # arm64, ppc64le, s390x CPUs: + # "ERROR: Could not find a version that satisfies the requirement pyqt5" + sudo apt-get -qq install libxcb-xinerama0 pyqt5-dev-tools + python3 -m pip install pyqt5 + fi -# raqm -pushd depends && ./install_raqm.sh && popd + # webp + pushd depends && ./install_webp.sh && popd -# extra test images -pushd depends && ./install_extra_test_images.sh && popd + # libimagequant + pushd depends && ./install_imagequant.sh && popd + + # raqm + pushd depends && ./install_raqm.sh && popd + + # extra test images + pushd depends && ./install_extra_test_images.sh && popd +else + cd depends && ./install_extra_test_images.sh && cd .. +fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f4fbfd1f869..6e2dc5b596d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -41,24 +41,15 @@ jobs: run: | bash.exe -c "python3 .github/workflows/system-info.py" - - name: Ensure Python dependencies are installed + - name: Install dependencies run: | - bash.exe -c "python3 -m pip install pip wheel setuptools" - bash.exe -c "python3 -m pip install -r requirements.txt" + bash.exe .ci/install.sh - - name: Download extra test images + - name: Build run: | - bash.exe -c "cd depends; dash install_extra_test_images.sh" + bash.exe .ci/build_cygwin.sh - - name: Build Pillow - run: | - bash.exe -c "python3 setup.py bdist_wheel" - - - name: Install Pillow - run: | - bash.exe -c "python3 -m pip install dist/*.whl" - - - name: Test Pillow + - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From 6fded1ac97a5d2a588a7b60e49869525d04eb6f3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 14:07:51 +1100 Subject: [PATCH 025/326] Install cjpeg and djpeg --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 6e2dc5b596d..2dcdecd3009 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -28,7 +28,7 @@ jobs: with: platform: ${{ matrix.architecture }} packages: > - ImageMagick python3${{ matrix.python-minor-version }}-cffi + ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy python3${{ matrix.python-minor-version }}-sip From 131212368d4f57f23b6c3a4520b3ca9545a57282 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 3 Feb 2022 15:55:44 +1100 Subject: [PATCH 026/326] Install netpbm --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 2dcdecd3009..e169a31419e 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -35,7 +35,7 @@ jobs: python3${{ matrix.python-minor-version }}-tkinter ghostscript libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel - libxcb-xinerama0 qt5-devel-tools xorg-server-extra zlib-devel + libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel - name: Build system information run: | From 2d6dee1dae7c6f4ce639e1042c05152ef5d14c9e Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 6 Feb 2022 07:34:15 -0500 Subject: [PATCH 027/326] CI: Try to get Cygwin workflow working. --- .github/workflows/test-cygwin.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index e169a31419e..3b133254206 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -11,7 +11,7 @@ jobs: python-minor-version: [7, 8] architecture: ["x86", "x86_64"] - timeout-minutes: 30 + timeout-minutes: 40 name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} @@ -31,11 +31,18 @@ jobs: ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy + python3${{ matrix.python-minor-version }}-cython liblapack-devel gcc-g++ python3${{ matrix.python-minor-version }}-sip python3${{ matrix.python-minor-version }}-tkinter ghostscript libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel + subversion make + + - name: Add Lapack to PATH + uses: egor-tensin/cleanup-path@v1 + with: + dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' - name: Build system information run: | @@ -45,10 +52,22 @@ jobs: run: | bash.exe .ci/install.sh + - name: Install a different NumPy + run: | + bash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*'" + + - name: Check imports + run: | + bash.exe -c "python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)'" + - name: Build run: | bash.exe .ci/build_cygwin.sh + - name: Rebase dlls + run: | + bash.exe -c '/usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe)' + - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh @@ -56,7 +75,7 @@ jobs: - name: Prepare to upload errors if: failure() run: | - mkdir -p Tests/errors + dash.exe -c "mkdir -p Tests/errors" - name: Upload errors uses: actions/upload-artifact@v2 From 5588f572de4cd386f576fc761e50d373cf1ca239 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sun, 6 Feb 2022 12:03:36 -0500 Subject: [PATCH 028/326] BUG: Only set title in ImageShow.DisplayViewer when title provided --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index f8829fc21e0..d76c2609016 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -269,7 +269,7 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") args = ["display"] - if "title" in options: + if "title" in options and options["title"] is not None: args += ["-name", options["title"]] args.append(path) From 4d0e294eb0b5ede4a4c92293aa4ef2794c3b6025 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 7 Feb 2022 17:00:37 -0500 Subject: [PATCH 029/326] CI: Revert changes to test.sh Some early runs needed to explicitly specify the minor version to avoid calling python3.9 which had none of the dependencies installed. That problem should be fixed. --- .ci/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/test.sh b/.ci/test.sh index b0f019d6607..8ff7c5f6483 100755 --- a/.ci/test.sh +++ b/.ci/test.sh @@ -2,6 +2,6 @@ set -e -python3$1 -c "from PIL import Image" +python3 -c "from PIL import Image" -python3$1 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE +python3 -bb -m pytest -v -x -W always --cov PIL --cov Tests --cov-report term Tests $REVERSE From 1ed05715d234d3b5e1e361dab3d11bb1395d159f Mon Sep 17 00:00:00 2001 From: Piolie Date: Sat, 9 Jan 2021 01:33:29 -0300 Subject: [PATCH 030/326] Expand preamble and `_open` function --- src/PIL/PpmImagePlugin.py | 48 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 9d32927d4ca..c94b27a4773 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -23,21 +23,25 @@ b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" MODES = { - # standard - b"P4": "1", - b"P5": "L", - b"P6": "RGB", + # standard, plain + b"P1": ("plain", "1"), + b"P2": ("plain", "L"), + b"P3": ("plain", "RGB"), + # standard, raw + b"P4": ("raw", "1"), + b"P5": ("raw", "L"), + b"P6": ("raw", "RGB"), # extensions - b"P0CMYK": "CMYK", + b"P0CMYK": ("raw", "CMYK"), # PIL extensions (for test purposes only) - b"PyP": "P", - b"PyRGBA": "RGBA", - b"PyCMYK": "CMYK", + b"PyP": ("raw", "P"), + b"PyRGBA": ("raw", "RGBA"), + b"PyCMYK": ("raw", "CMYK"), } def _accept(prefix): - return prefix[0:1] == b"P" and prefix[1] in b"0456y" + return prefix[0:1] == b"P" and prefix[1] in b"0123456y" ## @@ -86,22 +90,19 @@ def _read_token(self): def _open(self): magic_number = self._read_magic() try: - mode = MODES[magic_number] + decoder, mode = MODES[magic_number] except KeyError: raise SyntaxError("not a PPM file") self.custom_mimetype = { + b"P1": "image/x-portable-bitmap", + b"P2": "image/x-portable-graymap", + b"P3": "image/x-portable-pixmap", b"P4": "image/x-portable-bitmap", b"P5": "image/x-portable-graymap", b"P6": "image/x-portable-pixmap", }.get(magic_number) - if mode == "1": - self.mode = "1" - rawmode = "1;I" - else: - self.mode = rawmode = mode - for ix in range(3): token = int(self._read_token()) if ix == 0: # token is the x size @@ -109,12 +110,16 @@ def _open(self): elif ix == 1: # token is the y size ysize = token if mode == "1": + self.mode = "1" + rawmode = "1;I" break + else: + self.mode = rawmode = mode elif ix == 2: # token is maxval maxval = token if maxval > 255: if not mode == "L": - raise ValueError(f"Too many colors for band: {token}") + raise ValueError(f"Too many colors for band: {maxval}") if maxval < 2 ** 16: self.mode = "I" rawmode = "I;16B" @@ -123,7 +128,14 @@ def _open(self): rawmode = "I;32B" self._size = xsize, ysize - self.tile = [("raw", (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1))] + self.tile = [ + ( + decoder, # decoder + (0, 0, xsize, ysize), # region: whole image + self.fp.tell(), # offset to image data + (rawmode, 0, 1), # parameters for decoder + ) + ] # From ea7e108ca3c6fcd00014de370075ed0361a08138 Mon Sep 17 00:00:00 2001 From: Piolie Date: Sat, 16 Jan 2021 16:52:40 -0300 Subject: [PATCH 031/326] Implement bitonal decoder --- src/PIL/PpmImagePlugin.py | 104 +++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index c94b27a4773..6bebec6de01 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -142,6 +142,108 @@ def _open(self): # -------------------------------------------------------------------- +class PpmPlainDecoder(ImageFile.PyDecoder): + _pulls_fd = True + + def _read_block(self, block_size=10 ** 6): + return bytearray(self.fd.read(block_size)) + # return self.fd.read(block_size) + + def _find_comment_end(self, block, start=0): + a = block.find(b"\n", start) + b = block.find(b"\r", start) + return min(a, b) if a * b > 0 else max(a, b) # lowest nonnegative index (or -1) + + def _ignore_comments(self, block): + """ + Deletes comments from block. If comment does not end in this + block, raises a flag. + """ + + comment_spans = False + while True: + comment_start = block.find(b"#") # look for next comment + if comment_start == -1: # no comment found + break + comment_end = self._find_comment_end(block, comment_start) + if comment_end != -1: # comment ends in this block + block = ( + block[:comment_start] + block[comment_end + 1 :] + ) # delete comment + else: # last comment continues to next block(s) + block = block[:comment_start] + comment_spans = True + break + return block, comment_spans + + def _decode_bitonal(self): + """ + The reason this is a separate method is that in the plain PBM + format all data tokens are exactly one byte, and so the + inter-token whitespace is optional. + """ + decoded_data = bytearray() + total_tokens = self.size + + comment_spans = False + tokens_read = 0 + while True: + block = self._read_block() # read next block + if not block: + raise ValueError("Reached EOF while reading data") + + while block and comment_spans: + comment_end = self._find_comment_end(block) + if comment_end != -1: # comment ends in this block + comment_spans = False + block = block[comment_end + 1 :] # delete tail of previous comment + else: # comment spans whole block + block = self._read_block() + + block, comment_spans = self._ignore_comments(block) + + tokens = b"".join(block.split()) + + for token in tokens: + if token in (48, 49): + tokens_read += 1 + else: + raise ValueError(f"Invalid token for this mode: {bytes([token])}") + + decoded_data.append(token) + if tokens_read == total_tokens: # finished! + invert = bytes.maketrans(b"01", b"\xFF\x00") + decoded_data = decoded_data.translate(invert) + return decoded_data + + def _decode_blocks(self, channels=1, depth=8): + raise NotImplementedError + + def decode(self, buffer): + self.size = self.state.xsize * self.state.ysize + rawmode = self.args[0] + + if self.mode == "1": + decoded_data = self._decode_bitonal() + rawmode = "1;8" + elif self.mode == "L": + decoded_data = self._decode_blocks(channels=1, depth=8) + elif self.mode == "I": + if rawmode == "I;16B": + decoded_data = self._decode_blocks(channels=1, depth=16) + elif rawmode == "I;32B": + decoded_data = self._decode_blocks(channels=1, depth=32) + elif self.mode == "RGB": + decoded_data = self._decode_blocks(channels=3, depth=8) + + self.set_as_raw(bytes(decoded_data), rawmode) + return -1, 0 + + +# +# -------------------------------------------------------------------- + + def _save(im, fp, filename): if im.mode == "1": rawmode, head = "1;I", b"P4" @@ -177,7 +279,7 @@ def _save(im, fp, filename): # # -------------------------------------------------------------------- - +Image.register_decoder("plain", PpmPlainDecoder) Image.register_open(PpmImageFile.format, PpmImageFile, _accept) Image.register_save(PpmImageFile.format, _save) From 652f447412b50e08bfb17e217783560c04a4de5c Mon Sep 17 00:00:00 2001 From: Piolie Date: Sat, 16 Jan 2021 16:53:24 -0300 Subject: [PATCH 032/326] Implement grayscale/color decoder --- src/PIL/PpmImagePlugin.py | 63 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 6bebec6de01..86616b89f16 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -217,7 +217,68 @@ def _decode_bitonal(self): return decoded_data def _decode_blocks(self, channels=1, depth=8): - raise NotImplementedError + decoded_data = bytearray() + if depth == 32: + maxval = 2 ** 31 - 1 # HACK: 32-bit grayscale uses signed int + else: + maxval = 2 ** depth - 1 # FIXME: should be passed by _open + max_len = 10 + bytes_per_sample = depth // 8 + total_tokens = self.size * channels + + token_spans = False + comment_spans = False + half_token = False + tokens_read = 0 + while True: + block = self._read_block() # read next block + if not block: + if token_spans: + block = bytearray(b" ") # flush half_token + else: + raise ValueError("Reached EOF while reading data") + + while block and comment_spans: + comment_end = self._find_comment_end(block) + if comment_end != -1: # comment ends in this block + block = block[comment_end + 1 :] # delete tail of previous comment + break + else: # comment spans whole block + block = self._read_block() + + block, comment_spans = self._ignore_comments(block) + + if token_spans: + block = half_token + block # stitch half_token to new block + token_spans = False + + tokens = block.split() + + if block and not block[-1:].isspace(): # block might split token + token_spans = True + half_token = tokens.pop() # save half token for later + if len(half_token) > max_len: # prevent buildup of half_token + raise ValueError( + f"Token too long found in data: {half_token[:max_len + 1]}" + ) + + for token in tokens: + if len(token) > max_len: + raise ValueError( + f"Token too long found in data: {token[:max_len + 1]}" + ) + try: + token = int(token) + except ValueError: + raise ValueError( + f"Non-decimal-ASCII found in data: {token}" + ) from None + tokens_read += 1 + if token > maxval: + raise ValueError(f"Channel value too large for this mode: {token}") + decoded_data += token.to_bytes(bytes_per_sample, "big") + if tokens_read == total_tokens: # finished! + return decoded_data def decode(self, buffer): self.size = self.state.xsize * self.state.ysize From c1744e8536d64bec7b983c0f926754f9e6bc861e Mon Sep 17 00:00:00 2001 From: Piolie Date: Sun, 31 Jan 2021 20:47:02 -0300 Subject: [PATCH 033/326] Add tests for plain PPM --- Tests/images/hopper_16bit.pgm | Bin 0 -> 32785 bytes Tests/images/hopper_16bit_plain.pgm | 4 + Tests/images/hopper_1bit.pbm | Bin 0 -> 2059 bytes Tests/images/hopper_1bit_plain.pbm | 14 +++ Tests/images/hopper_32bit.pgm | Bin 0 -> 65558 bytes Tests/images/hopper_32bit_plain.pgm | 4 + Tests/images/hopper_8bit.pgm | Bin 0 -> 16399 bytes Tests/images/hopper_8bit.ppm | Bin 0 -> 49167 bytes Tests/images/hopper_8bit_plain.pgm | 4 + Tests/images/hopper_8bit_plain.ppm | 4 + Tests/test_file_ppm.py | 152 ++++++++++++++++++++++++++-- 11 files changed, 172 insertions(+), 10 deletions(-) create mode 100644 Tests/images/hopper_16bit.pgm create mode 100644 Tests/images/hopper_16bit_plain.pgm create mode 100644 Tests/images/hopper_1bit.pbm create mode 100644 Tests/images/hopper_1bit_plain.pbm create mode 100644 Tests/images/hopper_32bit.pgm create mode 100644 Tests/images/hopper_32bit_plain.pgm create mode 100644 Tests/images/hopper_8bit.pgm create mode 100644 Tests/images/hopper_8bit.ppm create mode 100644 Tests/images/hopper_8bit_plain.pgm create mode 100644 Tests/images/hopper_8bit_plain.ppm diff --git a/Tests/images/hopper_16bit.pgm b/Tests/images/hopper_16bit.pgm new file mode 100644 index 0000000000000000000000000000000000000000..e482493dd40403fbfeeb1ca9b914891ea36914ec GIT binary patch literal 32785 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 Gs``JQ)gOca literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_16bit_plain.pgm b/Tests/images/hopper_16bit_plain.pgm new file mode 100644 index 00000000000..a48ab55441a --- /dev/null +++ b/Tests/images/hopper_16bit_plain.pgm @@ -0,0 +1,4 @@ +P2 +128 128 +65535 +6425 5654 3598 6682 7453 7453 4626 5654 8738 8995 6939 5911 7710 7710 6682 7710 9252 9509 8224 6168 5654 5911 6168 7967 8481 7710 5397 7967 5654 28270 58853 41634 46517 42405 47031 30069 5654 10023 9252 4626 13107 44461 18247 5140 9766 31097 52685 60395 63479 62194 61680 61423 60395 50629 11822 14906 26471 23387 23901 24158 25700 25186 26985 26214 25700 22873 25700 24158 23901 25700 25700 25186 24929 24415 24415 24415 24415 24672 25443 25957 25700 25443 24929 24929 25700 25700 25957 26214 25700 25700 25700 25443 25186 25443 25443 25186 25186 25186 25186 25443 25443 25443 25443 25443 25443 25443 25186 25186 25443 25700 25957 25957 25443 25186 25443 26214 27756 28784 29555 29555 29812 29812 29812 29555 28784 28527 28527 28784 6425 5654 4112 6939 7196 7196 4369 5140 8224 8481 6425 5397 7453 7453 6939 8224 8738 9509 7967 5654 5397 5397 5911 7453 6425 8224 6682 6939 7710 33667 45489 23644 44718 15163 12336 22616 16191 7967 5140 6939 14392 43176 18761 7453 4369 7967 20560 37265 51657 61937 62451 61937 60652 52685 10794 9766 23644 24415 25443 21331 18761 20817 23644 25186 25186 25957 25186 25186 26471 24672 23644 25443 25443 24415 24415 26214 26471 24929 24415 24929 25700 25443 25186 25443 25957 25957 26471 26214 26214 25957 26214 25700 25443 25443 25443 25186 25700 25186 25186 25186 25186 25443 25443 25700 25700 25700 25443 25443 25186 25186 25186 25443 26214 25957 25700 25957 27242 28013 29298 29555 30069 30069 30326 29812 29298 29041 29041 29298 6425 6168 4369 7196 7196 6939 3855 4883 7967 8224 5911 5140 7196 7453 6682 8224 7967 8995 7967 5911 5654 5911 6168 7453 8995 7453 5654 7196 8738 30583 25186 15934 43433 17219 7453 7710 6168 8481 11565 5397 18504 44461 14649 4626 7196 8995 6168 10023 13364 51657 60652 61680 60909 57568 35466 7453 9252 10537 9509 7453 13364 9766 10537 13364 11565 16448 21331 26214 25700 29041 24929 27756 25443 25443 25186 25186 24929 24929 25957 27242 25957 25700 25700 25957 26214 26214 26214 26214 26985 26471 26471 26214 26214 25700 25443 25443 25957 25700 25186 25186 25186 25443 25700 25957 25700 25443 25443 25443 25443 25186 25186 25186 26471 26214 25957 25957 26471 27499 28784 30069 30069 30326 30326 30069 29555 29298 29555 29812 5911 5654 4626 7453 7453 6682 3598 4883 8224 8738 5911 5397 7196 7710 6682 8481 7967 9509 7967 5911 5654 6168 6425 7196 9766 6939 5397 7453 7453 17990 10537 20817 46003 15420 6168 6425 7710 6939 9252 5397 14392 45232 15934 5654 5654 7710 7967 8481 7967 48830 63222 37265 32896 44204 14649 6682 10023 7453 6682 20303 32382 31354 19018 13621 12079 12079 12079 10537 20560 24672 27499 23901 25186 25700 25186 24929 26471 28013 26471 23644 26214 26214 26214 26471 26728 26728 26471 25957 26728 26471 26471 26214 26214 25957 25957 25957 25957 25700 25700 25443 25443 25700 25957 25957 25186 25443 25443 25443 25700 25700 25443 25443 25700 25957 25957 25700 25957 26985 28527 29812 29298 29555 29555 29298 29298 29298 29555 29812 5397 5654 4883 7196 7196 6425 3855 5397 8481 8738 6425 5397 7196 7710 6939 8224 7967 9252 8481 5911 5654 6168 6425 6939 5911 7967 7196 5911 5654 9252 5397 21845 42662 12336 7453 6168 9252 9252 7967 4883 22616 43433 8481 6682 7453 6168 8995 8224 11565 52685 51914 12079 6939 4626 8224 7967 10280 39064 35723 18761 34952 61166 29041 8224 31354 43176 20303 12079 10794 13621 24672 28527 25957 26471 26985 26728 25957 25957 26728 27499 26471 26471 26728 26728 27242 26985 26471 26214 26214 26214 26471 26471 26214 26214 26214 26214 26214 26214 26214 26214 26214 26214 26214 25957 25957 25957 25957 25957 25957 25957 25957 25700 25186 25700 26214 25957 25957 26471 27499 28527 28013 28270 28527 28527 28527 28527 28784 29041 5397 5654 5140 7710 7196 6425 3855 5654 8481 8738 6425 5397 7453 7453 6425 7710 7710 9252 8224 5397 4883 5654 5911 6425 6682 8738 6682 5397 6682 6425 5654 21331 45232 13107 8738 7710 8738 6939 5654 8738 23130 44975 10280 7710 5911 6425 7967 5140 12079 55512 21845 11051 5140 5397 6682 22616 60138 64250 64764 53970 35723 65021 43690 40863 63993 65021 62451 43690 10537 12079 13878 24415 27499 24672 24415 27756 28270 25957 24929 26471 27242 26728 26985 27242 27242 27242 26728 26214 25700 25700 25957 25957 26471 26214 26471 26471 26214 26471 26471 26728 26471 26471 26214 25957 26728 26471 26214 25957 25700 25700 25700 25700 25700 25957 25957 25957 25957 26214 26471 26985 27242 27242 27499 27756 27756 28013 28270 28527 5140 6168 5654 8224 7196 6168 3855 5397 7710 8224 6168 5654 7710 7967 6682 7967 7710 9252 7967 5397 4626 5654 5654 6168 8995 7967 5397 5911 6425 6168 6682 21588 42919 13878 6425 6425 9766 9509 7453 4883 21588 43433 10280 5140 5140 9509 7967 10280 11822 47288 42148 38036 7196 5911 31868 63736 57054 40349 59881 62708 65278 63993 62194 65021 63222 53713 52685 59110 49858 12850 11051 34952 49601 39578 30069 26471 26728 26985 27242 27499 27242 26985 26985 26985 26985 26985 26728 26471 25700 25957 25957 25957 26471 26214 26214 26214 26728 26728 26728 26471 26471 26471 26471 26471 26985 26471 26214 25700 25443 25443 25700 25957 26214 25957 25700 25957 26214 26214 26214 25957 26471 26985 27499 27756 27756 27756 28013 28270 5654 6425 5654 8481 7453 6168 3341 5140 6939 7710 6168 5397 7710 7967 6682 7967 8224 10023 8481 5140 4626 5397 5911 6168 6168 6939 6425 6682 4626 7196 6939 19789 43690 15420 5397 7967 9509 6682 7710 7196 28013 40349 7710 5911 7967 7967 7453 27756 55512 64507 65278 34695 6168 16962 31354 22102 31097 24415 19018 26214 42662 57825 57054 42148 25957 22616 33410 31611 18761 25957 8995 13107 60395 64764 63222 51657 36751 27756 26471 27499 27499 27242 27242 26728 26985 26985 26985 26471 25957 25957 25957 25957 26214 26214 26214 26214 27242 26985 26471 26214 26214 26471 26728 26728 26471 26214 25700 25186 25186 25443 25957 26214 26214 25957 25443 25700 26214 26728 26728 26728 26728 27242 27499 27756 28013 28013 28270 28270 5911 6939 29555 10280 6168 5654 4112 4369 7196 8995 5654 4626 10794 8738 5911 7453 6939 10023 6682 6168 4626 5654 4369 5397 7453 7196 7453 5140 4883 6425 7710 20303 44204 13621 6682 8224 8738 8995 7710 6682 25186 41634 7967 8995 6425 11308 49858 63479 65535 64250 65278 26985 7710 7196 7453 34952 48830 62194 59624 36751 59881 50886 54484 51400 37779 62965 55769 54484 21331 6682 12079 9509 47288 65278 63736 64764 64507 53713 32639 27242 28270 27756 25700 29298 24672 28784 27499 26214 26471 26471 26728 26471 26728 26471 26471 26728 26985 26471 26214 26471 26471 26214 26471 26985 25957 25957 25700 25443 25443 25700 26214 26728 25700 26214 26728 26471 26214 25957 26214 26471 26471 26471 26471 26471 26728 26985 27499 27499 5654 9509 52428 23130 9252 3855 5397 4369 9509 6682 3341 5140 10280 5654 7453 7710 9252 8738 6682 6682 5397 4112 4626 6939 7196 6939 6168 6168 7967 6939 7196 19018 43690 13621 6425 8481 8995 8738 7196 6168 25700 41634 8224 3598 20046 58339 63736 65021 62965 65021 63993 22102 6682 8481 8481 30840 47802 56026 57311 62965 64250 62965 64507 64250 63993 58596 58339 54484 22359 13364 7196 8481 37779 65021 65021 62194 65021 64764 60652 46774 29555 25700 27242 28270 27756 27242 25186 28013 26728 26471 26471 26471 26471 26728 26471 26728 26471 26214 25957 26471 26471 26214 26471 26985 26214 26214 25957 25957 25700 25957 26471 26728 25957 25957 26214 26214 25957 25957 25957 25957 26471 26471 26471 26471 26728 26985 27499 27499 4112 8995 54741 57311 11308 6425 4112 8995 6425 7710 6168 6682 6939 11051 6168 6682 6682 7196 8481 5654 3855 5911 7710 4369 8481 8224 6168 5397 7196 6425 7967 19018 43690 14392 6939 8481 8995 8224 6939 5654 29041 41634 7967 23130 64250 65278 63993 63736 63736 62194 56797 16191 10280 6425 7967 13878 54741 52428 54741 58082 56283 56026 53456 56026 59624 56540 46003 55769 18247 10023 13364 12336 39321 65278 63993 65278 64764 65278 65278 63736 53970 32125 27242 28784 28270 24929 29812 25186 26214 26214 26214 26214 26214 26471 26214 26471 26214 25957 25957 26214 26214 26214 26471 26985 26471 26471 26471 26214 26214 26214 26728 26985 26728 26471 26471 26471 26471 26471 26471 26214 26728 26471 26728 26728 26985 27242 27499 27756 5140 10023 50886 61680 32896 20817 44204 23387 6939 7453 5397 5911 8481 32125 9766 6168 8738 8738 8738 6425 3084 4369 4369 5911 6939 7453 7453 5911 5140 6168 7967 18504 43690 15420 6939 8224 9509 8224 7196 6168 30583 38036 15163 62965 62194 64764 63222 57825 44461 44461 53199 15163 3084 5911 5654 9766 27756 55769 52171 63222 64250 64250 64507 62965 64250 63479 53456 42148 10794 5911 7453 6682 29812 60652 64764 65021 62451 65021 64764 65021 65021 56540 29555 26985 26985 28527 25957 26728 25700 25957 25700 25700 25700 25700 25700 25700 26214 25700 25700 26214 26471 26214 26214 26728 26728 26728 26728 26471 26471 26471 26728 26985 27242 26728 26471 26471 26728 26728 26471 25957 26728 26728 26985 26985 26985 27242 27499 27756 6682 7453 50115 64507 60652 58853 50115 18761 7967 6682 5397 6682 9252 46774 24158 8224 6168 8738 6682 6168 6168 7710 1799 5911 5397 6425 6425 7196 5654 6682 6425 16448 44204 16705 6425 7453 8995 7710 7453 6682 28013 40092 54998 63222 64764 61937 40606 35209 29298 15420 8481 6168 5397 8224 6682 9766 47288 56540 39321 54998 63736 65021 65278 65535 63736 61423 39835 55769 27242 10280 8481 10023 15163 32125 48830 55769 64507 64764 63222 64507 62451 64507 48830 26985 26471 26471 26985 26214 25700 25700 25700 25957 25700 25700 25700 25700 26214 25443 25700 25957 26214 25957 25957 26471 26214 26471 26471 26471 26214 25957 26214 26471 26728 26214 25957 25700 26214 26214 25957 25700 26728 26728 26728 26728 26985 27242 27756 28013 8995 34438 58339 61937 61166 60652 41634 9766 6939 7710 7196 6425 7967 50886 42148 6939 8738 9252 24415 26214 6168 3598 6939 4626 7710 14649 5140 6682 6425 6682 6168 16448 43690 17733 6168 6939 8995 7196 7453 6682 25700 50372 61166 63479 58853 31354 25700 12079 4112 5397 3598 5911 3855 4112 8224 6425 33667 42148 51914 58339 57825 64764 63222 64250 54741 53970 49087 51400 23644 8481 12079 7967 8995 6168 8481 25443 43433 56283 62965 64764 64764 65021 61680 32896 29298 25700 25443 28013 26214 26214 26214 26471 26214 26214 26214 26471 25957 25700 25700 26214 26214 25957 25957 26471 25957 26214 26471 26471 26214 25957 25957 25957 26728 26471 26214 25957 26214 26214 26214 26214 26471 26471 26728 26728 27242 27499 28270 28270 43947 59110 61937 64507 62451 59110 29555 4112 8738 8738 4626 5140 10537 54484 54741 21845 20560 48830 53456 20817 5654 6168 5140 4883 12593 35466 9766 5397 6168 5911 7453 16191 43176 18247 6168 6939 9252 6939 7453 6682 24929 56026 62451 60395 27242 13878 3598 4626 3341 3084 23901 28784 8224 6682 4369 10023 32125 61166 59624 53713 41634 58339 63993 63479 49344 54484 60395 60909 22359 10537 10023 8224 8995 26728 8738 10280 6939 20817 39578 49344 63736 61166 59624 53713 23901 29298 27499 26728 26471 26471 26471 26471 26471 26471 26471 26471 26214 25957 25957 26471 26471 25957 25957 26214 25957 26214 26471 26471 26214 25957 25700 25957 26985 26985 26728 26471 26471 26471 26728 26985 26471 26728 26471 26728 27242 27756 28527 28784 46003 57568 61937 63479 63736 63736 44204 15934 6939 8738 5911 9509 11822 51400 59110 57568 61680 59110 41377 7710 7710 5140 3084 5654 13878 54741 18247 6168 6939 6168 8738 13621 43176 18504 6168 7196 9766 7196 7710 6939 23130 58596 59881 34695 6682 3084 5140 27242 25186 11822 8481 38293 7196 16191 19789 9766 25186 48573 49601 49087 54227 39835 24672 27499 43433 48316 49601 52685 25443 11822 16191 8995 19275 35466 7196 25700 28527 6939 8738 19532 41891 61166 60909 57825 31611 26471 27242 27499 26471 26728 26471 26471 26471 26471 26728 26471 26471 25957 25957 26471 26471 25957 25957 26214 25957 26214 26471 26471 26214 25957 25700 25700 26471 26471 26728 26471 25957 25957 26471 26985 26214 26214 26471 26728 27242 27756 28527 28784 6939 12850 30840 63479 62708 52428 49344 23130 7710 9252 6425 19532 42662 60909 60138 59624 60652 55255 14906 8481 6425 7453 2056 5397 17990 58853 37008 6682 14649 30326 12336 14906 43947 20046 5911 5911 9252 8481 6425 7453 21588 47545 36237 6425 5911 6682 6939 44975 52428 49344 47031 37522 49601 57054 60138 54998 55255 31097 25186 9252 8224 12593 9766 9509 9766 12593 21074 45746 49858 59367 62194 44718 47288 35466 55512 56026 45232 13621 24929 14906 12079 42148 62708 57054 40863 27242 28013 26985 27242 25957 27499 25957 27756 26214 26471 26471 26985 26471 26214 26214 26471 26728 26471 26214 26471 26214 26214 25957 25957 25957 26214 26471 26728 26985 27242 27499 27242 27242 27499 27499 27756 27499 27499 27499 27756 28013 28527 28784 6682 6939 21588 62708 49087 16448 14649 10794 6939 13364 37779 56283 62965 60909 62451 61680 60138 27242 8224 7710 6939 2570 8738 4883 19275 58339 53456 36751 50115 32639 8224 10537 43176 20817 6425 5911 9252 8481 6425 6425 5654 1285 4112 4626 2570 6425 5654 11822 34952 38550 14135 44975 58339 46003 44975 57568 61423 60395 62451 49344 29298 20046 7967 13878 27242 50629 63222 60138 61166 58596 51400 60909 59367 35980 47545 51657 48573 57311 51914 11822 5911 11051 48573 47288 28013 13878 10794 24158 27242 26214 26985 25700 27499 26471 26214 26214 26985 26214 25957 25957 26214 26214 26214 25957 26471 26471 26214 26214 26214 26471 26728 26985 27242 27499 27756 27756 27499 27499 28013 28270 28013 28013 28013 28013 28270 28527 28784 29041 4112 9509 20303 60395 32639 7196 7196 5654 7710 10537 24415 45489 56797 63736 62451 61423 57825 43690 11308 8481 4112 4626 6168 7196 28270 60909 56540 46260 44461 13107 6939 12593 41891 22102 6425 5911 8738 8481 5654 4883 6425 4112 4112 4883 3855 4112 2570 5654 3341 8995 15420 27242 13364 8738 46260 50629 44204 37265 25700 19275 16191 20046 12850 19532 17990 20817 37008 40863 54741 55769 33667 19789 28527 28784 4626 9509 25700 49344 44461 40863 41891 12593 13878 7453 4626 3855 7196 12079 26728 26985 26985 25700 27756 26728 26985 26728 26985 26728 26214 26214 26471 26471 26214 26214 26728 26728 26471 26471 26728 26985 27242 27499 27242 27499 27756 28013 27756 27756 28013 28270 28013 28013 28270 28527 28527 28784 29041 29041 5140 8481 15934 53199 16705 7967 7453 3598 5397 9766 5140 9509 19018 50629 63479 63222 61937 59110 27756 7710 6939 12079 24158 35980 53456 60909 50372 46774 24158 5397 5911 9252 40863 23130 5654 6425 8481 6682 4883 3598 1799 4626 4626 3855 1799 3598 5654 4883 6682 3341 2827 6682 14392 7967 7967 8481 8224 8738 7710 5140 7710 6682 4883 9252 7453 6168 11565 8738 11822 12593 15677 17990 44461 46260 48316 16448 6425 6168 24415 35209 14392 8738 6425 4626 6682 2827 6168 3598 23644 27242 26985 25957 27242 26728 27242 27242 27499 27499 27242 27242 27242 26985 26985 26728 26985 26985 26985 26985 26985 27242 27242 27499 27242 27499 27756 27756 27756 27756 28013 28270 27756 28013 28270 28527 28784 28784 29041 29041 7453 8738 15420 38036 8224 7967 7196 4883 4883 6168 6425 4883 9766 50372 61166 29812 33410 50629 52428 14906 6682 12336 21845 38550 51657 56797 56283 45489 11308 6168 6682 9766 40092 23901 4626 6939 7710 4883 3341 3341 4369 3084 3598 5911 4883 3084 4369 771 5397 4112 3598 6939 4112 6682 6168 3341 5140 4626 6682 5654 9252 6168 7710 6168 7967 7453 5911 7196 9509 6168 6425 9509 25186 33410 56797 54227 20560 8481 5654 3855 3341 4369 3855 2313 4112 3598 1542 4626 19018 25957 25957 26214 26471 25957 26728 27499 27756 28013 28013 28013 28013 27756 27499 27499 27499 27499 27499 27242 27242 27242 27242 27242 27756 27756 28013 28013 27756 28013 28270 28527 28013 28270 28527 28784 29041 29041 29041 29041 3598 7967 11051 15420 6682 8738 6682 4626 7453 7453 7967 8995 6939 51400 48316 11308 7967 9766 23644 21845 5140 6168 5140 8224 33153 57311 57825 47802 28527 7196 6939 7453 38807 25700 4883 6682 6425 3598 3084 3341 2570 4626 2827 1799 5911 3084 3341 6168 2827 4626 3598 1799 4883 3855 3598 8481 8224 5654 4883 5140 3598 3855 5911 4112 5654 2827 5140 7196 4369 7710 6939 7196 10794 23644 12336 7196 4626 6168 2056 4369 3084 2056 3341 4626 2570 4369 2827 5397 15934 25957 26214 26985 26985 26471 26985 28270 28013 28270 28527 28527 28270 28013 27756 27756 28013 28013 27756 27756 27499 27499 27242 27242 28270 28270 28270 28270 28013 28013 28527 28784 28527 28784 28784 29041 29298 29555 29555 29555 6939 8738 7196 8224 12336 9252 5654 5654 6682 7967 2827 7967 10280 51914 26214 8995 8481 11308 9509 10280 6682 3341 4626 3598 25186 61166 49344 48830 50115 13107 3598 8224 37522 28527 5397 5140 4369 3084 4369 2827 4369 3341 4883 2827 3855 4112 4626 1028 3598 1799 4369 4369 514 5140 3855 1285 2827 3855 4883 6682 4369 5140 4883 4883 4369 5140 6168 5140 5654 7196 2570 4112 3084 2313 4626 2056 4112 2570 0 3598 3084 3084 3341 3855 1028 2056 4883 4112 15934 27756 26985 28013 27756 27499 28013 28270 28013 28270 28784 28784 28270 28013 28013 28270 28527 28527 28270 28013 28013 28013 27756 27756 28527 28527 28527 28270 28013 28013 28270 28527 28527 28527 28784 28784 29041 29298 29812 29812 5140 10537 6682 7453 10023 10280 7710 3855 4883 6682 6168 5654 11565 42405 8481 9509 7967 8995 8224 6939 5397 5397 3855 4626 32382 57311 20560 10023 37265 32382 6939 5911 36494 30326 6425 4626 2827 3084 5140 3084 3855 2313 4883 3084 4626 4112 3598 3855 3084 3341 1799 4112 4883 257 4369 2313 3341 2827 2827 2056 2827 3341 2313 3084 3855 2827 2313 3084 1799 1799 4112 2827 3084 4369 1285 2313 3084 1799 6168 2313 3598 1028 4112 2570 4626 4369 2570 3341 16448 28270 27242 28013 27756 28013 27499 27756 28270 28527 29041 29041 28527 28270 28270 28527 28527 28527 28270 28270 28270 28270 28270 28270 28270 28270 28270 28013 27499 27499 28013 28270 28527 28270 28527 28527 28784 29041 29555 29812 5911 8224 7196 8738 9509 10280 6939 5140 7196 6168 4626 7710 11308 14649 9252 6939 8995 8738 8738 7710 5654 3855 3855 4626 37779 42662 6425 3598 7453 13107 7196 5140 33410 30583 5654 3598 3341 4112 4112 3855 3598 4112 3341 3084 3598 3084 2827 3341 2056 3084 3341 3084 2827 3341 3084 1799 2570 3341 2827 2570 3598 3084 2313 2827 3598 3084 2827 2827 3084 3084 2827 2570 2570 3084 3084 2570 2570 3341 3084 2056 3341 2570 3084 3341 3341 4369 4369 2827 16448 28784 27756 28013 26728 29041 27756 29812 28784 28784 29041 29041 28527 28270 28270 28270 28270 28527 28784 28784 28784 28784 28527 28527 28270 28270 28013 28013 28270 28784 28784 29041 29041 28527 28013 28013 28527 28784 28527 28527 6168 8224 7196 8738 9252 10023 6425 4369 5654 6939 6168 8224 8224 10023 7196 8224 8224 8481 8738 7967 5911 4369 4112 4626 33924 16448 5654 6425 4112 4369 5140 6939 31868 31611 4883 3598 3084 4112 3855 3598 2313 4626 4112 2570 2570 3855 3855 4626 3855 3855 3341 2827 2313 2570 3598 4883 3084 3598 3341 3084 3598 3341 2313 2570 2313 2570 2570 2570 3084 3341 3598 3341 2313 3341 3598 3084 2313 2313 2570 2827 2056 1028 1799 2570 2056 2570 3855 4369 14392 28270 28527 29041 28013 29555 28013 29812 29041 29041 29298 29298 29041 28784 28784 28784 29041 29041 29298 29298 29041 29041 28784 28784 29041 28784 28527 28527 28527 28784 28784 29041 29041 28527 28013 28013 28270 28527 28527 28527 5911 8224 7453 8995 8995 9766 5911 4112 5397 6682 6425 7453 6168 6168 6168 9509 7967 8224 8738 8224 6168 4626 4112 4626 14906 5911 9252 3341 2570 5911 5654 5397 30583 33924 4112 4112 3084 3855 3598 3598 5397 4112 2313 2827 4883 4112 1542 1542 2827 2056 2570 4369 4112 2827 2313 3084 2827 2827 2827 2827 3084 3341 2827 3084 3598 3598 3598 3341 3084 2827 2827 2570 3598 3084 3084 3341 3598 3598 3341 3598 3598 2313 3084 4369 3598 2570 3084 3084 12336 28527 29041 29812 28527 29812 28270 29812 29298 29298 29555 29812 29555 29298 29298 29298 29555 29555 29812 29812 29555 29298 29298 29041 29555 29555 29298 29041 28784 29041 29041 29298 28784 28527 28270 28013 28270 28527 28527 28527 5911 8224 7196 8995 8995 9509 5654 3598 6682 6425 4626 6425 7453 6425 7453 9509 8481 8481 8738 7967 6168 4112 3855 4369 4369 8224 7196 4626 5397 6168 3598 6425 30069 36237 4112 4626 3084 3855 3341 3341 3341 3598 3084 2827 3084 2570 2827 4883 4626 3341 2313 2827 3341 3341 3598 3855 4369 3598 3855 4112 3598 3598 4112 4369 3855 4112 4112 4369 4883 4883 5397 5654 4626 3855 3341 3341 3084 2570 2570 3084 2827 1542 2056 3084 3084 3855 4112 3341 10794 29298 29298 29812 28270 29298 28784 30583 29812 29812 30069 30069 29812 29812 29812 29812 30069 30069 30069 30069 30069 29812 29812 29555 29812 29812 29555 29298 29298 29298 29298 29298 28784 28527 28527 28270 28527 28527 28784 29041 5911 7710 7196 8738 8738 9766 5654 3855 7196 6425 4112 6168 8738 6682 8481 9252 8995 8995 8481 7196 5397 3855 3598 4369 7196 8224 5140 7196 3084 3341 3855 6168 29812 35980 4112 4369 2827 3598 3084 3341 1285 3598 4369 3598 3855 4112 2313 2056 3341 5654 8224 9509 11051 13107 14135 14392 12850 10794 10280 9766 7453 6939 7967 7453 9766 10280 12079 13878 16448 18761 20560 21588 22616 20560 16962 12850 8224 4626 3341 3084 3341 3084 3084 3084 2056 3341 4369 3341 7967 29041 29555 29555 28784 29555 29298 30840 29812 30069 30326 30326 30069 29812 30069 30326 29812 29812 29812 29812 29812 29812 30069 30069 29812 29812 29555 29555 29555 29555 29555 29812 29041 29041 28784 28784 28784 28784 29298 29298 5911 7967 6939 8224 8481 9509 5911 3855 5911 6682 5140 6168 8738 5911 8738 8738 9252 8995 8481 7453 5140 3855 3598 4369 6682 6682 8481 4626 2313 5397 5397 4883 29812 33410 3855 3855 3084 3341 3598 3598 4112 2827 2056 3084 3598 2570 4626 9509 19532 25443 31611 34181 34695 34952 35209 34952 34181 31354 31097 30069 26214 24672 25443 24672 25957 25957 27756 30069 32896 35723 37265 38036 35723 36237 37008 36494 33667 24929 12850 3598 2313 2056 3598 3855 2570 2570 3598 2827 5140 28527 29298 30326 30069 30326 29812 30583 30326 30326 30583 30326 30326 30069 30326 30326 30069 29812 29812 29812 29812 30069 30326 30326 30069 29812 29812 29812 29812 30069 30069 30069 29298 29298 29298 29298 29041 29298 29555 30069 6168 8224 6939 8224 8224 9252 5911 4369 4883 6939 5654 5911 8481 5654 9252 7967 8995 8481 8224 7453 5654 3598 3598 4626 5911 4626 7453 5654 6425 5397 2570 6682 31868 31611 4883 3084 2570 3341 3341 3084 4112 3084 2313 3084 4626 9766 22873 37008 40349 42148 43690 43690 43947 44204 45232 46003 46003 43690 44461 44718 40606 39064 40092 39064 40863 40606 41120 42148 44461 45489 45232 44461 43433 42405 41377 41377 43176 41120 33153 24672 9509 4369 1542 2827 3084 3084 3598 4369 4369 29298 29555 30583 30326 30583 30326 30326 30840 30840 30840 30840 30326 30326 30583 30583 30326 30326 30069 30069 29812 30069 30069 30069 30326 30326 30326 30326 30326 30326 30326 30326 29812 30069 29812 29555 29555 29555 30069 30583 6682 8481 6939 7967 8224 9252 5911 4112 4369 6939 5140 5397 8481 6425 10023 7967 8224 8224 8224 7453 5911 3855 3598 4369 6425 7196 6425 6939 3855 4626 4883 4883 33667 30840 5911 3341 3084 3341 2827 2827 2570 5911 6425 7967 19018 33667 42148 43433 43947 42148 41891 44975 48573 49858 50629 50886 50115 47802 50115 51914 48830 48059 49344 48316 49087 48316 48573 49858 50886 51400 50372 48830 45232 46003 45746 43690 43690 44204 42148 38550 32896 18247 5911 3855 3855 3341 3855 4883 5397 30583 29812 29812 29812 30069 30326 30583 31097 31097 31097 30840 30583 30583 30583 30840 30840 30583 30326 30069 30069 30069 30069 30069 30583 30583 30840 30326 30583 30583 30326 30326 30069 30326 30326 30326 29812 29812 30326 30840 19275 9766 7453 9766 7453 9766 6682 4369 4626 7453 6425 6682 5911 9252 9252 7710 8481 7967 7710 7967 6168 3598 3598 4626 5397 8224 5911 5397 6425 3598 3855 6168 31868 33410 3341 3084 2570 3598 4112 3855 10794 20817 17990 22359 35209 41634 45232 46774 44975 45746 46517 47288 47802 49344 50629 51657 52685 51657 49858 50629 50372 50372 53199 52685 49344 52685 52685 51400 49087 53970 53970 51914 51914 46774 47802 43947 46517 43947 44204 39578 39578 32382 24158 15934 3341 3084 4369 3341 6682 30840 30326 29298 30840 30583 31354 30583 31097 30840 30583 30583 30840 30840 30583 30326 30583 30326 30069 30326 30326 30326 30326 30069 30326 30326 30326 30326 30326 30326 30326 30326 30583 30583 30326 30583 31097 31354 31868 32125 48573 8481 8738 9252 6682 16705 10794 5140 5911 6425 3855 7453 6425 6939 7196 7710 7710 7710 7710 7967 6168 3855 3855 4626 6168 8995 7196 6168 6168 4369 4626 4883 30583 36237 3341 3084 4883 1542 1028 9252 22873 23644 21588 29555 39835 43690 45746 44975 46517 46517 47802 48830 50115 51657 52428 52685 51143 50886 51143 52685 51400 49858 50886 50115 52428 52171 51400 51657 50629 49601 48573 48830 49087 51914 45489 43433 44461 43947 42919 40092 40092 36237 35209 24158 14906 4112 4626 2570 7710 30326 29812 29298 30583 29555 30583 31097 31354 31097 30840 30840 31097 31097 31097 30840 31097 30840 30583 30583 30840 30840 30583 30326 30583 30583 30840 30840 30840 30840 30840 30840 30840 30840 30840 31097 31097 31354 31611 31611 60138 38550 7710 12336 34952 45232 8481 2827 7196 8995 7453 7453 6425 8481 7196 7453 6425 6939 7967 7967 6168 3855 3855 4626 5911 7967 7196 6682 5911 5140 6168 4883 31611 34181 3084 4626 3084 3855 8738 17990 21331 19789 25700 35466 40092 42662 45489 45232 48316 49087 50115 50372 49087 48059 47031 46774 49601 50372 50886 52428 50886 48830 50115 49601 47288 48830 48316 45746 45489 41377 37522 35466 37522 37265 40606 40863 38807 40349 40606 41377 39835 37779 37265 33410 23387 7967 2827 5140 10537 31354 31097 30583 31611 30326 31611 32382 31611 31354 31097 31354 31611 31611 31611 31354 31611 31354 31097 31097 31354 31097 30840 30840 31097 31354 31354 31611 31611 31611 31611 31611 31097 31097 31354 31611 31611 31611 31354 31354 59624 57825 25957 51914 59367 24672 7196 3598 18247 30583 6168 7453 6939 7196 7453 7453 5911 6939 7967 7710 5911 3598 3598 4883 6939 7967 7453 7453 6168 5397 6425 4883 32382 34695 3598 3855 514 4112 10537 16191 19789 22616 31868 36494 36751 40092 42662 42919 39835 39064 37522 35466 34181 34695 37265 39578 46774 49344 50629 51143 49858 49601 51914 50115 44718 47288 46517 41120 39835 34695 33153 34438 31097 34952 41120 41634 46774 41634 39321 39835 40092 38807 35209 34695 30840 17990 5654 3341 12336 31868 31611 30069 30840 30840 30840 31097 31611 31611 31354 31611 31868 31868 31868 31611 31868 31611 31354 31611 31611 31611 31611 31354 31868 31868 31868 31868 31868 31868 31611 31611 31354 31354 31611 31611 31868 31868 31868 31868 57568 61680 61423 58339 47802 10537 4883 5140 19789 55769 13621 9509 6682 4369 11051 8738 6425 6939 7710 6939 5397 3855 3855 4626 7196 7453 6939 7453 6168 4112 5397 4883 30069 36494 4369 3084 3598 5654 9766 17990 24158 26985 32639 35209 38036 41891 43176 43176 42919 40092 37008 34952 35209 36494 39321 41891 42919 47545 49601 49344 48316 49858 52428 49344 48830 45232 41891 40092 40092 30069 24158 25186 24158 27242 32896 38550 45232 42405 45746 37779 37265 40606 39321 29555 30840 19532 11051 6168 14649 32896 32896 30583 31097 32382 32382 31097 31868 31611 31611 31611 31868 32125 31868 31868 31868 31611 31611 31611 32125 32125 32125 31868 32382 32382 32125 32125 31868 31611 31354 31354 31868 31611 31611 31611 31868 32382 32639 32896 61423 58596 57825 57054 24415 7710 9252 2827 24672 58082 39835 9252 7967 25700 39578 12336 6939 7453 7453 6939 5397 3855 3598 4112 4883 6168 5140 5140 4369 3341 4883 5654 30840 32639 4369 4883 4112 7710 18504 26985 26728 29041 31868 36751 40863 40349 39321 39835 39835 35466 30069 28270 28270 28784 29555 29812 38807 43947 46260 45489 44461 47288 51143 49344 48316 45746 41891 37779 33667 25700 22359 23644 20560 15934 17733 16448 14649 16448 16191 19018 19789 24158 24158 25700 29298 21331 10537 7967 19018 33924 33410 31354 31611 33667 32896 31868 31868 31868 31611 31868 32125 32125 32125 31868 32125 31868 31868 32125 32382 32382 32382 32125 32639 32639 32382 32382 32125 31868 31611 31611 32125 32125 31868 31868 32125 32639 33153 33410 57825 60138 57825 59110 22873 9252 3084 3855 20303 58082 60909 27242 44718 58082 33924 7453 6939 7196 7453 6682 5911 4883 4112 3598 4626 8738 6939 3855 3341 3855 5140 5140 34181 30069 5140 4369 1799 8738 26214 28784 24929 26985 30069 38036 40349 37008 38293 38036 37265 32896 28013 25700 25700 27499 30583 33924 31354 38036 43433 45232 44461 46517 51400 51914 50115 45489 38293 34181 30840 25957 16448 9252 14906 21331 26214 23901 23644 20560 11051 11051 29041 23901 11565 17990 22616 23901 11051 10794 23130 34181 32382 30840 31097 31868 30840 31611 32382 32125 31868 32125 32125 32125 32125 31868 32639 32639 32382 32382 32382 32639 32382 32125 32639 32639 32639 32639 32639 32382 32382 32125 32382 32382 32382 32382 32639 32896 33153 33153 59367 62708 62194 58082 54484 14135 8224 4369 25443 55769 61423 60395 54998 48316 10794 5397 6168 6939 7453 7196 6425 5140 3855 3341 8481 14649 11565 5140 3855 4369 5140 3855 32639 32382 6168 2827 6425 14906 29555 29298 25700 23644 20817 23130 18504 12336 12593 7453 6939 7967 9766 10537 9509 8738 11822 15163 23130 32639 43176 50115 50115 49601 52171 52942 50886 46517 38550 30326 18504 17990 26985 38550 31097 34181 36237 38293 31097 38036 59367 27756 7196 12079 10794 13364 18761 21845 16448 15934 29555 37265 34181 33667 33667 32382 31354 33667 32639 32382 32125 32125 32382 32382 32125 31868 33410 33153 32896 32639 32639 32639 32382 32125 32639 32896 32896 32896 32896 32896 32896 32896 32382 32639 32639 32896 32896 32896 32896 32896 45746 60395 52942 56026 60138 43433 6939 14906 50886 59110 58082 57311 54998 24415 9766 5397 6682 6939 6682 8224 3855 19275 13107 12593 36494 41891 7710 5654 2827 4626 4112 4883 31354 31097 7967 4883 8995 25957 34181 27756 21845 16705 7710 13107 15677 13107 7967 30069 48830 44461 36494 31868 32125 38293 38807 24672 20817 16448 29812 38293 49344 49858 47288 46260 42148 36237 27756 31097 45232 35209 30069 13107 9766 11051 15677 19789 30840 31097 39835 51400 15934 12336 22873 17990 17733 23644 27756 24415 33924 32382 34695 33667 33153 33924 32382 32382 32639 32639 32639 32639 32382 32382 32382 32382 32896 32896 33153 33410 33410 33153 33153 33153 32896 32896 32896 33153 33153 33153 32896 32896 33153 32896 32896 32896 32896 32896 33153 33153 37779 54998 17990 14649 24929 39064 23644 14649 52171 56797 56283 56026 48830 10794 7453 6168 6168 7196 6682 7967 5397 18504 28013 33410 46774 20303 4369 2570 4626 4369 3855 4883 31868 30069 7967 13621 19275 30583 31354 24158 17476 14649 4626 7196 4112 10537 32382 33153 34952 25186 15163 12336 11565 10280 23130 39321 35466 33667 35723 36751 43176 43690 43433 38036 36751 31354 32382 32896 30840 28013 12079 20303 7967 13364 11822 10794 16191 22616 20046 18504 20303 17990 15420 10280 10537 20817 31354 38293 44461 40349 32639 34952 34181 33153 33667 34952 33153 33153 33153 32896 32896 32896 32896 32896 33410 33410 33667 33667 33667 33410 33410 33153 33153 33153 33153 33410 33153 33153 33153 32896 32639 32896 32896 33153 33153 33410 33667 33667 35723 48059 9252 9766 5140 8481 7453 6939 31868 55769 59881 59110 55512 29041 8995 7196 6939 8738 6939 6682 5140 12336 28013 28784 28270 8738 4626 3598 3341 4112 5397 4369 32382 34438 7453 13621 29812 33924 27499 18761 12079 12593 14906 13364 7196 25957 29041 30840 22873 12850 10280 8995 9766 8995 14392 19275 30583 29298 28784 37779 38550 34181 35980 34695 33924 27242 36494 34695 28013 14906 20303 12079 3855 9509 9766 11051 11822 18761 20303 23387 31097 25700 20560 20817 23130 32639 36494 27756 34181 43176 33924 35466 32125 32896 35466 33667 33667 33667 33667 33667 33667 33667 33410 33410 33924 33924 34181 34181 33924 33924 33667 33410 33667 33667 33667 33667 33410 33410 33410 33153 32896 33153 33410 33667 33667 33667 33667 33667 30326 28527 6682 8224 7967 6425 3341 3598 10537 51143 59110 52685 59110 51657 11822 3855 5654 8481 7453 6168 6939 15934 30583 26728 9509 2570 5397 5911 2313 4112 6168 4626 29041 35980 8738 14649 39578 34952 29555 27499 18247 21331 15163 16962 12593 22873 17990 25700 12593 11051 14392 6425 4369 11565 22616 26728 17219 21588 23644 35980 32896 42919 49601 49087 42405 37779 39321 37779 26985 32125 28527 24929 22616 20560 19018 20046 22616 23901 27499 32382 39064 35466 37779 32896 28784 27756 31097 17990 15934 37008 35466 34438 31868 32896 35723 31868 34181 34181 34181 34181 34181 34181 34181 34181 34438 34438 34438 34438 34438 34181 33924 33667 33667 33667 33924 33924 33924 33924 33667 33667 33667 33924 34181 34181 33924 33667 33410 33153 19789 15934 10280 5140 5911 8738 4626 3855 7196 51400 37522 11051 26471 46003 29812 5911 6682 8224 7967 5397 4369 16191 29555 31868 25957 2056 3598 4369 5140 4112 4626 4883 29041 34695 8738 20817 35723 22359 21588 27242 28013 28270 26985 32896 27499 21331 20303 19018 17733 16705 18761 19275 25957 25700 25957 33153 35209 38550 38807 39835 25700 45489 52942 52942 47802 31868 45489 43176 39835 34952 32382 37779 34695 33153 30583 28527 30326 34695 36237 34181 36494 38293 42662 34181 30840 29298 35209 37265 15677 31354 33153 33153 35209 33410 33924 34438 34438 34438 34438 34438 34438 34438 34438 34438 34695 34695 34695 34695 34438 34181 33924 33667 33924 33924 34181 34438 34438 34438 34438 34438 34181 34438 34438 34438 34181 33924 33667 33410 10023 6168 5911 8224 8738 8481 3598 3084 10280 52171 20303 7196 8995 8224 13878 6939 6939 6168 7453 7453 6168 15420 22873 30069 38550 21074 3598 4112 4883 5140 4369 4112 30840 33153 6425 23901 28013 20817 27756 29041 29298 26985 37779 39321 29555 28270 19532 18247 15934 18247 26214 37522 43947 41377 36751 39064 37522 44718 49344 43433 25186 43947 50886 53199 51657 43176 43690 46774 39321 35723 44975 43176 46774 49601 46774 41891 35466 42405 43433 39835 40606 40349 40606 33667 30326 38550 40863 36237 21588 31097 33924 32639 35466 33924 33153 36237 34695 34695 34695 34695 34695 34695 34695 34695 34952 34952 34952 34695 34695 34438 34181 33924 34181 34181 34438 34438 34695 34695 34695 34695 33924 34181 34181 34438 34438 34438 34438 34438 5140 6682 7196 8224 6939 6168 3341 3855 11565 38293 5140 7967 9509 5140 6168 4112 7967 7196 7196 6939 5140 5911 4883 8224 22102 27756 5911 6425 3084 5397 4883 4369 32125 34695 5654 16448 21331 17733 31354 28270 26728 28013 35723 41891 33410 33410 25186 22359 22616 28527 35723 46003 46260 46774 42919 38036 34438 46517 51143 38036 31611 48316 52685 50115 52171 46260 34952 48059 49087 41120 36237 44204 47802 45489 42662 39835 35466 40606 43690 40863 42662 41377 41891 37779 31868 41120 48316 39578 22359 34181 35209 33410 33667 34695 34181 34952 34695 34695 34952 34952 34952 34952 34952 35209 34952 34952 34952 34952 34952 34695 34438 34181 34181 34181 34695 34695 34695 34695 34438 34438 34181 34181 34181 34181 34438 34438 34695 34695 6939 5397 6682 5397 7967 7453 4112 3855 8481 14649 6939 8995 7453 7453 6425 6168 5654 8481 7710 5911 5911 3855 2827 3855 5140 7710 6682 7196 4112 3598 4369 4883 28270 33924 7196 13107 26214 16448 35466 38036 32639 23644 33667 38036 35723 34181 35209 30583 26214 34695 33924 40092 37522 33667 30326 35466 50629 48830 44461 30840 40606 50372 52942 52685 50372 52685 36751 43690 46260 52428 48316 39578 38807 33924 36237 32896 34181 35980 44461 43176 38550 38807 38807 37265 35980 30583 41634 41891 24415 35980 33924 35209 33153 35466 34952 34438 34952 34952 34952 34952 35209 35209 35209 35209 35209 35209 35209 35209 35209 34952 34695 34438 34695 34695 34952 34695 34695 34438 34181 34181 34695 34695 34181 34181 34181 34438 34438 34438 5654 6168 5911 7196 8224 6682 4112 3598 6168 5654 6682 7710 7453 6425 5911 6682 6682 8224 6939 6682 4883 3341 4112 2827 3598 4626 5140 6168 3598 4883 4626 4369 28784 34438 5654 7967 31354 19018 40092 37265 29812 24672 35209 36237 38550 35466 38293 31868 25186 33667 34181 34438 35723 37265 45489 52685 52428 49344 43176 31354 47288 50115 53970 52942 53199 53713 45746 38293 49601 51400 51400 53970 51400 48316 43433 39578 40349 44718 45746 43176 40092 38807 38036 37008 29041 19018 33924 41377 32125 32639 34695 35466 33924 34181 35209 35980 35466 35209 34952 34952 35209 35466 35209 35209 35209 34952 35209 35723 35466 34952 34438 34695 35209 35209 35209 34952 34695 34438 34181 34181 34438 34438 34438 34438 34438 34438 34438 34438 6168 6425 6425 7196 8224 6425 3855 3084 5397 5397 6168 7453 7196 5911 5397 5911 6939 8224 7196 6425 5140 3855 4369 3598 2827 5654 4112 4112 6682 4883 4883 5140 30840 36237 3084 12593 30583 25957 41891 37522 24929 24672 33667 34181 38807 39321 39064 31097 31611 37008 42919 48059 51400 53456 54741 52942 51143 53713 38293 41377 49344 49858 52685 54484 53970 51914 53713 40863 44204 49858 52171 51400 51400 51914 49087 46003 46260 46517 43176 40349 41120 39321 31868 41120 34695 20817 28527 41120 37779 36494 36494 35723 33667 33667 34952 34438 35466 35209 34952 34952 35209 35466 35209 35209 35723 35209 35209 35466 35466 34952 34952 35209 35209 35209 35209 34952 34695 34695 34438 34438 34695 34695 34438 34438 34438 34695 34695 34695 5911 6168 6425 6939 7967 5911 3598 3084 5654 5911 6425 7710 7453 6425 5654 6425 6939 7967 6682 5911 4883 3855 4626 3855 4626 4883 4112 4112 6682 2827 4883 5911 28527 35466 14649 40092 49087 33924 40092 39578 24415 31097 37008 32125 34952 38807 38550 34695 25957 31611 47288 54484 53456 54227 55512 54227 54227 50372 32639 47545 50886 52685 52428 54484 53970 52171 52685 47031 32896 45232 50115 50372 52171 51657 46774 43176 44204 43176 40349 39064 39064 34438 35466 41377 45232 29041 31868 36237 38036 35723 35209 34695 34438 35466 36237 35466 35466 35209 35209 35209 35209 35466 35466 35209 35980 35466 35209 35466 35466 35209 35209 35723 35209 35209 34952 34952 34952 34952 34952 34952 34695 34695 34952 34952 34695 34695 34695 34695 5654 5911 6168 6682 7710 5911 3855 3598 5397 5911 6682 7710 7710 6939 6682 6682 6682 7710 6168 5397 4369 3341 4626 3598 5397 3341 5654 4626 4883 4626 4369 3084 21588 43433 38293 46774 50629 43176 46260 34952 33153 42405 42148 31611 30326 35466 36751 38293 37008 32639 42405 51914 53199 53713 51657 49344 46003 31354 30840 44975 48316 52428 52685 51143 53456 53970 54227 50115 30840 25700 38550 43433 44204 43176 43433 45746 48573 47802 42919 37779 35466 34695 37522 42662 47288 40863 32125 36237 37008 35466 35209 35209 34952 35209 35209 34181 35723 35466 35209 35209 35466 35466 35466 35466 35723 35209 35209 35466 35723 35209 35466 35723 35209 35209 35209 34952 35209 35209 35466 35466 34952 34952 34952 34952 34952 34952 34952 34952 5397 5911 5911 6425 7196 5654 3598 3598 4626 5397 6168 7196 7453 6939 6939 7196 6939 7710 6168 5140 4369 3341 4112 3341 3855 4112 6425 4626 4369 6939 3341 8995 33924 42662 40349 46517 50629 42919 43947 39835 41120 47802 43947 32896 30583 34181 33924 37265 41891 33924 35209 41377 47802 48573 40092 34181 26728 25443 41634 46260 48573 51914 53713 51914 52942 52171 50886 52171 46774 32639 28784 30069 25957 26471 34181 42919 44975 45746 42405 34952 35723 36237 36494 41634 46774 37522 30840 39578 35980 35209 35466 35980 35209 35466 35723 34952 35723 35466 35209 35209 35466 35723 35466 35466 35466 35209 35209 35980 35980 35466 35209 35466 35209 35209 35209 35209 35209 35209 35466 35466 34952 34952 34952 34952 35209 35209 35466 35466 6168 6425 6168 6425 6682 5140 3341 3598 4626 5140 6425 7196 7196 7453 7196 7196 6682 7453 6168 5397 4626 4112 4626 3341 3855 5140 4883 5397 4626 2570 6425 31868 44461 44204 38550 48573 51914 44204 41891 43947 41120 46260 44718 36494 32639 33153 32896 36237 42662 39835 33410 24929 23901 27242 27242 26985 18761 37522 48059 45489 47288 49858 52428 51143 51914 52171 49858 49344 45489 42405 29812 41634 44718 43433 46260 47031 42148 40606 40349 36494 37522 35209 39578 43690 43690 32125 40092 38550 35209 34438 34952 35466 34695 35466 36494 36237 35980 35723 35466 35466 35723 35723 35723 35466 35466 35209 35466 35980 35980 35466 35209 35209 35209 35209 35209 35209 35209 35466 35466 35466 35209 35209 35209 35209 35209 35209 35723 35723 6168 6425 6425 6168 6682 5140 3084 3341 4883 5654 6682 7196 7453 7196 7196 6939 6425 6939 5654 5397 4883 4369 5140 3598 4626 4883 4112 5654 4112 4369 22102 48059 47031 45232 36751 49344 50372 39064 39321 45489 41891 43947 44718 37522 32896 32125 34181 37008 37008 42405 41891 38293 38807 42148 40092 32382 24929 43690 40606 38550 43433 45232 49087 48316 46003 43947 44975 38550 38550 37522 24415 31611 45232 47545 47802 44975 41120 39064 39064 39578 37522 35466 39064 43690 38036 40863 43690 38550 36751 35723 35723 35466 34181 34695 34952 34181 35980 35723 35466 35466 35723 35723 35723 35723 35980 35466 35723 35980 35980 35466 35209 35466 35466 35209 35209 35209 35209 35466 35466 35466 35209 35209 35209 35466 35466 35466 35466 35466 5397 6168 5911 6425 6682 5140 3341 3855 4369 5397 6168 6682 6682 6425 6168 6168 5397 6425 4883 5397 4883 4369 5140 3598 3598 4626 4883 3855 4112 15677 41120 44975 44204 45489 37008 49344 51143 34952 37779 42148 47545 43690 42405 35466 32382 33924 37008 37008 34695 39321 40863 43433 46003 47288 40349 24415 20046 26471 20817 22873 30840 32382 37008 38293 38550 37008 28527 25700 14135 20560 13107 22102 37265 43690 43690 41120 42405 41120 39064 41891 38807 35723 38036 30069 41891 46003 37265 41891 34952 34181 34952 35723 35723 36494 36494 35466 35980 35723 35466 35466 35723 35980 35723 35723 36494 35980 35723 35980 35723 35466 35466 35980 35466 35209 35209 35209 35209 35466 35466 35466 35466 35466 35466 35466 35466 35466 35723 35723 3855 7710 6168 4883 10023 4626 3084 4626 6425 2570 6682 8481 6682 6168 4626 6682 6168 7967 6425 4369 4369 5140 5140 2313 3855 3598 5140 5397 18761 38293 44461 45489 44461 44204 38036 49601 48059 35209 38293 43947 38550 38036 25186 32382 34695 33924 37265 40349 36751 38036 41120 41377 43176 38550 25957 20046 39835 32125 15677 15420 16962 19532 23130 23130 21588 20046 24158 19018 22616 38293 25443 11565 26214 35723 38550 36751 40863 38550 40092 42405 39835 39835 35980 32639 32896 34438 42148 39064 35980 35209 35209 35466 35466 35209 35723 36237 35466 35980 36237 35980 35980 35723 35980 36237 36751 36494 35980 35723 35466 35209 35209 34952 35209 34952 34952 34952 35466 35466 35466 35209 35466 35466 35466 35466 35723 35980 36237 36237 7196 5654 4626 9509 5654 5397 4369 771 5140 6425 5140 5397 6939 5397 5140 5140 5397 5654 4369 4369 4369 4626 5140 5140 3084 5140 5654 24929 41891 41891 45232 45232 44461 44204 38293 49087 47545 38550 39064 43690 41891 39321 42405 45232 38550 33667 36494 39578 41120 36751 42405 40606 38807 34181 19532 31868 43947 42662 40863 41120 39578 30840 19275 10280 15677 29041 36751 38550 37522 40349 33153 14906 13107 27756 34438 36494 37779 37779 41120 41377 38807 42148 48830 43947 45489 40863 45489 38293 35466 34952 34952 35466 35723 35466 35466 35723 35466 35980 36237 35980 35980 35723 35980 36237 36494 36237 35980 35466 35209 35209 35209 35209 34952 34695 34695 34695 34952 35209 35209 34952 35209 35209 35466 35466 35723 35723 35980 35980 5397 6682 9252 14906 6939 1799 4883 5397 3084 6168 7710 8224 7967 5911 5397 5397 5397 4883 4112 4112 5140 4883 3855 4626 3084 7967 36494 45489 39578 45232 45746 44718 44204 43690 39064 48830 47288 41377 37522 40863 46260 44461 48059 48830 45489 35466 35980 40606 42148 38036 38807 39835 32896 22359 25700 40349 42148 40863 42919 40606 43176 41377 37265 32639 34438 40092 37779 39835 38293 35723 40349 30840 12336 16962 26214 29041 33667 37522 40863 41891 40092 41120 46003 47802 41120 46517 43947 35980 35209 35209 35209 35723 35980 35723 35723 35980 35466 35980 35980 35980 35723 35723 35980 35980 35980 35723 35466 35209 35209 35209 35209 35209 35209 34952 34695 34695 34952 35209 34952 34952 34952 34952 35209 35209 35466 35466 35723 35980 16962 9509 32382 20046 6682 5911 2570 3084 8224 3341 5654 6425 5654 6425 5140 4369 5140 5911 5140 3855 5397 4883 2827 3855 8481 38807 44975 43690 45232 42662 45489 43690 43947 42662 39064 48830 47031 42148 32382 34438 47031 49858 47545 51143 45232 35980 38036 40863 42405 39321 37008 35723 24672 16448 37522 40863 41891 42919 44461 42405 43433 44718 44975 41891 44975 41120 39578 39835 41891 39835 41891 38550 21845 11565 22102 26985 36237 38550 37265 39064 38293 42662 48830 44718 44204 43947 37522 34695 35466 34952 35209 35980 36237 35723 35723 35980 35466 35980 35980 35980 35723 35723 35723 35980 35723 35466 35466 35209 35209 35209 35209 35209 35466 35209 34952 34952 35209 35209 35209 34952 34952 34952 34952 34952 35209 35466 35466 35723 42405 39835 35209 7710 4883 3855 4112 2827 3855 6168 5654 5911 5911 4112 4883 5911 5140 5140 5654 4112 4626 3855 5397 13878 33924 40092 44975 44461 44461 45232 45489 45489 44975 42405 39578 49858 47802 41377 27499 27499 42405 48059 47288 48573 31097 34438 40349 37779 44461 38293 39835 29298 17476 24929 38036 41634 43176 46517 44975 46774 45232 47802 51143 49344 49344 46517 46517 41120 42662 43690 42662 40092 37522 21845 18761 26214 33410 37008 39321 42662 39064 37779 49087 47288 40092 39578 37779 34952 34952 34695 34952 35980 35980 35723 35466 35723 35723 35980 35980 35980 35723 35723 35723 35980 35466 35466 35209 35209 35209 35209 35466 35466 35466 35209 35209 35209 35466 35466 35209 35209 34952 34952 34952 35209 35209 35466 35466 35466 51914 42148 26471 7196 4112 6682 2570 3855 10794 19275 8995 6425 8224 2827 5397 6168 5140 4112 5654 5140 4626 4112 11051 28527 40606 41377 44718 47288 43176 43176 47545 46517 45232 42662 39578 49858 49087 40606 25443 22102 28013 39578 43176 31354 8481 23901 36237 43176 43433 41120 39578 27242 19532 34695 35209 43690 43176 46003 43690 48316 44975 47031 51143 49601 49344 48316 47031 46260 46517 44204 42919 40349 39321 33924 20046 34438 38550 41377 42148 38807 38807 25186 30326 39064 34952 41377 39578 34695 34952 34438 34695 35209 35466 34952 35209 35466 35723 35723 35980 35980 35723 35723 35723 35980 35723 35723 35466 35466 35466 35466 35723 35723 35209 34952 34952 34952 35209 35209 34952 34695 34952 34952 34952 34952 35209 35466 35209 35466 47545 43947 12850 5911 6939 3341 3084 15677 32125 22616 7196 3084 6682 5140 4369 3084 4883 4883 5911 4626 5654 6682 12850 30069 35209 44204 44461 45489 43690 43690 49087 43690 43947 43176 40092 48573 49601 40863 27242 20560 25443 29555 29812 15420 6425 21588 34181 40349 40863 44461 41377 31868 27756 36494 37008 40349 38293 34952 32639 34181 32639 35209 42662 44718 40092 36751 33153 36237 36751 35723 37265 41120 37265 39578 27499 38036 42919 43433 43176 39064 34438 19532 19789 27756 46260 41891 36494 34438 35209 34952 34695 35209 35209 34952 35209 35723 35209 35723 35980 35723 35723 35466 35723 35980 35980 35980 35723 35723 35723 35723 35980 35980 35209 34952 34952 34952 34952 34952 34952 34695 34952 34952 34952 34952 35209 35466 35466 35723 50372 43947 16962 6168 3855 5140 3084 19018 29041 6425 7196 6939 3855 6168 3084 6168 3855 5140 6168 2570 4883 8224 10280 20817 32382 42148 44975 45489 44718 43433 45746 46517 43176 43176 40092 47288 49601 41377 29555 20560 21588 26728 37008 27242 11308 11565 35209 40863 41377 43433 48316 36494 33924 35209 37522 33924 23644 17476 19532 19789 20046 19789 24158 26214 20303 20046 20560 19789 17219 19275 19018 25700 30069 36237 40349 42919 48573 42662 38807 39835 38293 17476 23130 36751 39321 35980 38036 34695 35723 35209 35466 35466 35466 35209 35466 35980 35209 35723 35980 35723 35723 35466 35723 35980 36237 36237 35980 35723 35723 35980 35980 35980 35466 35209 35209 35209 35209 35209 35209 34952 34952 34952 34952 34952 35209 35466 35466 35723 57311 51143 34695 6939 5140 4369 2313 18761 13364 8481 6168 5654 7196 3341 3855 5397 4883 5654 5654 3598 3855 9252 12850 11822 30840 38550 45489 45746 46260 43433 46517 46003 43690 43176 39835 47288 47031 34695 28784 21074 17219 15677 13878 15420 11308 10280 29812 37522 40863 43947 46774 46517 33667 32125 25443 20560 17219 23387 25957 27242 30326 28784 23901 22359 25443 33667 41634 39835 37265 40092 39321 36494 33410 37779 38807 47545 45489 44461 40606 40349 33667 20817 24929 35466 42662 42919 35466 36494 35209 35466 35466 35980 35980 35723 35723 35723 35980 35723 35723 35723 35723 35723 35723 35723 36751 36237 35723 35466 35723 35723 35466 35209 35723 35723 35723 35723 35209 34952 34952 34695 34438 34695 34952 35209 35209 35209 35723 35980 38550 43176 44204 16448 2827 5911 514 25443 15420 4369 6682 5397 5140 5140 4369 5397 5140 5397 5397 4112 4626 8995 12079 11565 24158 39578 44975 45232 45489 44204 46517 46003 43947 42405 40092 48059 44204 30069 28270 28270 15677 7967 3855 8224 10794 11565 26985 37265 39321 39064 45232 46774 40349 34952 36237 35209 37265 37779 38807 42405 45489 43947 43176 45746 45746 42405 44204 45489 43947 41120 38550 38807 41377 35466 44204 46003 44975 40349 42919 39321 30583 21074 27499 36494 44718 41891 36494 35209 35723 35723 35723 35980 35723 35723 35723 35723 36237 35980 35723 35723 35723 35723 35723 35723 35980 35723 35723 35466 35209 35209 35466 35723 35723 35723 35723 35723 35209 34952 34952 34952 34695 34952 35209 35466 35466 35466 35723 35980 20303 3084 23387 29555 6425 3084 6168 38293 24415 2827 5654 4883 3598 5911 3598 5397 5397 4883 4626 4369 5140 8738 11565 11051 16448 37779 45232 45746 45232 44461 46003 44975 43176 42405 40349 47288 42148 28013 29555 33153 29812 16448 8481 8481 10537 9766 19789 33667 36751 39578 46003 43433 40606 34695 41634 38036 37265 39064 39321 38807 42405 46517 45746 42405 43176 41120 41634 39321 35980 35980 38807 43433 42662 35466 42919 47288 40349 40349 39578 36751 24158 22616 31097 37265 41891 36494 36237 36237 36237 35980 35980 35723 35723 35980 36237 35980 36237 35980 35980 35723 35723 35723 35723 35723 35209 35466 35723 35466 34952 34695 35209 35723 35723 35723 35723 35723 35209 35209 34952 34952 35209 35209 35466 35466 35466 35466 35723 35980 7196 5654 4626 7196 4626 4626 3598 29298 33667 7453 5654 5397 4883 5397 2570 5911 5140 4883 4369 3341 4369 8738 11822 10537 11565 30840 45746 46260 45232 44975 45746 44461 42919 42919 39835 46003 42148 30583 31611 32125 37522 36237 30840 15934 7453 6939 14392 30583 37008 37522 41377 45232 42148 33410 40349 41377 37779 35723 34952 34695 32896 31097 29555 27499 25443 26728 30069 31354 34952 41634 42919 41120 40092 35980 41377 40349 40606 36494 37265 32382 22616 25700 31354 35723 37265 33153 36494 36751 36751 36237 36237 35980 35980 35980 36237 36237 36494 36237 36237 35980 35980 35723 35723 35723 35466 35466 35466 35466 34952 34952 35209 35466 35723 35723 35723 35723 35466 35209 35209 34952 35209 35209 35466 35466 35209 35209 35466 35723 4883 6939 4626 6168 4626 3855 2313 6168 28013 11051 6425 5397 6939 4626 3598 6682 4883 5140 4883 3341 4369 10023 12593 10537 11308 21331 42662 46003 44975 44975 45746 43690 42662 42148 38807 45489 43176 32382 32125 30840 35723 33410 37779 38036 29812 13878 7196 24929 33667 36751 36494 42662 37779 36237 39321 40092 42662 40863 39321 35466 29812 25957 25957 26471 26985 24415 26471 29555 34181 38807 38036 35980 40863 39835 38807 38550 37779 34952 36751 26985 26214 30326 29041 34695 35723 36494 37008 35723 36494 36494 36494 36494 36494 36237 36494 36237 36751 36494 36237 36237 35980 35980 35980 35980 36494 35980 35466 35466 35466 35466 35466 35209 35723 35723 35723 35723 35466 35209 35209 34952 34952 34952 35209 35209 34952 34952 34952 35209 5911 4369 5911 4626 2827 4626 4883 4626 10280 8481 6168 4112 6425 4369 5140 5397 4883 4883 4883 4626 6425 11308 13107 10537 12336 14392 34181 43690 44204 44461 45746 42662 43433 41634 38036 45746 43176 30840 31097 31868 36237 33667 34181 34438 33410 25700 14392 22359 31097 38036 35209 36494 32639 40349 41377 37008 35209 37779 37522 36237 38807 43176 44718 42919 41634 39064 39321 37008 34181 34695 37008 40606 41634 41120 37779 41891 34181 35723 36237 21074 28013 33153 30840 35466 35209 37265 36494 35723 36494 36751 37008 37008 37008 36751 36494 36237 37008 36494 36494 36237 36237 36237 36237 36237 37008 36237 35723 35466 35980 35980 35723 35209 35980 35980 35980 35723 35466 35209 34952 34952 34695 34952 34952 34952 34695 34695 34952 35209 6939 3084 5140 7453 5911 2827 514 3341 1799 4883 5911 3341 4883 4112 4883 3855 4626 3855 5397 7710 10023 12336 12336 11051 12336 12850 22359 40092 42662 43690 45232 42662 43176 42148 39064 45489 42148 30583 31868 32639 32896 32382 33153 33153 31097 24672 14649 21845 35209 33410 33153 37008 38807 40092 41377 43176 42919 40863 39835 43947 49858 49858 48316 48830 49087 47545 48830 48059 46774 46774 46260 46003 40606 37008 39064 35466 37008 32896 37779 22359 27756 34438 34952 36751 35209 36494 35466 37008 36751 37008 36751 36751 36751 37008 36751 37008 36494 36494 36494 36494 36237 36494 36494 36494 36751 36494 35980 35980 35980 35980 35980 35980 36237 36237 35980 35980 35466 35209 34952 34695 34695 34952 35209 35209 34952 34952 35209 35466 4369 6682 4883 5654 2056 3084 4112 3598 5140 4112 5911 4112 4883 4112 5140 4112 4626 2827 5397 10794 13107 12850 11822 11565 11565 13621 13621 37265 41377 43690 44975 42148 41891 43947 40863 44718 41120 32382 33410 31354 31868 33924 33924 33410 33667 30840 18247 17990 37265 31611 34181 35209 40863 37265 38807 40606 37265 41634 41634 42405 47802 50372 50115 50886 49858 46260 47288 49344 49344 47802 44975 45489 38807 37008 34438 29298 35209 34695 37008 32125 29812 34952 36751 35723 36494 37008 36494 37265 37265 36751 36751 37008 36751 37008 37265 37522 36494 36494 36494 36494 36494 36494 36494 36751 35980 36237 36494 36237 35723 35723 35980 36494 36494 36237 36237 35980 35466 34952 34695 34695 34952 35209 35209 35466 35209 35209 35466 35723 4626 5397 5654 4883 3855 3084 2827 3341 5140 4626 4369 4626 4626 4112 4112 4626 3598 6425 10794 13107 13107 11822 11308 11308 11565 13107 14649 23644 40863 41120 42405 38293 40092 48316 37779 39835 38807 31097 35980 31868 32382 33667 33153 34952 32639 34181 15677 9766 34695 37522 31097 33667 35723 37522 34181 38807 40349 41377 40606 39578 43433 43690 47545 48316 44204 44718 47288 49858 49601 45746 41891 40863 34181 31097 27756 32639 32382 31097 35466 32896 33410 35209 36494 37265 37265 36751 37522 36751 37522 36494 35980 36494 36751 36751 36751 37522 37008 36751 36751 36751 36751 36751 36751 36751 36237 36237 36237 36237 36237 36237 36237 36237 36751 36494 36494 36237 35980 35723 35723 35466 35466 35466 35466 35466 35466 35466 35466 35466 4883 5397 5654 5140 3855 3084 3084 3598 3598 6168 6425 3855 3341 4369 4883 4883 6425 8995 12079 13621 13107 12336 11565 11051 11565 12850 14906 15677 34438 41120 43690 34952 39064 50115 38036 35209 33667 30326 35723 31097 36237 29298 32639 37008 33667 33153 16448 19789 42919 36751 28270 29298 33153 35466 31354 35723 40092 39835 37779 37008 40863 40606 42405 41377 38550 41891 43176 42405 42405 42148 39064 34695 30326 21588 24415 20303 24415 37522 34695 12593 36751 33410 34695 35466 37265 37779 35466 36751 36237 35723 36237 36494 37008 36494 37265 37779 36751 36751 36751 36751 36751 36751 36751 36751 36494 36494 36237 36237 36237 36237 36237 36494 36751 36494 36494 36237 35723 35723 35466 35466 35466 35466 35466 35466 35466 35466 35466 35466 4883 5397 5397 5140 3855 3341 3598 3598 4369 7196 5911 3855 4883 5654 5140 5397 10280 11308 13364 14135 13621 12593 11565 11051 11822 13878 15163 11565 24929 40606 42919 35723 37779 50372 37265 32125 30069 28527 34695 31354 34438 33667 33410 39578 31097 23387 7196 44975 44204 39321 31868 22873 20560 23901 24158 28270 35980 38036 37779 37008 37522 34695 34438 32639 33924 37522 38293 36494 35466 35723 32125 26471 20303 16962 13621 17476 32639 42405 32125 4112 14649 31097 36237 36237 36751 37265 38807 36237 35980 36237 36494 36751 36494 36237 36494 37522 36751 36751 36751 36751 36751 36751 36751 36751 37008 36751 36494 36237 36237 36237 36494 36494 36751 36751 36494 36237 35723 35466 35466 35209 35466 35466 35466 35466 35466 35466 35466 35466 4883 5397 5397 5140 3855 3341 3598 4112 4112 5397 4369 3855 4883 4369 5140 8481 12593 13107 13621 14135 13621 12850 11822 11051 12079 14135 13878 13107 16705 37522 40092 38550 37522 49087 36751 32125 30069 26985 32896 33410 36751 30583 34952 36494 28013 5911 8738 59110 37779 40606 37008 24672 13621 10280 13621 17733 25443 30326 31868 30326 27242 23130 23901 25443 28013 27756 26728 25957 25700 24672 22616 19789 14135 10023 13364 31097 37779 41120 32125 2827 5654 9252 27499 36494 35209 36494 36494 38036 36751 37008 37008 37008 37008 36751 36751 36751 36751 36751 36751 36751 36751 36751 36751 36751 37265 37008 36494 36237 35980 36237 36237 36494 36751 36751 36494 36237 35723 35466 35466 35209 35723 35723 35723 35723 35723 35723 35723 35723 5140 5397 5397 4626 3855 3598 3598 4369 4112 5654 5140 4369 4369 5397 8995 14392 13364 13364 13878 13878 13621 12850 11822 11051 11308 13107 12850 13107 12850 32382 38807 36751 39064 49087 38550 31868 29812 25957 33667 33410 35466 29812 35980 31868 5397 2570 29812 50629 30326 39321 36751 33153 22359 8995 10023 11565 15677 18761 16962 14392 11308 10537 13364 16191 16962 13878 10794 10280 12850 15420 17476 19018 15934 14906 29555 37779 38807 41634 28784 3084 4883 3598 5140 23644 38036 37779 37008 34695 37265 37008 37008 37265 37779 37779 37522 37265 37008 37008 37008 37008 37008 37008 37008 37008 37265 37008 36751 36237 36237 36237 36237 36237 36751 36751 36494 36494 35980 35723 35466 35466 35723 35723 35723 35723 35723 35723 35723 35723 5140 5654 5654 4883 3598 3084 3598 4369 4883 5397 5140 4112 5397 10023 14135 14649 12850 13107 13621 13878 13107 12593 11822 10794 11308 12336 13364 11051 13364 26214 40092 31868 41120 50372 41891 29041 26985 25443 34695 29555 32639 31868 25957 6682 3084 2313 41634 44975 22873 40349 36751 36494 30840 18247 17476 16191 15420 14649 7967 6425 5397 5911 5911 6168 6168 5911 4626 5654 11051 18504 22102 22102 19275 27499 37265 35723 40863 41120 30069 4369 2313 3855 3855 4626 21331 37008 37779 40092 37522 37779 38036 37522 37522 37779 37008 36494 37265 37008 37008 37008 37008 37008 37008 37008 37265 37265 37008 36751 36494 36237 36237 36237 36751 36751 36751 36494 36237 35980 35980 35723 35980 35980 35980 35980 35980 35980 35980 35980 5140 5397 5140 4626 3598 3084 3341 4112 4626 4369 6425 8224 8738 12593 14392 11308 12079 13107 13878 14135 13621 12336 11308 10794 11051 12593 13364 12079 14135 19789 40349 28527 42405 50629 44204 26471 24929 23644 33924 23644 30583 12593 3341 4883 3598 3341 48830 46260 16191 42148 38293 34695 32125 26214 23901 23901 22102 20303 12336 10023 7710 6939 4883 3341 3855 6168 8738 11822 18247 25957 27242 24672 24158 30326 34181 37779 37265 42405 35723 1799 5397 2313 3341 3855 1542 13364 33410 37522 36237 37522 38550 38293 37522 37265 36494 35980 37008 37008 37008 37008 37008 37008 37008 37008 37522 37522 37522 37265 37008 36751 36237 36237 36751 36751 36751 36751 36237 36237 36237 36237 35980 35980 35980 35980 35980 35980 35980 35980 5654 5654 5140 4369 3084 2570 3341 4112 4883 7196 15163 19018 14392 13107 14906 13878 12079 12850 14392 14649 13621 12079 10794 10280 10280 13364 11565 14906 12336 14649 38293 28013 42662 49344 44461 25957 24415 21588 32125 19532 4883 3855 2827 2570 4369 1542 51657 53713 13878 41120 38550 35209 34181 29298 22873 27499 26471 26471 19018 14649 8481 6939 6168 7453 6682 8995 11822 15677 22102 27242 26728 23644 26985 35980 34695 37779 38293 53713 31611 3341 3084 4626 3855 3341 3855 2827 6682 17990 32896 35723 38293 38293 37522 37522 37522 37779 37008 37008 37008 37008 37265 37008 37265 37008 38036 37779 37779 37779 37522 37008 36494 36237 36494 36494 36751 36751 36494 36494 36494 36494 36237 36237 36237 36237 35980 35980 35980 36237 5397 6168 6425 2313 4112 3084 3341 3084 7453 28270 43433 35723 13621 15420 14649 12593 13107 13878 14649 14649 13364 12079 10537 10280 11308 12593 11565 12079 14392 14392 35466 28784 40863 48830 42405 25957 16705 10280 4369 3855 3084 3598 2827 2313 3598 4112 53713 56797 17219 35723 39321 35466 33924 28784 26471 26985 27756 26471 21845 17219 15163 13364 10537 8995 9509 11051 14135 17733 25700 28784 23901 24672 30326 36751 33410 39321 46517 58082 30326 3598 3084 3341 3855 3598 3598 3341 3855 3855 2570 10280 24158 36494 39835 37265 36237 38807 37522 37008 36237 37265 37779 37522 36751 36237 37265 37779 37779 37522 36751 36751 37008 37008 37008 36751 36751 36751 36751 36751 36751 37008 35980 35723 35723 35723 35980 36237 36237 36237 5397 4626 5397 3855 5397 3598 2313 10023 34438 45489 43176 34695 23130 14649 14649 12850 10794 12079 13878 14649 14392 12850 11308 10280 9252 12850 13364 12336 11565 12593 33667 32382 31354 29555 17733 5397 2827 2827 1799 3084 2827 3341 3341 3084 4626 4112 53456 57568 17219 30583 41634 34695 31354 31097 29041 25443 27756 25700 21074 19532 17219 15163 13364 14906 17476 17219 19275 23644 28013 26728 22873 27499 33924 33667 38036 39578 59110 59110 25186 3341 3084 3341 3341 3341 3084 3084 3598 3341 4112 5140 4369 6939 19789 35209 39835 35466 37522 37522 37265 37008 36751 37008 38036 39321 38036 38036 38036 37779 37522 36751 36751 36751 37008 36751 37265 37265 37265 37265 36751 37008 36494 36494 36237 35980 35980 36237 35980 36237 4883 3855 5911 4626 3598 2570 5397 28013 44204 48830 41634 35723 36494 19789 14135 13878 12336 13364 14135 14135 13107 11565 10023 8995 11565 12079 11308 12850 14906 14392 19275 12593 3855 4369 2570 1799 3341 4626 3598 3341 3598 3598 3341 3598 4626 2827 52171 58596 10794 24672 38550 40092 34181 30326 26728 29555 28013 24929 20560 21588 18761 16705 15934 21074 21845 20046 21588 25957 28784 26471 24672 30326 32382 35723 38807 49344 60909 58596 21331 3084 2570 2827 3084 3084 2827 2827 3084 3341 4112 2056 3855 7196 4883 3598 15420 31868 38036 37265 36751 38293 39064 38550 37265 36494 37522 36751 36751 37522 37522 37265 37522 38293 36494 36751 37265 37522 37522 37265 37008 36494 37008 37008 36494 35980 35980 36494 35980 35723 5911 4369 5397 4369 2313 3341 7967 42405 45232 45489 41634 35980 43433 30583 18247 15677 12079 12850 13621 14135 13364 12336 10794 10023 8481 12336 13878 11565 6682 4369 4626 3598 3341 3598 3598 2827 1799 2313 3341 3855 3855 3341 3084 3341 4112 2313 49344 60395 16191 19018 39835 39321 33410 33667 30326 30583 29041 26214 21588 22873 20046 18761 18761 25186 24929 23901 23901 27242 30069 29555 29041 32382 33667 35466 42405 59881 57311 58339 14392 3341 2570 2827 3084 3084 2570 2570 2827 3084 4369 5140 5140 4369 4112 5140 5911 5911 12079 25957 37008 36751 34952 37265 39064 37522 39321 38036 38036 38550 37779 36237 35980 36751 36751 36751 37008 37522 37265 37008 36751 36494 37008 36494 36237 35980 35980 36237 35980 35980 6425 4369 3855 4112 3855 5654 7453 42662 46517 47031 43176 38807 46003 41377 27499 13107 12593 13364 14135 14135 12593 10794 7967 6682 7453 6425 4883 3598 3598 4626 2570 1799 2313 2570 3855 4369 3855 4369 4369 2570 3341 2570 2827 4112 4369 3084 43690 60138 47802 12079 32639 39835 35723 31354 31354 32639 31097 28784 24672 24158 22102 21588 22873 27756 27756 26214 26214 28784 30069 28784 29812 32896 36751 31868 52685 60909 59624 57568 4883 4369 3084 3341 3084 3084 2570 2570 3084 3341 4112 2827 3598 5654 6425 5397 4112 4626 6168 2313 7196 23901 37008 39578 37779 37522 37008 36751 37522 38807 38293 37265 36751 37522 37008 37008 37008 36751 36751 36751 36751 37008 36494 36494 35723 35723 36237 36237 36237 36237 4883 4883 4626 4112 4112 5140 7710 42148 43433 48573 43433 43433 47031 48573 43947 19018 13621 12850 10794 8738 6939 5140 3341 2313 2827 2313 2827 2570 2827 3855 2827 3341 3855 3598 3598 2827 2056 3341 4112 2056 2570 2827 3341 4369 4112 2313 32639 55769 61166 31868 22359 36237 34695 32125 33410 30326 32382 29555 26471 23901 24158 23387 25700 29298 26728 25186 26214 31354 30840 26214 29298 34438 30069 41377 59367 58339 60909 48316 2056 4369 3341 3084 3084 2827 2570 2570 3084 3341 3598 4369 5140 5140 4626 4369 5140 6425 4883 5911 5654 4369 6682 15677 27499 35980 38293 38550 38807 38293 37008 36494 37522 38036 37522 37265 37265 36751 36751 36494 36494 36751 36237 35980 35980 35723 35980 36237 35980 35980 4112 5140 5140 3598 3084 2827 9252 43947 45232 48573 45232 44718 47545 51914 52685 25443 7967 6939 4883 3598 2827 3341 3598 3598 4626 2827 3084 2313 1285 1542 3341 3341 771 2570 4369 4369 3855 3855 3855 3855 3084 3341 2827 3855 2827 2313 24415 53199 61166 54227 18247 30840 37008 31097 30069 32639 32896 29041 26471 23387 25700 23644 26728 29555 26985 26471 27499 32896 31611 27242 29812 33410 31611 56026 59367 59110 59110 30840 4112 3598 3341 3084 3341 3084 2570 2570 3084 3341 3341 3598 4626 5911 5911 5140 4883 5140 4626 5397 5397 5140 4369 4883 6168 7196 12593 19532 28784 34952 38036 38807 38036 36751 37522 37265 37522 37265 36751 36494 36494 36237 36751 36237 35723 35466 35980 35980 35723 35723 4883 4626 4112 3341 4369 2827 9509 41634 47802 45489 48059 45746 49858 51914 43176 13107 3855 3598 3341 3855 4112 3855 3341 2570 4112 2313 3598 4112 4626 3084 5140 2056 4112 3855 2570 2570 3598 2827 2313 3341 3341 3341 2827 3341 3084 5140 22873 56283 59624 62194 30840 17990 34695 36494 31868 30840 33667 28527 26471 23387 27499 23901 27242 29812 28270 28784 28270 31097 29555 25957 27242 25700 47288 59881 59881 59624 59881 17219 2827 3598 3341 3341 3084 2827 2313 2313 2570 3084 3598 5140 5397 4369 4883 6425 6168 4369 5911 5654 4883 4626 5911 6682 5911 4626 5911 6682 6425 6168 10537 20817 32382 38807 37522 37522 37779 37522 37522 37008 36237 35723 37008 36751 35980 35723 35980 35723 35723 35466 5654 4112 4883 3855 4112 2827 8995 40863 47288 47545 47802 45746 42148 33667 7967 4369 3598 3598 3341 3598 3341 3341 3341 3084 3341 3341 3598 3598 3341 3084 3084 3084 4112 3598 3341 3084 3084 3084 3084 2570 4369 3341 3341 2827 3855 2827 19789 57311 57311 63222 57311 16705 27499 34181 34952 30326 32382 32125 26214 24672 24158 18247 28784 26985 26471 24929 29041 31611 26985 25700 19275 38807 58339 62451 59881 59110 57825 5911 3855 2570 2827 2827 3084 3084 2570 2570 2827 3341 3084 3598 4369 5140 5397 5654 5911 5911 5654 5654 5911 5911 5654 5397 5654 5397 6168 6168 6682 6168 4626 4112 6682 10280 26728 37779 37779 36494 36751 38036 35466 37265 36237 36237 36237 36237 35723 35980 35980 35723 4883 4883 4883 4883 4626 2313 6682 41377 44975 49858 41634 7453 4626 4883 514 5397 3598 3598 3855 3855 3598 3598 3341 3341 3598 3341 3598 3598 3341 3084 3341 3084 3084 3084 2827 2827 3598 3341 3341 3341 2827 2570 4112 4112 4112 3855 14392 58853 57825 61166 62965 53970 23387 31868 32639 33924 32639 34181 27242 23130 25700 17476 21588 22873 22102 24415 24929 33153 27242 17733 30840 58596 61680 61166 63222 59110 38036 3855 2313 3341 2827 3084 3341 3084 2570 2570 3341 3598 3855 4369 4626 5140 5140 5397 5397 5654 5654 6168 6168 5911 5654 5911 5911 5911 6939 5911 5397 6425 6168 5397 5654 6168 6168 8995 26728 38293 36751 36237 37008 35466 36494 36237 36494 36237 35980 35466 35723 35723 4369 5397 3598 4626 4883 3084 5397 42919 50372 44461 7710 5397 2827 3598 4883 3598 3855 3855 4112 4112 3598 3598 3855 3598 3341 3598 3598 3341 3598 3084 3084 2827 2827 3084 3084 2827 3341 3598 3598 3598 3855 3855 4112 4369 2827 4369 8481 57311 60652 61166 65021 61937 56540 26728 34695 36237 34952 33667 28784 25186 25700 17219 21845 25443 21588 19275 30326 29812 23387 25700 58853 60652 64507 61423 62451 58596 15677 2313 2313 3341 3084 3341 3341 3341 2827 2827 3341 3598 4369 4626 4369 4883 4883 5140 5397 5654 5397 5654 5911 5911 5654 5654 5654 5911 6939 5397 5140 6425 6939 5911 4883 4369 4883 5654 7967 16191 34181 37779 36751 36494 36237 36494 36494 36237 35723 35209 35723 35723 5140 5397 3084 4112 4369 4112 4369 42148 47288 20046 3341 5911 1799 3341 3084 3084 3855 4112 3855 4112 3855 3855 3598 3598 3084 3084 3341 3598 3084 3084 3341 2827 3341 3084 3084 3341 3341 3341 3084 3084 3855 3855 3855 3855 2570 4112 5140 42919 57568 63736 63222 65021 63993 50886 25443 37008 41634 35723 31868 29298 24158 16962 25700 23901 21588 23130 33153 30069 21588 58082 61937 64250 64764 63736 60652 51143 4883 2570 3084 3084 3598 3341 3598 3341 3341 3341 3855 4112 3598 4112 4626 4883 5140 5397 5654 5140 5397 5654 5654 5654 5397 5397 5654 5911 6682 6168 6168 5911 5654 5140 5397 6425 6939 5140 4626 5911 5397 12593 32125 37008 35980 36237 36237 35723 35723 35209 35466 35466 5654 5140 3855 4112 3341 3855 3598 40606 44204 3855 7710 1285 2056 6168 3855 4112 3855 3855 4369 4369 4112 4112 3855 3598 3084 3084 3341 3084 3341 3341 2827 3084 3341 3341 3341 3084 3341 3341 3341 3341 2313 3084 4112 4112 4369 3598 3598 20303 56283 58339 65278 64764 63993 63736 42919 27756 39321 38550 37008 33924 27756 22359 31354 23644 21588 34952 31354 21588 58596 64250 63736 65021 64250 65278 62451 32382 4112 3855 3855 3598 3855 3598 3341 3598 3341 3598 4112 4112 3598 4112 4883 5140 5397 5397 5397 4883 5140 5397 5654 5654 5654 5397 5397 5654 5654 5911 5911 5654 5397 5140 5911 6939 6425 7710 4883 4883 4883 5397 15163 37779 35980 36237 36237 35980 35723 34952 34952 35466 10794 4369 4626 5397 2827 3598 4369 39064 23901 3341 1542 6168 4112 2570 3855 4883 3855 4112 3855 4112 3855 4112 3598 3598 3341 3341 3341 3084 3084 3341 3084 2827 2827 2827 3084 3084 3341 3341 3598 3855 3341 3598 3855 3598 4883 3855 5140 6425 47802 57311 61680 64507 65278 64507 63736 38293 31868 39835 43176 38807 35209 30326 36237 30069 32639 34181 26471 59110 64507 63222 65021 65021 64764 65021 60395 12079 5140 4369 3598 4369 4112 4112 3341 3598 3598 3855 4112 4112 4883 5397 5397 5911 5911 5397 5397 5140 5654 5654 5911 5911 5397 5654 5911 5911 5911 5397 5397 5654 5654 5397 5140 4883 4112 3855 7710 5911 4369 4626 4112 31097 36237 36494 36237 35980 35466 34952 34952 35466 26471 7453 4369 5140 3341 3341 4369 34695 11051 4626 3855 2056 4369 4626 4626 2313 3598 3855 3598 3855 3598 3598 3341 3341 3341 3341 3084 3084 3084 3084 2827 2827 2827 3341 3598 3341 3341 3084 3341 3341 4369 4369 3598 2827 4112 3598 5911 4369 31097 58082 61680 65278 64507 65278 63993 62451 36751 33924 39835 38293 37522 34438 35466 33410 34181 38293 58853 63479 63479 65278 65278 63993 65278 63993 43433 3598 4883 4369 4626 4369 4883 4369 3598 3598 4112 4112 4626 4626 5654 5654 5140 5140 5397 5140 5397 5140 5654 5911 5911 5654 5654 5654 5654 5911 5140 5140 5140 5140 5654 5397 4626 3855 6939 3855 7453 4112 3341 5911 4883 17733 36237 36237 36237 36237 35466 35209 35209 35209 43690 12850 3855 4369 3598 3341 3084 28784 3341 1799 4626 4883 3855 3598 4112 4626 3598 3598 3598 3855 3598 3598 3341 3341 3341 3341 3341 3341 3341 2827 3084 2827 3598 3598 3598 3341 2827 2827 2827 3084 2056 3084 3084 3084 3855 3341 4369 5911 20817 58853 58853 65278 65021 63993 65021 64250 60395 35980 41120 47545 52171 53456 52171 50115 48316 39835 61166 64764 65021 64507 64507 65278 65278 63479 23130 5140 4883 3598 5911 4112 4883 4369 3598 3855 4112 4369 4369 4626 5140 4883 4369 4369 4369 4883 5397 5654 5397 5654 5911 5397 5397 5140 5397 5397 4883 5397 5140 4883 4369 4626 4883 5397 4626 5654 4626 4626 7710 3084 5397 7710 36237 36237 36237 35980 35723 35466 34952 35209 47802 36237 4883 4369 3598 3598 5397 13621 3598 3855 4112 4369 4369 4112 3598 3598 4369 3855 3855 3598 3341 3341 3341 3598 3084 3084 3341 3341 3341 3341 2827 2570 3084 3341 3598 3598 3598 3341 3084 3084 3084 3084 3084 3598 3598 3855 4626 4883 9509 54998 59624 62965 64507 64764 65278 64764 63993 62708 61166 58853 59624 57054 47288 41120 12593 5911 15420 57568 64764 65278 65278 64507 63222 54741 5911 5654 5911 4883 3341 5140 4369 4626 4626 4626 4626 4626 4883 4883 5654 5654 5654 5397 5140 4883 4883 4883 5397 5397 5140 5397 5654 5654 5140 4626 4883 4626 4626 4626 4883 4626 4626 4369 3855 4626 4883 4883 4626 4112 3341 3598 32896 35466 35466 37008 35209 36494 34695 35980 47031 42919 18247 2827 3855 3341 4883 7710 3598 3598 3855 4112 4112 3855 3855 3598 3855 3855 3855 3598 3598 3598 3598 3598 3341 3341 3341 3341 3341 3341 3084 3084 3084 3341 3598 3855 3341 3084 3084 3084 3341 3341 3598 3341 3341 3341 3855 4112 6425 43690 60909 62194 62965 65021 63993 65021 63993 34952 20303 18761 12850 8738 7453 6168 6168 6425 3855 9766 52685 63993 65278 64507 65278 29812 5654 3855 4369 4883 4369 5140 4883 4883 4883 4883 5140 5397 5140 5140 5140 5140 5140 5397 5397 5397 5654 5654 5654 5397 5397 5654 5654 5654 5140 4883 5911 5654 5397 5140 4883 4626 4369 4112 3855 4112 4883 4883 4626 3855 3598 3598 21588 34695 37265 34952 35980 35723 35209 36751 45489 45746 34181 12079 3598 4626 6168 4112 3598 3341 3598 3855 3855 3598 3341 3598 3598 3855 3598 3598 3598 3341 3341 3341 3855 3855 3598 3341 3341 3341 3084 3084 2827 3084 3598 3598 3341 3084 3341 3341 3084 3341 3598 3341 3341 3341 3341 3598 5654 30326 57825 62451 65021 63479 65278 63993 28527 5911 1799 7453 4626 4369 6168 4626 4112 5140 7967 4626 11051 55512 65021 62708 57825 10023 6168 4626 4369 5654 5140 5140 5140 5140 5140 5140 5397 5397 5140 5140 5397 5397 5654 5654 5654 5654 5911 5654 6168 5911 5911 6168 6168 6168 5911 5654 6425 6168 5654 5140 4883 4369 3855 3598 3855 4369 4626 4626 4369 3855 3855 4112 10794 37522 37522 34952 36237 34952 36237 35980 45232 43176 44204 30583 4626 5654 5654 4112 3598 3341 3598 3855 3855 3855 3341 3598 3598 3855 3855 3855 3598 3598 3598 3341 4112 4112 3855 3341 3341 3341 3341 3341 3084 3341 3598 3598 3084 3084 3084 3341 2570 2827 3341 3341 3341 3341 3598 3598 4626 21074 57568 64507 65021 64250 63736 26728 4883 3855 6682 6939 5911 6939 5140 3855 6939 3598 2570 2827 7710 12336 58339 63993 34952 6168 5397 6168 5140 5397 5140 4883 5140 5140 5140 5140 5397 5140 4883 4883 6168 6168 6168 5911 5654 5397 5397 5397 5911 5911 5911 6168 6168 6168 5911 5654 5654 5397 5140 4883 4626 4369 4112 3855 4112 4369 4369 4369 4112 3598 3855 3855 5654 35980 34695 36751 35723 35209 37522 34952 46517 41891 47288 40863 11565 5140 3598 4369 3855 3855 3598 3855 3855 3855 3598 3855 3598 3855 3855 3855 3855 3855 3598 3598 4369 4112 3855 3598 3341 3341 3341 3341 3341 3341 3598 3598 3084 2827 2827 3084 2570 2827 3084 3341 3341 3341 3598 3598 5140 9252 51657 60909 63736 65278 25443 3341 4112 5911 5397 2827 4112 5397 4112 6939 5140 5140 5911 5397 4883 3855 21331 58853 12336 5397 4626 5397 4883 4369 5397 4369 5397 5397 5397 5140 5397 5397 5140 5140 6425 6425 6168 6168 5911 5397 5397 5140 5140 5397 5397 5397 5397 5397 5397 5397 5397 5140 4883 4626 4883 4626 4369 4112 4369 4112 4369 4112 3855 3341 3598 3598 3598 23130 35466 36751 34952 35980 37265 35723 46003 42662 44975 38550 21845 5911 2056 4112 3855 3855 3598 3598 3855 3855 3598 3855 3341 3855 3855 4112 4112 3855 3855 3598 4112 3855 3855 3598 3341 3341 3341 3341 3341 3598 3598 3341 2827 2570 2827 2827 2827 3084 3341 3341 3084 3084 3341 3341 3598 6939 32125 61680 65278 24929 5140 5654 4883 2827 3855 3855 3598 4369 3855 4883 4112 4369 3598 4112 2570 8481 3341 17219 5911 3341 6939 4883 5140 4626 5911 4626 5654 5654 5397 5397 5654 5654 5397 5397 5911 5911 5911 6168 5911 5654 5397 5397 5140 5397 5654 5654 5397 5397 5654 5654 5654 5397 5140 5140 4883 4883 4626 4369 4112 4112 4369 4112 3855 3341 3598 3598 3855 8995 37522 35980 36237 37008 35209 37779 45232 42919 42919 37522 29555 5654 3855 2827 3855 3855 3598 3855 3598 3598 3598 3855 3341 3598 3855 3855 4112 3855 3598 3598 3598 3598 3598 3598 3598 3341 3341 3341 3084 3341 3341 3341 2827 2827 2827 3084 3084 3084 3341 3084 3084 3084 3598 3855 2827 7196 17219 61166 30069 4626 2827 2827 3855 2570 5397 4883 2313 3855 4369 3341 4626 5140 5654 4112 7710 2313 5140 6168 6939 3855 7967 5654 5140 5654 5397 4883 5397 5397 5397 5397 5654 5654 5397 5397 5654 5911 5911 5911 5654 5397 5140 4883 5397 5654 5911 5654 5397 5397 5911 6168 5654 5397 5140 4883 5140 4883 4626 4369 3598 3855 4369 3855 3855 3341 3598 3341 4369 3855 30583 36237 36751 36494 35209 38036 46003 42919 42662 41377 31097 4883 5654 1542 3598 3341 3341 3598 3341 3341 3341 3598 3084 3341 3598 3855 3855 3855 3598 3341 3341 3341 3598 3598 3598 3598 3341 3084 2827 3084 3341 3084 2827 2827 3084 3341 2570 2827 2827 3084 3084 3598 4112 4626 5397 2570 8224 26985 3855 4883 4626 2570 3598 3084 4369 3598 2827 3341 3084 4883 4112 4112 5397 4883 3084 7710 5140 5911 4626 5654 5140 5397 3598 5911 4112 5140 4883 4883 5140 5140 5397 5397 5397 5397 6168 6168 6168 5911 5654 4883 4626 4369 4369 4883 5140 5140 4626 4626 5140 5397 4883 4626 4626 4626 4883 4883 4883 4626 3341 3598 4112 4112 3855 3341 3341 3341 2570 4369 18247 36751 36237 35466 37522 36751 46260 42148 43690 39835 32382 7710 3084 3855 3341 3084 2827 3341 3084 3598 3855 4369 3341 3341 3341 3341 3341 3598 3598 3855 3855 3598 3341 3341 3598 3598 3341 3084 3341 3341 3084 3084 2827 2827 3084 3084 3598 3598 3341 3084 3084 3341 3598 4112 4112 4112 7196 3855 4112 6425 2570 4112 5911 4626 5140 4112 5911 2827 5397 10023 7453 8738 11308 5397 4883 4626 4883 5911 5397 5140 4883 5140 5654 5654 5397 5140 5397 5397 5397 5397 5654 5911 5654 5911 5911 5654 5397 5654 5911 5911 5911 5654 4883 4883 4626 4112 4112 4369 5140 5654 5397 4883 4626 4369 4369 4626 4626 4626 3598 3598 3598 3855 3855 3855 3855 4112 4369 3855 8738 37008 36751 35980 37522 36494 46774 43176 43433 39835 32639 6682 2570 3598 3341 3084 3341 3598 3341 3341 3341 3598 3341 3598 3598 3341 3598 3598 3855 3855 3598 3341 3341 3341 3598 3598 3341 3084 3341 3341 3341 3084 2827 3084 3084 3084 3341 3341 3341 3084 2827 2827 3341 3855 4369 4112 6425 4369 2827 3341 2056 4626 3084 3855 3341 32639 43433 50886 54998 52428 53970 51400 47288 40349 7196 4112 4883 4112 4883 4883 4626 4883 5397 5654 5397 5140 5140 4883 4626 4369 4626 4626 4883 4883 5140 5140 5140 5654 6168 6168 6168 5654 4883 4883 4626 4112 4112 4369 5140 5654 5654 5140 4626 4369 4369 4369 4112 4369 3855 4112 3598 3598 3341 3341 3341 3341 3084 2827 5654 31354 35980 36751 37008 37265 46260 43690 42405 39578 33153 5140 2570 3341 3341 3341 3598 3598 3341 3084 2827 2827 3341 3598 3598 3598 3598 3855 3855 3855 3598 3341 3084 3084 3341 3598 3598 3341 3598 3598 3341 3341 3084 3084 3341 3341 3341 3341 3341 3084 2827 2827 3341 3598 3084 4369 5397 4369 3855 5911 6425 4112 6425 5397 26471 52171 61423 64507 65278 65278 64507 64507 61937 57054 23130 6425 4369 3855 4369 4369 4626 4883 5140 5397 5654 5397 5140 4883 4369 3855 3855 4112 4112 4369 4626 4883 5140 5911 6168 6168 5911 5397 4883 4883 4626 4112 4112 4369 4883 5397 5654 5140 4626 4369 4112 4112 3855 3598 4369 4369 3855 3855 3598 3341 3084 3084 2570 2827 2570 22616 35466 37008 36494 37779 45489 43947 41634 39578 32382 3855 3341 3341 3341 3598 3341 3341 3084 3084 2827 2827 3598 3598 3598 3598 3598 3855 3855 3855 3341 3084 3084 3084 3341 3598 3598 3341 3598 3598 3598 3598 3341 3341 3341 3341 3598 3855 3598 3341 3084 3084 3341 3855 3855 4626 4112 4369 4112 5140 6682 3341 4369 7196 44718 56540 65278 65278 63479 65278 63993 63736 60652 56797 29555 6168 3855 5397 4369 4369 4626 4883 5140 5140 5654 5654 5397 5140 4626 4112 4369 4369 4369 4369 4626 5140 5397 5911 5911 5654 5140 4626 4626 4626 4369 4112 3855 3855 4369 4883 5140 4883 4369 4112 4112 4112 3855 3598 4369 3855 3855 3598 3341 3084 2827 2827 3341 3084 2313 14135 36751 37265 36494 37522 45489 44204 41634 39835 29298 3341 3855 3598 3598 3598 3084 2827 2827 3084 3341 3341 3341 3341 3341 3598 3598 3598 3598 3598 3598 3341 3084 3084 3341 3341 3598 3341 3598 3598 3598 3598 3341 3341 3341 3341 3598 3598 3598 3598 3084 3084 3341 3855 4369 3598 3341 5911 5654 4112 6168 7196 5397 26214 51657 63736 65278 65021 65278 64507 65021 64764 61166 57825 16191 4369 4626 4112 4369 4626 5140 5397 5140 5397 5397 5654 5140 5140 5140 5140 5397 5140 4883 4883 5397 5654 5911 5911 5397 4626 4112 3855 4626 4626 4369 3855 3598 3598 4112 4369 4883 4369 4112 4112 4112 4112 3855 3598 3598 3855 3341 3341 3084 3341 3084 3084 3855 3084 3598 7967 37008 37008 36751 37008 45746 43690 42148 40092 24415 3084 3598 3341 3341 3598 3084 2827 2827 3084 3598 3855 3341 3341 3341 3598 3598 3598 3598 3598 4369 3855 3341 3084 3341 3341 3341 3341 3598 3598 3598 3598 3341 3341 3341 3341 3084 3341 3341 3341 3084 3084 3084 3598 3084 4112 4369 4626 6682 6425 4883 4883 13364 41891 56797 65021 64764 63993 65278 65021 63736 61423 59110 43947 5397 4626 6682 3341 4883 5140 5654 5654 5397 5397 5397 5654 5140 5140 5397 5654 5911 5654 5397 5140 6168 6168 5911 5397 4626 3855 3598 3598 4626 4626 4369 4112 3598 3598 4112 4369 4369 4112 4112 4112 4112 4112 3855 3855 3598 3598 3341 3084 3084 3341 3084 3341 3598 2570 3855 4112 34438 37265 37265 37008 45489 42919 42405 39835 19532 3341 3084 3341 3341 3341 3598 3598 3084 3341 3598 3598 3341 3341 3598 3598 3855 3598 3598 3598 4883 4369 3855 3341 3341 3341 3084 3084 3341 3341 3598 3598 3341 3341 3084 3084 3084 3084 3341 3341 3084 3084 3341 3598 3598 5140 5397 3341 4883 4883 4112 10794 37779 52942 63736 64507 65278 65278 65278 64250 65278 60395 53713 19275 5140 5397 5911 5397 5397 5654 6168 5911 5397 5140 5140 5397 5654 5654 5654 5654 5911 5911 5654 5654 6168 6168 5654 4883 3855 3598 3855 4112 5140 4883 4883 4369 4112 3855 4112 4369 4369 4369 4112 3855 3855 3855 3598 3341 3341 3598 3341 3084 3084 3084 3341 3341 3341 3084 3341 3598 29298 37522 37265 37522 44975 41891 42405 39835 16191 3598 3598 3855 3598 3341 3598 3598 3598 3341 3084 2827 3341 3598 3598 3855 3855 3855 3855 3598 5140 4626 3855 3598 3341 3084 3084 2827 3341 3341 3598 3598 3341 3341 3084 3084 3084 3341 3598 3598 3341 3341 3598 3855 4369 2313 5140 6682 6939 3855 11822 37522 52685 59881 64764 65021 63736 65021 64764 65278 61937 52428 44718 5397 6425 7196 4112 6425 5654 6168 6425 6168 5397 5397 5397 5654 6425 6425 6168 5911 6168 5911 5911 6168 5911 5911 5397 4369 3598 3341 3855 4369 5397 5654 5140 4626 4626 4369 4626 4883 4883 4626 4369 3855 3855 3598 3341 3084 3341 3341 3341 3341 2827 3084 3084 2827 3855 3855 2827 4112 25443 38036 36751 37779 43176 42919 42405 40863 13107 3084 4112 2570 4112 3855 3341 3084 3084 3084 3084 3341 2827 2313 4883 1028 6168 3855 2570 4112 4112 3084 2827 4626 1799 3341 3341 2313 1799 5397 3598 2827 3341 3598 4626 3084 4112 4112 3855 3598 3341 3341 3598 3855 4883 2827 6425 5911 6168 5397 39321 49344 58596 64507 63993 65021 65021 65021 64507 63736 54227 48573 15677 6682 6939 5911 4626 6425 6939 6425 5911 5911 5140 4369 4369 5140 6168 3855 6168 5397 4883 6168 5140 4883 5654 4369 4883 2827 5654 3341 4883 4883 5140 3341 5140 3598 5140 5654 4883 4626 4883 4112 4369 4112 4112 4112 2827 3341 3855 3855 3855 3598 3341 3341 3341 3341 4369 3084 3341 4626 20560 37779 38807 37522 43690 42919 41891 40863 10023 2570 4112 3084 3855 3598 3341 3341 3084 3341 3084 3598 5654 2313 3084 6682 2827 2827 7967 3084 4369 4112 4112 5654 2570 3855 4112 4112 2570 1799 2827 3341 5654 5911 1028 6168 3855 3855 3855 3341 3084 3084 3341 3598 3598 4883 4626 4369 6168 8481 32639 50115 57054 65021 65021 64764 65021 65021 62451 55255 40606 29041 8995 5140 5654 6682 5140 5911 6425 6168 5397 5654 5397 4626 4626 5140 5911 4369 5654 7196 6682 5911 6682 6682 5397 5140 4112 3598 2056 3855 4883 4883 6425 3598 7196 2570 3598 3341 4369 4883 4369 3341 3341 3855 4369 4369 3855 4112 3598 3855 3598 3598 3084 3598 3341 3598 3855 3341 3855 4112 17219 38293 38807 38036 44718 41891 41120 40606 6425 2313 4112 3855 3598 3084 3084 3341 3084 3341 3084 3084 1799 3598 33667 35466 35980 31354 28527 23644 21588 18504 13621 10537 5140 4626 3341 2570 4883 3855 4626 3598 2056 5397 2313 3598 3855 3855 3598 3341 3084 2827 3084 3341 2827 6168 2827 4112 5140 7710 19532 47031 55769 62194 63993 65021 65021 63479 53713 38550 29041 12079 5397 5911 5654 7453 5654 5654 6168 5654 5654 5911 5911 5140 4883 5397 4883 6682 4883 5140 4883 5140 5140 3598 4369 5654 5654 6425 3084 5654 5397 4883 3084 5397 4626 4883 6168 5654 3341 3598 4626 4112 3855 4112 3855 2827 2570 3084 3084 3341 3341 3341 3084 3598 3341 3598 3855 4112 4112 4112 12850 39064 38550 39064 44461 41634 40863 39321 3855 2827 3855 3855 3084 3084 3341 3598 3341 3341 3084 3084 7453 2056 34181 47802 65021 65021 62965 37008 37779 39578 41377 45746 46774 47545 43433 40349 34438 32896 25700 19789 3855 771 6425 4369 3855 3855 3598 3341 3084 3084 3084 3341 3341 4883 2827 5654 5140 5140 7967 38807 49601 60395 64764 63993 61680 56026 40092 26214 21588 6425 6425 6939 6168 6425 5911 5397 5397 5654 5911 5911 5911 5397 5397 5397 3598 6939 5911 5654 6168 5654 6682 19275 14906 16448 17476 17476 17219 16962 16962 17476 18504 28784 5140 4369 3855 4626 2570 5140 3341 3598 3084 4626 4369 2827 3598 4112 2827 3084 3341 3084 3341 3598 3341 3855 3598 4369 4112 3341 8995 38550 38036 38550 43433 41377 41634 35723 3341 3341 3341 3341 2827 3084 3341 3598 3341 3341 3084 3084 2570 8224 36494 50372 63993 64507 53713 31354 37522 38807 38550 41120 42662 46517 46774 47545 45746 46003 43433 43947 24672 4626 1542 2313 3855 3855 3855 3598 3341 3341 3855 3855 4112 3341 4626 6425 6168 5911 4883 25700 46260 56026 62965 60395 54741 39578 27242 31097 12593 5140 6168 5911 6168 4112 5397 5654 5140 5654 5911 6168 5911 5654 5654 5140 7967 4626 3598 5140 7453 4112 4369 35980 21588 22873 21331 21331 23130 22873 22359 25443 22873 45489 7710 4369 4883 5140 4626 4626 5140 4626 2827 4369 4112 2056 3084 2570 3084 3341 3341 3341 3598 3598 3855 3855 3341 4112 3341 2827 6168 34952 37265 37779 43176 41377 42148 29298 3598 4112 3084 3341 3341 3084 3341 3341 3084 3341 3084 3084 1799 10794 43176 47802 54741 58082 51914 41377 36751 42405 44975 47031 46003 46774 45746 47031 46774 44461 43433 44975 39321 20046 4626 3598 3598 3855 4112 3855 3598 3598 4112 4369 4883 3341 5140 4626 5654 6939 5654 11051 45746 49858 57311 56283 43176 33410 35980 34952 5654 5140 5911 5140 6682 3855 5397 5397 5397 5911 5911 5911 5911 5911 5397 5140 4626 5397 7710 4369 5140 5654 5397 39321 21845 22359 22616 22616 23130 23130 23387 23901 25186 44975 10537 4883 4626 2570 5654 3341 2570 3855 3341 5911 5140 1799 3598 3598 3341 3341 3341 3598 3855 3855 4112 3855 4112 4112 4626 3855 5397 30583 37779 38036 42919 42405 42148 22102 4112 3855 2827 3341 3598 3598 3598 3341 3084 3341 3084 3341 5654 14906 43947 54227 52942 49601 57054 38293 35723 43690 48316 51400 51143 51400 49601 50886 50629 49087 48059 46517 48830 38807 20817 2056 3341 3855 4112 4112 3855 3855 4369 4626 4883 4369 4626 4626 5140 5654 6425 4369 34438 51143 55769 47288 34695 39835 49858 20303 4883 5397 5911 5140 5911 5397 5397 5397 5654 6425 6425 5911 5397 5654 5397 4883 6168 4883 4626 6939 23644 40092 26985 28270 25186 21331 30840 25186 25957 22359 29812 20817 32896 35980 23387 32382 26471 23644 19789 17733 19018 25700 28013 26985 16191 3855 3084 3855 3341 3855 3855 3855 4369 4369 3855 3855 4626 4112 5911 4112 4883 24672 38293 39321 42919 42662 42148 17476 4369 3598 2827 3855 3855 3598 3598 3341 3084 3084 3084 3341 3855 19532 33153 44718 57568 55769 38293 31354 34695 39578 40092 41377 42148 44204 43947 46003 45746 44718 43947 42919 43947 42148 39064 8481 3341 3598 3855 4112 3855 3855 4369 4626 3855 4626 3598 6682 5911 3855 7196 6425 18761 50372 48059 39835 35466 35723 40092 4112 5397 3598 5140 4112 4369 6425 5140 4883 6168 6682 6425 5654 5397 5911 5397 4883 4369 6425 11051 21074 42662 62451 44975 27499 35723 25957 44461 29555 33924 25443 42405 25186 47545 28270 27756 49601 34695 32896 22616 24929 20817 34695 42662 40092 22102 3341 2056 4626 3855 4112 3855 4112 4369 4369 4112 3855 3855 3855 5654 3855 4112 20046 37779 39321 44204 41634 42405 10023 3598 4369 3084 3084 3084 3084 3341 3341 3084 2827 3084 3341 4112 13621 23901 28784 28270 28270 31868 36237 36494 37779 39578 40863 41891 42662 44204 45489 42662 43433 41891 41891 41634 40349 39578 8224 3084 3341 3598 3855 3598 3341 3341 3084 4112 4369 4369 4369 4626 4626 4883 5140 4112 46260 44718 29298 20046 36751 10794 3598 5140 4369 3598 3855 4626 5397 5397 5654 7196 5654 4626 5397 6168 5397 5140 5911 5654 12079 20046 26728 47802 61166 44975 26728 35209 25700 46774 32125 36751 30326 43176 27499 47288 30840 26985 45232 36237 32639 24415 23901 22359 30069 38036 41377 21588 4369 3598 4112 3855 4112 4369 4112 4369 3855 4112 4369 4369 4626 5140 3598 5140 15420 37779 39321 43690 41891 40092 7967 2827 4112 3084 3598 3598 3598 3855 3598 3598 3341 3855 3855 2313 3341 4112 3084 1799 2570 5397 8224 10023 12079 14906 18247 21845 25186 28527 30326 34181 36751 37522 38550 39064 39321 35723 7710 3084 3341 3341 3341 3341 3341 3598 3598 4112 4112 4112 4369 4369 4112 4369 4626 5911 23130 36494 17990 14906 24415 3084 4883 5140 4626 4369 4112 4626 4883 4883 5397 4883 4626 5397 6168 5140 4369 4883 6425 13107 22359 26471 25186 22873 30326 22359 23644 30326 39578 35723 30840 32125 38550 27242 28784 34952 28013 22359 25443 22102 24672 21845 19275 20560 20817 22102 30840 9766 2313 2827 4369 3855 4112 4626 4883 4369 4112 4112 3855 4626 4369 4883 4626 4883 11822 38550 39578 42919 42148 35466 5397 2570 4112 2827 3855 3084 3341 3341 3341 3084 3084 3598 3598 3855 3341 3084 3341 3598 3598 3598 3341 4112 4112 3855 3341 2827 2570 3084 3341 4112 3598 4883 8224 8995 11308 13621 3855 3084 3084 3084 2827 3084 3341 4112 4369 4112 4112 3855 3855 4112 3855 3855 4112 4626 6682 21074 15163 15934 8481 3084 4369 4112 3855 4112 4369 4626 4883 5397 5654 5911 5397 5397 5140 4883 6168 10280 14906 31868 44975 45232 39835 20046 20046 20817 34952 41891 48573 19018 37522 46260 58596 16962 23387 44204 38807 29555 22616 22359 22102 21074 20817 21588 20046 21074 34695 7967 4369 3341 5140 3855 3855 4626 4883 4369 4112 4112 4112 4369 4112 4369 5911 4369 7453 39321 39321 42148 42148 30840 3084 2313 4112 2570 3855 2827 2827 3084 2827 3084 2827 3341 3341 2827 3084 3341 3855 3855 3855 3598 3598 3598 3855 3855 3598 3341 3084 3598 3855 3598 2056 3341 4369 1799 2056 3084 3084 3084 3084 2827 2827 3084 3341 3855 3855 4369 4112 3855 3598 3855 3855 3855 3598 3084 3855 7710 16191 14392 2056 5654 3598 3341 3855 4112 4112 4112 4626 5397 6168 6682 5911 5397 4626 5654 8738 14649 19532 35980 46774 43690 38807 21074 20303 20817 29812 41377 49344 25186 38036 40092 53970 23130 28527 42919 36237 27756 22873 24415 21331 19789 21331 22616 21845 23901 33410 9766 4112 2056 2827 3855 4369 4369 4369 4369 4369 3855 4112 4369 4626 4369 5911 4112 4626 40092 38807 41377 41377 25957 2827 3084 4112 2313 3598 3341 3341 3598 3341 3341 3084 3598 3598 3855 4369 4112 3341 2570 2313 2827 3341 3084 3341 3598 3598 3341 3084 3084 3084 3084 3598 5140 2827 3084 6425 4112 2313 3084 3084 3084 3084 3084 3084 3084 3084 4112 4112 3598 3855 3341 3341 3341 3341 4369 3341 4626 9766 6425 4626 3084 5140 4369 4112 4112 3855 4112 4369 4883 5654 5397 5140 4626 5140 6168 7710 10280 12079 17476 21331 16962 15934 9766 10537 10280 11565 14906 16191 7196 9766 9252 17733 8995 9766 12593 10023 6939 6168 6168 6682 5654 4883 6425 4883 5654 9509 3341 2313 4369 5140 4112 4369 4369 4112 3855 4112 4112 4369 4112 4626 4626 4883 4369 4626 38293 38550 41634 40092 21074 3341 3598 4112 2570 3341 3084 3084 3341 3084 3084 3084 3084 3598 3598 3598 3855 4369 4883 4626 4112 3598 3855 4112 4369 4626 4626 4626 4369 4112 4626 3855 5397 2056 2313 3855 2827 3084 3341 3341 3084 3084 3084 3084 2827 2827 3598 3598 3855 3855 3598 3598 3598 4112 4369 3855 4883 3855 2570 5654 2827 3855 3598 3855 3855 4112 4369 5140 5654 6168 5140 4626 4369 4626 5654 5911 5140 4883 5397 5654 4883 6425 6168 5911 6682 7196 6682 5654 6425 7710 7453 6168 5654 6682 6168 5654 5911 8224 4112 5654 5397 4883 6425 4369 4112 1542 4112 2313 4369 3855 4369 4369 4112 3855 3855 4112 4112 4626 4112 4626 4883 3855 4626 4626 32639 38807 42662 38807 17476 3855 3855 3341 3084 3341 2827 2570 3084 2570 2827 2827 2827 3341 4883 3598 2313 1799 2570 3341 3598 3598 3341 3341 3341 3341 3341 3084 2827 2570 2313 514 4626 4112 5140 1799 2827 4626 3341 3084 3084 2827 2827 2827 2827 3084 3341 3341 3598 3855 3855 3855 4112 4626 3598 5140 3855 4369 3341 4112 5140 3084 3598 3855 4369 4626 5397 5654 5911 5911 5140 4883 4883 4369 5140 5140 5654 5654 6682 5654 7196 8224 6682 5654 4883 6168 6939 8224 6939 4883 5911 8481 7967 4112 5654 5397 5654 7710 5397 4626 5140 6425 4112 5140 6425 4112 4626 3084 3341 3084 4112 4112 4112 4112 4112 4112 4112 4369 4626 3855 4883 3341 4883 4112 24929 39578 43433 38036 15420 4112 3855 2827 3598 3341 3084 3084 3341 3084 3084 3341 3341 3598 3341 3598 3855 3855 3855 4112 3855 3855 3855 3598 3598 3598 3598 3598 3598 3598 4369 2056 4369 1542 4626 3341 5397 3084 3598 3084 2827 2570 2570 2827 3084 3341 3341 3341 3598 3855 3855 4112 4369 4626 5397 2570 3084 5397 2056 3855 4883 4626 5397 5140 5140 5911 5911 5397 4626 3855 3598 4369 5140 5140 5140 5140 5911 6168 5911 5397 5911 5911 4369 7196 5911 6168 5140 5911 5654 8738 6682 5140 5140 6682 5654 6939 5911 3341 5397 5911 4883 4369 4883 4883 4883 4626 2313 4369 3855 5140 4112 4112 4626 4369 4112 4369 4112 4112 4883 2827 4883 3341 5140 3341 19275 40349 \ No newline at end of file diff --git a/Tests/images/hopper_1bit.pbm b/Tests/images/hopper_1bit.pbm new file mode 100644 index 0000000000000000000000000000000000000000..216a94ffa8902f6105f46818851cb88039554acf GIT binary patch literal 2059 zcmajg&x<2P6bJB^>DhExf~B`zn3)a;det3~g@rA3z@zKMt1O5&LH_`;Y783ovcuv@ zMxFT+7CfxrV0Rc~kMZb1m=+In+eG%#A(eW5>s5EA69o&Bs!zT8m8$B_`)^!-{nqUP zKG&JqWoHSpRhpY$*32e9Tm?hHJ9rZ z=u=rVksdMs%v|2%c`vMj(f>zSv3ejrW_~jDy?1W+3vrI?IVsV$*O>G3BG^hCi#&V# z%NL2ZuROMJzilFV1s}|ZboyI?Qa4a+Z_xcdZLLf_Dw3jQ%Ip?xH*rtf9h-=xzoTBq zX~L997`~#{60@+vV4Oyhj*FF1>QV=_At8>j`$V-fW=eWHM4!dD4*u>2(cr#Rh+S}O z0Ndmb3}a&){cYk?wshq*+!rdt7kR|HE#vTt6vsR3#5gmo@VK>|THLo&^p^FO-Fql> zY+zV_d2RF-(MvW^e($y6fanup^uaBn`O4Mq3!0MWg;bz_weucU{k&}Kc0R6z!+6VG2ajFTewjI3sv$>DxUme#ikIvr%n8l~iE)wHJq_)p1GWl24{uW}MI^Zq+yF!i`#N1Jjv)UnJ`f8BH0?rS~5@k}TpL$qF$IMc>Zap&+x0z%G z*RQ|YV|I01mci#7=iwsyQhMR5_Ovmc+qLvBr!m~rH?Ogk>;6TZ*Khe|9&T(m;*`a4 zt61+h?CR~{I_{dcBm5LSeYi&6e~;XPeth~qb)z1RgKh*Mv%#?8oh z9A3qT%zuCI(NVAuJ{C`crSS!_VX}M0TA+X4Q0GJCu Aj{pDw literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_1bit_plain.pbm b/Tests/images/hopper_1bit_plain.pbm new file mode 100644 index 00000000000..9f5c6e501ff --- /dev/null +++ b/Tests/images/hopper_1bit_plain.pbm @@ -0,0 +1,14 @@ +P1 +128 128 +111111111111111111111111111111000001 +111110111100000000111111111111111111 +111111111111111111111111111111111111 +111111111111111111111111111111111111 +111111111111100101111111101111100000 +001111111111111111111111111111111111 +111111111111111111111111111111111111 +111111111111111111111111111111111111 +011111111011111110000001111111111111 +111111111111111111111111111111111111 +111111111111111111111111111111111111 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 0 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 1 1 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 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 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 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 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 0 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 0 1 0 0 0 0 1 1 0 0 0 0 1 1 1 1 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 1 1 1 0 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 1 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 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 0 0 0 1 0 0 1 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 1 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 0 0 1 1 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 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 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 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 1 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 1 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 0 0 0 0 0 1 1 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 1 0 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 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 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 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 1 1 1 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 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 1 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 1 1 1 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 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 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 1 1 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 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 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 1 1 1 0 1 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 1 0 0 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 1 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 0 0 0 1 0 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 1 0 1 0 1 1 1 1 1 0 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 1 1 0 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 1 0 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 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 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 0 0 0 1 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 1 0 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 \ No newline at end of file diff --git a/Tests/images/hopper_32bit.pgm b/Tests/images/hopper_32bit.pgm new file mode 100644 index 0000000000000000000000000000000000000000..63728fb7dd1ffaba9dd0b031e94441fb9b8931d7 GIT binary patch literal 65558 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 V+K=*SOLqE5|Ix580gg^JQ9% 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%0Aqq#x%T63SRDSolO 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 YO*KSTmU8bzk3UlGfmv^Nr)&HE0J9EJjsO4v literal 0 HcmV?d00001 diff --git a/Tests/images/hopper_8bit_plain.pgm b/Tests/images/hopper_8bit_plain.pgm new file mode 100644 index 00000000000..37d152c73f3 --- /dev/null +++ b/Tests/images/hopper_8bit_plain.pgm @@ -0,0 +1,4 @@ +P2 +128 128 +255 +25 22 14 26 29 29 18 22 34 35 27 23 30 30 26 30 36 37 32 24 22 23 24 31 33 30 21 31 22 110 229 162 181 165 183 117 22 39 36 18 51 173 71 20 38 121 205 235 247 242 240 239 235 197 46 58 103 91 93 94 100 98 105 102 100 89 100 94 93 100 100 98 97 95 95 95 95 96 99 101 100 99 97 97 100 100 101 102 100 100 100 99 98 99 99 98 98 98 98 99 99 99 99 99 99 99 98 98 99 100 101 101 99 98 99 102 108 112 115 115 116 116 116 115 112 111 111 112 25 22 16 27 28 28 17 20 32 33 25 21 29 29 27 32 34 37 31 22 21 21 23 29 25 32 26 27 30 131 177 92 174 59 48 88 63 31 20 27 56 168 73 29 17 31 80 145 201 241 243 241 236 205 42 38 92 95 99 83 73 81 92 98 98 101 98 98 103 96 92 99 99 95 95 102 103 97 95 97 100 99 98 99 101 101 103 102 102 101 102 100 99 99 99 98 100 98 98 98 98 99 99 100 100 100 99 99 98 98 98 99 102 101 100 101 106 109 114 115 117 117 118 116 114 113 113 114 25 24 17 28 28 27 15 19 31 32 23 20 28 29 26 32 31 35 31 23 22 23 24 29 35 29 22 28 34 119 98 62 169 67 29 30 24 33 45 21 72 173 57 18 28 35 24 39 52 201 236 240 237 224 138 29 36 41 37 29 52 38 41 52 45 64 83 102 100 113 97 108 99 99 98 98 97 97 101 106 101 100 100 101 102 102 102 102 105 103 103 102 102 100 99 99 101 100 98 98 98 99 100 101 100 99 99 99 99 98 98 98 103 102 101 101 103 107 112 117 117 118 118 117 115 114 115 116 23 22 18 29 29 26 14 19 32 34 23 21 28 30 26 33 31 37 31 23 22 24 25 28 38 27 21 29 29 70 41 81 179 60 24 25 30 27 36 21 56 176 62 22 22 30 31 33 31 190 246 145 128 172 57 26 39 29 26 79 126 122 74 53 47 47 47 41 80 96 107 93 98 100 98 97 103 109 103 92 102 102 102 103 104 104 103 101 104 103 103 102 102 101 101 101 101 100 100 99 99 100 101 101 98 99 99 99 100 100 99 99 100 101 101 100 101 105 111 116 114 115 115 114 114 114 115 116 21 22 19 28 28 25 15 21 33 34 25 21 28 30 27 32 31 36 33 23 22 24 25 27 23 31 28 23 22 36 21 85 166 48 29 24 36 36 31 19 88 169 33 26 29 24 35 32 45 205 202 47 27 18 32 31 40 152 139 73 136 238 113 32 122 168 79 47 42 53 96 111 101 103 105 104 101 101 104 107 103 103 104 104 106 105 103 102 102 102 103 103 102 102 102 102 102 102 102 102 102 102 102 101 101 101 101 101 101 101 101 100 98 100 102 101 101 103 107 111 109 110 111 111 111 111 112 113 21 22 20 30 28 25 15 22 33 34 25 21 29 29 25 30 30 36 32 21 19 22 23 25 26 34 26 21 26 25 22 83 176 51 34 30 34 27 22 34 90 175 40 30 23 25 31 20 47 216 85 43 20 21 26 88 234 250 252 210 139 253 170 159 249 253 243 170 41 47 54 95 107 96 95 108 110 101 97 103 106 104 105 106 106 106 104 102 100 100 101 101 103 102 103 103 102 103 103 104 103 103 102 101 104 103 102 101 100 100 100 100 100 101 101 101 101 102 103 105 106 106 107 108 108 109 110 111 20 24 22 32 28 24 15 21 30 32 24 22 30 31 26 31 30 36 31 21 18 22 22 24 35 31 21 23 25 24 26 84 167 54 25 25 38 37 29 19 84 169 40 20 20 37 31 40 46 184 164 148 28 23 124 248 222 157 233 244 254 249 242 253 246 209 205 230 194 50 43 136 193 154 117 103 104 105 106 107 106 105 105 105 105 105 104 103 100 101 101 101 103 102 102 102 104 104 104 103 103 103 103 103 105 103 102 100 99 99 100 101 102 101 100 101 102 102 102 101 103 105 107 108 108 108 109 110 22 25 22 33 29 24 13 20 27 30 24 21 30 31 26 31 32 39 33 20 18 21 23 24 24 27 25 26 18 28 27 77 170 60 21 31 37 26 30 28 109 157 30 23 31 31 29 108 216 251 254 135 24 66 122 86 121 95 74 102 166 225 222 164 101 88 130 123 73 101 35 51 235 252 246 201 143 108 103 107 107 106 106 104 105 105 105 103 101 101 101 101 102 102 102 102 106 105 103 102 102 103 104 104 103 102 100 98 98 99 101 102 102 101 99 100 102 104 104 104 104 106 107 108 109 109 110 110 23 27 115 40 24 22 16 17 28 35 22 18 42 34 23 29 27 39 26 24 18 22 17 21 29 28 29 20 19 25 30 79 172 53 26 32 34 35 30 26 98 162 31 35 25 44 194 247 255 250 254 105 30 28 29 136 190 242 232 143 233 198 212 200 147 245 217 212 83 26 47 37 184 254 248 252 251 209 127 106 110 108 100 114 96 112 107 102 103 103 104 103 104 103 103 104 105 103 102 103 103 102 103 105 101 101 100 99 99 100 102 104 100 102 104 103 102 101 102 103 103 103 103 103 104 105 107 107 22 37 204 90 36 15 21 17 37 26 13 20 40 22 29 30 36 34 26 26 21 16 18 27 28 27 24 24 31 27 28 74 170 53 25 33 35 34 28 24 100 162 32 14 78 227 248 253 245 253 249 86 26 33 33 120 186 218 223 245 250 245 251 250 249 228 227 212 87 52 28 33 147 253 253 242 253 252 236 182 115 100 106 110 108 106 98 109 104 103 103 103 103 104 103 104 103 102 101 103 103 102 103 105 102 102 101 101 100 101 103 104 101 101 102 102 101 101 101 101 103 103 103 103 104 105 107 107 16 35 213 223 44 25 16 35 25 30 24 26 27 43 24 26 26 28 33 22 15 23 30 17 33 32 24 21 28 25 31 74 170 56 27 33 35 32 27 22 113 162 31 90 250 254 249 248 248 242 221 63 40 25 31 54 213 204 213 226 219 218 208 218 232 220 179 217 71 39 52 48 153 254 249 254 252 254 254 248 210 125 106 112 110 97 116 98 102 102 102 102 102 103 102 103 102 101 101 102 102 102 103 105 103 103 103 102 102 102 104 105 104 103 103 103 103 103 103 102 104 103 104 104 105 106 107 108 20 39 198 240 128 81 172 91 27 29 21 23 33 125 38 24 34 34 34 25 12 17 17 23 27 29 29 23 20 24 31 72 170 60 27 32 37 32 28 24 119 148 59 245 242 252 246 225 173 173 207 59 12 23 22 38 108 217 203 246 250 250 251 245 250 247 208 164 42 23 29 26 116 236 252 253 243 253 252 253 253 220 115 105 105 111 101 104 100 101 100 100 100 100 100 100 102 100 100 102 103 102 102 104 104 104 104 103 103 103 104 105 106 104 103 103 104 104 103 101 104 104 105 105 105 106 107 108 26 29 195 251 236 229 195 73 31 26 21 26 36 182 94 32 24 34 26 24 24 30 7 23 21 25 25 28 22 26 25 64 172 65 25 29 35 30 29 26 109 156 214 246 252 241 158 137 114 60 33 24 21 32 26 38 184 220 153 214 248 253 254 255 248 239 155 217 106 40 33 39 59 125 190 217 251 252 246 251 243 251 190 105 103 103 105 102 100 100 100 101 100 100 100 100 102 99 100 101 102 101 101 103 102 103 103 103 102 101 102 103 104 102 101 100 102 102 101 100 104 104 104 104 105 106 108 109 35 134 227 241 238 236 162 38 27 30 28 25 31 198 164 27 34 36 95 102 24 14 27 18 30 57 20 26 25 26 24 64 170 69 24 27 35 28 29 26 100 196 238 247 229 122 100 47 16 21 14 23 15 16 32 25 131 164 202 227 225 252 246 250 213 210 191 200 92 33 47 31 35 24 33 99 169 219 245 252 252 253 240 128 114 100 99 109 102 102 102 103 102 102 102 103 101 100 100 102 102 101 101 103 101 102 103 103 102 101 101 101 104 103 102 101 102 102 102 102 103 103 104 104 106 107 110 110 171 230 241 251 243 230 115 16 34 34 18 20 41 212 213 85 80 190 208 81 22 24 20 19 49 138 38 21 24 23 29 63 168 71 24 27 36 27 29 26 97 218 243 235 106 54 14 18 13 12 93 112 32 26 17 39 125 238 232 209 162 227 249 247 192 212 235 237 87 41 39 32 35 104 34 40 27 81 154 192 248 238 232 209 93 114 107 104 103 103 103 103 103 103 103 103 102 101 101 103 103 101 101 102 101 102 103 103 102 101 100 101 105 105 104 103 103 103 104 105 103 104 103 104 106 108 111 112 179 224 241 247 248 248 172 62 27 34 23 37 46 200 230 224 240 230 161 30 30 20 12 22 54 213 71 24 27 24 34 53 168 72 24 28 38 28 30 27 90 228 233 135 26 12 20 106 98 46 33 149 28 63 77 38 98 189 193 191 211 155 96 107 169 188 193 205 99 46 63 35 75 138 28 100 111 27 34 76 163 238 237 225 123 103 106 107 103 104 103 103 103 103 104 103 103 101 101 103 103 101 101 102 101 102 103 103 102 101 100 100 103 103 104 103 101 101 103 105 102 102 103 104 106 108 111 112 27 50 120 247 244 204 192 90 30 36 25 76 166 237 234 232 236 215 58 33 25 29 8 21 70 229 144 26 57 118 48 58 171 78 23 23 36 33 25 29 84 185 141 25 23 26 27 175 204 192 183 146 193 222 234 214 215 121 98 36 32 49 38 37 38 49 82 178 194 231 242 174 184 138 216 218 176 53 97 58 47 164 244 222 159 106 109 105 106 101 107 101 108 102 103 103 105 103 102 102 103 104 103 102 103 102 102 101 101 101 102 103 104 105 106 107 106 106 107 107 108 107 107 107 108 109 111 112 26 27 84 244 191 64 57 42 27 52 147 219 245 237 243 240 234 106 32 30 27 10 34 19 75 227 208 143 195 127 32 41 168 81 25 23 36 33 25 25 22 5 16 18 10 25 22 46 136 150 55 175 227 179 175 224 239 235 243 192 114 78 31 54 106 197 246 234 238 228 200 237 231 140 185 201 189 223 202 46 23 43 189 184 109 54 42 94 106 102 105 100 107 103 102 102 105 102 101 101 102 102 102 101 103 103 102 102 102 103 104 105 106 107 108 108 107 107 109 110 109 109 109 109 110 111 112 113 16 37 79 235 127 28 28 22 30 41 95 177 221 248 243 239 225 170 44 33 16 18 24 28 110 237 220 180 173 51 27 49 163 86 25 23 34 33 22 19 25 16 16 19 15 16 10 22 13 35 60 106 52 34 180 197 172 145 100 75 63 78 50 76 70 81 144 159 213 217 131 77 111 112 18 37 100 192 173 159 163 49 54 29 18 15 28 47 104 105 105 100 108 104 105 104 105 104 102 102 103 103 102 102 104 104 103 103 104 105 106 107 106 107 108 109 108 108 109 110 109 109 110 111 111 112 113 113 20 33 62 207 65 31 29 14 21 38 20 37 74 197 247 246 241 230 108 30 27 47 94 140 208 237 196 182 94 21 23 36 159 90 22 25 33 26 19 14 7 18 18 15 7 14 22 19 26 13 11 26 56 31 31 33 32 34 30 20 30 26 19 36 29 24 45 34 46 49 61 70 173 180 188 64 25 24 95 137 56 34 25 18 26 11 24 14 92 106 105 101 106 104 106 106 107 107 106 106 106 105 105 104 105 105 105 105 105 106 106 107 106 107 108 108 108 108 109 110 108 109 110 111 112 112 113 113 29 34 60 148 32 31 28 19 19 24 25 19 38 196 238 116 130 197 204 58 26 48 85 150 201 221 219 177 44 24 26 38 156 93 18 27 30 19 13 13 17 12 14 23 19 12 17 3 21 16 14 27 16 26 24 13 20 18 26 22 36 24 30 24 31 29 23 28 37 24 25 37 98 130 221 211 80 33 22 15 13 17 15 9 16 14 6 18 74 101 101 102 103 101 104 107 108 109 109 109 109 108 107 107 107 107 107 106 106 106 106 106 108 108 109 109 108 109 110 111 109 110 111 112 113 113 113 113 14 31 43 60 26 34 26 18 29 29 31 35 27 200 188 44 31 38 92 85 20 24 20 32 129 223 225 186 111 28 27 29 151 100 19 26 25 14 12 13 10 18 11 7 23 12 13 24 11 18 14 7 19 15 14 33 32 22 19 20 14 15 23 16 22 11 20 28 17 30 27 28 42 92 48 28 18 24 8 17 12 8 13 18 10 17 11 21 62 101 102 105 105 103 105 110 109 110 111 111 110 109 108 108 109 109 108 108 107 107 106 106 110 110 110 110 109 109 111 112 111 112 112 113 114 115 115 115 27 34 28 32 48 36 22 22 26 31 11 31 40 202 102 35 33 44 37 40 26 13 18 14 98 238 192 190 195 51 14 32 146 111 21 20 17 12 17 11 17 13 19 11 15 16 18 4 14 7 17 17 2 20 15 5 11 15 19 26 17 20 19 19 17 20 24 20 22 28 10 16 12 9 18 8 16 10 0 14 12 12 13 15 4 8 19 16 62 108 105 109 108 107 109 110 109 110 112 112 110 109 109 110 111 111 110 109 109 109 108 108 111 111 111 110 109 109 110 111 111 111 112 112 113 114 116 116 20 41 26 29 39 40 30 15 19 26 24 22 45 165 33 37 31 35 32 27 21 21 15 18 126 223 80 39 145 126 27 23 142 118 25 18 11 12 20 12 15 9 19 12 18 16 14 15 12 13 7 16 19 1 17 9 13 11 11 8 11 13 9 12 15 11 9 12 7 7 16 11 12 17 5 9 12 7 24 9 14 4 16 10 18 17 10 13 64 110 106 109 108 109 107 108 110 111 113 113 111 110 110 111 111 111 110 110 110 110 110 110 110 110 110 109 107 107 109 110 111 110 111 111 112 113 115 116 23 32 28 34 37 40 27 20 28 24 18 30 44 57 36 27 35 34 34 30 22 15 15 18 147 166 25 14 29 51 28 20 130 119 22 14 13 16 16 15 14 16 13 12 14 12 11 13 8 12 13 12 11 13 12 7 10 13 11 10 14 12 9 11 14 12 11 11 12 12 11 10 10 12 12 10 10 13 12 8 13 10 12 13 13 17 17 11 64 112 108 109 104 113 108 116 112 112 113 113 111 110 110 110 110 111 112 112 112 112 111 111 110 110 109 109 110 112 112 113 113 111 109 109 111 112 111 111 24 32 28 34 36 39 25 17 22 27 24 32 32 39 28 32 32 33 34 31 23 17 16 18 132 64 22 25 16 17 20 27 124 123 19 14 12 16 15 14 9 18 16 10 10 15 15 18 15 15 13 11 9 10 14 19 12 14 13 12 14 13 9 10 9 10 10 10 12 13 14 13 9 13 14 12 9 9 10 11 8 4 7 10 8 10 15 17 56 110 111 113 109 115 109 116 113 113 114 114 113 112 112 112 113 113 114 114 113 113 112 112 113 112 111 111 111 112 112 113 113 111 109 109 110 111 111 111 23 32 29 35 35 38 23 16 21 26 25 29 24 24 24 37 31 32 34 32 24 18 16 18 58 23 36 13 10 23 22 21 119 132 16 16 12 15 14 14 21 16 9 11 19 16 6 6 11 8 10 17 16 11 9 12 11 11 11 11 12 13 11 12 14 14 14 13 12 11 11 10 14 12 12 13 14 14 13 14 14 9 12 17 14 10 12 12 48 111 113 116 111 116 110 116 114 114 115 116 115 114 114 114 115 115 116 116 115 114 114 113 115 115 114 113 112 113 113 114 112 111 110 109 110 111 111 111 23 32 28 35 35 37 22 14 26 25 18 25 29 25 29 37 33 33 34 31 24 16 15 17 17 32 28 18 21 24 14 25 117 141 16 18 12 15 13 13 13 14 12 11 12 10 11 19 18 13 9 11 13 13 14 15 17 14 15 16 14 14 16 17 15 16 16 17 19 19 21 22 18 15 13 13 12 10 10 12 11 6 8 12 12 15 16 13 42 114 114 116 110 114 112 119 116 116 117 117 116 116 116 116 117 117 117 117 117 116 116 115 116 116 115 114 114 114 114 114 112 111 111 110 111 111 112 113 23 30 28 34 34 38 22 15 28 25 16 24 34 26 33 36 35 35 33 28 21 15 14 17 28 32 20 28 12 13 15 24 116 140 16 17 11 14 12 13 5 14 17 14 15 16 9 8 13 22 32 37 43 51 55 56 50 42 40 38 29 27 31 29 38 40 47 54 64 73 80 84 88 80 66 50 32 18 13 12 13 12 12 12 8 13 17 13 31 113 115 115 112 115 114 120 116 117 118 118 117 116 117 118 116 116 116 116 116 116 117 117 116 116 115 115 115 115 115 116 113 113 112 112 112 112 114 114 23 31 27 32 33 37 23 15 23 26 20 24 34 23 34 34 36 35 33 29 20 15 14 17 26 26 33 18 9 21 21 19 116 130 15 15 12 13 14 14 16 11 8 12 14 10 18 37 76 99 123 133 135 136 137 136 133 122 121 117 102 96 99 96 101 101 108 117 128 139 145 148 139 141 144 142 131 97 50 14 9 8 14 15 10 10 14 11 20 111 114 118 117 118 116 119 118 118 119 118 118 117 118 118 117 116 116 116 116 117 118 118 117 116 116 116 116 117 117 117 114 114 114 114 113 114 115 117 24 32 27 32 32 36 23 17 19 27 22 23 33 22 36 31 35 33 32 29 22 14 14 18 23 18 29 22 25 21 10 26 124 123 19 12 10 13 13 12 16 12 9 12 18 38 89 144 157 164 170 170 171 172 176 179 179 170 173 174 158 152 156 152 159 158 160 164 173 177 176 173 169 165 161 161 168 160 129 96 37 17 6 11 12 12 14 17 17 114 115 119 118 119 118 118 120 120 120 120 118 118 119 119 118 118 117 117 116 117 117 117 118 118 118 118 118 118 118 118 116 117 116 115 115 115 117 119 26 33 27 31 32 36 23 16 17 27 20 21 33 25 39 31 32 32 32 29 23 15 14 17 25 28 25 27 15 18 19 19 131 120 23 13 12 13 11 11 10 23 25 31 74 131 164 169 171 164 163 175 189 194 197 198 195 186 195 202 190 187 192 188 191 188 189 194 198 200 196 190 176 179 178 170 170 172 164 150 128 71 23 15 15 13 15 19 21 119 116 116 116 117 118 119 121 121 121 120 119 119 119 120 120 119 118 117 117 117 117 117 119 119 120 118 119 119 118 118 117 118 118 118 116 116 118 120 75 38 29 38 29 38 26 17 18 29 25 26 23 36 36 30 33 31 30 31 24 14 14 18 21 32 23 21 25 14 15 24 124 130 13 12 10 14 16 15 42 81 70 87 137 162 176 182 175 178 181 184 186 192 197 201 205 201 194 197 196 196 207 205 192 205 205 200 191 210 210 202 202 182 186 171 181 171 172 154 154 126 94 62 13 12 17 13 26 120 118 114 120 119 122 119 121 120 119 119 120 120 119 118 119 118 117 118 118 118 118 117 118 118 118 118 118 118 118 118 119 119 118 119 121 122 124 125 189 33 34 36 26 65 42 20 23 25 15 29 25 27 28 30 30 30 30 31 24 15 15 18 24 35 28 24 24 17 18 19 119 141 13 12 19 6 4 36 89 92 84 115 155 170 178 175 181 181 186 190 195 201 204 205 199 198 199 205 200 194 198 195 204 203 200 201 197 193 189 190 191 202 177 169 173 171 167 156 156 141 137 94 58 16 18 10 30 118 116 114 119 115 119 121 122 121 120 120 121 121 121 120 121 120 119 119 120 120 119 118 119 119 120 120 120 120 120 120 120 120 120 121 121 122 123 123 234 150 30 48 136 176 33 11 28 35 29 29 25 33 28 29 25 27 31 31 24 15 15 18 23 31 28 26 23 20 24 19 123 133 12 18 12 15 34 70 83 77 100 138 156 166 177 176 188 191 195 196 191 187 183 182 193 196 198 204 198 190 195 193 184 190 188 178 177 161 146 138 146 145 158 159 151 157 158 161 155 147 145 130 91 31 11 20 41 122 121 119 123 118 123 126 123 122 121 122 123 123 123 122 123 122 121 121 122 121 120 120 121 122 122 123 123 123 123 123 121 121 122 123 123 123 122 122 232 225 101 202 231 96 28 14 71 119 24 29 27 28 29 29 23 27 31 30 23 14 14 19 27 31 29 29 24 21 25 19 126 135 14 15 2 16 41 63 77 88 124 142 143 156 166 167 155 152 146 138 133 135 145 154 182 192 197 199 194 193 202 195 174 184 181 160 155 135 129 134 121 136 160 162 182 162 153 155 156 151 137 135 120 70 22 13 48 124 123 117 120 120 120 121 123 123 122 123 124 124 124 123 124 123 122 123 123 123 123 122 124 124 124 124 124 124 123 123 122 122 123 123 124 124 124 124 224 240 239 227 186 41 19 20 77 217 53 37 26 17 43 34 25 27 30 27 21 15 15 18 28 29 27 29 24 16 21 19 117 142 17 12 14 22 38 70 94 105 127 137 148 163 168 168 167 156 144 136 137 142 153 163 167 185 193 192 188 194 204 192 190 176 163 156 156 117 94 98 94 106 128 150 176 165 178 147 145 158 153 115 120 76 43 24 57 128 128 119 121 126 126 121 124 123 123 123 124 125 124 124 124 123 123 123 125 125 125 124 126 126 125 125 124 123 122 122 124 123 123 123 124 126 127 128 239 228 225 222 95 30 36 11 96 226 155 36 31 100 154 48 27 29 29 27 21 15 14 16 19 24 20 20 17 13 19 22 120 127 17 19 16 30 72 105 104 113 124 143 159 157 153 155 155 138 117 110 110 112 115 116 151 171 180 177 173 184 199 192 188 178 163 147 131 100 87 92 80 62 69 64 57 64 63 74 77 94 94 100 114 83 41 31 74 132 130 122 123 131 128 124 124 124 123 124 125 125 125 124 125 124 124 125 126 126 126 125 127 127 126 126 125 124 123 123 125 125 124 124 125 127 129 130 225 234 225 230 89 36 12 15 79 226 237 106 174 226 132 29 27 28 29 26 23 19 16 14 18 34 27 15 13 15 20 20 133 117 20 17 7 34 102 112 97 105 117 148 157 144 149 148 145 128 109 100 100 107 119 132 122 148 169 176 173 181 200 202 195 177 149 133 120 101 64 36 58 83 102 93 92 80 43 43 113 93 45 70 88 93 43 42 90 133 126 120 121 124 120 123 126 125 124 125 125 125 125 124 127 127 126 126 126 127 126 125 127 127 127 127 127 126 126 125 126 126 126 126 127 128 129 129 231 244 242 226 212 55 32 17 99 217 239 235 214 188 42 21 24 27 29 28 25 20 15 13 33 57 45 20 15 17 20 15 127 126 24 11 25 58 115 114 100 92 81 90 72 48 49 29 27 31 38 41 37 34 46 59 90 127 168 195 195 193 203 206 198 181 150 118 72 70 105 150 121 133 141 149 121 148 231 108 28 47 42 52 73 85 64 62 115 145 133 131 131 126 122 131 127 126 125 125 126 126 125 124 130 129 128 127 127 127 126 125 127 128 128 128 128 128 128 128 126 127 127 128 128 128 128 128 178 235 206 218 234 169 27 58 198 230 226 223 214 95 38 21 26 27 26 32 15 75 51 49 142 163 30 22 11 18 16 19 122 121 31 19 35 101 133 108 85 65 30 51 61 51 31 117 190 173 142 124 125 149 151 96 81 64 116 149 192 194 184 180 164 141 108 121 176 137 117 51 38 43 61 77 120 121 155 200 62 48 89 70 69 92 108 95 132 126 135 131 129 132 126 126 127 127 127 127 126 126 126 126 128 128 129 130 130 129 129 129 128 128 128 129 129 129 128 128 129 128 128 128 128 128 129 129 147 214 70 57 97 152 92 57 203 221 219 218 190 42 29 24 24 28 26 31 21 72 109 130 182 79 17 10 18 17 15 19 124 117 31 53 75 119 122 94 68 57 18 28 16 41 126 129 136 98 59 48 45 40 90 153 138 131 139 143 168 170 169 148 143 122 126 128 120 109 47 79 31 52 46 42 63 88 78 72 79 70 60 40 41 81 122 149 173 157 127 136 133 129 131 136 129 129 129 128 128 128 128 128 130 130 131 131 131 130 130 129 129 129 129 130 129 129 129 128 127 128 128 129 129 130 131 131 139 187 36 38 20 33 29 27 124 217 233 230 216 113 35 28 27 34 27 26 20 48 109 112 110 34 18 14 13 16 21 17 126 134 29 53 116 132 107 73 47 49 58 52 28 101 113 120 89 50 40 35 38 35 56 75 119 114 112 147 150 133 140 135 132 106 142 135 109 58 79 47 15 37 38 43 46 73 79 91 121 100 80 81 90 127 142 108 133 168 132 138 125 128 138 131 131 131 131 131 131 131 130 130 132 132 133 133 132 132 131 130 131 131 131 131 130 130 130 129 128 129 130 131 131 131 131 131 118 111 26 32 31 25 13 14 41 199 230 205 230 201 46 15 22 33 29 24 27 62 119 104 37 10 21 23 9 16 24 18 113 140 34 57 154 136 115 107 71 83 59 66 49 89 70 100 49 43 56 25 17 45 88 104 67 84 92 140 128 167 193 191 165 147 153 147 105 125 111 97 88 80 74 78 88 93 107 126 152 138 147 128 112 108 121 70 62 144 138 134 124 128 139 124 133 133 133 133 133 133 133 133 134 134 134 134 134 133 132 131 131 131 132 132 132 132 131 131 131 132 133 133 132 131 130 129 77 62 40 20 23 34 18 15 28 200 146 43 103 179 116 23 26 32 31 21 17 63 115 124 101 8 14 17 20 16 18 19 113 135 34 81 139 87 84 106 109 110 105 128 107 83 79 74 69 65 73 75 101 100 101 129 137 150 151 155 100 177 206 206 186 124 177 168 155 136 126 147 135 129 119 111 118 135 141 133 142 149 166 133 120 114 137 145 61 122 129 129 137 130 132 134 134 134 134 134 134 134 134 134 135 135 135 135 134 133 132 131 132 132 133 134 134 134 134 134 133 134 134 134 133 132 131 130 39 24 23 32 34 33 14 12 40 203 79 28 35 32 54 27 27 24 29 29 24 60 89 117 150 82 14 16 19 20 17 16 120 129 25 93 109 81 108 113 114 105 147 153 115 110 76 71 62 71 102 146 171 161 143 152 146 174 192 169 98 171 198 207 201 168 170 182 153 139 175 168 182 193 182 163 138 165 169 155 158 157 158 131 118 150 159 141 84 121 132 127 138 132 129 141 135 135 135 135 135 135 135 135 136 136 136 135 135 134 133 132 133 133 134 134 135 135 135 135 132 133 133 134 134 134 134 134 20 26 28 32 27 24 13 15 45 149 20 31 37 20 24 16 31 28 28 27 20 23 19 32 86 108 23 25 12 21 19 17 125 135 22 64 83 69 122 110 104 109 139 163 130 130 98 87 88 111 139 179 180 182 167 148 134 181 199 148 123 188 205 195 203 180 136 187 191 160 141 172 186 177 166 155 138 158 170 159 166 161 163 147 124 160 188 154 87 133 137 130 131 135 133 136 135 135 136 136 136 136 136 137 136 136 136 136 136 135 134 133 133 133 135 135 135 135 134 134 133 133 133 133 134 134 135 135 27 21 26 21 31 29 16 15 33 57 27 35 29 29 25 24 22 33 30 23 23 15 11 15 20 30 26 28 16 14 17 19 110 132 28 51 102 64 138 148 127 92 131 148 139 133 137 119 102 135 132 156 146 131 118 138 197 190 173 120 158 196 206 205 196 205 143 170 180 204 188 154 151 132 141 128 133 140 173 168 150 151 151 145 140 119 162 163 95 140 132 137 129 138 136 134 136 136 136 136 137 137 137 137 137 137 137 137 137 136 135 134 135 135 136 135 135 134 133 133 135 135 133 133 133 134 134 134 22 24 23 28 32 26 16 14 24 22 26 30 29 25 23 26 26 32 27 26 19 13 16 11 14 18 20 24 14 19 18 17 112 134 22 31 122 74 156 145 116 96 137 141 150 138 149 124 98 131 133 134 139 145 177 205 204 192 168 122 184 195 210 206 207 209 178 149 193 200 200 210 200 188 169 154 157 174 178 168 156 151 148 144 113 74 132 161 125 127 135 138 132 133 137 140 138 137 136 136 137 138 137 137 137 136 137 139 138 136 134 135 137 137 137 136 135 134 133 133 134 134 134 134 134 134 134 134 24 25 25 28 32 25 15 12 21 21 24 29 28 23 21 23 27 32 28 25 20 15 17 14 11 22 16 16 26 19 19 20 120 141 12 49 119 101 163 146 97 96 131 133 151 153 152 121 123 144 167 187 200 208 213 206 199 209 149 161 192 194 205 212 210 202 209 159 172 194 203 200 200 202 191 179 180 181 168 157 160 153 124 160 135 81 111 160 147 142 142 139 131 131 136 134 138 137 136 136 137 138 137 137 139 137 137 138 138 136 136 137 137 137 137 136 135 135 134 134 135 135 134 134 134 135 135 135 23 24 25 27 31 23 14 12 22 23 25 30 29 25 22 25 27 31 26 23 19 15 18 15 18 19 16 16 26 11 19 23 111 138 57 156 191 132 156 154 95 121 144 125 136 151 150 135 101 123 184 212 208 211 216 211 211 196 127 185 198 205 204 212 210 203 205 183 128 176 195 196 203 201 182 168 172 168 157 152 152 134 138 161 176 113 124 141 148 139 137 135 134 138 141 138 138 137 137 137 137 138 138 137 140 138 137 138 138 137 137 139 137 137 136 136 136 136 136 136 135 135 136 136 135 135 135 135 22 23 24 26 30 23 15 14 21 23 26 30 30 27 26 26 26 30 24 21 17 13 18 14 21 13 22 18 19 18 17 12 84 169 149 182 197 168 180 136 129 165 164 123 118 138 143 149 144 127 165 202 207 209 201 192 179 122 120 175 188 204 205 199 208 210 211 195 120 100 150 169 172 168 169 178 189 186 167 147 138 135 146 166 184 159 125 141 144 138 137 137 136 137 137 133 139 138 137 137 138 138 138 138 139 137 137 138 139 137 138 139 137 137 137 136 137 137 138 138 136 136 136 136 136 136 136 136 21 23 23 25 28 22 14 14 18 21 24 28 29 27 27 28 27 30 24 20 17 13 16 13 15 16 25 18 17 27 13 35 132 166 157 181 197 167 171 155 160 186 171 128 119 133 132 145 163 132 137 161 186 189 156 133 104 99 162 180 189 202 209 202 206 203 198 203 182 127 112 117 101 103 133 167 175 178 165 136 139 141 142 162 182 146 120 154 140 137 138 140 137 138 139 136 139 138 137 137 138 139 138 138 138 137 137 140 140 138 137 138 137 137 137 137 137 137 138 138 136 136 136 136 137 137 138 138 24 25 24 25 26 20 13 14 18 20 25 28 28 29 28 28 26 29 24 21 18 16 18 13 15 20 19 21 18 10 25 124 173 172 150 189 202 172 163 171 160 180 174 142 127 129 128 141 166 155 130 97 93 106 106 105 73 146 187 177 184 194 204 199 202 203 194 192 177 165 116 162 174 169 180 183 164 158 157 142 146 137 154 170 170 125 156 150 137 134 136 138 135 138 142 141 140 139 138 138 139 139 139 138 138 137 138 140 140 138 137 137 137 137 137 137 137 138 138 138 137 137 137 137 137 137 139 139 24 25 25 24 26 20 12 13 19 22 26 28 29 28 28 27 25 27 22 21 19 17 20 14 18 19 16 22 16 17 86 187 183 176 143 192 196 152 153 177 163 171 174 146 128 125 133 144 144 165 163 149 151 164 156 126 97 170 158 150 169 176 191 188 179 171 175 150 150 146 95 123 176 185 186 175 160 152 152 154 146 138 152 170 148 159 170 150 143 139 139 138 133 135 136 133 140 139 138 138 139 139 139 139 140 138 139 140 140 138 137 138 138 137 137 137 137 138 138 138 137 137 137 138 138 138 138 138 21 24 23 25 26 20 13 15 17 21 24 26 26 25 24 24 21 25 19 21 19 17 20 14 14 18 19 15 16 61 160 175 172 177 144 192 199 136 147 164 185 170 165 138 126 132 144 144 135 153 159 169 179 184 157 95 78 103 81 89 120 126 144 149 150 144 111 100 55 80 51 86 145 170 170 160 165 160 152 163 151 139 148 117 163 179 145 163 136 133 136 139 139 142 142 138 140 139 138 138 139 140 139 139 142 140 139 140 139 138 138 140 138 137 137 137 137 138 138 138 138 138 138 138 138 138 139 139 15 30 24 19 39 18 12 18 25 10 26 33 26 24 18 26 24 31 25 17 17 20 20 9 15 14 20 21 73 149 173 177 173 172 148 193 187 137 149 171 150 148 98 126 135 132 145 157 143 148 160 161 168 150 101 78 155 125 61 60 66 76 90 90 84 78 94 74 88 149 99 45 102 139 150 143 159 150 156 165 155 155 140 127 128 134 164 152 140 137 137 138 138 137 139 141 138 140 141 140 140 139 140 141 143 142 140 139 138 137 137 136 137 136 136 136 138 138 138 137 138 138 138 138 139 140 141 141 28 22 18 37 22 21 17 3 20 25 20 21 27 21 20 20 21 22 17 17 17 18 20 20 12 20 22 97 163 163 176 176 173 172 149 191 185 150 152 170 163 153 165 176 150 131 142 154 160 143 165 158 151 133 76 124 171 166 159 160 154 120 75 40 61 113 143 150 146 157 129 58 51 108 134 142 147 147 160 161 151 164 190 171 177 159 177 149 138 136 136 138 139 138 138 139 138 140 141 140 140 139 140 141 142 141 140 138 137 137 137 137 136 135 135 135 136 137 137 136 137 137 138 138 139 139 140 140 21 26 36 58 27 7 19 21 12 24 30 32 31 23 21 21 21 19 16 16 20 19 15 18 12 31 142 177 154 176 178 174 172 170 152 190 184 161 146 159 180 173 187 190 177 138 140 158 164 148 151 155 128 87 100 157 164 159 167 158 168 161 145 127 134 156 147 155 149 139 157 120 48 66 102 113 131 146 159 163 156 160 179 186 160 181 171 140 137 137 137 139 140 139 139 140 138 140 140 140 139 139 140 140 140 139 138 137 137 137 137 137 137 136 135 135 136 137 136 136 136 136 137 137 138 138 139 140 66 37 126 78 26 23 10 12 32 13 22 25 22 25 20 17 20 23 20 15 21 19 11 15 33 151 175 170 176 166 177 170 171 166 152 190 183 164 126 134 183 194 185 199 176 140 148 159 165 153 144 139 96 64 146 159 163 167 173 165 169 174 175 163 175 160 154 155 163 155 163 150 85 45 86 105 141 150 145 152 149 166 190 174 172 171 146 135 138 136 137 140 141 139 139 140 138 140 140 140 139 139 139 140 139 138 138 137 137 137 137 137 138 137 136 136 137 137 137 136 136 136 136 136 137 138 138 139 165 155 137 30 19 15 16 11 15 24 22 23 23 16 19 23 20 20 22 16 18 15 21 54 132 156 175 173 173 176 177 177 175 165 154 194 186 161 107 107 165 187 184 189 121 134 157 147 173 149 155 114 68 97 148 162 168 181 175 182 176 186 199 192 192 181 181 160 166 170 166 156 146 85 73 102 130 144 153 166 152 147 191 184 156 154 147 136 136 135 136 140 140 139 138 139 139 140 140 140 139 139 139 140 138 138 137 137 137 137 138 138 138 137 137 137 138 138 137 137 136 136 136 137 137 138 138 138 202 164 103 28 16 26 10 15 42 75 35 25 32 11 21 24 20 16 22 20 18 16 43 111 158 161 174 184 168 168 185 181 176 166 154 194 191 158 99 86 109 154 168 122 33 93 141 168 169 160 154 106 76 135 137 170 168 179 170 188 175 183 199 193 192 188 183 180 181 172 167 157 153 132 78 134 150 161 164 151 151 98 118 152 136 161 154 135 136 134 135 137 138 136 137 138 139 139 140 140 139 139 139 140 139 139 138 138 138 138 139 139 137 136 136 136 137 137 136 135 136 136 136 136 137 138 137 138 185 171 50 23 27 13 12 61 125 88 28 12 26 20 17 12 19 19 23 18 22 26 50 117 137 172 173 177 170 170 191 170 171 168 156 189 193 159 106 80 99 115 116 60 25 84 133 157 159 173 161 124 108 142 144 157 149 136 127 133 127 137 166 174 156 143 129 141 143 139 145 160 145 154 107 148 167 169 168 152 134 76 77 108 180 163 142 134 137 136 135 137 137 136 137 139 137 139 140 139 139 138 139 140 140 140 139 139 139 139 140 140 137 136 136 136 136 136 136 135 136 136 136 136 137 138 138 139 196 171 66 24 15 20 12 74 113 25 28 27 15 24 12 24 15 20 24 10 19 32 40 81 126 164 175 177 174 169 178 181 168 168 156 184 193 161 115 80 84 104 144 106 44 45 137 159 161 169 188 142 132 137 146 132 92 68 76 77 78 77 94 102 79 78 80 77 67 75 74 100 117 141 157 167 189 166 151 155 149 68 90 143 153 140 148 135 139 137 138 138 138 137 138 140 137 139 140 139 139 138 139 140 141 141 140 139 139 140 140 140 138 137 137 137 137 137 137 136 136 136 136 136 137 138 138 139 223 199 135 27 20 17 9 73 52 33 24 22 28 13 15 21 19 22 22 14 15 36 50 46 120 150 177 178 180 169 181 179 170 168 155 184 183 135 112 82 67 61 54 60 44 40 116 146 159 171 182 181 131 125 99 80 67 91 101 106 118 112 93 87 99 131 162 155 145 156 153 142 130 147 151 185 177 173 158 157 131 81 97 138 166 167 138 142 137 138 138 140 140 139 139 139 140 139 139 139 139 139 139 139 143 141 139 138 139 139 138 137 139 139 139 139 137 136 136 135 134 135 136 137 137 137 139 140 150 168 172 64 11 23 2 99 60 17 26 21 20 20 17 21 20 21 21 16 18 35 47 45 94 154 175 176 177 172 181 179 171 165 156 187 172 117 110 110 61 31 15 32 42 45 105 145 153 152 176 182 157 136 141 137 145 147 151 165 177 171 168 178 178 165 172 177 171 160 150 151 161 138 172 179 175 157 167 153 119 82 107 142 174 163 142 137 139 139 139 140 139 139 139 139 141 140 139 139 139 139 139 139 140 139 139 138 137 137 138 139 139 139 139 139 137 136 136 136 135 136 137 138 138 138 139 140 79 12 91 115 25 12 24 149 95 11 22 19 14 23 14 21 21 19 18 17 20 34 45 43 64 147 176 178 176 173 179 175 168 165 157 184 164 109 115 129 116 64 33 33 41 38 77 131 143 154 179 169 158 135 162 148 145 152 153 151 165 181 178 165 168 160 162 153 140 140 151 169 166 138 167 184 157 157 154 143 94 88 121 145 163 142 141 141 141 140 140 139 139 140 141 140 141 140 140 139 139 139 139 139 137 138 139 138 136 135 137 139 139 139 139 139 137 137 136 136 137 137 138 138 138 138 139 140 28 22 18 28 18 18 14 114 131 29 22 21 19 21 10 23 20 19 17 13 17 34 46 41 45 120 178 180 176 175 178 173 167 167 155 179 164 119 123 125 146 141 120 62 29 27 56 119 144 146 161 176 164 130 157 161 147 139 136 135 128 121 115 107 99 104 117 122 136 162 167 160 156 140 161 157 158 142 145 126 88 100 122 139 145 129 142 143 143 141 141 140 140 140 141 141 142 141 141 140 140 139 139 139 138 138 138 138 136 136 137 138 139 139 139 139 138 137 137 136 137 137 138 138 137 137 138 139 19 27 18 24 18 15 9 24 109 43 25 21 27 18 14 26 19 20 19 13 17 39 49 41 44 83 166 179 175 175 178 170 166 164 151 177 168 126 125 120 139 130 147 148 116 54 28 97 131 143 142 166 147 141 153 156 166 159 153 138 116 101 101 103 105 95 103 115 133 151 148 140 159 155 151 150 147 136 143 105 102 118 113 135 139 142 144 139 142 142 142 142 142 141 142 141 143 142 141 141 140 140 140 140 142 140 138 138 138 138 138 137 139 139 139 139 138 137 137 136 136 136 137 137 136 136 136 137 23 17 23 18 11 18 19 18 40 33 24 16 25 17 20 21 19 19 19 18 25 44 51 41 48 56 133 170 172 173 178 166 169 162 148 178 168 120 121 124 141 131 133 134 130 100 56 87 121 148 137 142 127 157 161 144 137 147 146 141 151 168 174 167 162 152 153 144 133 135 144 158 162 160 147 163 133 139 141 82 109 129 120 138 137 145 142 139 142 143 144 144 144 143 142 141 144 142 142 141 141 141 141 141 144 141 139 138 140 140 139 137 140 140 140 139 138 137 136 136 135 136 136 136 135 135 136 137 27 12 20 29 23 11 2 13 7 19 23 13 19 16 19 15 18 15 21 30 39 48 48 43 48 50 87 156 166 170 176 166 168 164 152 177 164 119 124 127 128 126 129 129 121 96 57 85 137 130 129 144 151 156 161 168 167 159 155 171 194 194 188 190 191 185 190 187 182 182 180 179 158 144 152 138 144 128 147 87 108 134 136 143 137 142 138 144 143 144 143 143 143 144 143 144 142 142 142 142 141 142 142 142 143 142 140 140 140 140 140 140 141 141 140 140 138 137 136 135 135 136 137 137 136 136 137 138 17 26 19 22 8 12 16 14 20 16 23 16 19 16 20 16 18 11 21 42 51 50 46 45 45 53 53 145 161 170 175 164 163 171 159 174 160 126 130 122 124 132 132 130 131 120 71 70 145 123 133 137 159 145 151 158 145 162 162 165 186 196 195 198 194 180 184 192 192 186 175 177 151 144 134 114 137 135 144 125 116 136 143 139 142 144 142 145 145 143 143 144 143 144 145 146 142 142 142 142 142 142 142 143 140 141 142 141 139 139 140 142 142 141 141 140 138 136 135 135 136 137 137 138 137 137 138 139 18 21 22 19 15 12 11 13 20 18 17 18 18 16 16 18 14 25 42 51 51 46 44 44 45 51 57 92 159 160 165 149 156 188 147 155 151 121 140 124 126 131 129 136 127 133 61 38 135 146 121 131 139 146 133 151 157 161 158 154 169 170 185 188 172 174 184 194 193 178 163 159 133 121 108 127 126 121 138 128 130 137 142 145 145 143 146 143 146 142 140 142 143 143 143 146 144 143 143 143 143 143 143 143 141 141 141 141 141 141 141 141 143 142 142 141 140 139 139 138 138 138 138 138 138 138 138 138 19 21 22 20 15 12 12 14 14 24 25 15 13 17 19 19 25 35 47 53 51 48 45 43 45 50 58 61 134 160 170 136 152 195 148 137 131 118 139 121 141 114 127 144 131 129 64 77 167 143 110 114 129 138 122 139 156 155 147 144 159 158 165 161 150 163 168 165 165 164 152 135 118 84 95 79 95 146 135 49 143 130 135 138 145 147 138 143 141 139 141 142 144 142 145 147 143 143 143 143 143 143 143 143 142 142 141 141 141 141 141 142 143 142 142 141 139 139 138 138 138 138 138 138 138 138 138 138 19 21 21 20 15 13 14 14 17 28 23 15 19 22 20 21 40 44 52 55 53 49 45 43 46 54 59 45 97 158 167 139 147 196 145 125 117 111 135 122 134 131 130 154 121 91 28 175 172 153 124 89 80 93 94 110 140 148 147 144 146 135 134 127 132 146 149 142 138 139 125 103 79 66 53 68 127 165 125 16 57 121 141 141 143 145 151 141 140 141 142 143 142 141 142 146 143 143 143 143 143 143 143 143 144 143 142 141 141 141 142 142 143 143 142 141 139 138 138 137 138 138 138 138 138 138 138 138 19 21 21 20 15 13 14 16 16 21 17 15 19 17 20 33 49 51 53 55 53 50 46 43 47 55 54 51 65 146 156 150 146 191 143 125 117 105 128 130 143 119 136 142 109 23 34 230 147 158 144 96 53 40 53 69 99 118 124 118 106 90 93 99 109 108 104 101 100 96 88 77 55 39 52 121 147 160 125 11 22 36 107 142 137 142 142 148 143 144 144 144 144 143 143 143 143 143 143 143 143 143 143 143 145 144 142 141 140 141 141 142 143 143 142 141 139 138 138 137 139 139 139 139 139 139 139 139 20 21 21 18 15 14 14 17 16 22 20 17 17 21 35 56 52 52 54 54 53 50 46 43 44 51 50 51 50 126 151 143 152 191 150 124 116 101 131 130 138 116 140 124 21 10 116 197 118 153 143 129 87 35 39 45 61 73 66 56 44 41 52 63 66 54 42 40 50 60 68 74 62 58 115 147 151 162 112 12 19 14 20 92 148 147 144 135 145 144 144 145 147 147 146 145 144 144 144 144 144 144 144 144 145 144 143 141 141 141 141 141 143 143 142 142 140 139 138 138 139 139 139 139 139 139 139 139 20 22 22 19 14 12 14 17 19 21 20 16 21 39 55 57 50 51 53 54 51 49 46 42 44 48 52 43 52 102 156 124 160 196 163 113 105 99 135 115 127 124 101 26 12 9 162 175 89 157 143 142 120 71 68 63 60 57 31 25 21 23 23 24 24 23 18 22 43 72 86 86 75 107 145 139 159 160 117 17 9 15 15 18 83 144 147 156 146 147 148 146 146 147 144 142 145 144 144 144 144 144 144 144 145 145 144 143 142 141 141 141 143 143 143 142 141 140 140 139 140 140 140 140 140 140 140 140 20 21 20 18 14 12 13 16 18 17 25 32 34 49 56 44 47 51 54 55 53 48 44 42 43 49 52 47 55 77 157 111 165 197 172 103 97 92 132 92 119 49 13 19 14 13 190 180 63 164 149 135 125 102 93 93 86 79 48 39 30 27 19 13 15 24 34 46 71 101 106 96 94 118 133 147 145 165 139 7 21 9 13 15 6 52 130 146 141 146 150 149 146 145 142 140 144 144 144 144 144 144 144 144 146 146 146 145 144 143 141 141 143 143 143 143 141 141 141 141 140 140 140 140 140 140 140 140 22 22 20 17 12 10 13 16 19 28 59 74 56 51 58 54 47 50 56 57 53 47 42 40 40 52 45 58 48 57 149 109 166 192 173 101 95 84 125 76 19 15 11 10 17 6 201 209 54 160 150 137 133 114 89 107 103 103 74 57 33 27 24 29 26 35 46 61 86 106 104 92 105 140 135 147 149 209 123 13 12 18 15 13 15 11 26 70 128 139 149 149 146 146 146 147 144 144 144 144 145 144 145 144 148 147 147 147 146 144 142 141 142 142 143 143 142 142 142 142 141 141 141 141 140 140 140 141 21 24 25 9 16 12 13 12 29 110 169 139 53 60 57 49 51 54 57 57 52 47 41 40 44 49 45 47 56 56 138 112 159 190 165 101 65 40 17 15 12 14 11 9 14 16 209 221 67 139 153 138 132 112 103 105 108 103 85 67 59 52 41 35 37 43 55 69 100 112 93 96 118 143 130 153 181 226 118 14 12 13 15 14 14 13 15 15 10 40 94 142 155 145 141 151 146 144 141 145 147 146 143 141 145 147 147 146 143 143 144 144 144 143 143 143 143 143 143 144 140 139 139 139 140 141 141 141 21 18 21 15 21 14 9 39 134 177 168 135 90 57 57 50 42 47 54 57 56 50 44 40 36 50 52 48 45 49 131 126 122 115 69 21 11 11 7 12 11 13 13 12 18 16 208 224 67 119 162 135 122 121 113 99 108 100 82 76 67 59 52 58 68 67 75 92 109 104 89 107 132 131 148 154 230 230 98 13 12 13 13 13 12 12 14 13 16 20 17 27 77 137 155 138 146 146 145 144 143 144 148 153 148 148 148 147 146 143 143 143 144 143 145 145 145 145 143 144 142 142 141 140 140 141 140 141 19 15 23 18 14 10 21 109 172 190 162 139 142 77 55 54 48 52 55 55 51 45 39 35 45 47 44 50 58 56 75 49 15 17 10 7 13 18 14 13 14 14 13 14 18 11 203 228 42 96 150 156 133 118 104 115 109 97 80 84 73 65 62 82 85 78 84 101 112 103 96 118 126 139 151 192 237 228 83 12 10 11 12 12 11 11 12 13 16 8 15 28 19 14 60 124 148 145 143 149 152 150 145 142 146 143 143 146 146 145 146 149 142 143 145 146 146 145 144 142 144 144 142 140 140 142 140 139 23 17 21 17 9 13 31 165 176 177 162 140 169 119 71 61 47 50 53 55 52 48 42 39 33 48 54 45 26 17 18 14 13 14 14 11 7 9 13 15 15 13 12 13 16 9 192 235 63 74 155 153 130 131 118 119 113 102 84 89 78 73 73 98 97 93 93 106 117 115 113 126 131 138 165 233 223 227 56 13 10 11 12 12 10 10 11 12 17 20 20 17 16 20 23 23 47 101 144 143 136 145 152 146 153 148 148 150 147 141 140 143 143 143 144 146 145 144 143 142 144 142 141 140 140 141 140 140 25 17 15 16 15 22 29 166 181 183 168 151 179 161 107 51 49 52 55 55 49 42 31 26 29 25 19 14 14 18 10 7 9 10 15 17 15 17 17 10 13 10 11 16 17 12 170 234 186 47 127 155 139 122 122 127 121 112 96 94 86 84 89 108 108 102 102 112 117 112 116 128 143 124 205 237 232 224 19 17 12 13 12 12 10 10 12 13 16 11 14 22 25 21 16 18 24 9 28 93 144 154 147 146 144 143 146 151 149 145 143 146 144 144 144 143 143 143 143 144 142 142 139 139 141 141 141 141 19 19 18 16 16 20 30 164 169 189 169 169 183 189 171 74 53 50 42 34 27 20 13 9 11 9 11 10 11 15 11 13 15 14 14 11 8 13 16 8 10 11 13 17 16 9 127 217 238 124 87 141 135 125 130 118 126 115 103 93 94 91 100 114 104 98 102 122 120 102 114 134 117 161 231 227 237 188 8 17 13 12 12 11 10 10 12 13 14 17 20 20 18 17 20 25 19 23 22 17 26 61 107 140 149 150 151 149 144 142 146 148 146 145 145 143 143 142 142 143 141 140 140 139 140 141 140 140 16 20 20 14 12 11 36 171 176 189 176 174 185 202 205 99 31 27 19 14 11 13 14 14 18 11 12 9 5 6 13 13 3 10 17 17 15 15 15 15 12 13 11 15 11 9 95 207 238 211 71 120 144 121 117 127 128 113 103 91 100 92 104 115 105 103 107 128 123 106 116 130 123 218 231 230 230 120 16 14 13 12 13 12 10 10 12 13 13 14 18 23 23 20 19 20 18 21 21 20 17 19 24 28 49 76 112 136 148 151 148 143 146 145 146 145 143 142 142 141 143 141 139 138 140 140 139 139 19 18 16 13 17 11 37 162 186 177 187 178 194 202 168 51 15 14 13 15 16 15 13 10 16 9 14 16 18 12 20 8 16 15 10 10 14 11 9 13 13 13 11 13 12 20 89 219 232 242 120 70 135 142 124 120 131 111 103 91 107 93 106 116 110 112 110 121 115 101 106 100 184 233 233 232 233 67 11 14 13 13 12 11 9 9 10 12 14 20 21 17 19 25 24 17 23 22 19 18 23 26 23 18 23 26 25 24 41 81 126 151 146 146 147 146 146 144 141 139 144 143 140 139 140 139 139 138 22 16 19 15 16 11 35 159 184 185 186 178 164 131 31 17 14 14 13 14 13 13 13 12 13 13 14 14 13 12 12 12 16 14 13 12 12 12 12 10 17 13 13 11 15 11 77 223 223 246 223 65 107 133 136 118 126 125 102 96 94 71 112 105 103 97 113 123 105 100 75 151 227 243 233 230 225 23 15 10 11 11 12 12 10 10 11 13 12 14 17 20 21 22 23 23 22 22 23 23 22 21 22 21 24 24 26 24 18 16 26 40 104 147 147 142 143 148 138 145 141 141 141 141 139 140 140 139 19 19 19 19 18 9 26 161 175 194 162 29 18 19 2 21 14 14 15 15 14 14 13 13 14 13 14 14 13 12 13 12 12 12 11 11 14 13 13 13 11 10 16 16 16 15 56 229 225 238 245 210 91 124 127 132 127 133 106 90 100 68 84 89 86 95 97 129 106 69 120 228 240 238 246 230 148 15 9 13 11 12 13 12 10 10 13 14 15 17 18 20 20 21 21 22 22 24 24 23 22 23 23 23 27 23 21 25 24 21 22 24 24 35 104 149 143 141 144 138 142 141 142 141 140 138 139 139 17 21 14 18 19 12 21 167 196 173 30 21 11 14 19 14 15 15 16 16 14 14 15 14 13 14 14 13 14 12 12 11 11 12 12 11 13 14 14 14 15 15 16 17 11 17 33 223 236 238 253 241 220 104 135 141 136 131 112 98 100 67 85 99 84 75 118 116 91 100 229 236 251 239 243 228 61 9 9 13 12 13 13 13 11 11 13 14 17 18 17 19 19 20 21 22 21 22 23 23 22 22 22 23 27 21 20 25 27 23 19 17 19 22 31 63 133 147 143 142 141 142 142 141 139 137 139 139 20 21 12 16 17 16 17 164 184 78 13 23 7 13 12 12 15 16 15 16 15 15 14 14 12 12 13 14 12 12 13 11 13 12 12 13 13 13 12 12 15 15 15 15 10 16 20 167 224 248 246 253 249 198 99 144 162 139 124 114 94 66 100 93 84 90 129 117 84 226 241 250 252 248 236 199 19 10 12 12 14 13 14 13 13 13 15 16 14 16 18 19 20 21 22 20 21 22 22 22 21 21 22 23 26 24 24 23 22 20 21 25 27 20 18 23 21 49 125 144 140 141 141 139 139 137 138 138 22 20 15 16 13 15 14 158 172 15 30 5 8 24 15 16 15 15 17 17 16 16 15 14 12 12 13 12 13 13 11 12 13 13 13 12 13 13 13 13 9 12 16 16 17 14 14 79 219 227 254 252 249 248 167 108 153 150 144 132 108 87 122 92 84 136 122 84 228 250 248 253 250 254 243 126 16 15 15 14 15 14 13 14 13 14 16 16 14 16 19 20 21 21 21 19 20 21 22 22 22 21 21 22 22 23 23 22 21 20 23 27 25 30 19 19 19 21 59 147 140 141 141 140 139 136 136 138 42 17 18 21 11 14 17 152 93 13 6 24 16 10 15 19 15 16 15 16 15 16 14 14 13 13 13 12 12 13 12 11 11 11 12 12 13 13 14 15 13 14 15 14 19 15 20 25 186 223 240 251 254 251 248 149 124 155 168 151 137 118 141 117 127 133 103 230 251 246 253 253 252 253 235 47 20 17 14 17 16 16 13 14 14 15 16 16 19 21 21 23 23 21 21 20 22 22 23 23 21 22 23 23 23 21 21 22 22 21 20 19 16 15 30 23 17 18 16 121 141 142 141 140 138 136 136 138 103 29 17 20 13 13 17 135 43 18 15 8 17 18 18 9 14 15 14 15 14 14 13 13 13 13 12 12 12 12 11 11 11 13 14 13 13 12 13 13 17 17 14 11 16 14 23 17 121 226 240 254 251 254 249 243 143 132 155 149 146 134 138 130 133 149 229 247 247 254 254 249 254 249 169 14 19 17 18 17 19 17 14 14 16 16 18 18 22 22 20 20 21 20 21 20 22 23 23 22 22 22 22 23 20 20 20 20 22 21 18 15 27 15 29 16 13 23 19 69 141 141 141 141 138 137 137 137 170 50 15 17 14 13 12 112 13 7 18 19 15 14 16 18 14 14 14 15 14 14 13 13 13 13 13 13 13 11 12 11 14 14 14 13 11 11 11 12 8 12 12 12 15 13 17 23 81 229 229 254 253 249 253 250 235 140 160 185 203 208 203 195 188 155 238 252 253 251 251 254 254 247 90 20 19 14 23 16 19 17 14 15 16 17 17 18 20 19 17 17 17 19 21 22 21 22 23 21 21 20 21 21 19 21 20 19 17 18 19 21 18 22 18 18 30 12 21 30 141 141 141 140 139 138 136 137 186 141 19 17 14 14 21 53 14 15 16 17 17 16 14 14 17 15 15 14 13 13 13 14 12 12 13 13 13 13 11 10 12 13 14 14 14 13 12 12 12 12 12 14 14 15 18 19 37 214 232 245 251 252 254 252 249 244 238 229 232 222 184 160 49 23 60 224 252 254 254 251 246 213 23 22 23 19 13 20 17 18 18 18 18 18 19 19 22 22 22 21 20 19 19 19 21 21 20 21 22 22 20 18 19 18 18 18 19 18 18 17 15 18 19 19 18 16 13 14 128 138 138 144 137 142 135 140 183 167 71 11 15 13 19 30 14 14 15 16 16 15 15 14 15 15 15 14 14 14 14 14 13 13 13 13 13 13 12 12 12 13 14 15 13 12 12 12 13 13 14 13 13 13 15 16 25 170 237 242 245 253 249 253 249 136 79 73 50 34 29 24 24 25 15 38 205 249 254 251 254 116 22 15 17 19 17 20 19 19 19 19 20 21 20 20 20 20 20 21 21 21 22 22 22 21 21 22 22 22 20 19 23 22 21 20 19 18 17 16 15 16 19 19 18 15 14 14 84 135 145 136 140 139 137 143 177 178 133 47 14 18 24 16 14 13 14 15 15 14 13 14 14 15 14 14 14 13 13 13 15 15 14 13 13 13 12 12 11 12 14 14 13 12 13 13 12 13 14 13 13 13 13 14 22 118 225 243 253 247 254 249 111 23 7 29 18 17 24 18 16 20 31 18 43 216 253 244 225 39 24 18 17 22 20 20 20 20 20 20 21 21 20 20 21 21 22 22 22 22 23 22 24 23 23 24 24 24 23 22 25 24 22 20 19 17 15 14 15 17 18 18 17 15 15 16 42 146 146 136 141 136 141 140 176 168 172 119 18 22 22 16 14 13 14 15 15 15 13 14 14 15 15 15 14 14 14 13 16 16 15 13 13 13 13 13 12 13 14 14 12 12 12 13 10 11 13 13 13 13 14 14 18 82 224 251 253 250 248 104 19 15 26 27 23 27 20 15 27 14 10 11 30 48 227 249 136 24 21 24 20 21 20 19 20 20 20 20 21 20 19 19 24 24 24 23 22 21 21 21 23 23 23 24 24 24 23 22 22 21 20 19 18 17 16 15 16 17 17 17 16 14 15 15 22 140 135 143 139 137 146 136 181 163 184 159 45 20 14 17 15 15 14 15 15 15 14 15 14 15 15 15 15 15 14 14 17 16 15 14 13 13 13 13 13 13 14 14 12 11 11 12 10 11 12 13 13 13 14 14 20 36 201 237 248 254 99 13 16 23 21 11 16 21 16 27 20 20 23 21 19 15 83 229 48 21 18 21 19 17 21 17 21 21 21 20 21 21 20 20 25 25 24 24 23 21 21 20 20 21 21 21 21 21 21 21 21 20 19 18 19 18 17 16 17 16 17 16 15 13 14 14 14 90 138 143 136 140 145 139 179 166 175 150 85 23 8 16 15 15 14 14 15 15 14 15 13 15 15 16 16 15 15 14 16 15 15 14 13 13 13 13 13 14 14 13 11 10 11 11 11 12 13 13 12 12 13 13 14 27 125 240 254 97 20 22 19 11 15 15 14 17 15 19 16 17 14 16 10 33 13 67 23 13 27 19 20 18 23 18 22 22 21 21 22 22 21 21 23 23 23 24 23 22 21 21 20 21 22 22 21 21 22 22 22 21 20 20 19 19 18 17 16 16 17 16 15 13 14 14 15 35 146 140 141 144 137 147 176 167 167 146 115 22 15 11 15 15 14 15 14 14 14 15 13 14 15 15 16 15 14 14 14 14 14 14 14 13 13 13 12 13 13 13 11 11 11 12 12 12 13 12 12 12 14 15 11 28 67 238 117 18 11 11 15 10 21 19 9 15 17 13 18 20 22 16 30 9 20 24 27 15 31 22 20 22 21 19 21 21 21 21 22 22 21 21 22 23 23 23 22 21 20 19 21 22 23 22 21 21 23 24 22 21 20 19 20 19 18 17 14 15 17 15 15 13 14 13 17 15 119 141 143 142 137 148 179 167 166 161 121 19 22 6 14 13 13 14 13 13 13 14 12 13 14 15 15 15 14 13 13 13 14 14 14 14 13 12 11 12 13 12 11 11 12 13 10 11 11 12 12 14 16 18 21 10 32 105 15 19 18 10 14 12 17 14 11 13 12 19 16 16 21 19 12 30 20 23 18 22 20 21 14 23 16 20 19 19 20 20 21 21 21 21 24 24 24 23 22 19 18 17 17 19 20 20 18 18 20 21 19 18 18 18 19 19 19 18 13 14 16 16 15 13 13 13 10 17 71 143 141 138 146 143 180 164 170 155 126 30 12 15 13 12 11 13 12 14 15 17 13 13 13 13 13 14 14 15 15 14 13 13 14 14 13 12 13 13 12 12 11 11 12 12 14 14 13 12 12 13 14 16 16 16 28 15 16 25 10 16 23 18 20 16 23 11 21 39 29 34 44 21 19 18 19 23 21 20 19 20 22 22 21 20 21 21 21 21 22 23 22 23 23 22 21 22 23 23 23 22 19 19 18 16 16 17 20 22 21 19 18 17 17 18 18 18 14 14 14 15 15 15 15 16 17 15 34 144 143 140 146 142 182 168 169 155 127 26 10 14 13 12 13 14 13 13 13 14 13 14 14 13 14 14 15 15 14 13 13 13 14 14 13 12 13 13 13 12 11 12 12 12 13 13 13 12 11 11 13 15 17 16 25 17 11 13 8 18 12 15 13 127 169 198 214 204 210 200 184 157 28 16 19 16 19 19 18 19 21 22 21 20 20 19 18 17 18 18 19 19 20 20 20 22 24 24 24 22 19 19 18 16 16 17 20 22 22 20 18 17 17 17 16 17 15 16 14 14 13 13 13 13 12 11 22 122 140 143 144 145 180 170 165 154 129 20 10 13 13 13 14 14 13 12 11 11 13 14 14 14 14 15 15 15 14 13 12 12 13 14 14 13 14 14 13 13 12 12 13 13 13 13 13 12 11 11 13 14 12 17 21 17 15 23 25 16 25 21 103 203 239 251 254 254 251 251 241 222 90 25 17 15 17 17 18 19 20 21 22 21 20 19 17 15 15 16 16 17 18 19 20 23 24 24 23 21 19 19 18 16 16 17 19 21 22 20 18 17 16 16 15 14 17 17 15 15 14 13 12 12 10 11 10 88 138 144 142 147 177 171 162 154 126 15 13 13 13 14 13 13 12 12 11 11 14 14 14 14 14 15 15 15 13 12 12 12 13 14 14 13 14 14 14 14 13 13 13 13 14 15 14 13 12 12 13 15 15 18 16 17 16 20 26 13 17 28 174 220 254 254 247 254 249 248 236 221 115 24 15 21 17 17 18 19 20 20 22 22 21 20 18 16 17 17 17 17 18 20 21 23 23 22 20 18 18 18 17 16 15 15 17 19 20 19 17 16 16 16 15 14 17 15 15 14 13 12 11 11 13 12 9 55 143 145 142 146 177 172 162 155 114 13 15 14 14 14 12 11 11 12 13 13 13 13 13 14 14 14 14 14 14 13 12 12 13 13 14 13 14 14 14 14 13 13 13 13 14 14 14 14 12 12 13 15 17 14 13 23 22 16 24 28 21 102 201 248 254 253 254 251 253 252 238 225 63 17 18 16 17 18 20 21 20 21 21 22 20 20 20 20 21 20 19 19 21 22 23 23 21 18 16 15 18 18 17 15 14 14 16 17 19 17 16 16 16 16 15 14 14 15 13 13 12 13 12 12 15 12 14 31 144 144 143 144 178 170 164 156 95 12 14 13 13 14 12 11 11 12 14 15 13 13 13 14 14 14 14 14 17 15 13 12 13 13 13 13 14 14 14 14 13 13 13 13 12 13 13 13 12 12 12 14 12 16 17 18 26 25 19 19 52 163 221 253 252 249 254 253 248 239 230 171 21 18 26 13 19 20 22 22 21 21 21 22 20 20 21 22 23 22 21 20 24 24 23 21 18 15 14 14 18 18 17 16 14 14 16 17 17 16 16 16 16 16 15 15 14 14 13 12 12 13 12 13 14 10 15 16 134 145 145 144 177 167 165 155 76 13 12 13 13 13 14 14 12 13 14 14 13 13 14 14 15 14 14 14 19 17 15 13 13 13 12 12 13 13 14 14 13 13 12 12 12 12 13 13 12 12 13 14 14 20 21 13 19 19 16 42 147 206 248 251 254 254 254 250 254 235 209 75 20 21 23 21 21 22 24 23 21 20 20 21 22 22 22 22 23 23 22 22 24 24 22 19 15 14 15 16 20 19 19 17 16 15 16 17 17 17 16 15 15 15 14 13 13 14 13 12 12 12 13 13 13 12 13 14 114 146 145 146 175 163 165 155 63 14 14 15 14 13 14 14 14 13 12 11 13 14 14 15 15 15 15 14 20 18 15 14 13 12 12 11 13 13 14 14 13 13 12 12 12 13 14 14 13 13 14 15 17 9 20 26 27 15 46 146 205 233 252 253 248 253 252 254 241 204 174 21 25 28 16 25 22 24 25 24 21 21 21 22 25 25 24 23 24 23 23 24 23 23 21 17 14 13 15 17 21 22 20 18 18 17 18 19 19 18 17 15 15 14 13 12 13 13 13 13 11 12 12 11 15 15 11 16 99 148 143 147 168 167 165 159 51 12 16 10 16 15 13 12 12 12 12 13 11 9 19 4 24 15 10 16 16 12 11 18 7 13 13 9 7 21 14 11 13 14 18 12 16 16 15 14 13 13 14 15 19 11 25 23 24 21 153 192 228 251 249 253 253 253 251 248 211 189 61 26 27 23 18 25 27 25 23 23 20 17 17 20 24 15 24 21 19 24 20 19 22 17 19 11 22 13 19 19 20 13 20 14 20 22 19 18 19 16 17 16 16 16 11 13 15 15 15 14 13 13 13 13 17 12 13 18 80 147 151 146 170 167 163 159 39 10 16 12 15 14 13 13 12 13 12 14 22 9 12 26 11 11 31 12 17 16 16 22 10 15 16 16 10 7 11 13 22 23 4 24 15 15 15 13 12 12 13 14 14 19 18 17 24 33 127 195 222 253 253 252 253 253 243 215 158 113 35 20 22 26 20 23 25 24 21 22 21 18 18 20 23 17 22 28 26 23 26 26 21 20 16 14 8 15 19 19 25 14 28 10 14 13 17 19 17 13 13 15 17 17 15 16 14 15 14 14 12 14 13 14 15 13 15 16 67 149 151 148 174 163 160 158 25 9 16 15 14 12 12 13 12 13 12 12 7 14 131 138 140 122 111 92 84 72 53 41 20 18 13 10 19 15 18 14 8 21 9 14 15 15 14 13 12 11 12 13 11 24 11 16 20 30 76 183 217 242 249 253 253 247 209 150 113 47 21 23 22 29 22 22 24 22 22 23 23 20 19 21 19 26 19 20 19 20 20 14 17 22 22 25 12 22 21 19 12 21 18 19 24 22 13 14 18 16 15 16 15 11 10 12 12 13 13 13 12 14 13 14 15 16 16 16 50 152 150 152 173 162 159 153 15 11 15 15 12 12 13 14 13 13 12 12 29 8 133 186 253 253 245 144 147 154 161 178 182 185 169 157 134 128 100 77 15 3 25 17 15 15 14 13 12 12 12 13 13 19 11 22 20 20 31 151 193 235 252 249 240 218 156 102 84 25 25 27 24 25 23 21 21 22 23 23 23 21 21 21 14 27 23 22 24 22 26 75 58 64 68 68 67 66 66 68 72 112 20 17 15 18 10 20 13 14 12 18 17 11 14 16 11 12 13 12 13 14 13 15 14 17 16 13 35 150 148 150 169 161 162 139 13 13 13 13 11 12 13 14 13 13 12 12 10 32 142 196 249 251 209 122 146 151 150 160 166 181 182 185 178 179 169 171 96 18 6 9 15 15 15 14 13 13 15 15 16 13 18 25 24 23 19 100 180 218 245 235 213 154 106 121 49 20 24 23 24 16 21 22 20 22 23 24 23 22 22 20 31 18 14 20 29 16 17 140 84 89 83 83 90 89 87 99 89 177 30 17 19 20 18 18 20 18 11 17 16 8 12 10 12 13 13 13 14 14 15 15 13 16 13 11 24 136 145 147 168 161 164 114 14 16 12 13 13 12 13 13 12 13 12 12 7 42 168 186 213 226 202 161 143 165 175 183 179 182 178 183 182 173 169 175 153 78 18 14 14 15 16 15 14 14 16 17 19 13 20 18 22 27 22 43 178 194 223 219 168 130 140 136 22 20 23 20 26 15 21 21 21 23 23 23 23 23 21 20 18 21 30 17 20 22 21 153 85 87 88 88 90 90 91 93 98 175 41 19 18 10 22 13 10 15 13 23 20 7 14 14 13 13 13 14 15 15 16 15 16 16 18 15 21 119 147 148 167 165 164 86 16 15 11 13 14 14 14 13 12 13 12 13 22 58 171 211 206 193 222 149 139 170 188 200 199 200 193 198 197 191 187 181 190 151 81 8 13 15 16 16 15 15 17 18 19 17 18 18 20 22 25 17 134 199 217 184 135 155 194 79 19 21 23 20 23 21 21 21 22 25 25 23 21 22 21 19 24 19 18 27 92 156 105 110 98 83 120 98 101 87 116 81 128 140 91 126 103 92 77 69 74 100 109 105 63 15 12 15 13 15 15 15 17 17 15 15 18 16 23 16 19 96 149 153 167 166 164 68 17 14 11 15 15 14 14 13 12 12 12 13 15 76 129 174 224 217 149 122 135 154 156 161 164 172 171 179 178 174 171 167 171 164 152 33 13 14 15 16 15 15 17 18 15 18 14 26 23 15 28 25 73 196 187 155 138 139 156 16 21 14 20 16 17 25 20 19 24 26 25 22 21 23 21 19 17 25 43 82 166 243 175 107 139 101 173 115 132 99 165 98 185 110 108 193 135 128 88 97 81 135 166 156 86 13 8 18 15 16 15 16 17 17 16 15 15 15 22 15 16 78 147 153 172 162 165 39 14 17 12 12 12 12 13 13 12 11 12 13 16 53 93 112 110 110 124 141 142 147 154 159 163 166 172 177 166 169 163 163 162 157 154 32 12 13 14 15 14 13 13 12 16 17 17 17 18 18 19 20 16 180 174 114 78 143 42 14 20 17 14 15 18 21 21 22 28 22 18 21 24 21 20 23 22 47 78 104 186 238 175 104 137 100 182 125 143 118 168 107 184 120 105 176 141 127 95 93 87 117 148 161 84 17 14 16 15 16 17 16 17 15 16 17 17 18 20 14 20 60 147 153 170 163 156 31 11 16 12 14 14 14 15 14 14 13 15 15 9 13 16 12 7 10 21 32 39 47 58 71 85 98 111 118 133 143 146 150 152 153 139 30 12 13 13 13 13 13 14 14 16 16 16 17 17 16 17 18 23 90 142 70 58 95 12 19 20 18 17 16 18 19 19 21 19 18 21 24 20 17 19 25 51 87 103 98 89 118 87 92 118 154 139 120 125 150 106 112 136 109 87 99 86 96 85 75 80 81 86 120 38 9 11 17 15 16 18 19 17 16 16 15 18 17 19 18 19 46 150 154 167 164 138 21 10 16 11 15 12 13 13 13 12 12 14 14 15 13 12 13 14 14 14 13 16 16 15 13 11 10 12 13 16 14 19 32 35 44 53 15 12 12 12 11 12 13 16 17 16 16 15 15 16 15 15 16 18 26 82 59 62 33 12 17 16 15 16 17 18 19 21 22 23 21 21 20 19 24 40 58 124 175 176 155 78 78 81 136 163 189 74 146 180 228 66 91 172 151 115 88 87 86 82 81 84 78 82 135 31 17 13 20 15 15 18 19 17 16 16 16 17 16 17 23 17 29 153 153 164 164 120 12 9 16 10 15 11 11 12 11 12 11 13 13 11 12 13 15 15 15 14 14 14 15 15 14 13 12 14 15 14 8 13 17 7 8 12 12 12 12 11 11 12 13 15 15 17 16 15 14 15 15 15 14 12 15 30 63 56 8 22 14 13 15 16 16 16 18 21 24 26 23 21 18 22 34 57 76 140 182 170 151 82 79 81 116 161 192 98 148 156 210 90 111 167 141 108 89 95 83 77 83 88 85 93 130 38 16 8 11 15 17 17 17 17 17 15 16 17 18 17 23 16 18 156 151 161 161 101 11 12 16 9 14 13 13 14 13 13 12 14 14 15 17 16 13 10 9 11 13 12 13 14 14 13 12 12 12 12 14 20 11 12 25 16 9 12 12 12 12 12 12 12 12 16 16 14 15 13 13 13 13 17 13 18 38 25 18 12 20 17 16 16 15 16 17 19 22 21 20 18 20 24 30 40 47 68 83 66 62 38 41 40 45 58 63 28 38 36 69 35 38 49 39 27 24 24 26 22 19 25 19 22 37 13 9 17 20 16 17 17 16 15 16 16 17 16 18 18 19 17 18 149 150 162 156 82 13 14 16 10 13 12 12 13 12 12 12 12 14 14 14 15 17 19 18 16 14 15 16 17 18 18 18 17 16 18 15 21 8 9 15 11 12 13 13 12 12 12 12 11 11 14 14 15 15 14 14 14 16 17 15 19 15 10 22 11 15 14 15 15 16 17 20 22 24 20 18 17 18 22 23 20 19 21 22 19 25 24 23 26 28 26 22 25 30 29 24 22 26 24 22 23 32 16 22 21 19 25 17 16 6 16 9 17 15 17 17 16 15 15 16 16 18 16 18 19 15 18 18 127 151 166 151 68 15 15 13 12 13 11 10 12 10 11 11 11 13 19 14 9 7 10 13 14 14 13 13 13 13 13 12 11 10 9 2 18 16 20 7 11 18 13 12 12 11 11 11 11 12 13 13 14 15 15 15 16 18 14 20 15 17 13 16 20 12 14 15 17 18 21 22 23 23 20 19 19 17 20 20 22 22 26 22 28 32 26 22 19 24 27 32 27 19 23 33 31 16 22 21 22 30 21 18 20 25 16 20 25 16 18 12 13 12 16 16 16 16 16 16 16 17 18 15 19 13 19 16 97 154 169 148 60 16 15 11 14 13 12 12 13 12 12 13 13 14 13 14 15 15 15 16 15 15 15 14 14 14 14 14 14 14 17 8 17 6 18 13 21 12 14 12 11 10 10 11 12 13 13 13 14 15 15 16 17 18 21 10 12 21 8 15 19 18 21 20 20 23 23 21 18 15 14 17 20 20 20 20 23 24 23 21 23 23 17 28 23 24 20 23 22 34 26 20 20 26 22 27 23 13 21 23 19 17 19 19 19 18 9 17 15 20 16 16 18 17 16 17 16 16 19 11 19 13 20 13 75 157 \ No newline at end of file diff --git a/Tests/images/hopper_8bit_plain.ppm b/Tests/images/hopper_8bit_plain.ppm new file mode 100644 index 00000000000..c39a92a0bd6 --- /dev/null +++ b/Tests/images/hopper_8bit_plain.ppm @@ -0,0 +1,4 @@ +P3 +128 128 +255 +20 21 67 17 18 62 9 10 54 21 23 62 24 26 65 24 27 60 13 17 44 16 19 54 29 28 86 30 27 94 21 20 80 17 17 71 25 25 77 24 25 73 20 21 69 25 25 77 29 29 93 31 30 96 25 26 83 17 19 70 14 18 65 15 19 66 15 21 71 22 27 82 24 28 91 23 24 88 15 14 74 29 25 75 26 16 51 116 105 122 240 225 228 180 157 143 216 171 142 196 157 128 193 180 172 115 117 129 17 20 51 32 36 74 32 32 70 27 13 28 83 42 14 207 165 125 83 70 51 19 21 20 36 37 57 120 117 146 209 200 229 243 230 248 255 245 244 252 240 226 250 238 224 245 239 225 239 236 227 194 198 201 36 47 69 44 59 98 82 103 160 67 92 156 64 96 155 64 99 155 69 106 159 66 103 158 77 108 172 71 104 175 68 101 182 55 91 169 69 104 170 62 98 160 61 96 164 67 104 174 65 104 173 61 104 173 59 103 174 55 102 172 55 102 172 57 102 167 58 103 158 62 102 163 65 101 179 70 102 185 67 103 177 66 103 173 66 100 172 66 100 172 68 102 176 67 103 177 68 104 180 67 106 181 65 104 181 63 105 181 61 105 180 62 104 180 63 102 179 65 101 179 67 100 179 66 99 178 63 101 176 63 102 177 63 102 177 64 103 178 64 103 178 64 103 178 64 103 178 64 103 178 64 103 178 64 103 178 63 102 177 63 102 177 64 103 178 65 104 179 66 105 180 66 105 180 65 102 180 64 101 179 66 102 178 69 105 179 76 110 184 81 115 186 84 117 188 85 118 187 86 119 186 86 119 186 84 119 187 83 118 186 79 116 187 78 115 186 77 115 188 78 116 189 20 21 67 17 18 62 10 12 53 21 23 64 23 25 64 23 26 59 12 16 43 15 18 51 27 26 83 28 26 91 20 19 77 16 16 68 24 24 76 24 24 74 21 22 70 25 27 78 27 27 89 30 30 94 24 25 81 16 18 67 14 18 63 14 18 63 14 20 68 20 25 80 16 20 81 25 26 90 20 19 79 25 21 72 32 25 59 138 126 146 187 172 177 111 87 75 208 164 137 90 50 25 57 46 42 86 87 105 57 60 95 25 28 71 16 15 57 36 21 40 90 46 21 203 160 125 85 71 58 29 29 37 15 15 41 28 27 61 84 74 109 151 139 163 212 197 200 251 238 230 254 241 232 249 240 231 241 236 230 206 205 210 39 41 62 29 37 73 78 90 140 77 94 148 78 100 150 61 85 133 52 75 119 60 83 127 73 93 144 79 98 156 76 97 162 77 102 166 72 101 157 70 101 158 73 106 173 63 100 171 56 96 168 62 104 176 60 104 177 56 100 173 58 100 172 64 108 173 67 110 165 63 103 164 62 98 174 64 99 180 67 103 177 66 103 173 67 101 173 68 102 174 69 103 177 68 104 178 68 106 181 67 106 181 67 106 181 64 106 180 63 107 180 63 105 179 64 103 178 66 102 178 67 100 179 67 100 177 65 103 178 63 102 177 63 102 177 63 102 177 63 102 177 64 103 178 64 103 178 65 104 179 65 104 179 65 104 179 64 103 178 64 103 178 63 102 177 63 102 177 63 102 177 64 103 178 68 105 183 67 104 182 67 103 179 68 104 178 74 108 182 78 112 183 83 116 187 85 118 187 87 120 187 87 120 187 86 121 189 84 119 187 81 118 189 80 117 188 79 117 190 80 118 191 20 21 65 18 20 61 11 13 54 22 24 65 23 25 64 21 24 59 10 14 39 14 17 48 25 26 80 26 26 86 17 18 72 14 16 65 22 24 73 23 25 74 20 22 71 26 27 81 25 25 85 28 30 87 24 26 77 16 19 64 14 19 61 15 20 62 14 21 67 19 25 77 26 30 91 22 23 87 16 16 76 26 22 73 36 28 65 123 114 135 107 94 103 81 56 49 202 160 135 96 58 37 38 25 32 27 28 56 18 21 64 26 28 79 41 39 89 30 14 40 106 61 42 209 163 137 69 53 54 17 15 37 25 24 64 32 29 76 25 17 64 43 32 66 61 47 60 211 197 197 246 232 231 250 236 235 246 234 236 231 221 229 143 134 151 31 25 51 37 31 67 39 37 74 35 34 66 26 26 54 51 49 73 39 35 58 43 37 63 52 48 75 43 42 73 57 63 95 70 84 119 82 104 151 73 102 168 80 116 192 60 101 180 69 112 191 62 103 182 63 102 181 65 101 177 65 102 172 66 102 162 66 102 162 68 105 176 73 109 185 68 105 176 66 104 175 67 104 175 68 105 176 69 105 179 69 105 179 68 106 179 66 106 178 69 109 181 66 108 180 66 108 180 65 107 179 66 106 178 66 104 177 66 102 178 66 102 178 66 104 179 65 104 179 63 102 177 63 102 177 63 102 177 64 103 178 65 104 179 66 105 180 65 104 179 64 103 178 64 103 178 64 103 178 64 103 178 63 102 177 63 102 177 63 102 177 69 106 184 68 105 183 68 104 180 68 104 178 71 105 179 76 110 181 81 115 186 85 120 188 85 120 188 86 121 189 86 121 189 85 120 188 82 119 190 81 118 189 81 119 192 82 120 193 18 20 59 17 19 58 12 14 55 23 25 66 23 25 66 20 23 58 9 13 38 14 17 48 26 28 77 27 28 84 17 19 68 14 17 62 22 24 73 23 25 76 20 21 75 26 27 83 24 26 83 29 32 85 25 27 75 17 20 63 15 20 58 16 21 61 15 22 68 18 24 76 29 33 94 18 22 85 15 15 75 26 23 76 29 23 61 75 65 90 50 36 49 98 75 69 210 170 144 88 52 30 32 20 32 22 22 56 24 26 74 21 22 76 33 30 83 32 13 41 90 45 26 212 166 140 73 56 66 22 18 51 19 17 64 27 24 77 31 24 75 37 26 66 39 26 44 200 185 192 255 242 249 155 140 147 138 123 130 183 167 177 67 51 64 34 20 37 47 33 56 37 23 46 33 20 40 87 74 91 135 121 134 132 116 127 85 68 78 63 47 58 54 43 57 47 45 59 43 46 63 31 42 70 62 80 130 72 97 163 78 108 180 62 95 172 66 99 178 68 101 180 67 100 177 66 100 171 72 108 168 78 114 174 72 106 178 59 95 171 68 106 179 68 106 177 68 106 177 70 107 178 71 107 181 71 107 181 69 107 180 67 105 178 68 108 180 66 109 178 66 109 178 67 107 176 67 107 177 67 105 176 67 105 178 67 105 178 66 105 180 65 104 179 65 104 179 64 103 178 64 103 178 65 104 179 66 105 180 66 105 180 63 102 177 64 103 178 64 103 178 64 103 178 65 104 179 65 104 179 64 103 178 64 103 178 66 103 181 67 104 182 68 104 180 67 103 177 70 104 176 74 108 179 80 114 185 84 119 187 82 117 185 83 118 186 83 118 186 81 118 188 81 118 189 81 118 189 81 119 192 82 120 193 16 18 56 17 19 57 13 15 56 23 24 68 23 24 68 20 22 60 10 14 41 15 19 48 27 29 77 28 29 83 18 21 66 15 18 61 22 24 72 23 25 76 20 21 78 25 26 83 23 26 81 29 32 83 26 29 74 17 20 61 15 20 58 16 21 61 15 22 68 17 23 75 13 19 77 22 26 89 22 22 82 20 17 70 23 16 58 40 31 60 29 16 33 101 80 75 196 160 126 75 41 14 35 25 36 18 21 56 29 32 77 30 32 81 27 25 74 29 11 35 122 78 51 205 160 127 45 27 39 24 22 59 23 24 72 19 19 69 32 29 76 34 26 65 50 41 60 213 201 211 210 197 207 57 41 54 37 21 34 28 12 23 42 26 37 42 26 36 50 34 45 162 146 157 147 134 144 81 68 78 145 132 141 247 234 241 122 110 112 40 29 33 127 119 130 170 167 176 82 78 77 49 47 50 41 39 61 48 50 89 85 92 147 94 108 173 79 99 172 76 104 178 74 108 179 71 109 174 68 107 166 68 106 168 73 106 183 74 109 190 69 107 180 68 108 178 68 108 180 70 108 181 71 109 184 70 108 183 69 107 180 68 106 179 68 106 177 67 107 176 67 108 174 67 108 174 67 107 176 67 107 176 67 107 177 67 107 177 66 106 178 66 106 178 66 106 178 66 106 178 66 106 178 66 106 178 66 106 178 65 105 177 65 105 177 65 105 177 65 105 177 65 105 177 65 105 177 65 105 177 65 105 177 65 104 179 64 101 179 66 103 181 67 105 180 67 105 178 68 105 176 70 107 177 76 110 181 79 114 182 77 112 180 78 113 181 78 115 185 78 115 185 78 115 186 77 115 186 78 116 189 79 117 190 15 18 53 17 19 57 14 16 57 25 26 70 23 24 68 20 22 60 10 14 41 16 20 49 26 29 74 27 29 80 19 22 65 15 18 59 23 25 73 23 24 78 19 19 79 24 24 84 22 25 80 28 32 80 25 28 73 15 18 59 12 17 55 14 19 59 13 20 66 15 21 73 16 22 80 25 29 90 18 20 79 15 15 69 25 20 61 28 19 50 29 17 37 97 78 74 206 170 134 77 46 15 39 31 44 23 27 65 26 31 73 20 23 68 19 17 64 44 27 45 125 82 47 209 167 129 51 34 44 28 26 63 17 20 63 18 21 66 29 26 69 21 15 49 51 44 60 220 213 221 91 81 92 50 38 52 27 15 29 30 16 29 35 22 29 99 84 87 244 230 229 255 249 248 255 251 255 218 207 213 146 136 144 255 253 255 174 169 165 163 159 156 254 247 254 255 252 255 250 242 231 179 169 160 50 37 46 53 41 63 56 48 85 90 91 137 95 105 158 77 96 152 71 97 156 77 112 170 79 115 173 68 106 169 64 100 176 70 105 186 70 110 182 69 109 179 69 109 181 70 110 182 71 109 184 71 109 184 69 107 182 68 106 179 66 104 175 66 105 172 67 106 173 66 107 171 67 108 174 67 107 176 68 108 178 68 108 178 66 106 178 67 107 179 67 107 179 68 108 180 67 107 179 67 107 179 66 106 178 65 105 177 68 108 180 67 107 179 66 106 178 65 105 177 64 104 176 64 104 176 64 104 176 64 104 176 65 103 178 66 104 179 67 105 178 67 105 178 68 105 176 69 106 177 72 106 177 73 108 176 74 109 177 73 110 178 74 111 181 75 112 182 74 112 183 75 113 184 76 114 187 77 115 188 17 17 51 20 20 56 18 17 59 28 27 71 25 23 70 21 20 60 9 13 42 15 19 48 25 26 72 27 27 77 18 21 62 16 19 60 24 26 74 24 25 81 19 19 81 24 24 88 23 25 82 29 32 83 25 27 75 14 17 62 12 15 58 14 19 61 13 19 67 14 20 72 25 31 89 22 26 87 13 15 74 18 18 70 25 19 63 26 19 52 30 21 42 97 79 77 196 162 124 79 50 16 30 21 38 19 22 65 30 35 77 31 34 77 25 24 66 29 13 26 119 77 35 203 162 118 51 34 42 17 16 50 14 18 55 31 35 70 28 28 62 39 37 61 47 45 56 185 184 189 166 163 170 150 147 156 31 25 37 28 20 31 131 122 123 254 247 239 230 222 209 165 157 144 239 232 226 247 243 242 255 254 255 249 250 252 239 246 238 254 255 248 254 244 242 221 206 201 217 203 190 241 227 218 204 189 196 59 45 60 49 38 55 137 133 150 191 192 210 147 154 180 103 117 156 82 104 154 78 107 163 74 109 173 71 109 184 71 110 189 71 111 181 70 110 179 70 110 180 70 109 184 70 109 186 70 109 186 69 108 185 68 106 181 66 104 175 68 105 173 68 105 173 67 106 171 67 108 174 67 107 176 65 108 177 65 107 179 68 108 180 68 108 180 68 108 180 67 107 179 67 107 179 67 107 179 67 107 179 67 107 179 69 109 181 67 107 179 66 106 178 64 104 176 63 103 175 63 103 175 64 104 176 65 105 177 67 105 180 66 104 179 66 104 177 67 105 178 68 106 177 68 106 177 69 106 176 68 105 175 70 107 177 72 109 179 74 111 182 74 112 183 74 112 185 74 112 185 75 113 186 76 114 187 19 18 52 21 21 57 19 18 58 29 28 72 25 24 68 21 20 60 10 11 39 14 18 47 22 23 69 25 25 75 18 20 61 15 18 59 24 26 74 24 25 81 19 19 83 24 24 88 24 26 85 31 34 89 26 28 79 14 16 64 12 14 62 15 17 65 15 19 67 15 20 75 14 19 77 18 22 83 18 20 77 19 21 72 15 13 53 30 23 56 32 22 46 90 72 72 197 165 127 84 55 25 24 16 40 25 27 76 29 33 80 19 22 67 26 25 69 39 22 38 144 102 60 193 150 105 41 24 32 21 20 52 25 29 58 26 30 57 27 27 53 107 106 124 215 216 220 250 252 249 254 255 255 136 135 140 26 23 32 69 64 70 128 120 117 93 86 70 129 122 96 104 97 69 81 74 58 108 101 93 167 166 164 224 226 223 220 226 214 165 166 152 113 98 91 104 84 75 145 128 108 136 120 104 83 69 69 110 97 106 41 31 40 55 48 56 237 235 238 250 253 255 241 247 255 189 202 236 124 145 188 84 111 164 72 106 170 75 110 178 76 111 177 73 111 176 71 110 179 70 108 181 70 109 186 69 108 187 69 108 187 68 107 182 68 104 178 68 105 175 68 105 173 68 105 173 68 107 174 65 108 176 65 107 179 64 108 179 70 110 182 69 109 181 67 107 179 66 106 178 66 106 178 67 107 179 68 108 180 68 108 180 67 107 179 66 106 178 64 104 176 62 102 174 62 102 174 63 103 175 65 105 177 66 106 178 67 105 180 66 104 179 65 103 176 66 104 177 68 106 177 70 108 179 69 108 177 69 108 177 71 108 178 71 110 179 73 111 182 74 112 183 75 113 186 75 113 186 76 114 187 76 114 187 23 19 52 24 23 57 112 110 149 37 35 75 22 19 62 18 18 56 13 14 42 13 14 44 25 23 70 30 30 82 17 18 62 12 14 55 36 37 85 29 28 85 16 16 78 22 22 86 20 20 82 33 33 93 21 20 77 19 19 71 13 13 65 16 18 67 10 13 64 12 17 72 19 24 82 18 23 81 21 24 79 14 16 65 16 14 54 25 20 52 33 25 48 93 74 76 199 166 135 77 46 26 30 20 55 26 26 86 27 29 86 27 30 83 26 24 73 36 19 38 132 90 52 197 154 112 44 24 33 34 31 62 21 23 46 41 43 64 194 192 213 248 246 255 255 255 255 250 251 245 254 255 249 106 106 104 34 29 33 35 26 27 38 28 19 147 135 113 204 190 153 255 244 203 248 231 203 157 140 122 244 230 221 207 197 187 218 213 193 208 200 181 166 141 134 255 243 232 234 215 183 226 211 180 93 81 69 32 23 26 50 44 58 39 35 50 184 184 192 252 255 255 243 250 255 247 255 255 244 255 255 194 211 239 109 128 171 86 107 160 86 112 169 79 111 170 70 103 170 81 118 188 62 99 177 76 115 194 71 110 189 67 106 183 70 106 182 70 107 178 72 107 175 70 107 175 69 108 177 66 109 178 66 108 182 65 109 182 69 109 181 67 107 179 66 106 178 67 107 179 67 107 179 66 106 178 67 107 179 69 109 181 65 105 177 65 105 177 64 104 176 63 103 175 63 103 175 64 104 176 66 106 178 68 108 180 66 104 177 68 106 179 70 108 181 69 107 180 68 106 177 67 105 176 68 106 177 68 108 178 69 107 178 69 107 178 69 107 178 69 107 178 70 108 181 71 109 182 72 110 185 72 110 185 23 18 48 36 33 64 203 199 234 88 86 123 35 31 68 13 12 44 19 19 43 14 15 43 33 31 80 23 20 73 9 8 52 15 16 60 34 35 83 17 17 69 24 23 80 24 24 84 29 29 91 28 28 88 21 20 77 20 20 74 16 16 68 11 11 63 11 13 64 19 22 75 21 23 80 18 23 78 16 19 72 18 20 68 28 26 65 27 22 54 31 23 47 85 69 72 198 163 135 78 45 28 30 18 56 26 26 90 27 29 88 26 29 84 24 22 71 34 17 36 134 91 57 198 154 115 45 26 30 15 11 34 76 78 91 226 226 236 250 247 255 255 252 255 250 245 239 255 255 243 252 250 238 90 86 77 33 24 25 41 31 30 44 30 21 134 118 95 205 186 144 238 217 172 243 220 186 255 245 221 255 250 241 255 242 234 255 253 234 255 251 234 255 249 239 248 222 207 246 226 189 228 212 176 98 85 69 61 49 49 35 24 38 35 31 46 148 147 153 251 255 255 251 255 255 237 245 247 249 255 255 247 255 255 229 236 255 171 182 212 97 116 158 77 103 154 80 109 167 80 113 180 75 111 185 70 109 188 63 101 182 73 112 191 69 107 182 70 106 180 72 106 177 70 107 177 69 107 178 68 108 180 66 108 182 65 109 184 68 107 182 66 106 178 65 105 177 67 107 179 67 107 179 66 106 178 67 107 179 69 109 181 66 106 178 66 106 178 65 105 177 65 105 177 64 104 176 65 105 177 67 107 179 68 108 180 67 105 178 67 105 178 68 106 179 68 106 179 67 105 176 66 106 176 66 106 176 66 106 176 68 108 178 68 108 178 69 107 178 69 107 178 70 108 181 71 109 182 72 110 185 72 110 185 18 12 40 35 31 58 214 209 239 222 219 248 45 40 70 24 22 46 15 14 32 33 33 59 22 19 66 26 23 78 21 19 66 22 21 65 24 22 69 40 38 85 20 18 67 21 21 73 21 20 77 23 22 80 29 26 81 19 16 69 11 9 59 18 18 68 25 25 75 10 12 63 25 28 81 24 27 80 17 19 70 16 17 63 26 24 61 25 20 52 32 27 50 85 69 72 198 163 133 83 48 28 33 19 54 27 26 86 28 30 87 25 28 79 22 23 67 31 17 32 147 104 70 197 154 112 45 26 20 93 88 95 251 251 249 255 255 250 255 247 245 255 246 243 255 248 237 253 240 224 231 220 202 73 61 47 50 36 35 37 21 22 45 27 23 70 51 34 233 211 174 225 202 160 234 210 176 244 222 199 235 214 211 230 214 215 215 206 201 225 217 206 244 230 217 234 218 195 194 179 140 233 217 181 88 66 53 53 33 34 63 46 54 55 44 52 157 153 150 254 255 250 246 250 253 252 255 255 252 253 255 255 255 253 255 255 248 248 249 251 201 211 236 108 126 166 85 108 158 86 115 173 78 113 181 62 100 175 81 119 200 63 101 182 68 105 183 69 105 181 71 105 177 69 106 176 68 106 177 67 107 179 65 107 183 64 108 183 67 106 181 65 105 177 65 105 177 66 106 178 66 106 178 66 106 178 67 107 179 69 109 181 67 107 179 67 107 179 67 107 179 66 106 178 66 106 178 66 106 178 68 108 180 69 109 181 70 108 179 69 107 178 69 107 178 69 107 178 68 108 178 68 108 178 67 107 179 65 107 179 67 109 181 66 108 180 68 108 180 68 108 180 70 108 183 71 109 184 72 110 185 73 111 186 23 15 39 41 36 58 200 195 217 241 238 255 130 125 145 82 79 96 172 170 183 91 89 110 26 20 68 27 22 78 19 15 65 19 18 62 29 28 70 122 121 161 34 34 72 21 20 60 30 28 77 29 29 81 30 28 77 22 20 67 9 7 54 14 12 59 14 12 59 17 18 66 21 23 72 21 25 73 23 25 73 17 19 60 18 16 53 24 19 51 33 27 53 83 67 70 200 164 130 89 53 27 34 20 46 27 27 79 29 32 85 24 28 75 23 25 64 33 19 32 153 110 78 185 140 99 74 55 41 249 244 240 246 243 228 255 255 237 255 245 232 241 221 210 190 168 155 190 169 152 223 205 183 72 57 38 24 9 4 34 18 21 36 17 19 54 34 25 127 106 75 235 216 176 221 201 168 255 246 225 255 248 248 255 247 255 255 250 254 249 245 242 255 250 238 255 248 222 222 209 167 179 164 125 59 37 23 41 16 19 43 22 29 39 20 24 126 113 105 243 235 224 255 252 248 255 253 250 247 242 238 255 255 244 255 255 236 255 255 244 251 255 255 210 220 247 99 117 155 84 107 157 77 108 172 78 115 186 65 104 183 67 108 188 65 104 181 66 104 179 67 104 175 69 103 174 67 104 175 66 104 177 63 105 181 63 105 181 66 106 178 64 104 176 64 104 176 66 106 178 67 107 179 66 106 178 66 106 178 68 108 180 68 108 180 68 108 180 68 108 180 67 107 179 67 107 179 67 107 179 68 108 180 69 109 181 72 110 181 70 108 179 69 107 178 68 108 178 69 109 179 69 109 179 66 108 180 64 106 178 67 109 181 67 109 181 69 109 181 69 109 181 70 108 183 71 109 184 72 110 185 73 111 186 31 22 41 33 26 42 198 192 204 253 250 255 240 234 244 231 228 235 196 194 199 74 71 88 31 24 68 26 18 75 21 14 65 24 21 64 34 32 69 180 179 210 92 92 118 29 30 58 20 20 56 31 30 70 23 21 61 21 19 58 21 19 58 27 25 64 4 3 43 17 19 60 14 17 62 18 21 66 20 21 65 25 24 64 19 18 52 26 21 53 27 21 49 77 58 62 203 165 128 95 59 25 34 20 35 26 25 65 28 32 77 22 27 69 23 27 62 34 22 32 144 100 71 193 148 107 232 211 190 253 246 228 255 255 230 253 241 215 176 155 134 157 131 116 135 107 95 81 54 37 50 30 5 38 22 0 33 18 13 42 27 32 36 21 28 49 34 31 199 183 157 234 220 183 168 153 120 225 213 191 255 246 247 255 252 255 252 255 255 255 255 255 255 247 237 252 238 211 168 158 109 230 219 174 119 103 87 53 35 35 48 26 38 55 32 40 77 53 43 143 121 100 208 187 166 232 215 195 255 253 237 255 253 240 251 247 235 252 252 244 245 243 244 250 252 255 180 190 217 88 107 147 76 106 160 70 108 173 70 109 184 65 107 183 65 104 181 66 104 177 67 104 174 69 104 172 67 104 174 66 104 175 65 104 179 65 104 179 66 106 178 64 104 174 65 105 175 66 106 176 67 107 177 66 106 176 66 106 176 68 108 178 67 107 177 68 108 178 68 108 178 68 108 178 67 107 177 66 106 176 67 107 177 68 108 178 70 108 179 68 106 177 66 106 176 65 105 175 66 106 178 65 107 179 64 106 178 63 105 177 67 109 181 67 109 181 67 109 183 67 109 183 70 109 184 71 110 185 73 111 186 74 112 187 40 32 45 139 131 142 231 225 229 244 240 241 241 237 236 240 236 233 166 162 159 39 36 47 29 20 63 31 22 77 28 20 71 24 19 60 31 27 60 196 196 220 161 163 178 24 26 41 32 32 58 32 33 64 93 92 123 100 99 130 23 20 51 14 10 43 24 23 57 14 14 52 25 27 66 51 54 95 14 16 57 22 22 60 22 21 55 26 21 53 26 20 48 77 58 64 205 162 127 101 61 26 32 19 29 23 23 59 27 32 74 20 25 67 23 27 62 34 22 34 134 89 68 232 186 153 255 236 212 255 248 227 239 230 201 137 121 95 122 95 76 71 39 26 38 8 0 41 15 0 30 10 0 36 21 0 24 13 7 24 13 17 40 27 37 35 21 20 144 130 103 179 165 126 216 203 161 241 227 198 234 223 217 255 251 255 245 246 250 251 250 248 226 210 197 227 208 178 206 193 141 215 203 153 105 90 69 46 28 26 62 40 52 47 24 30 53 30 16 44 20 0 53 29 3 117 95 72 185 166 149 230 217 201 254 244 234 255 253 244 255 253 246 255 253 252 235 241 255 113 130 160 89 117 165 69 105 165 64 104 174 70 114 187 65 107 181 68 106 179 69 106 176 71 106 174 69 106 174 69 106 176 68 106 179 67 107 179 66 106 176 65 105 175 65 105 175 67 107 177 67 107 177 66 106 176 66 106 176 68 108 178 66 106 176 67 107 177 68 108 178 68 108 178 67 107 177 66 106 176 66 106 176 66 106 176 70 108 179 69 107 178 67 107 177 66 106 176 65 107 179 65 107 179 65 107 179 65 107 179 66 108 180 66 108 180 67 109 183 67 109 183 71 110 185 72 111 186 75 113 188 75 113 188 178 168 176 234 228 232 245 240 237 255 251 245 247 243 234 233 231 219 119 116 107 18 15 22 35 27 68 35 26 81 18 10 61 19 14 54 40 37 66 211 210 228 212 213 218 83 86 91 78 79 97 187 188 209 207 205 227 80 78 100 22 18 43 24 21 48 17 17 45 15 16 47 43 46 81 133 135 173 33 35 73 17 17 55 21 20 54 24 18 52 31 24 55 77 56 65 204 159 126 105 62 30 34 18 31 23 23 61 28 32 80 19 23 71 23 26 67 33 21 41 129 85 76 254 206 186 255 241 227 246 234 218 117 105 83 71 50 31 38 5 0 42 9 4 34 6 2 26 8 0 105 93 67 120 113 87 36 32 23 30 24 26 25 13 23 50 36 33 140 125 92 255 240 190 252 233 177 230 209 162 179 159 134 240 224 209 255 249 237 255 246 233 210 187 171 233 208 177 255 235 182 255 238 186 105 85 58 57 37 28 53 34 36 45 27 27 48 32 16 118 103 80 48 31 13 55 36 22 44 22 11 99 75 65 174 148 135 210 187 171 255 248 232 246 237 228 230 233 238 196 211 234 70 97 140 84 119 175 71 112 178 66 110 181 66 108 180 68 108 178 70 107 175 72 107 173 72 107 173 70 107 175 69 107 178 68 108 178 67 107 177 66 106 176 66 106 176 68 108 178 68 108 178 66 106 176 66 106 176 67 107 177 66 106 176 67 107 177 68 108 178 68 108 178 67 107 177 66 106 176 65 105 175 66 106 176 71 109 180 71 109 180 69 109 179 68 108 178 66 108 180 66 108 180 66 110 181 67 111 182 64 108 181 65 109 182 66 108 182 67 109 183 71 110 185 73 112 187 76 114 189 77 115 190 184 176 187 229 222 229 246 240 240 250 247 242 251 248 241 251 248 243 176 172 169 65 59 71 28 20 61 36 26 76 24 16 57 39 32 63 47 43 60 202 199 206 232 231 227 226 225 221 241 240 246 230 228 242 161 158 179 29 27 49 29 27 51 18 18 44 9 9 37 20 19 50 51 51 79 211 211 237 71 68 95 24 21 48 27 23 48 26 20 46 36 29 60 66 46 57 203 158 127 107 63 34 35 18 34 25 24 64 30 34 82 20 24 72 24 27 68 32 22 47 115 80 84 255 219 211 248 229 222 144 134 124 37 25 9 26 8 0 41 12 8 128 97 95 117 92 85 60 43 25 45 34 4 157 151 119 36 29 10 71 63 50 87 74 66 51 35 19 116 98 58 209 189 138 214 193 138 212 190 141 231 208 177 173 152 131 110 93 77 121 104 88 190 164 147 209 184 154 214 192 143 225 205 155 118 97 66 63 42 23 78 60 46 48 32 16 86 75 47 150 139 107 41 27 1 115 97 77 129 106 90 48 21 4 59 28 7 99 71 49 180 159 140 248 236 224 241 236 240 220 225 244 109 125 158 80 107 152 78 110 167 74 112 175 69 107 178 68 108 180 69 107 178 70 107 177 70 107 175 70 107 175 69 108 177 69 107 178 68 108 178 66 106 176 66 106 176 68 108 178 68 108 178 66 106 176 66 106 176 67 107 177 66 106 176 67 107 177 68 108 178 68 108 178 67 107 177 66 106 176 65 105 175 65 105 175 69 107 178 69 107 178 69 109 179 68 108 178 64 106 178 64 106 178 66 108 180 67 111 182 65 107 181 65 107 181 66 108 182 67 109 183 71 110 185 73 112 187 76 114 189 77 115 190 30 22 46 53 46 64 123 118 125 249 247 250 246 243 252 206 202 216 194 190 205 92 86 112 33 22 65 39 28 70 30 20 44 80 73 81 173 164 165 243 236 230 242 234 221 240 232 219 244 235 228 220 213 220 59 54 77 33 29 62 22 22 56 24 27 60 5 5 39 18 18 46 71 67 84 234 226 237 151 141 149 34 23 29 63 54 57 123 115 126 50 44 70 69 52 62 205 162 130 111 70 40 31 18 35 17 20 61 28 33 75 25 30 70 21 21 59 31 25 53 93 79 92 195 180 185 148 138 139 31 23 20 29 23 11 33 25 14 42 22 21 192 169 163 222 200 177 210 190 155 201 183 143 162 147 104 209 194 151 238 223 182 252 234 194 231 214 171 233 215 169 139 121 75 117 98 56 54 35 2 48 30 8 64 46 32 53 34 27 51 34 24 54 35 18 64 47 21 98 82 46 193 178 139 211 193 157 248 230 194 255 243 205 192 174 134 202 185 139 156 139 93 233 216 172 236 217 177 196 173 139 74 50 16 119 93 60 80 54 27 64 42 28 178 160 156 254 239 244 227 219 232 159 157 178 98 105 133 93 111 149 82 108 157 76 109 176 66 104 179 70 112 188 64 106 180 73 113 182 68 107 174 70 107 175 70 107 177 71 109 180 69 107 178 68 106 177 68 106 177 69 107 178 70 108 179 69 107 178 68 106 177 69 107 180 68 106 179 68 106 179 67 105 178 67 105 178 67 105 178 68 106 179 69 107 180 69 109 179 70 110 180 71 111 181 72 112 182 71 111 181 71 111 181 71 111 183 70 112 184 72 112 184 71 111 183 71 111 183 71 111 183 72 112 184 73 113 185 75 115 187 76 116 188 28 21 54 29 23 49 86 82 96 245 243 254 191 188 207 65 61 84 57 53 80 44 37 71 29 20 63 55 45 82 152 143 160 225 216 219 253 243 241 246 236 227 252 242 230 250 238 226 244 231 223 114 103 109 36 27 54 29 25 62 23 23 61 4 7 42 30 31 61 19 17 38 80 72 83 234 224 225 218 205 199 153 140 132 204 193 187 133 124 129 35 27 51 53 35 47 202 159 125 114 73 41 32 20 34 17 21 58 28 34 70 27 31 66 24 20 55 25 21 48 23 19 36 7 4 13 20 14 18 22 17 14 14 11 2 29 25 16 32 18 18 60 41 35 153 133 108 168 148 113 74 54 17 194 175 133 245 227 181 198 180 132 194 176 128 242 224 178 255 239 197 252 234 196 255 243 215 207 190 170 128 110 98 91 74 67 44 26 26 67 49 47 120 102 90 211 194 176 255 247 225 250 233 205 255 237 204 245 227 189 219 200 157 255 237 192 252 230 183 161 139 92 207 184 140 222 199 157 212 186 149 246 220 185 224 198 165 66 42 16 38 19 5 58 38 37 202 183 189 194 178 189 114 106 117 53 52 68 32 42 69 74 96 137 80 108 171 69 105 179 68 109 188 61 105 180 70 113 181 67 108 174 69 106 174 69 106 174 70 109 178 68 106 177 67 105 176 67 105 176 68 106 177 68 106 177 68 106 177 67 105 176 69 107 180 69 107 180 68 106 179 68 106 179 68 106 179 69 107 180 70 108 181 71 109 182 71 111 181 72 112 182 73 113 183 73 113 183 72 112 182 72 112 182 73 113 185 74 114 186 73 113 185 73 113 185 73 113 185 73 113 185 74 114 186 75 115 187 76 116 188 77 117 189 16 10 48 37 32 64 79 76 95 234 233 249 126 124 146 28 25 52 27 24 53 23 17 53 31 22 69 44 33 75 99 91 112 181 174 182 227 218 223 255 246 245 250 241 236 248 237 233 235 220 225 177 165 179 48 39 68 34 28 64 13 11 51 15 15 49 23 22 40 30 27 36 117 107 115 246 234 234 232 218 207 192 178 167 184 170 167 57 47 56 31 22 51 62 42 54 198 154 119 121 78 43 33 20 30 18 21 54 29 32 65 29 30 60 23 18 48 20 15 38 28 22 36 19 14 20 20 14 16 23 18 15 17 17 7 20 17 8 19 7 9 34 18 18 27 9 0 50 32 12 76 58 36 122 104 80 70 50 23 51 32 2 197 178 146 213 196 166 188 171 143 160 144 119 114 97 79 88 72 59 77 59 55 90 74 74 62 45 51 89 70 74 86 65 62 98 75 69 162 138 128 177 154 140 233 208 188 237 213 189 153 127 100 98 73 42 134 107 77 135 108 78 40 11 0 62 32 4 124 94 70 217 186 165 197 166 146 180 154 137 177 159 149 60 45 42 65 50 53 36 25 33 22 15 23 15 14 28 19 30 50 30 50 85 80 106 163 74 108 179 68 110 184 62 106 177 70 114 179 69 110 174 71 110 177 71 108 176 70 109 178 69 108 177 68 106 177 68 106 177 69 107 178 69 107 178 68 106 177 68 106 177 70 108 179 70 108 179 69 107 180 69 107 180 70 108 181 71 109 182 72 110 183 73 111 184 71 111 181 72 112 182 73 113 183 74 114 184 73 113 183 73 113 183 74 114 184 75 115 185 73 113 185 73 113 185 74 114 186 75 115 187 75 115 187 76 116 188 77 117 189 77 117 189 20 14 58 32 28 65 62 59 86 204 205 226 64 61 90 31 27 60 29 25 58 13 9 46 22 14 63 40 31 76 21 16 39 40 34 48 79 71 84 202 194 205 254 244 252 253 242 250 250 236 251 238 224 247 111 102 133 30 24 62 26 21 61 45 44 75 94 93 101 143 139 138 216 205 209 247 233 232 209 193 180 194 180 167 104 90 90 28 17 31 27 17 51 49 28 43 196 150 114 126 82 43 32 17 24 22 23 51 30 31 59 24 24 50 20 15 38 19 10 29 11 5 15 22 16 20 25 16 17 19 14 10 9 9 0 16 16 6 28 19 22 27 15 19 36 22 21 25 10 5 23 8 1 37 23 14 69 53 40 44 28 13 44 28 12 46 30 14 43 30 14 46 32 19 41 27 18 30 17 11 40 26 26 34 22 26 28 15 22 46 31 38 43 24 26 38 18 19 61 40 37 50 29 24 63 40 32 66 44 31 79 56 40 89 66 48 193 168 148 200 175 155 209 183 166 85 59 42 47 19 5 44 18 5 116 89 78 153 133 122 67 53 44 41 32 27 32 22 23 24 15 20 29 24 30 10 10 20 16 25 42 0 15 48 69 94 148 75 109 173 70 109 178 64 107 176 69 113 176 69 110 172 72 111 176 73 110 178 72 111 180 72 111 180 72 110 181 72 110 181 72 110 181 71 109 180 71 109 180 70 108 179 71 109 180 71 109 180 71 109 182 71 109 182 71 109 182 72 110 183 72 110 183 73 111 184 71 111 181 72 112 182 73 113 183 73 113 183 73 113 183 73 113 183 74 114 184 75 115 185 72 112 184 73 113 185 74 114 186 75 115 187 76 116 188 76 116 188 77 117 189 77 117 189 27 23 73 31 28 73 57 56 90 144 145 175 29 28 62 29 27 64 26 24 61 16 14 54 19 12 63 23 17 63 26 21 51 21 16 38 40 35 57 199 191 214 243 234 253 120 111 132 137 124 150 204 190 223 207 197 234 59 51 92 26 20 58 48 44 69 89 84 88 156 149 143 210 198 198 232 217 214 232 216 203 191 174 164 53 41 43 30 19 36 29 19 56 51 30 47 194 146 108 130 85 43 30 14 15 26 24 48 30 28 49 19 16 35 18 9 28 19 8 24 22 15 22 17 11 11 21 12 13 27 22 18 19 21 10 13 14 6 21 15 19 9 0 7 26 19 26 20 14 18 20 11 16 33 24 27 22 14 12 32 24 21 31 22 17 20 11 6 27 18 13 24 17 11 32 24 21 26 21 18 40 34 36 28 22 26 35 28 35 28 22 26 35 29 31 34 28 28 30 21 22 36 26 25 46 35 31 33 22 16 35 22 14 48 35 26 108 96 84 140 128 116 231 219 207 221 209 197 91 77 68 44 31 22 32 19 11 23 14 5 19 12 4 21 16 12 19 13 15 13 8 12 19 15 16 13 14 19 0 7 20 4 20 46 52 76 124 72 104 163 68 106 171 66 107 173 66 110 171 66 107 169 70 109 174 73 112 179 73 112 181 74 113 182 74 113 182 74 113 182 74 113 182 73 112 181 73 111 182 73 111 182 73 111 182 73 111 182 73 111 182 72 110 181 72 110 181 72 110 181 72 110 181 72 110 181 73 113 183 73 113 183 74 114 184 74 114 184 73 113 183 74 114 184 75 115 185 76 116 186 73 113 185 74 114 186 75 115 187 76 116 188 77 117 189 77 117 189 77 117 189 77 117 189 10 7 62 27 25 74 40 39 79 56 56 92 23 22 62 31 30 70 22 22 60 15 13 53 27 23 74 26 23 70 30 26 61 34 31 60 27 22 54 200 195 227 190 183 214 46 39 70 35 25 60 42 31 71 94 85 130 86 79 121 21 15 51 25 20 43 24 19 23 38 30 27 139 125 125 234 219 216 238 221 213 198 183 178 119 108 114 33 24 43 29 21 58 40 22 38 190 142 102 139 92 46 32 14 14 26 23 42 24 23 39 14 12 25 19 8 22 19 9 18 14 8 10 23 17 17 16 10 10 11 7 4 22 25 16 10 15 9 14 12 17 28 22 32 14 8 20 21 15 27 18 12 22 10 5 12 22 17 23 19 13 17 18 12 14 39 30 33 38 29 32 29 20 21 24 18 18 24 18 20 17 13 14 19 14 18 27 22 26 20 15 19 24 22 25 11 11 13 22 20 21 31 27 28 22 16 16 34 29 26 33 25 22 34 26 23 48 41 35 98 91 85 54 47 41 34 27 21 24 17 11 29 24 18 13 8 2 20 17 12 12 13 7 8 9 4 15 13 14 20 18 21 11 10 8 17 18 20 4 13 22 8 23 46 42 65 107 75 104 160 71 106 170 71 110 175 70 111 173 68 109 171 71 110 177 75 114 183 74 113 182 75 114 183 76 115 184 76 115 184 75 114 183 74 113 182 74 112 183 74 112 183 75 113 184 75 113 184 74 112 183 74 112 183 73 111 182 73 111 182 72 110 181 72 110 181 75 115 185 75 115 185 75 115 185 75 115 185 74 114 184 74 114 184 76 116 186 77 117 187 75 115 187 76 116 188 76 116 188 77 117 189 78 118 190 79 119 191 79 119 191 79 119 191 22 21 78 29 29 81 23 24 68 26 28 69 43 44 90 31 32 76 17 19 57 18 18 56 22 20 69 27 25 74 9 7 44 28 27 61 39 34 74 201 196 237 101 96 136 34 29 69 34 27 68 44 37 81 37 30 81 39 33 79 26 21 53 15 10 30 21 15 27 20 11 16 107 95 97 248 234 234 203 188 191 199 186 193 202 192 200 52 48 65 13 9 44 43 26 42 185 137 97 150 102 56 35 17 13 21 18 35 16 16 26 12 11 19 23 13 22 18 8 16 22 16 16 16 12 11 22 18 17 12 11 9 11 18 11 11 20 15 17 18 23 5 2 13 17 11 23 12 4 15 22 14 25 22 14 25 5 0 6 27 17 25 22 12 20 13 2 8 19 8 14 23 12 16 27 16 20 34 23 27 23 14 17 27 18 19 26 17 18 24 18 18 21 16 20 22 20 23 27 23 24 23 19 20 26 20 22 33 27 27 17 8 9 22 14 12 18 10 8 15 7 5 22 17 13 12 7 3 20 15 11 13 10 5 3 0 0 14 15 10 10 15 9 9 14 10 13 13 15 17 15 18 5 4 2 7 9 8 14 21 27 5 18 37 44 63 105 83 110 165 75 109 172 76 114 179 73 114 176 72 113 177 74 113 182 76 114 185 74 113 182 75 114 183 77 116 185 77 116 185 75 114 183 74 113 182 74 113 182 75 114 183 76 115 184 76 115 184 76 114 185 75 113 184 75 113 184 75 113 184 74 112 183 74 112 183 76 116 185 76 116 185 76 116 186 75 115 185 74 114 184 74 114 184 75 115 185 76 116 186 76 116 186 76 116 186 76 116 188 76 116 188 77 117 189 78 118 190 80 120 192 80 120 192 15 14 72 35 35 89 20 21 69 24 25 71 34 34 84 35 36 82 25 27 65 10 12 50 16 14 61 23 21 68 20 20 58 18 18 56 42 40 87 161 159 208 30 28 75 33 32 76 27 26 70 32 30 77 28 25 80 25 21 71 20 17 48 21 18 39 18 11 29 22 14 29 133 122 130 230 219 227 88 75 92 45 34 50 148 142 154 127 124 141 24 24 58 32 18 31 181 133 93 156 110 61 39 21 17 17 17 29 11 11 19 13 12 17 25 18 25 18 9 14 18 14 13 10 9 7 22 18 19 12 12 12 13 22 17 9 20 16 12 15 22 14 14 24 14 11 20 15 12 19 9 6 13 18 15 22 22 17 24 4 0 5 22 15 22 14 7 14 19 10 15 17 8 13 17 8 11 14 5 8 15 9 11 18 12 12 14 8 8 17 11 11 18 14 15 13 11 14 12 8 9 15 11 12 11 5 7 11 5 7 23 14 15 18 9 10 19 10 11 22 16 16 10 4 4 13 9 6 15 11 10 8 7 5 25 24 22 8 10 7 11 17 15 1 7 5 15 16 20 10 10 12 19 18 16 17 17 17 7 12 16 4 15 33 47 65 105 87 112 166 77 109 170 76 114 179 74 114 176 73 114 180 73 111 182 74 112 185 75 114 183 76 115 184 78 117 186 78 117 186 76 115 184 75 114 183 75 114 183 76 115 184 76 115 184 76 115 184 76 114 185 76 114 185 76 114 185 76 114 185 76 114 185 76 114 185 75 115 184 75 115 184 75 115 185 74 114 184 72 112 182 72 112 182 74 114 184 75 115 185 76 116 186 75 115 185 75 115 187 75 115 187 76 116 188 77 117 189 79 119 191 80 120 192 16 17 73 25 27 78 22 24 73 28 30 78 30 32 83 34 36 84 21 24 59 15 18 51 22 24 65 19 20 64 13 15 53 25 27 66 39 39 89 52 52 104 31 31 81 21 23 71 29 31 79 28 30 79 27 28 84 25 25 75 19 19 53 13 13 39 15 11 38 19 14 37 151 142 163 171 161 185 29 19 46 16 10 36 28 27 43 47 50 67 22 26 55 28 16 26 167 122 80 158 111 65 34 19 12 12 14 26 11 14 21 15 16 20 17 15 20 19 14 18 15 14 12 16 16 16 15 13 16 11 12 16 9 18 15 6 16 15 8 11 20 10 13 22 5 9 12 9 15 13 10 16 14 8 14 14 9 13 14 13 14 16 12 12 14 7 7 9 10 10 12 13 13 15 11 11 13 10 10 10 14 14 14 13 13 11 10 10 8 12 12 10 16 14 15 14 12 15 14 10 11 14 10 11 16 10 12 16 10 12 17 8 11 16 7 10 14 8 10 16 10 12 16 10 12 13 9 8 10 10 10 12 14 13 11 13 12 6 10 9 7 16 15 5 13 15 10 13 18 14 13 18 14 13 11 17 17 17 14 19 23 2 13 31 50 65 104 90 113 167 80 110 172 78 113 179 71 109 174 79 118 185 74 112 185 82 120 193 77 116 185 78 117 184 79 118 185 79 118 185 77 116 183 76 115 182 75 114 183 75 114 183 75 114 183 76 115 184 77 116 185 77 116 185 77 116 185 77 116 185 76 115 184 76 115 184 75 114 183 75 114 183 74 113 182 74 113 182 75 114 183 77 116 185 78 116 187 79 117 188 79 117 188 77 115 186 75 113 184 75 113 184 77 115 186 78 116 187 77 115 188 77 115 188 17 19 70 25 27 78 22 24 73 28 30 79 30 31 85 33 35 83 19 22 57 12 15 46 17 19 58 21 23 64 19 21 59 26 28 69 27 27 79 32 33 89 22 23 77 25 28 79 23 29 77 24 30 80 26 29 84 24 26 77 18 20 58 13 14 45 15 12 43 18 13 45 133 127 161 66 58 95 22 16 54 25 21 54 13 15 36 12 17 37 13 19 45 35 24 30 161 116 74 162 115 69 31 16 9 12 14 26 9 14 18 13 17 20 15 15 17 16 14 17 9 9 9 18 18 18 17 16 21 9 10 14 5 14 11 9 18 17 15 15 23 18 17 25 13 17 16 11 18 11 9 16 9 7 14 7 6 11 7 9 11 10 13 15 14 19 19 19 12 12 12 14 14 14 13 13 13 12 12 12 14 14 14 12 14 13 10 10 8 11 11 9 11 9 10 13 9 10 14 8 10 15 9 9 19 10 11 20 11 12 21 11 12 20 10 11 16 7 8 20 11 12 19 13 13 15 11 12 9 9 9 8 10 9 10 11 13 9 13 14 3 11 13 1 6 9 7 7 15 11 10 15 10 8 9 10 10 10 13 16 21 10 18 37 42 57 96 90 111 166 83 113 175 82 117 183 76 114 179 81 120 187 75 113 186 81 119 194 78 117 186 79 118 185 80 119 186 80 119 186 79 118 185 78 117 184 77 116 185 77 116 185 78 117 186 78 117 186 79 118 187 79 118 187 78 117 186 78 117 186 77 116 185 77 116 185 78 117 186 77 116 185 76 115 184 76 115 184 76 115 184 77 116 185 78 116 187 79 117 188 79 117 188 77 115 186 75 113 184 75 113 184 76 114 185 77 115 186 77 115 188 77 115 188 18 18 68 27 27 77 24 24 74 30 30 82 30 29 86 33 33 83 19 19 55 12 13 44 16 18 56 21 23 62 20 22 60 23 25 66 18 19 73 17 18 75 18 19 73 29 32 85 21 28 80 22 29 81 24 30 82 24 28 76 19 20 64 14 14 52 12 13 43 15 14 48 55 52 99 21 17 67 34 31 74 9 9 45 4 8 37 17 23 47 16 22 46 28 18 19 155 111 66 171 124 78 28 13 6 12 16 27 7 15 18 11 17 17 13 14 18 14 14 16 21 21 23 16 16 18 10 8 13 12 11 16 16 21 17 15 17 14 10 5 9 10 4 8 13 12 8 9 10 2 11 12 4 18 18 10 18 17 12 15 11 8 13 9 6 17 11 11 16 10 10 16 10 10 16 10 10 16 10 10 17 11 11 16 12 11 16 10 10 17 11 11 21 12 13 21 12 13 21 11 12 21 11 10 21 9 9 20 8 8 21 7 6 20 6 5 23 11 11 20 10 9 18 10 8 18 12 12 16 14 15 14 14 14 13 14 16 11 15 18 11 15 18 6 10 13 12 11 19 20 15 21 17 13 14 12 10 11 10 13 18 5 13 32 34 49 88 91 112 167 85 115 177 85 120 184 78 116 179 82 121 188 76 114 187 82 120 193 79 118 187 80 119 186 81 120 187 82 121 188 81 120 187 80 119 186 80 119 186 80 119 186 81 120 187 81 120 187 81 120 189 81 120 189 80 119 188 79 118 187 79 118 187 78 117 186 80 119 188 80 119 188 79 118 187 78 117 186 77 116 185 78 117 186 78 117 186 79 118 187 78 116 187 77 115 186 76 114 185 75 113 184 76 114 185 77 115 186 77 115 186 77 115 186 18 19 65 26 27 75 23 23 73 29 29 83 30 29 87 34 31 84 18 18 54 10 11 42 22 22 60 20 22 61 13 15 53 20 21 65 22 23 79 18 19 76 21 24 79 27 33 85 22 29 84 22 29 83 24 30 82 23 27 75 17 20 65 11 13 52 11 12 42 14 14 48 13 10 65 27 26 84 24 22 71 13 15 54 16 19 52 18 22 51 9 13 38 34 22 22 155 108 64 180 132 84 28 13 6 15 18 27 7 15 17 9 18 17 12 13 17 13 13 15 13 13 15 14 14 16 15 10 16 15 10 14 14 13 8 13 10 5 20 8 8 31 15 15 31 14 7 26 9 1 22 5 0 25 6 0 28 9 2 29 8 3 30 9 4 33 9 7 35 11 9 32 8 6 32 8 8 33 9 9 29 8 7 29 8 7 31 10 9 32 11 10 30 9 8 31 10 9 34 10 8 35 11 7 37 12 8 38 13 8 41 14 7 42 15 8 37 12 7 32 9 3 29 8 3 27 9 5 24 8 8 19 7 7 17 7 8 16 10 12 13 11 14 7 6 11 11 6 13 17 10 17 17 11 11 18 14 13 15 16 21 5 14 31 28 43 82 94 115 168 87 117 177 84 120 182 77 115 177 80 119 184 78 116 187 85 123 194 81 120 189 82 121 188 83 122 189 83 122 189 82 121 188 82 121 188 82 121 188 82 121 188 83 122 189 83 122 189 82 121 190 82 121 190 82 121 190 81 120 189 81 120 189 80 119 188 81 120 189 81 120 189 80 119 188 79 118 187 79 118 187 79 118 187 79 118 187 79 118 187 78 116 187 77 115 186 77 115 186 76 114 185 77 115 186 77 115 186 78 116 187 79 117 188 19 18 62 27 25 72 24 22 72 30 27 84 31 26 90 35 30 88 19 17 56 11 12 43 24 24 62 22 21 61 11 13 52 19 20 64 27 28 84 20 20 80 25 28 83 28 31 86 25 30 88 25 30 88 25 28 81 22 24 72 16 17 61 9 12 47 9 13 38 12 15 46 22 22 76 26 26 86 15 15 67 23 24 68 7 9 47 9 10 41 12 13 41 36 20 21 157 106 63 181 130 83 29 12 4 17 17 25 8 13 16 11 17 15 12 13 15 13 13 15 5 5 7 18 13 17 23 14 19 21 11 12 23 14 7 26 13 5 25 4 1 29 0 0 42 1 0 56 9 1 66 19 11 73 24 17 79 30 23 87 37 30 91 41 34 94 41 35 88 35 29 80 27 21 77 26 22 75 24 20 66 15 11 64 13 9 66 17 13 64 16 12 73 25 21 73 28 23 81 34 28 88 41 33 99 51 39 109 60 46 116 67 52 120 71 56 124 76 62 114 68 55 99 55 44 79 39 31 59 22 16 43 8 4 37 3 1 31 5 4 21 11 10 15 11 12 19 9 17 18 9 14 15 6 7 16 12 11 16 17 22 5 14 31 17 32 71 92 115 167 87 118 175 84 120 180 79 118 177 81 121 182 81 118 186 87 124 194 82 121 188 83 122 187 84 123 188 84 123 188 83 122 187 82 121 186 83 122 189 84 123 190 82 121 188 82 121 188 82 121 188 82 121 188 82 121 188 82 121 188 82 121 190 82 121 190 81 120 189 81 120 189 80 119 188 80 119 188 80 119 188 80 119 188 80 119 188 81 120 189 79 117 188 79 117 188 78 116 187 78 116 187 78 116 187 78 116 187 80 118 189 80 118 189 19 18 60 27 26 70 24 21 74 29 25 84 31 24 91 35 29 89 20 18 58 13 12 44 20 18 57 23 22 62 17 16 56 19 20 66 27 28 85 17 17 77 26 29 84 26 29 84 28 30 91 27 29 90 26 27 84 23 24 72 15 17 55 9 13 42 9 14 36 12 15 46 20 22 71 19 20 77 26 27 83 12 13 61 6 5 45 18 17 51 20 17 48 32 13 17 159 106 62 174 120 73 31 11 2 14 15 20 8 14 14 10 16 14 13 15 14 14 14 14 18 16 17 15 9 11 16 4 8 22 8 8 25 11 2 25 5 0 40 10 2 66 26 18 115 61 51 144 82 71 168 106 93 178 116 103 181 118 103 182 119 104 185 119 103 184 118 102 181 115 99 170 104 88 169 103 89 163 100 85 148 85 70 142 79 64 144 82 69 141 79 66 144 85 71 145 86 70 153 92 74 162 101 82 174 112 91 187 123 98 193 129 102 196 132 105 185 124 96 186 126 102 187 129 109 184 128 111 169 117 103 132 84 72 84 37 27 41 4 0 22 4 2 15 6 7 22 11 17 23 12 16 18 8 7 14 10 7 13 14 18 4 13 28 5 21 57 90 113 163 87 118 173 87 123 181 84 123 180 84 124 183 83 121 186 86 123 191 84 123 188 84 123 188 85 124 189 84 123 188 84 123 188 83 122 187 84 123 190 84 123 190 83 122 189 82 121 188 82 121 188 82 121 188 82 121 188 83 122 189 83 122 191 83 122 191 82 121 190 81 120 189 81 120 189 81 120 189 81 120 189 82 121 190 82 121 190 82 121 190 80 118 189 80 118 189 80 118 189 80 118 189 79 117 188 80 118 189 81 119 190 83 121 192 21 19 59 29 26 71 25 20 74 30 24 84 32 22 91 36 27 90 22 17 58 14 13 47 16 14 53 24 23 63 18 17 59 18 19 65 26 27 84 15 15 77 28 31 86 24 26 83 28 28 92 28 25 92 27 25 88 25 23 73 18 19 50 11 13 34 9 14 36 12 16 45 17 20 61 11 14 65 22 23 80 16 16 70 22 21 61 21 17 50 11 5 39 42 19 27 169 112 69 168 112 63 36 14 3 13 12 17 8 12 11 10 15 11 13 13 13 14 12 13 19 15 16 19 10 11 20 5 8 26 6 7 34 14 3 58 32 17 116 80 68 179 131 119 200 143 124 210 148 127 216 154 131 217 155 130 219 155 128 220 156 128 225 160 130 228 163 133 229 164 132 218 155 122 221 158 125 222 159 126 205 144 113 199 138 107 202 141 112 198 137 108 207 143 115 207 142 112 210 144 112 215 149 114 225 157 120 229 161 122 230 161 119 227 158 116 221 154 111 214 151 110 209 147 110 207 148 114 210 155 125 201 147 121 167 117 94 126 87 70 52 32 25 25 14 12 17 2 5 22 7 10 20 10 8 16 11 7 14 15 17 9 19 31 2 18 52 92 116 164 86 119 172 87 124 179 85 125 177 85 126 180 85 123 185 85 123 186 86 125 190 86 125 190 86 125 190 86 125 190 84 123 188 84 123 188 85 124 189 85 124 189 84 123 190 84 123 190 83 122 189 83 122 189 82 121 188 83 122 189 83 122 189 83 122 189 83 122 191 83 122 191 83 122 191 83 122 191 83 122 191 83 122 191 83 122 191 83 122 191 81 120 189 82 121 190 82 120 191 81 119 190 81 119 190 81 119 190 83 121 192 85 123 194 26 21 53 32 27 67 27 20 72 29 23 85 30 23 91 34 28 90 20 18 58 14 13 45 15 13 50 24 22 62 16 15 57 17 15 64 28 27 84 18 19 76 31 34 89 23 26 81 25 25 87 27 25 90 27 25 88 26 23 76 19 20 50 12 14 35 11 13 34 12 16 43 19 23 58 21 24 69 18 19 75 22 21 78 11 10 52 15 14 48 19 13 51 35 11 24 175 120 79 165 109 60 40 18 5 15 13 16 12 13 8 13 14 8 14 10 9 15 9 11 17 7 8 35 19 20 44 18 19 54 23 18 98 67 49 158 124 99 196 154 132 208 157 136 214 157 130 210 149 120 211 147 119 223 160 129 237 174 143 244 179 147 246 182 147 247 183 147 243 181 144 234 172 135 243 181 142 248 189 149 237 177 140 234 174 138 239 179 143 237 174 139 241 175 141 239 173 138 241 173 136 246 178 139 249 184 142 250 186 142 246 182 136 239 177 130 225 163 116 229 166 122 227 164 121 220 156 118 217 157 120 216 160 123 205 153 116 184 140 113 150 121 107 88 65 59 42 17 12 32 9 3 29 11 7 19 11 8 15 15 15 12 20 33 4 23 56 95 122 169 85 121 173 83 122 177 83 123 175 84 123 178 85 124 181 85 125 186 87 127 189 87 126 191 87 126 191 86 125 190 85 124 189 85 124 189 85 124 189 86 125 190 86 125 190 85 124 189 84 123 190 83 122 187 83 122 189 83 122 187 83 122 189 83 122 189 85 124 191 85 124 191 85 124 193 84 123 190 84 123 192 84 123 192 83 122 191 83 122 191 82 121 190 83 122 191 83 122 191 83 122 191 82 120 191 82 120 191 84 122 193 86 124 195 79 71 86 42 33 60 29 21 72 37 28 93 26 21 87 33 32 89 20 24 61 11 15 44 14 15 46 25 25 63 22 19 64 22 20 70 18 18 70 28 31 84 26 32 84 19 26 80 26 28 85 25 25 85 25 23 86 27 24 79 20 20 58 12 12 38 12 13 33 15 17 38 16 19 52 26 29 72 17 18 72 15 16 70 20 21 67 11 10 50 12 10 49 37 17 29 163 114 74 171 120 73 30 8 0 16 11 8 14 11 2 18 14 5 24 14 13 26 10 13 60 35 39 105 71 72 104 56 54 125 73 60 174 127 97 198 153 114 213 167 133 221 171 138 221 162 128 227 164 129 231 166 134 233 168 138 235 170 142 241 176 148 246 181 153 249 185 157 251 190 161 246 187 155 240 181 147 243 184 150 241 182 150 243 182 151 253 192 163 253 189 161 241 176 146 255 190 158 255 190 158 248 185 152 236 177 145 252 198 164 250 200 163 240 192 154 243 191 154 226 170 133 235 171 135 222 156 121 231 166 128 219 157 116 214 162 115 192 144 106 188 143 124 159 115 102 130 83 63 91 53 34 34 6 2 22 8 8 20 16 17 6 14 27 10 28 66 94 124 176 84 125 177 78 121 174 87 126 181 87 124 179 87 128 184 85 125 184 87 127 188 86 126 188 85 125 187 85 125 187 86 126 188 86 126 188 85 125 187 84 124 186 85 125 187 84 124 186 83 122 187 84 124 186 84 123 188 84 124 186 84 123 188 83 122 187 84 123 188 84 123 188 84 123 190 84 123 188 84 123 190 84 123 190 84 123 190 84 123 190 85 124 191 85 124 191 84 123 190 85 124 191 86 125 194 87 126 195 89 128 197 90 129 198 198 186 188 40 29 43 38 27 67 36 29 81 22 19 74 59 61 109 35 40 72 13 19 45 20 21 49 22 21 55 14 8 54 27 23 74 21 18 73 21 22 76 18 24 76 20 27 79 20 26 78 22 25 80 24 24 84 25 25 79 20 19 61 13 12 43 12 13 34 15 17 38 18 21 56 28 31 76 21 23 74 17 19 70 19 20 66 12 14 53 14 14 52 30 14 24 156 111 70 180 132 84 28 9 0 16 11 8 23 19 10 12 5 0 14 0 0 52 30 33 112 80 83 123 79 78 127 68 60 161 99 78 199 143 106 213 160 116 219 167 127 218 164 126 226 168 130 228 168 131 234 171 138 239 174 144 245 179 153 250 184 160 252 188 163 253 189 164 244 184 158 244 184 156 243 186 156 249 192 162 246 186 158 240 179 151 246 182 157 243 179 154 250 189 160 249 188 159 249 184 156 251 185 159 243 182 154 237 180 151 228 177 148 228 179 147 231 180 149 244 189 158 227 162 130 221 152 119 225 157 120 220 157 116 212 155 110 197 145 105 196 144 120 182 127 106 181 124 94 133 83 56 83 50 41 30 10 11 24 16 14 6 10 21 14 32 70 92 122 174 82 123 175 78 121 176 88 124 182 85 119 180 86 125 182 87 127 186 88 128 189 87 127 189 86 126 188 86 126 188 87 127 189 87 127 189 87 127 189 86 126 188 87 127 189 86 126 188 85 125 187 85 125 187 86 126 188 86 126 188 85 125 187 84 124 186 85 124 189 85 124 189 86 125 190 86 125 190 86 125 190 86 125 190 86 125 192 86 125 192 86 125 192 86 125 192 86 125 192 87 126 193 87 126 193 88 127 194 88 127 196 88 127 196 244 231 223 158 148 147 35 26 45 49 44 74 134 132 169 171 174 207 28 32 59 7 9 32 27 25 49 34 31 62 29 22 66 28 21 75 23 17 79 28 26 89 21 23 80 20 25 80 16 22 72 18 24 74 24 25 82 25 25 79 20 19 61 12 12 46 11 13 38 15 16 44 18 20 59 25 27 75 22 23 77 19 22 73 17 20 63 15 17 55 20 21 52 29 14 19 159 115 70 172 126 74 26 8 0 22 17 21 16 12 9 21 13 10 47 29 29 89 63 64 108 73 69 110 66 55 142 86 69 183 123 97 199 144 105 209 156 112 221 165 128 220 164 129 232 176 139 235 179 142 238 182 149 241 182 152 238 176 151 233 171 148 229 167 144 227 167 143 238 178 152 239 182 155 242 185 156 248 191 162 243 183 157 236 174 151 243 178 156 241 176 154 228 171 142 236 176 148 240 170 145 234 158 135 233 157 134 216 142 117 196 129 103 184 123 94 191 132 102 190 131 99 207 142 112 209 143 111 201 135 103 206 142 107 205 145 109 205 149 114 194 143 114 188 135 104 188 133 94 169 119 86 116 83 68 47 26 21 21 8 2 20 19 27 28 42 77 99 125 174 91 126 180 86 125 184 95 126 190 90 121 185 92 126 190 93 131 194 90 128 191 88 128 190 87 127 189 88 128 190 89 129 191 89 129 191 89 129 191 88 128 190 89 129 191 88 128 190 87 127 189 87 127 189 88 128 190 87 127 189 86 126 188 86 126 188 87 126 191 88 127 192 88 127 192 89 128 193 89 128 193 89 128 193 89 128 195 89 128 195 87 126 193 87 126 193 88 127 194 89 128 195 89 128 195 89 128 195 88 127 194 88 127 194 244 230 219 234 223 217 107 98 101 203 200 211 230 228 250 94 94 118 25 26 47 14 12 33 71 68 87 120 115 138 27 17 54 31 21 71 28 18 78 26 20 80 23 23 77 21 25 73 14 21 65 17 24 70 23 26 81 24 24 78 19 18 62 11 11 45 11 12 40 15 16 46 21 23 64 24 26 77 21 24 79 22 25 76 18 21 62 14 19 51 20 24 51 29 15 15 162 119 68 173 128 73 27 11 0 17 14 21 5 1 2 24 14 13 56 35 34 86 55 52 106 68 55 122 77 58 165 111 87 186 129 99 185 132 92 198 145 103 213 152 121 215 152 121 202 142 106 197 139 102 192 132 98 184 123 94 181 117 90 183 119 94 193 129 104 201 139 114 228 167 139 237 178 148 242 183 153 244 185 155 240 179 151 241 177 150 250 186 161 242 180 155 216 161 131 230 170 142 235 163 139 219 139 116 219 132 112 197 112 91 187 109 86 188 116 91 170 105 77 184 120 92 209 144 116 211 146 118 232 166 140 210 146 119 199 139 111 198 142 115 194 145 115 189 140 107 179 126 86 173 124 91 144 113 95 86 66 57 35 18 11 17 10 18 37 48 80 102 126 174 95 126 183 87 121 184 91 122 187 91 122 187 90 123 190 90 125 191 90 128 191 89 129 191 88 128 190 89 129 191 90 130 192 90 130 192 90 130 192 89 129 191 90 130 192 89 129 191 88 128 190 89 129 191 89 129 191 89 129 191 89 129 191 88 128 190 90 129 194 90 129 194 90 129 194 90 129 194 90 129 194 90 129 194 89 128 195 89 128 195 88 127 194 88 127 194 89 128 195 89 128 195 90 129 196 90 129 196 90 129 196 90 129 196 237 220 213 250 237 231 246 237 232 231 225 227 188 184 198 40 39 57 18 17 35 21 18 35 81 75 85 222 214 225 59 47 71 41 30 64 28 19 64 16 10 58 40 38 78 29 31 69 16 24 61 18 25 67 23 26 77 22 22 74 17 16 58 12 11 45 12 12 40 14 15 46 23 24 68 22 24 75 19 22 77 22 25 76 17 21 59 10 15 44 16 21 43 28 17 11 155 110 55 180 135 78 31 14 4 13 11 22 15 13 18 31 19 19 56 31 27 95 61 51 125 84 64 141 95 69 166 115 86 178 125 93 187 138 98 206 152 114 216 152 125 220 150 124 221 150 118 212 139 104 199 126 93 192 118 89 192 118 91 195 124 96 206 135 107 215 146 117 217 151 117 234 170 135 241 179 142 240 178 141 236 174 137 243 180 145 252 189 156 239 178 147 231 178 146 220 163 134 217 145 120 216 134 112 220 132 112 182 94 72 155 73 49 154 78 54 146 76 50 156 90 64 177 111 87 198 133 111 224 159 139 210 149 130 219 164 143 184 136 114 180 135 112 192 148 121 192 142 109 150 106 77 140 114 99 91 72 65 59 38 35 32 20 30 48 57 86 108 130 177 102 131 189 90 121 186 90 124 188 95 130 196 94 129 197 90 125 191 91 129 192 89 129 191 89 129 191 89 129 191 90 130 192 91 131 193 90 130 192 90 130 192 90 130 192 89 129 191 89 129 191 89 129 191 91 131 193 91 131 193 91 131 193 90 130 192 93 131 196 93 131 196 92 130 195 92 130 195 91 129 194 90 128 193 89 127 192 89 127 192 91 129 194 90 128 193 90 127 195 90 127 195 91 128 196 93 130 198 94 131 199 95 132 200 253 234 230 240 225 218 234 223 217 228 220 218 99 93 103 32 28 43 37 33 50 14 8 22 102 93 96 233 223 224 164 151 160 44 31 48 36 25 55 102 95 129 153 150 179 44 45 75 19 25 59 19 27 66 23 25 74 21 22 70 18 16 56 15 11 44 14 11 38 13 13 41 15 14 56 19 19 69 14 15 69 14 16 64 11 15 52 8 12 39 14 19 39 32 19 11 158 113 56 166 120 61 31 14 4 20 18 29 19 14 20 40 26 25 95 64 59 134 95 80 137 95 70 148 103 72 161 114 86 180 133 103 194 150 115 196 146 113 201 136 114 208 137 115 206 139 110 188 122 90 168 101 72 160 93 66 160 93 67 162 95 69 165 98 71 167 100 71 201 135 103 222 156 121 231 165 130 227 162 124 223 158 120 234 170 132 250 184 149 241 178 143 230 176 140 223 164 132 215 146 117 204 127 101 192 110 86 161 79 55 143 67 43 146 74 50 131 63 40 111 45 23 118 51 32 112 47 29 102 41 23 106 50 33 98 51 33 105 64 46 105 68 50 123 85 66 129 84 61 129 91 70 131 109 98 95 80 75 58 34 34 41 25 36 66 73 101 112 135 177 106 132 191 91 125 189 90 128 191 97 136 201 94 133 198 91 129 194 90 130 192 90 130 192 89 129 191 90 130 192 91 131 193 91 131 193 91 131 193 90 130 192 91 131 193 90 130 192 90 130 192 91 131 193 92 132 194 92 132 194 92 132 194 91 131 193 94 132 197 94 132 197 93 131 196 93 131 196 92 130 195 91 129 194 90 128 193 90 128 193 92 130 195 92 130 195 91 128 196 91 128 196 92 129 197 94 131 199 96 133 201 97 134 202 241 220 215 247 230 223 235 222 216 238 228 227 94 86 97 40 33 49 16 9 25 20 12 23 87 77 75 235 225 215 247 234 226 114 103 101 182 170 182 231 222 241 134 129 151 27 27 53 19 25 59 20 25 65 23 24 72 23 21 68 22 18 53 19 15 42 16 13 32 14 11 32 18 14 47 32 29 72 24 22 69 10 11 55 8 11 44 11 13 38 16 19 38 32 17 10 174 125 69 158 109 51 34 16 6 19 16 25 13 4 7 48 29 23 129 93 81 146 101 80 132 87 58 140 95 64 150 108 83 179 139 114 186 149 122 180 134 108 194 133 114 195 132 114 184 132 110 163 117 94 144 98 75 136 88 68 136 88 68 146 94 73 162 105 85 178 117 96 170 106 79 197 132 104 221 152 121 230 159 129 227 156 124 235 164 132 255 182 153 254 185 152 243 181 144 225 163 124 201 132 99 187 116 86 173 102 74 151 84 57 112 48 23 82 20 0 105 42 24 129 66 49 151 84 68 139 76 61 134 77 60 115 68 50 71 35 13 66 37 19 135 105 97 117 84 79 74 34 26 96 61 55 101 83 81 104 89 92 62 35 40 55 35 47 84 90 114 114 136 175 100 129 185 89 124 188 84 128 189 86 132 192 83 127 188 88 129 191 92 132 193 91 131 192 90 130 191 91 131 192 91 131 192 91 131 192 91 131 192 90 130 191 93 133 194 93 133 194 92 132 193 92 132 193 92 132 193 93 133 194 92 132 193 91 131 192 94 132 195 94 132 195 94 132 195 94 132 195 94 132 195 93 131 194 93 131 196 92 130 195 93 131 196 93 131 196 93 131 196 93 131 196 94 132 197 95 133 198 96 134 199 96 134 199 246 227 213 255 241 231 253 239 236 234 223 227 217 209 222 59 52 68 36 28 43 24 14 22 108 97 91 228 216 200 251 238 221 245 233 219 223 211 211 194 184 195 44 39 61 20 17 48 18 22 59 19 24 66 25 23 72 25 22 67 25 20 52 22 17 37 19 13 23 16 10 22 34 29 52 58 52 86 44 39 79 17 15 54 13 12 43 15 15 41 17 18 39 29 11 7 169 118 65 170 117 63 40 20 9 16 9 16 34 23 19 77 53 41 146 105 85 150 103 77 137 90 60 126 82 55 110 72 53 117 81 65 97 65 44 79 38 18 93 34 18 74 12 0 63 14 0 63 20 3 70 27 11 75 29 14 71 25 9 71 22 5 87 32 12 105 44 23 140 74 48 179 110 81 223 150 118 252 177 145 252 177 145 249 175 140 255 187 155 255 192 157 250 183 140 233 166 123 203 134 95 170 102 67 121 56 26 116 55 27 147 91 68 191 136 116 164 106 92 178 116 103 189 123 111 196 132 120 164 106 92 184 137 117 255 225 199 130 102 81 49 20 16 70 38 39 70 30 30 77 42 40 87 67 68 99 80 82 86 55 60 79 53 64 115 113 134 130 146 180 109 136 189 99 135 197 93 138 197 86 135 191 83 130 186 93 138 197 93 133 194 92 132 193 91 131 192 91 131 192 92 132 193 92 132 193 91 131 192 90 130 191 96 136 197 95 135 196 94 134 195 93 133 194 93 133 194 93 133 194 92 132 193 91 131 192 94 132 195 95 133 196 95 133 196 95 133 196 95 133 196 95 133 196 95 133 198 95 133 198 93 131 196 94 132 197 94 132 197 95 133 198 95 133 198 95 133 198 95 133 198 95 133 198 193 177 152 247 234 217 214 203 201 224 214 223 239 230 247 172 165 183 32 24 37 66 55 61 208 195 189 241 228 212 239 224 203 235 222 203 223 212 206 101 91 102 41 32 63 21 15 59 21 22 68 21 23 71 24 20 71 32 25 69 19 10 37 80 72 85 57 49 47 55 47 45 147 139 150 166 158 181 32 25 58 23 17 51 10 7 36 17 15 39 15 13 37 34 14 13 166 112 65 166 110 59 52 25 14 28 16 16 49 31 19 124 96 74 169 123 97 147 96 67 122 75 47 98 56 32 57 21 9 76 43 34 83 54 40 79 42 26 72 16 1 163 101 86 233 174 160 215 157 145 184 126 114 166 108 96 168 110 96 193 134 116 199 134 114 146 79 53 137 63 34 121 45 11 175 97 58 208 131 87 251 174 130 251 177 132 241 168 123 236 164 116 222 148 99 199 125 76 164 91 48 174 105 66 227 160 131 182 122 96 156 104 83 89 38 21 78 24 12 85 27 16 109 42 34 125 58 49 166 104 89 162 107 86 190 146 117 229 193 167 87 53 44 73 38 36 121 77 74 99 59 57 89 61 58 112 84 81 138 96 97 120 84 88 141 127 140 119 125 151 116 136 187 102 134 193 93 136 191 94 141 195 89 134 189 89 134 191 93 133 194 94 132 194 94 132 194 94 132 194 93 131 193 93 131 193 93 131 193 93 131 193 95 133 195 95 133 195 96 134 196 97 135 197 97 135 197 96 134 196 96 134 196 96 134 196 95 133 196 95 133 196 95 133 196 96 134 197 96 134 197 96 134 197 95 133 196 95 133 196 96 134 197 95 133 196 95 133 198 95 133 198 95 133 198 95 133 198 96 134 199 96 134 199 159 147 121 225 213 197 78 67 73 64 52 72 100 92 116 155 147 170 96 88 103 63 54 59 214 200 197 233 219 206 230 218 196 230 217 198 199 189 180 48 38 47 33 23 58 23 17 65 20 18 68 23 23 75 24 20 71 33 24 67 26 16 40 80 69 75 118 108 99 139 129 119 190 179 183 86 75 89 20 12 36 12 6 34 20 14 42 17 13 38 15 11 36 33 13 15 169 112 69 164 106 56 55 23 10 67 48 44 97 70 51 147 112 84 162 111 80 136 81 51 107 57 30 90 47 28 45 8 0 52 19 14 38 8 0 69 32 16 165 112 96 174 112 99 180 120 110 141 82 74 102 43 35 90 32 21 88 30 16 84 25 7 138 73 53 205 135 110 194 119 88 190 112 76 201 119 79 205 124 79 228 150 104 230 153 107 227 153 106 206 132 83 203 126 74 184 104 53 188 107 62 187 109 71 176 102 73 158 92 70 89 32 15 118 65 51 72 15 6 95 36 28 94 27 18 93 23 11 114 45 29 136 72 47 122 65 35 111 60 31 113 67 51 103 59 48 95 47 35 74 28 15 70 32 19 111 71 59 161 107 97 186 135 131 194 164 166 162 153 172 113 126 171 112 138 197 103 138 194 95 136 190 96 137 193 102 142 201 96 135 194 96 134 196 96 134 196 95 133 195 95 133 195 95 133 195 95 133 195 95 133 195 97 135 197 97 135 197 98 136 198 98 136 198 98 136 198 97 135 197 97 135 197 96 134 196 96 134 197 96 134 197 96 134 197 97 135 198 96 134 197 96 134 197 96 134 197 95 133 196 94 132 195 95 133 196 95 133 198 96 134 199 96 134 199 97 135 200 98 136 201 98 136 201 149 137 123 195 184 182 42 30 52 42 32 67 22 14 53 35 28 61 32 25 43 32 25 32 132 121 119 228 215 206 244 231 215 241 229 213 224 215 206 118 111 118 37 30 63 27 21 67 24 21 74 30 27 82 26 19 73 27 19 60 26 14 36 57 45 47 119 107 93 123 111 95 118 107 105 42 30 40 22 13 34 18 9 36 15 9 35 18 12 38 21 17 44 32 11 16 172 114 74 182 121 74 58 20 7 74 45 39 144 108 86 167 122 91 151 94 64 118 59 29 88 34 10 84 37 21 88 47 43 79 41 38 54 20 8 133 91 75 155 98 81 163 104 90 127 74 66 84 37 31 74 27 21 69 22 14 72 25 15 71 22 8 98 41 24 123 58 38 173 101 76 173 94 64 174 91 59 210 126 90 213 129 93 195 113 75 200 121 82 196 117 74 195 113 63 170 87 35 209 120 78 202 112 78 174 86 62 117 36 17 131 59 45 94 30 18 50 0 0 82 20 9 88 20 7 96 23 8 104 26 4 132 52 25 136 60 28 144 74 40 166 107 75 142 87 57 124 67 38 124 67 40 131 77 49 170 113 86 193 125 102 156 91 73 169 119 112 187 159 171 127 128 172 119 138 196 101 127 184 100 131 188 107 142 200 100 136 196 98 137 196 98 137 196 98 137 196 98 137 196 98 137 196 98 137 196 97 136 195 97 136 195 99 138 197 99 138 197 100 139 198 100 139 198 99 138 197 99 138 197 98 137 196 97 136 195 98 136 198 98 136 198 98 136 198 98 136 198 97 135 197 97 135 197 97 135 198 96 134 197 95 133 196 96 134 197 97 135 198 98 136 199 98 136 199 98 136 199 98 136 199 98 136 199 126 115 119 118 107 121 29 19 56 34 24 74 32 24 73 26 19 60 15 10 32 18 12 22 48 39 40 208 197 193 240 227 219 214 203 197 236 228 226 205 199 209 46 42 69 12 10 50 17 17 67 27 27 81 27 22 76 26 17 60 34 22 42 71 59 61 129 117 103 115 103 87 46 35 31 17 6 14 26 17 36 27 18 45 11 5 31 16 12 37 24 20 47 32 11 18 156 101 62 190 127 83 65 23 9 84 47 41 189 143 120 178 123 92 164 99 69 156 91 63 115 57 35 121 70 53 93 46 38 98 54 45 80 39 21 126 78 56 117 54 36 148 82 66 91 33 22 81 28 20 93 43 32 62 12 1 54 2 0 84 31 15 133 72 54 154 86 65 124 48 22 146 62 34 157 70 40 207 118 84 193 107 72 229 147 110 254 174 137 250 173 131 227 147 98 213 127 78 223 130 89 219 122 90 175 79 55 191 100 81 170 89 72 150 78 63 137 70 54 129 62 45 127 54 37 136 58 36 151 65 40 159 69 42 173 84 52 188 106 69 205 136 95 187 124 83 197 133 95 177 113 77 162 97 59 162 92 56 178 102 68 127 51 25 110 44 28 174 131 138 137 132 172 117 133 192 104 124 183 104 130 189 111 141 203 94 128 189 102 138 198 100 139 198 100 139 198 100 139 198 100 139 198 100 139 198 100 139 198 100 139 198 101 140 199 101 140 199 101 140 199 101 140 199 101 140 199 100 139 198 99 138 197 98 137 196 98 136 198 98 136 198 99 137 199 99 137 199 99 137 199 99 137 199 98 136 199 98 136 199 98 136 199 99 137 200 100 138 201 100 138 201 99 137 200 98 136 199 97 135 198 96 134 197 82 72 97 66 56 90 42 32 82 20 12 69 21 16 72 31 28 73 18 15 42 17 13 27 32 26 30 207 197 198 154 143 149 50 39 47 108 100 113 180 176 193 115 113 137 20 20 54 20 22 70 26 27 81 28 25 78 22 15 57 23 11 35 72 59 66 126 113 104 135 122 113 110 98 100 14 4 15 17 9 32 19 13 39 20 16 41 15 13 37 18 15 42 32 13 17 154 102 63 185 122 78 68 22 7 114 69 64 182 125 106 137 71 45 138 66 41 158 88 63 155 93 70 153 96 76 147 91 74 167 115 94 148 94 66 128 69 39 133 61 37 131 53 33 127 48 31 121 44 28 128 52 36 129 56 37 153 82 62 153 82 60 156 82 57 188 109 79 202 115 85 217 127 93 220 128 91 223 133 96 164 79 40 237 158 117 255 192 151 255 193 150 246 169 123 187 105 58 246 154 117 240 143 111 228 128 102 206 110 86 189 103 80 205 127 104 191 116 93 184 110 85 178 98 73 175 89 62 185 94 65 204 111 80 210 117 83 197 112 73 201 124 80 202 134 89 218 151 108 186 117 76 177 102 62 174 95 54 199 117 79 205 125 92 118 41 21 158 106 110 129 123 161 112 128 187 117 137 196 107 131 193 104 135 199 102 138 200 101 140 199 101 140 199 101 140 199 101 140 199 101 140 199 101 140 199 101 140 199 101 140 199 102 141 200 102 141 200 102 141 200 102 141 200 101 140 199 100 139 198 99 138 197 98 137 196 100 136 198 100 136 198 101 137 199 102 138 200 102 138 200 102 138 200 102 138 200 102 138 200 101 137 199 102 138 200 103 138 202 103 138 202 102 137 201 101 136 200 100 135 199 99 134 198 41 33 72 25 18 60 23 16 68 30 25 81 30 27 82 29 28 72 11 11 39 13 10 27 44 38 48 209 199 210 86 74 94 32 22 49 39 29 63 32 28 61 52 52 78 21 26 56 20 24 69 17 20 71 24 24 76 29 23 67 29 19 44 68 56 66 98 87 83 126 115 111 157 146 154 88 77 94 18 9 36 17 12 42 19 16 43 18 18 42 15 15 39 27 12 15 159 110 69 177 116 71 61 12 0 129 78 75 157 91 75 136 61 40 165 87 67 170 93 73 163 97 73 152 90 65 193 132 104 201 138 107 163 101 64 162 94 57 136 55 28 136 48 26 129 37 16 138 46 23 167 79 55 208 124 98 231 150 123 220 141 111 205 122 92 216 130 97 215 122 88 244 150 114 255 172 134 238 146 107 161 78 38 230 152 113 252 183 141 255 194 152 255 186 143 228 149 108 236 148 112 251 158 125 223 128 98 207 116 85 238 153 122 228 148 115 241 162 131 253 173 140 247 160 130 231 140 109 209 114 82 235 141 107 236 146 111 218 135 95 217 140 98 212 141 99 210 142 103 184 115 76 180 98 60 214 129 90 222 139 99 203 120 86 144 62 40 161 105 106 131 127 164 106 128 185 117 139 197 105 134 194 96 134 196 105 147 207 101 141 200 102 141 200 102 141 200 102 141 200 102 141 200 102 141 200 102 141 200 102 141 200 103 142 201 103 142 201 103 142 201 102 141 200 102 141 200 101 140 199 100 139 198 99 138 197 101 137 199 101 137 199 102 138 200 102 138 200 103 139 201 103 139 201 103 139 201 103 139 201 100 136 198 101 137 199 102 137 201 103 138 202 103 138 202 103 138 202 103 138 202 103 138 202 21 14 55 25 20 61 25 22 67 27 28 74 20 23 68 17 21 59 10 11 39 14 13 31 47 43 57 153 144 165 24 12 52 35 22 75 38 27 87 18 13 67 20 20 58 9 15 47 22 29 71 19 25 73 21 23 74 24 21 66 24 15 44 29 18 34 26 16 17 39 29 30 93 82 96 113 103 127 25 18 51 26 20 54 10 10 36 17 19 42 14 19 39 25 14 12 162 117 75 181 123 75 58 9 0 104 48 47 134 64 52 127 48 31 182 99 83 169 88 69 156 86 60 159 93 61 193 123 87 218 147 105 186 114 66 191 112 69 164 75 45 157 61 37 160 62 37 181 86 58 205 116 86 242 157 128 242 159 129 244 161 131 230 145 114 214 125 93 203 110 76 253 156 123 255 181 146 217 124 90 186 102 66 246 170 134 255 190 155 246 180 145 255 187 151 236 162 125 196 116 81 249 167 130 254 170 133 222 140 102 201 122 81 233 154 111 248 166 126 243 155 117 236 142 106 227 130 95 208 114 78 224 136 100 230 151 112 216 142 103 222 148 113 215 144 112 211 148 117 198 131 102 185 104 74 224 138 105 249 169 132 214 134 101 147 65 44 172 116 119 132 134 172 103 134 189 103 134 191 104 139 197 94 141 197 96 145 203 100 142 200 102 141 200 103 142 201 103 142 201 103 142 201 103 142 201 103 142 201 104 143 202 103 142 201 103 142 201 103 142 201 103 142 201 103 142 201 102 141 200 101 140 199 100 139 198 102 138 198 102 138 198 103 139 201 103 139 201 103 139 201 103 139 201 102 138 200 102 138 200 101 137 199 101 137 199 102 137 201 102 137 201 103 138 202 103 138 202 104 139 203 104 139 203 24 22 61 18 16 55 22 21 65 16 17 61 25 28 71 24 26 65 13 14 42 12 13 34 33 30 51 59 53 81 30 18 66 36 25 85 28 19 84 27 21 81 20 21 65 17 21 59 15 19 64 25 29 77 24 26 74 19 18 60 22 19 48 16 13 30 15 9 19 19 13 23 24 16 37 32 26 54 25 21 56 25 24 58 12 13 43 9 13 38 12 17 37 27 16 14 146 102 57 179 121 71 63 16 0 87 36 33 153 83 73 124 41 25 201 114 95 210 125 104 184 108 82 147 74 41 190 112 74 210 129 86 201 121 72 197 114 70 203 114 82 188 94 66 174 77 45 205 111 77 198 109 77 219 134 103 207 126 96 190 111 80 179 98 68 200 117 85 255 178 144 255 168 133 240 150 115 187 98 64 220 137 105 253 177 145 255 190 159 255 190 158 246 180 146 255 189 155 199 125 90 228 152 116 239 161 123 255 189 149 246 171 129 213 136 92 212 133 90 195 112 70 208 119 79 195 106 66 199 111 73 202 120 80 229 156 113 223 151 111 207 131 99 207 133 104 199 136 105 196 129 100 202 118 92 185 95 69 222 142 109 221 144 114 150 75 56 174 125 129 124 130 166 108 141 194 102 133 188 107 143 201 97 144 200 94 143 201 102 142 201 103 142 201 103 142 201 103 142 201 104 143 202 104 143 202 104 143 202 104 143 202 104 143 202 104 143 202 104 143 202 104 143 202 104 143 202 103 142 201 102 141 200 101 140 199 104 140 200 104 140 200 104 140 202 103 139 201 103 139 201 102 138 200 101 137 199 101 137 199 103 139 201 103 139 201 102 137 201 102 137 201 102 137 201 103 138 202 103 138 202 103 138 202 18 19 50 20 20 56 20 18 65 24 22 72 29 27 74 23 22 62 14 13 44 14 11 38 23 20 49 23 17 53 25 19 65 30 23 74 27 23 74 21 19 68 18 19 65 23 21 68 26 19 71 32 25 77 22 23 67 18 24 58 12 19 45 6 14 33 12 15 32 9 10 30 14 11 38 16 15 47 14 17 52 18 22 59 7 12 44 13 17 46 14 16 41 29 13 13 153 102 55 182 122 70 52 11 0 62 19 13 170 104 92 134 53 32 223 132 105 213 122 91 176 95 66 155 76 46 203 114 82 208 118 83 216 128 90 201 118 78 209 129 94 186 103 69 166 76 39 199 109 72 196 112 76 193 114 83 195 121 92 198 127 99 230 159 131 255 189 157 255 188 153 251 173 135 228 149 110 183 103 66 241 165 131 251 176 147 255 195 170 255 190 163 255 193 160 255 195 160 230 161 128 204 131 99 250 174 142 255 182 150 255 182 148 255 197 160 255 183 145 243 171 131 225 152 111 210 137 96 215 140 98 232 157 115 236 161 119 227 150 108 218 136 99 210 132 94 200 133 90 197 128 87 179 89 65 143 48 28 196 110 83 216 142 117 168 109 95 150 117 124 126 134 170 113 141 191 106 135 191 104 136 195 104 142 204 104 146 206 105 144 203 106 142 200 105 141 199 105 141 199 106 142 200 107 143 201 106 142 200 106 142 200 106 142 200 105 141 199 106 142 200 108 144 202 107 143 201 105 141 199 103 139 197 104 140 198 106 142 202 106 142 202 105 141 203 104 140 202 103 139 201 102 138 200 101 137 199 101 137 199 102 138 200 102 138 200 103 138 202 103 138 202 103 138 202 103 138 202 103 138 202 103 138 202 18 22 51 19 22 57 21 19 68 24 22 72 29 26 73 22 20 59 14 11 40 12 9 36 21 17 50 20 15 55 21 18 63 26 23 70 24 23 67 19 18 62 16 17 63 21 17 67 29 18 74 33 24 77 22 24 65 16 25 56 9 21 45 4 17 36 12 17 39 12 12 38 11 7 40 20 18 55 11 13 51 10 14 51 19 24 56 13 17 46 17 17 41 35 15 14 162 110 63 190 130 76 38 2 0 76 39 30 163 104 88 159 81 58 230 141 107 213 123 88 159 76 46 156 75 46 197 108 78 202 109 76 218 128 93 216 132 96 208 134 97 178 104 65 186 103 61 209 124 83 228 148 111 244 169 137 252 183 154 255 193 167 255 200 172 255 190 161 251 183 148 255 196 157 206 132 93 218 144 105 247 174 141 248 177 147 255 188 163 255 198 172 255 196 165 250 187 154 255 195 163 213 142 112 230 153 125 252 175 147 255 186 156 255 182 151 253 183 149 254 186 149 241 176 136 231 164 122 234 165 123 237 164 121 227 150 108 217 138 97 222 140 102 212 135 93 176 110 60 215 144 100 202 110 87 151 54 35 175 88 61 212 142 117 182 134 124 157 135 147 129 142 177 114 142 192 107 133 190 104 134 194 104 140 202 101 139 201 107 143 203 106 142 200 105 141 199 105 141 199 106 142 200 107 143 201 106 142 200 106 142 200 108 144 202 106 142 200 106 142 200 107 143 201 107 143 201 105 141 199 105 141 199 106 142 200 106 142 202 106 142 202 105 141 203 104 140 202 103 139 201 103 139 201 102 138 200 102 138 200 103 139 201 103 139 201 103 138 202 103 138 202 103 138 202 104 139 203 104 139 203 104 139 203 17 22 52 18 22 57 19 20 68 21 22 70 27 26 68 20 20 54 12 12 36 10 10 34 20 19 50 20 18 57 22 19 64 27 24 71 25 24 68 21 20 62 17 18 62 21 19 68 28 19 74 31 24 76 20 23 64 14 23 54 10 20 45 8 16 37 16 16 40 16 11 41 20 13 47 19 13 51 13 11 50 10 13 48 21 24 55 7 9 34 19 17 38 37 18 14 153 101 53 185 128 73 87 49 26 183 147 135 233 177 160 184 114 88 219 135 99 218 133 96 154 75 44 178 102 70 207 123 89 192 103 69 202 114 78 214 130 93 207 133 94 190 119 77 161 83 37 185 104 61 243 165 127 255 199 166 255 193 164 255 197 169 255 205 177 255 198 169 255 198 163 249 180 141 180 111 70 238 169 130 250 182 147 255 189 157 254 188 162 255 198 171 255 196 165 253 188 156 255 189 158 239 165 136 187 107 80 236 155 128 255 174 145 255 176 146 255 187 155 255 184 149 235 166 127 223 151 111 228 155 114 226 151 109 217 138 97 212 133 92 213 133 96 191 116 76 189 125 77 215 146 104 242 152 128 182 87 67 189 102 74 192 124 101 177 136 134 147 134 152 120 138 178 108 139 193 106 137 194 109 141 202 110 145 209 105 143 206 107 143 203 106 142 200 106 142 200 106 142 200 106 142 200 107 143 201 107 143 201 106 142 200 109 145 203 107 143 201 106 142 200 107 143 201 107 143 201 106 142 200 106 142 200 108 144 202 106 142 202 106 142 202 105 141 201 105 141 201 104 140 202 104 140 202 104 140 202 104 140 202 103 139 201 103 139 201 104 140 202 104 140 202 104 139 203 104 139 203 104 139 203 104 139 203 15 20 52 17 21 58 18 19 67 21 22 68 26 26 64 20 21 49 13 14 34 12 13 33 19 18 49 20 18 57 23 20 67 26 24 73 26 25 69 23 22 64 20 22 63 23 21 68 26 19 71 28 24 75 18 21 62 12 21 52 8 18 43 7 13 35 18 14 41 17 8 39 26 14 50 17 6 46 21 17 54 15 15 49 16 17 45 15 16 37 18 15 32 27 8 1 125 75 24 215 159 102 180 140 115 209 173 159 233 185 165 216 152 124 239 161 123 198 116 78 185 111 76 220 147 114 223 145 109 185 103 66 182 97 60 201 117 80 199 126 83 204 133 87 206 125 80 190 107 65 226 146 109 255 185 153 255 192 165 255 194 169 255 183 158 247 173 146 236 161 129 178 104 69 178 102 66 231 157 120 243 170 137 255 188 157 255 188 162 249 183 157 255 194 164 255 197 165 255 197 167 251 176 147 180 99 72 162 78 52 213 127 102 231 147 119 232 151 122 227 148 117 226 150 116 236 160 124 246 171 132 245 167 128 227 148 109 207 128 89 197 119 83 190 118 80 196 132 88 219 150 109 248 162 135 226 134 111 188 103 72 190 124 102 166 135 140 138 135 162 116 139 183 107 142 198 107 139 198 107 141 202 104 142 205 99 139 201 108 144 204 107 143 201 106 142 200 106 142 200 107 143 201 107 143 201 107 143 201 107 143 201 108 144 202 106 142 200 106 142 200 107 143 201 108 144 202 106 142 200 107 143 201 108 144 202 106 142 202 106 142 202 106 142 202 105 141 201 105 141 203 105 141 203 106 142 204 106 142 204 104 140 202 104 140 202 104 140 202 104 140 202 105 140 204 105 140 204 105 140 204 105 140 204 15 19 56 17 20 61 17 19 67 19 22 65 23 26 59 17 22 44 11 14 29 11 14 29 15 16 44 17 17 55 21 19 66 24 22 71 25 24 68 21 23 64 21 23 64 24 23 67 25 21 71 26 24 73 16 21 61 11 20 51 10 17 43 8 12 37 18 11 42 17 6 40 22 7 46 20 8 48 25 19 57 16 15 47 15 15 39 26 25 43 16 10 22 50 31 17 171 124 70 211 157 97 189 148 120 209 172 156 230 188 164 209 154 123 226 154 114 214 137 95 213 144 105 238 170 133 224 155 114 185 110 70 183 98 61 197 112 73 189 116 71 203 129 82 229 142 99 201 109 70 204 114 80 227 138 108 249 163 138 252 166 143 219 133 110 198 110 86 169 82 54 165 76 46 226 140 107 243 158 127 251 168 138 255 184 157 255 193 170 255 184 161 255 190 161 255 186 157 253 179 152 255 185 159 244 160 136 192 104 80 178 88 64 183 93 67 165 78 51 166 81 52 195 112 80 227 147 112 236 156 119 238 159 120 227 145 107 197 117 80 195 121 86 195 125 89 191 128 87 214 146 107 244 160 132 210 123 96 183 99 65 200 138 117 153 133 145 126 136 172 112 142 192 109 145 203 107 141 202 108 142 203 106 144 207 102 142 203 108 144 204 107 143 201 106 142 200 106 142 200 107 143 201 108 144 202 107 143 201 107 143 201 107 143 201 106 142 200 106 142 200 109 145 203 109 145 203 107 143 201 106 142 200 107 143 201 107 141 202 107 141 202 107 141 202 107 141 202 107 141 202 107 141 202 108 142 205 108 142 205 106 140 203 106 140 203 106 140 203 106 140 203 107 141 204 107 141 204 107 141 205 107 141 205 17 21 59 19 22 63 18 20 68 19 22 65 21 24 55 15 20 39 9 13 24 10 14 26 15 16 44 17 16 56 21 19 68 24 22 72 23 24 70 23 25 66 22 24 65 24 23 65 24 20 70 26 24 71 17 21 59 14 20 52 11 18 44 12 14 39 19 14 44 17 7 41 20 8 46 23 13 50 20 14 50 20 17 46 18 15 36 12 8 22 32 21 29 142 119 103 214 165 107 217 163 101 185 141 112 219 180 163 231 194 167 209 163 129 213 148 108 227 154 113 212 144 105 230 165 125 225 160 118 197 126 84 191 106 69 195 107 69 186 112 65 199 125 78 232 145 102 226 132 94 199 106 73 164 73 44 161 69 44 173 81 58 173 81 60 174 79 57 142 48 22 215 121 93 254 163 134 243 154 124 247 162 133 254 173 146 255 187 164 254 180 155 254 185 156 255 186 157 249 175 148 251 171 146 240 154 131 232 141 120 183 91 68 229 137 114 242 150 125 235 145 119 245 158 130 246 161 130 227 143 109 220 138 101 219 137 100 201 123 85 202 129 94 189 121 86 202 140 101 220 155 117 229 150 119 188 103 74 219 135 101 194 135 117 142 133 152 115 136 181 106 141 195 104 144 203 104 140 200 108 142 203 109 147 209 108 146 208 109 145 205 108 144 202 107 143 201 107 143 201 108 144 202 108 144 202 108 144 202 107 143 201 107 143 201 106 142 200 107 143 201 109 145 203 109 145 203 107 143 201 106 142 200 106 142 200 107 141 202 107 141 202 107 141 202 107 141 202 107 141 202 108 142 203 108 142 205 108 142 205 107 141 204 107 141 204 107 141 204 107 141 204 107 141 204 107 141 204 108 142 206 108 142 206 18 20 61 20 21 65 19 20 68 19 20 64 20 24 53 14 20 36 7 14 22 8 14 26 14 18 45 17 19 58 21 21 71 23 23 75 23 24 72 23 24 68 22 24 65 23 22 66 22 19 66 24 22 69 15 19 57 14 20 52 12 19 45 12 16 41 19 16 45 14 9 41 20 13 47 20 14 48 15 12 43 21 19 43 17 13 30 23 13 22 97 81 84 209 181 160 225 175 116 222 167 103 181 132 100 223 182 162 224 189 161 187 144 109 201 139 98 231 162 120 215 147 108 220 157 114 222 161 116 200 131 89 192 107 70 193 103 66 193 116 70 202 128 79 206 125 80 230 145 104 227 142 105 213 127 94 216 129 101 230 140 114 222 132 108 194 102 77 164 73 46 236 147 117 223 136 106 212 129 97 228 149 118 232 157 128 244 173 145 240 170 144 231 162 133 224 153 125 232 156 130 209 129 104 213 127 104 213 122 101 164 69 49 192 97 75 245 151 126 254 160 135 253 162 135 241 152 122 223 139 105 214 132 95 214 132 95 213 135 99 202 129 94 189 123 88 201 137 101 220 155 117 205 129 95 219 139 106 230 150 115 189 136 120 141 141 167 113 143 193 106 145 202 104 144 205 102 138 196 104 139 197 105 141 199 102 138 198 109 145 203 108 144 202 107 143 201 107 143 201 108 144 202 108 144 202 108 144 202 108 144 202 109 145 203 107 143 201 108 144 202 109 145 203 109 145 203 107 143 201 106 142 200 107 143 201 108 142 203 107 141 202 107 141 202 107 141 202 107 141 202 108 142 203 108 142 203 108 142 203 107 141 204 107 141 204 107 141 204 108 142 205 108 142 205 108 142 205 108 142 205 108 142 205 18 17 57 20 19 61 20 18 65 19 21 62 22 23 53 14 20 36 8 15 23 10 16 28 12 16 43 16 18 57 19 19 71 20 20 74 20 21 69 20 21 67 19 20 64 20 19 63 20 14 62 22 19 64 14 16 54 13 20 49 12 19 45 11 17 41 16 18 43 11 11 39 13 10 41 16 15 46 17 17 43 15 13 34 19 13 25 69 57 61 175 154 153 203 167 145 216 164 104 225 167 103 185 132 100 226 181 160 227 192 162 167 129 92 193 135 95 215 150 108 235 170 130 219 156 113 211 153 107 190 123 80 191 105 70 199 109 74 204 126 80 201 128 77 195 118 72 214 135 92 221 139 101 232 148 114 244 157 129 248 161 134 223 133 107 161 71 45 144 54 27 168 81 53 143 60 30 148 69 38 176 101 70 180 109 79 196 127 98 201 131 105 202 133 104 197 126 98 168 92 66 159 79 54 117 33 9 145 57 35 120 25 5 156 60 38 215 119 95 239 145 120 237 146 119 226 137 107 230 143 113 222 139 105 213 133 96 221 145 109 205 135 99 190 124 89 197 133 97 169 101 64 219 145 108 237 161 125 202 127 95 197 151 138 129 135 167 103 138 194 102 142 203 105 145 206 109 144 200 113 146 200 112 147 203 108 143 199 109 145 203 108 144 202 107 143 201 107 143 201 108 144 202 109 145 203 108 144 202 108 144 202 111 147 205 109 145 203 108 144 202 109 145 203 108 144 202 107 143 201 107 143 201 109 145 203 108 142 203 107 141 202 107 141 202 107 141 202 107 141 202 108 142 203 108 142 203 108 142 203 108 142 205 108 142 205 108 142 205 108 142 205 108 142 205 108 142 205 109 143 206 109 143 206 13 11 48 27 25 64 21 18 63 16 15 55 37 36 67 14 17 36 8 12 23 14 18 30 22 23 51 7 6 46 21 21 73 27 27 81 21 21 71 19 20 66 13 14 58 22 21 65 25 17 64 31 24 68 19 22 57 9 16 44 9 17 40 13 21 42 14 20 42 4 9 31 10 14 39 9 13 38 15 20 40 18 20 35 77 70 78 161 145 146 195 165 163 210 168 144 220 164 103 221 162 96 190 136 102 228 182 159 215 180 150 168 130 94 191 138 98 220 157 116 200 135 95 197 134 91 144 86 40 177 112 70 198 114 80 199 110 76 207 126 81 214 141 90 201 127 80 207 130 86 220 141 102 223 141 104 231 146 115 215 128 100 167 77 50 144 54 27 220 133 105 188 103 74 122 41 11 119 40 10 122 47 16 130 59 29 142 73 44 142 73 44 137 66 38 133 59 32 151 75 49 133 53 28 150 66 42 212 126 103 166 74 51 114 19 0 171 77 52 208 114 89 217 126 99 209 120 90 222 137 106 212 129 95 215 137 99 222 148 109 208 139 100 207 139 102 191 125 90 179 111 74 183 111 71 189 117 79 215 148 119 181 142 135 127 140 175 104 143 202 103 143 204 105 144 203 108 143 197 110 141 195 110 143 197 112 145 199 108 143 199 109 144 202 110 145 203 109 144 202 109 144 202 108 143 201 109 144 202 110 145 203 112 147 205 111 146 204 109 144 202 108 143 201 107 142 200 106 141 199 106 141 199 105 140 198 107 141 202 106 140 201 106 140 201 106 140 201 108 142 203 108 142 203 108 142 203 107 141 202 108 142 203 108 142 203 108 142 205 108 142 205 109 143 206 110 144 207 111 145 208 111 145 208 29 24 54 22 17 49 19 12 53 36 31 71 21 18 47 19 20 40 15 17 29 0 3 18 17 18 46 22 21 61 16 14 63 16 16 68 21 22 70 16 17 63 15 16 62 16 15 59 22 14 61 23 16 58 14 14 48 10 16 42 10 18 37 12 19 37 15 20 39 15 20 39 7 12 32 15 20 40 16 22 38 94 97 106 169 160 163 178 158 157 200 167 160 212 166 140 221 163 100 223 162 97 193 137 100 228 180 157 215 178 149 181 143 107 193 141 104 218 156 117 212 149 108 202 139 96 212 153 109 226 161 121 211 130 100 195 109 76 202 124 78 213 137 87 218 144 97 202 125 81 225 146 105 221 137 100 215 129 96 199 110 80 143 52 23 191 100 73 237 147 120 229 144 115 221 137 109 219 140 110 212 135 107 176 101 72 132 56 30 95 21 0 118 42 16 170 93 67 200 123 97 209 129 104 207 125 101 220 134 111 194 106 82 125 33 10 119 27 2 176 84 59 201 110 83 208 119 89 210 125 94 207 127 92 219 141 103 218 144 105 204 135 96 216 148 111 242 173 140 223 155 120 229 162 120 209 145 107 219 163 138 170 140 142 124 139 178 103 142 201 103 141 203 107 143 203 110 143 197 110 142 193 111 142 196 112 143 198 110 142 199 109 144 202 110 145 203 109 144 202 109 144 202 108 143 201 109 144 202 110 145 203 111 146 204 110 145 203 109 144 202 107 142 200 106 141 199 106 141 199 106 141 199 106 141 199 106 140 201 105 139 200 105 139 200 105 139 200 106 140 201 107 141 202 107 141 202 106 140 201 107 141 202 107 141 202 108 142 205 108 142 205 109 143 206 109 143 206 110 144 207 110 144 207 26 17 34 29 21 44 40 30 65 60 52 89 29 22 53 8 4 27 19 18 32 18 20 35 10 10 36 20 20 56 26 25 69 29 27 74 27 26 70 19 18 62 16 17 63 17 16 60 22 14 61 20 13 55 14 13 45 11 15 40 15 20 39 16 19 34 13 15 28 17 16 32 9 11 26 28 30 45 138 142 153 176 177 182 162 152 151 192 171 166 205 168 160 211 164 136 221 161 99 221 160 93 195 140 101 228 178 153 215 176 147 193 154 121 187 134 100 206 146 110 230 166 128 222 159 118 235 173 132 241 175 140 235 158 130 199 118 88 199 122 78 217 141 92 224 147 101 209 130 87 214 131 91 221 133 97 197 104 71 157 62 32 172 74 47 227 131 106 233 139 113 225 135 109 232 144 120 221 135 110 230 146 120 223 139 113 207 123 97 187 106 79 194 113 86 216 135 108 207 126 99 215 134 107 209 128 101 201 117 91 220 134 109 185 97 73 116 24 0 134 42 17 169 78 51 178 91 61 193 110 78 205 127 91 216 141 102 218 146 106 208 141 99 212 144 107 233 162 132 238 169 138 209 146 105 223 169 133 203 161 145 151 134 144 121 138 181 106 141 199 105 141 203 108 144 204 111 144 198 111 143 194 112 142 196 113 144 199 110 142 199 109 144 202 109 144 202 109 144 202 108 143 201 108 143 201 109 144 202 109 144 202 109 144 202 108 143 201 107 142 200 106 141 199 106 141 199 106 141 199 106 141 199 106 141 199 106 141 199 105 140 198 105 139 200 105 139 200 106 140 201 107 141 202 106 140 201 106 140 201 106 140 201 106 140 201 107 141 202 107 141 202 108 142 205 108 142 205 109 143 206 110 144 207 74 63 67 45 33 45 133 120 146 83 72 104 30 21 50 24 19 42 11 8 25 11 10 26 31 29 53 11 10 41 20 18 55 22 21 61 19 18 58 22 21 61 15 16 60 13 12 56 20 14 58 22 17 57 18 17 48 13 13 37 20 19 35 21 17 31 16 8 21 22 11 25 38 30 43 154 148 160 175 174 182 172 170 173 184 173 171 181 161 154 204 167 158 207 160 130 221 161 98 217 156 89 196 141 100 227 180 152 215 174 146 197 156 126 166 115 84 178 122 87 231 169 132 244 180 142 235 171 133 248 184 149 228 158 133 195 121 94 206 131 89 217 143 94 222 148 103 212 135 91 206 124 84 203 118 79 163 73 38 133 40 7 216 121 91 228 134 106 231 139 114 231 144 117 236 150 125 227 143 117 231 147 121 236 152 126 238 152 127 228 140 116 240 152 128 223 137 112 216 132 106 217 133 107 223 142 115 215 134 107 225 141 115 214 128 101 151 61 35 112 21 0 152 62 35 170 83 53 203 120 86 210 130 95 202 127 88 207 135 95 201 134 92 216 151 113 244 172 147 226 156 130 218 160 120 205 162 128 165 140 135 137 132 152 120 139 182 108 140 197 107 141 204 110 144 207 112 145 199 112 143 197 112 143 197 113 144 199 110 142 199 109 144 202 109 144 202 109 144 202 108 143 201 108 143 201 108 143 201 109 144 202 108 143 201 107 142 200 107 142 200 106 141 199 106 141 199 106 141 199 106 141 199 106 141 199 107 142 200 106 141 199 106 140 201 106 140 201 107 141 202 107 141 202 107 141 202 106 140 201 106 140 201 106 140 201 106 140 201 106 140 201 107 141 204 108 142 205 108 142 205 109 143 206 177 163 152 165 151 150 145 131 148 37 23 49 25 12 40 20 10 35 19 12 30 13 9 24 15 13 34 22 22 46 20 19 50 21 20 52 19 20 51 12 12 48 16 15 55 19 18 60 16 15 57 16 16 54 18 19 49 16 14 35 22 14 29 23 10 20 36 14 26 70 46 59 146 124 137 168 150 162 182 171 179 180 171 172 183 170 164 192 172 163 201 169 156 212 167 136 223 165 101 214 155 87 195 144 101 228 185 153 218 177 149 195 151 124 145 96 66 149 94 63 212 152 116 236 172 136 233 169 134 237 174 143 169 104 82 184 118 92 210 141 100 202 131 85 226 158 111 204 133 89 212 138 93 174 95 54 131 48 8 161 76 39 212 126 93 225 140 109 230 146 118 240 160 133 232 155 129 239 162 136 235 155 130 246 165 138 255 180 154 255 170 143 255 170 145 249 157 132 247 157 131 224 138 111 228 144 116 230 149 120 228 144 116 220 134 107 212 122 96 151 61 34 140 49 22 168 79 49 193 109 75 206 123 89 212 134 96 221 149 109 202 137 97 196 132 96 244 173 151 233 167 143 197 145 106 181 147 120 154 144 152 129 135 167 117 138 183 109 138 194 106 140 203 108 144 206 110 145 201 110 143 197 110 142 199 111 143 200 108 143 201 109 144 202 109 144 202 109 144 202 108 143 201 108 143 201 108 143 201 109 144 202 107 142 200 107 142 200 106 141 199 106 141 199 106 141 199 106 141 199 107 142 200 107 142 200 109 141 200 108 140 199 108 140 199 108 140 199 109 141 202 109 141 202 108 140 201 108 140 201 107 139 200 107 139 200 107 139 200 108 140 201 108 140 201 109 141 202 110 141 205 110 141 205 217 201 176 177 161 148 114 97 107 38 20 44 25 9 36 32 20 44 15 6 23 17 13 28 43 40 57 75 73 94 34 32 54 23 23 49 30 30 56 7 8 38 17 17 55 18 20 61 14 17 58 10 14 49 19 20 48 22 17 37 29 12 28 33 7 18 67 31 43 139 98 112 182 146 160 181 151 163 187 168 174 194 180 180 180 165 158 182 164 152 207 179 165 214 173 141 222 167 102 214 157 88 193 146 100 227 186 154 223 182 154 192 148 123 134 89 60 126 75 44 153 97 62 203 140 105 218 153 121 168 107 79 76 18 0 137 79 55 191 126 86 221 153 106 222 154 109 215 144 100 211 137 92 167 88 45 139 56 16 199 114 77 202 116 81 233 148 117 228 147 118 238 158 131 227 150 124 245 168 142 236 154 130 245 161 135 255 179 155 255 171 146 255 169 144 255 164 139 252 158 133 246 156 130 245 159 132 234 150 122 229 145 117 221 135 108 218 131 103 198 108 81 145 54 25 201 110 81 214 128 95 223 141 104 225 145 108 206 134 94 201 136 96 148 83 51 170 99 81 200 135 115 173 127 91 181 157 133 150 153 170 117 136 178 116 137 184 111 136 192 107 138 202 106 141 205 107 143 201 106 141 197 106 141 199 108 142 203 108 143 201 108 143 201 109 144 202 109 144 202 108 143 201 108 143 201 108 143 201 109 144 202 108 143 201 108 143 201 107 142 200 107 142 200 107 142 200 107 142 200 108 143 201 108 143 201 108 140 199 107 139 198 107 139 198 107 139 198 108 140 201 108 140 201 107 139 200 106 138 199 107 139 200 107 139 200 107 139 200 107 139 200 108 140 201 109 141 202 109 140 204 110 141 205 201 185 151 187 169 147 64 44 46 35 16 35 37 19 45 20 6 31 18 7 24 64 58 72 127 123 138 88 87 101 27 26 42 10 11 29 23 24 45 18 18 44 14 13 47 7 9 48 10 18 55 9 18 51 20 21 49 22 14 35 36 14 27 50 14 26 82 34 48 154 100 114 171 120 135 201 159 171 192 165 172 191 171 172 183 166 159 184 166 154 209 186 170 200 164 130 215 164 98 214 160 90 191 149 101 221 182 149 225 184 156 193 149 124 140 96 71 117 70 40 141 87 53 162 102 66 164 101 70 105 45 19 63 11 0 124 72 48 181 119 80 210 142 97 215 142 99 232 155 113 223 142 99 189 104 63 176 86 49 211 118 84 213 120 87 224 133 104 213 126 99 199 113 88 189 105 81 197 110 90 194 103 82 206 111 89 238 139 118 248 146 124 232 127 105 217 115 92 201 103 78 208 117 90 208 121 93 201 118 88 207 124 94 223 138 109 210 123 95 220 130 103 174 83 54 215 124 95 233 144 112 232 148 112 230 148 111 209 135 96 184 120 82 124 61 30 128 59 43 154 91 74 214 171 137 177 160 142 128 144 169 109 137 185 117 139 188 114 137 191 107 138 202 104 142 207 104 143 202 105 141 199 105 141 203 107 143 205 107 141 202 108 143 201 109 144 202 108 143 201 108 143 201 107 142 200 108 143 201 109 144 202 109 144 202 109 144 202 108 143 201 108 143 201 108 143 201 108 143 201 109 144 202 109 144 202 108 140 199 107 139 198 107 139 198 107 139 198 107 139 198 107 139 198 107 139 200 106 138 199 107 139 200 107 139 200 107 139 200 107 139 200 108 140 201 109 141 202 109 141 202 110 142 203 214 193 164 190 167 149 82 60 63 39 16 34 26 7 29 29 14 35 19 8 22 78 72 82 114 111 122 25 23 37 27 26 42 27 25 46 12 13 34 20 22 47 8 9 40 18 21 56 7 13 49 13 19 51 21 22 50 14 5 26 35 11 25 61 19 31 84 20 34 130 59 73 169 106 117 198 149 155 197 166 171 192 172 171 189 170 163 184 165 151 197 174 156 211 175 141 214 158 99 218 158 95 194 148 98 216 177 138 227 184 152 197 151 125 151 104 78 119 69 42 125 72 40 145 92 61 184 132 108 142 95 75 76 34 18 79 34 13 182 124 86 212 144 99 218 143 103 229 150 109 251 168 128 208 120 82 201 109 72 206 113 79 217 122 90 201 107 79 160 68 43 134 44 20 143 52 31 145 52 34 147 52 34 149 50 31 168 65 48 178 73 54 157 50 30 154 49 28 152 53 30 146 52 26 133 44 14 140 53 23 137 52 21 163 78 47 182 95 65 206 119 89 224 134 100 234 144 109 255 168 130 228 146 108 208 133 94 207 139 102 195 135 101 114 54 26 138 72 56 185 129 112 182 146 120 149 139 127 132 151 181 108 140 189 116 142 193 114 139 196 109 140 205 107 142 208 107 143 203 106 142 200 105 144 203 107 146 205 106 142 200 108 143 201 109 144 202 108 143 201 108 143 201 107 142 200 108 143 201 109 144 202 110 145 203 110 145 203 109 144 202 108 143 201 108 143 201 109 144 202 109 144 202 109 144 202 109 141 200 108 140 199 108 140 199 108 140 199 108 140 199 108 140 199 108 140 201 107 139 200 107 139 200 107 139 200 107 139 200 107 139 200 108 140 201 109 141 202 109 141 202 110 142 203 245 215 207 221 191 189 153 126 135 42 19 35 32 13 32 24 12 26 13 6 14 76 71 78 54 51 60 33 31 45 23 21 43 19 19 47 24 25 55 8 11 42 9 14 43 15 20 50 16 15 49 22 18 51 19 19 47 17 9 32 30 7 23 67 21 32 105 25 38 108 18 27 173 97 101 190 134 133 197 169 166 191 174 167 197 174 168 190 163 152 203 176 155 212 171 139 223 155 110 225 152 101 200 144 93 223 176 130 221 173 135 175 124 93 155 98 71 126 68 44 108 54 28 93 51 29 77 47 36 77 54 46 63 38 33 68 31 15 160 104 69 200 131 89 216 142 103 228 153 114 243 163 126 243 161 123 195 110 73 192 103 69 167 76 45 149 55 27 136 42 16 160 66 41 170 75 55 175 80 62 188 91 75 182 85 69 165 64 52 160 58 44 173 70 55 204 103 85 232 135 116 224 130 105 214 121 90 225 132 98 220 130 95 206 121 84 192 110 73 209 127 89 217 130 87 252 164 118 242 158 111 231 157 110 206 144 105 199 144 113 175 117 93 124 66 46 138 83 63 172 127 108 187 160 143 171 166 163 124 141 167 114 147 192 109 141 198 107 141 205 110 140 210 111 142 209 112 142 204 109 144 200 106 145 202 104 145 201 108 145 200 109 144 200 109 144 200 109 144 200 109 144 200 109 144 200 109 144 200 109 144 200 113 148 204 111 146 202 109 144 200 108 143 199 109 144 200 109 144 200 108 143 199 107 142 198 108 143 201 108 143 201 108 143 201 108 143 201 107 141 202 106 140 201 106 140 201 105 139 200 104 138 199 105 139 200 106 140 201 107 141 202 107 141 204 107 141 204 109 143 206 110 144 207 170 141 146 186 159 168 188 164 178 77 56 73 22 5 21 30 19 33 7 0 7 102 97 103 62 59 68 17 15 29 26 22 47 20 17 48 17 16 50 15 18 49 11 16 45 16 20 47 20 15 47 21 16 48 18 18 46 18 13 35 33 10 26 69 19 30 107 20 29 115 14 20 157 68 70 200 135 131 198 167 162 191 172 165 196 171 166 193 165 154 205 174 154 214 169 138 227 154 111 226 148 99 201 145 94 225 179 130 207 164 122 156 106 71 160 95 63 162 93 60 110 47 12 68 19 0 37 8 0 48 28 17 61 36 29 72 36 22 149 92 62 198 129 88 209 136 95 210 135 93 235 158 116 242 163 122 219 137 97 200 115 76 207 119 83 204 114 79 214 121 88 216 122 94 220 126 100 234 139 117 246 151 131 240 144 128 237 141 127 247 151 135 250 150 134 235 138 119 239 147 126 245 153 128 242 147 115 231 137 99 217 128 88 215 130 91 221 142 103 198 119 78 238 152 105 247 159 109 239 156 104 215 141 94 211 155 118 191 142 112 158 107 80 121 69 47 142 96 73 169 133 117 188 169 165 162 162 174 126 145 177 110 142 189 106 144 206 105 144 211 109 142 209 112 142 206 112 142 202 111 143 200 106 145 200 106 145 200 109 146 201 110 145 201 109 144 200 109 144 200 109 144 200 109 144 200 109 144 200 109 144 200 110 145 201 109 144 200 109 144 200 108 143 199 107 142 198 107 142 198 108 143 199 109 144 200 108 143 201 108 143 201 108 143 201 108 143 201 107 141 202 106 140 201 106 140 201 106 140 201 105 139 200 106 140 201 107 141 202 108 142 203 108 142 205 108 142 205 109 143 206 110 144 207 92 72 83 24 6 18 101 84 102 124 109 128 33 20 38 16 8 23 28 21 29 153 146 154 99 93 103 15 8 24 26 17 44 21 14 47 15 9 43 21 20 51 11 12 40 17 19 44 20 17 48 18 15 46 13 17 42 17 14 33 34 12 25 69 18 27 107 17 26 116 11 16 131 35 37 200 125 122 206 165 161 195 172 164 193 170 162 194 167 156 206 171 152 215 164 133 226 153 100 223 151 92 196 149 97 213 179 133 193 158 120 145 100 61 169 100 58 192 110 62 180 98 42 120 49 0 72 20 0 64 25 0 70 33 14 71 28 9 123 63 35 185 115 79 199 127 79 214 138 86 239 163 111 229 152 100 220 140 89 199 116 66 228 142 95 216 127 83 212 123 81 220 130 93 220 130 95 219 128 97 231 141 114 247 157 131 243 155 131 231 141 117 237 142 120 229 134 112 228 138 114 218 131 103 207 117 82 207 118 76 217 130 87 232 149 107 226 147 108 198 119 78 233 146 101 251 163 115 223 137 88 216 139 95 199 140 108 181 132 102 131 85 49 122 79 45 149 113 89 165 138 129 171 159 169 136 140 169 122 143 188 113 144 201 108 146 209 106 146 208 108 144 206 108 143 201 110 143 197 111 144 198 109 146 201 109 145 203 111 146 202 110 145 201 110 145 201 109 144 200 109 144 200 109 144 200 109 144 200 109 144 200 107 142 198 108 143 199 109 144 200 108 143 199 106 141 197 105 140 196 107 142 198 109 144 200 108 143 201 108 143 201 108 143 201 108 143 201 107 141 202 107 141 202 106 140 201 106 140 201 107 141 202 107 141 202 108 142 203 108 142 203 108 142 205 108 142 205 109 143 206 110 144 207 32 24 39 27 18 35 22 13 34 32 23 44 22 14 35 22 15 31 19 11 22 118 111 119 136 128 139 34 25 42 26 16 43 24 15 46 21 14 47 21 16 48 8 8 34 21 21 45 17 17 45 17 17 43 14 16 37 17 10 26 36 8 20 74 17 24 110 18 23 117 8 11 118 14 15 181 95 94 216 163 159 204 171 164 196 169 160 196 168 156 208 169 152 212 162 129 222 153 94 220 155 89 193 148 93 208 174 128 194 159 119 158 110 70 180 108 58 192 106 47 219 126 57 211 123 52 179 105 44 111 49 0 70 14 0 68 12 0 104 39 17 172 101 73 201 127 82 205 130 75 221 144 90 238 158 105 227 146 93 193 112 59 221 138 88 227 141 92 213 126 81 205 118 75 202 114 76 199 114 77 192 106 73 184 99 70 177 93 65 171 85 58 165 75 49 170 80 53 180 95 66 185 101 67 203 115 75 228 141 98 231 147 103 222 141 98 215 137 98 200 121 82 227 140 95 224 136 90 226 137 93 202 123 84 191 130 102 165 115 88 120 81 42 128 95 60 142 117 97 151 135 136 146 142 165 119 128 169 120 143 197 114 146 207 111 147 209 107 147 206 108 147 204 109 146 199 110 145 199 110 145 199 110 145 203 110 145 203 112 147 203 111 146 202 111 146 202 110 145 201 110 145 201 109 144 200 109 144 200 109 144 200 108 143 199 108 143 199 108 143 199 108 143 199 106 141 197 106 141 197 107 142 198 108 143 199 108 143 201 108 143 201 108 143 201 108 143 201 108 142 203 107 141 202 107 141 202 106 140 201 107 141 202 107 141 202 108 142 203 108 142 203 107 141 204 107 141 204 108 142 205 109 143 206 17 18 38 24 25 46 17 15 39 23 21 45 18 15 36 16 12 29 13 7 17 28 21 29 115 105 116 48 39 56 29 19 46 24 15 46 29 22 55 19 14 44 13 11 35 23 24 45 14 19 41 17 19 40 18 17 35 23 7 20 46 4 14 85 19 23 115 21 22 117 9 9 121 11 12 151 55 56 216 145 143 214 165 160 200 167 158 197 168 154 210 168 152 208 159 126 215 156 96 212 155 84 192 143 84 215 169 119 206 159 115 169 115 69 180 110 59 186 102 42 213 119 49 209 109 33 224 127 48 220 129 58 180 98 42 111 36 0 80 7 0 150 77 62 188 113 74 204 126 77 204 124 75 228 148 99 210 128 80 204 122 74 218 134 87 220 136 90 230 146 102 224 139 98 216 132 95 200 118 81 178 95 63 162 81 51 160 81 51 163 82 53 170 83 55 160 73 43 165 82 50 176 96 59 197 114 70 214 132 85 210 129 82 201 122 79 216 141 101 214 136 97 216 131 90 217 128 86 214 125 85 196 116 81 191 126 104 142 94 72 131 96 64 136 115 86 124 111 102 135 133 146 130 138 174 124 142 192 120 146 205 110 142 203 111 147 205 108 149 203 108 149 201 108 149 201 111 148 201 111 146 202 114 144 206 113 143 205 114 146 205 112 147 203 111 146 202 111 146 202 110 145 201 110 145 201 110 145 201 110 145 201 112 147 203 110 145 201 108 143 199 108 143 199 108 143 199 108 143 199 108 143 199 107 142 198 108 143 201 108 143 201 108 143 201 108 143 201 108 142 203 107 141 202 107 141 202 106 140 201 106 140 201 106 140 201 107 141 202 107 141 202 106 140 203 106 140 203 106 140 203 107 141 204 15 23 46 10 16 42 17 21 50 15 15 43 10 8 30 19 15 32 22 16 28 23 15 26 45 37 50 38 29 48 28 19 46 18 11 44 25 20 52 16 13 42 17 18 39 17 20 39 12 19 38 17 18 36 27 14 31 39 7 20 66 6 16 100 19 25 118 23 21 115 10 7 127 15 14 131 25 25 196 106 108 218 150 147 204 160 151 200 164 150 209 168 150 203 157 123 214 161 95 210 154 80 193 139 79 221 168 118 211 157 113 168 107 62 178 105 54 190 106 46 218 120 49 214 108 30 219 110 25 217 112 29 206 109 41 169 78 34 118 32 19 145 65 54 180 101 68 209 130 87 198 119 76 204 123 78 189 108 63 221 138 94 225 141 97 208 124 80 202 117 76 211 126 87 209 125 88 203 120 86 211 131 98 229 148 118 233 154 124 226 147 117 225 140 109 215 131 97 213 133 98 203 125 86 195 114 69 197 117 68 204 126 78 215 141 96 217 145 105 217 143 104 210 127 87 230 142 102 199 111 75 198 119 89 189 123 107 118 70 56 131 103 82 141 127 114 121 120 126 129 139 164 119 138 181 121 148 201 114 145 202 109 144 200 111 148 200 108 150 200 108 152 201 109 152 203 111 150 207 113 147 208 116 144 209 117 142 206 115 147 206 112 147 203 112 147 203 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 114 149 205 111 146 202 109 144 200 108 143 199 110 145 201 110 145 201 109 144 200 107 142 198 109 144 202 109 144 202 109 144 202 108 143 201 108 142 203 107 141 202 106 140 201 106 140 201 105 139 200 106 140 201 106 140 201 106 140 201 105 139 202 105 139 202 106 140 203 107 141 204 17 27 54 4 11 39 14 19 49 26 27 55 22 20 42 12 8 25 4 0 10 17 11 21 10 4 16 20 16 33 23 19 46 13 9 42 17 16 48 12 13 43 14 19 41 10 15 34 12 18 34 18 12 26 41 10 26 69 12 27 98 12 25 117 18 23 119 19 17 116 13 8 127 15 13 129 16 18 163 54 59 219 129 131 208 149 143 203 159 146 207 166 148 201 157 120 216 159 88 217 155 78 201 140 85 225 164 120 212 150 111 171 104 62 186 106 55 197 107 47 207 106 38 209 103 27 213 107 23 212 107 26 201 98 31 172 71 25 130 29 11 153 59 47 199 115 87 189 111 73 188 110 72 204 125 86 213 131 91 218 136 96 224 141 99 231 148 108 231 146 107 223 138 101 218 134 98 233 150 116 254 174 141 255 174 144 247 168 137 251 170 140 254 170 136 248 164 128 249 171 133 246 168 129 242 164 118 243 165 116 239 163 114 236 163 118 211 142 103 199 127 89 212 133 94 201 117 80 207 123 89 185 109 83 192 130 117 119 75 72 124 102 104 138 132 142 129 136 162 125 145 182 110 141 188 111 147 199 108 144 194 113 151 198 110 151 195 109 152 197 106 152 202 106 151 206 109 149 210 113 147 211 119 144 210 120 144 208 115 145 205 112 147 203 112 147 203 112 147 203 111 146 202 112 147 203 112 147 203 112 147 203 113 148 204 112 147 203 110 145 201 110 145 201 110 145 201 110 145 201 110 145 201 110 145 201 110 145 203 110 145 203 109 144 202 109 144 202 108 142 203 107 141 202 106 140 201 105 139 200 105 139 200 106 140 201 107 141 202 107 141 202 106 140 203 106 140 203 107 141 204 108 142 205 8 17 46 18 25 54 14 17 48 19 19 47 9 5 28 13 9 26 18 15 24 16 13 22 20 18 31 15 14 32 20 20 48 11 14 47 14 17 50 10 15 44 13 20 39 11 17 31 18 16 30 24 4 16 55 4 19 95 17 31 124 18 32 130 15 22 120 16 13 118 15 8 125 11 10 136 17 19 139 15 23 219 113 117 213 140 134 206 157 142 206 165 145 201 156 115 214 155 77 227 161 77 212 146 88 226 159 117 214 144 108 182 109 68 194 111 59 194 102 39 203 102 32 215 109 33 215 111 26 211 109 27 211 108 39 197 95 46 145 43 21 139 43 29 205 124 95 179 105 68 191 115 79 196 118 80 219 140 101 207 125 85 213 132 89 221 138 96 210 125 84 226 141 102 225 142 102 228 144 107 248 166 129 255 177 140 255 176 141 255 180 145 255 174 140 243 159 125 243 165 129 249 174 134 252 174 128 245 169 120 233 159 112 232 161 117 203 135 98 196 128 93 192 116 80 174 94 59 196 117 86 189 117 93 184 130 118 150 114 116 127 110 126 134 133 164 130 143 185 116 142 191 112 147 201 111 150 205 110 148 197 114 152 197 111 153 195 109 152 195 106 152 202 105 152 208 108 149 211 113 148 212 121 146 212 122 146 210 115 145 205 112 147 203 112 147 203 112 147 203 112 147 203 112 147 203 112 147 203 113 148 204 110 145 201 111 146 202 112 147 203 111 146 202 109 144 200 109 144 200 110 145 201 112 147 203 111 146 204 110 145 203 110 145 203 109 144 202 108 142 203 106 140 201 105 139 200 105 139 200 106 140 201 107 141 202 107 141 202 108 142 203 107 141 204 107 141 204 108 142 205 109 143 206 12 17 46 15 19 48 18 19 49 19 16 43 17 12 34 14 10 25 13 10 19 13 12 20 18 20 32 13 18 37 11 16 45 11 17 49 11 17 49 8 15 43 9 16 35 18 17 31 24 8 21 53 12 26 91 19 33 118 20 35 135 13 26 135 8 15 123 11 9 121 12 7 127 11 11 138 13 17 148 17 25 174 57 63 216 136 129 203 145 131 199 154 133 187 140 98 209 147 64 247 178 87 206 132 71 212 138 93 208 133 94 182 103 60 206 121 66 196 104 39 205 104 32 212 110 28 210 109 21 216 116 31 203 106 35 205 111 59 128 36 13 102 14 0 191 116 85 199 130 91 176 104 64 187 114 73 197 122 80 205 128 84 195 114 69 213 132 85 220 138 91 224 142 95 221 139 92 217 135 88 231 150 105 232 151 106 247 166 123 250 168 128 235 151 117 236 153 121 243 164 131 252 176 140 253 174 133 237 160 116 220 146 101 214 143 101 184 118 83 171 105 71 164 91 56 183 109 74 181 108 76 169 104 82 172 125 115 148 119 124 137 124 150 131 134 175 125 141 193 121 147 206 113 149 211 109 149 210 113 152 207 111 149 198 115 153 198 108 151 194 103 149 199 105 150 205 107 149 209 111 147 209 119 144 208 123 147 209 116 147 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 112 147 205 111 146 204 111 146 204 110 145 203 110 144 205 109 143 204 109 143 204 108 142 203 108 142 203 108 142 203 108 142 203 108 142 203 108 142 205 108 142 205 108 142 205 108 142 205 15 16 46 18 18 46 21 18 47 20 16 41 17 12 32 14 10 24 13 12 18 13 14 19 10 14 26 18 25 43 17 24 52 8 14 46 7 12 42 13 15 40 16 18 33 27 15 27 49 13 27 77 15 30 109 18 33 130 18 30 143 11 22 141 7 14 128 10 10 123 9 8 129 9 11 139 12 19 151 17 26 146 25 30 196 109 100 208 142 126 209 157 136 179 126 82 207 143 55 255 186 88 210 133 63 199 119 68 193 112 67 181 99 52 206 120 63 193 101 34 217 121 44 194 94 8 209 108 16 221 125 38 201 113 41 191 111 60 117 45 23 127 59 46 218 151 122 195 127 90 163 94 55 169 97 57 185 112 71 196 121 79 181 104 60 199 121 75 218 137 90 217 136 89 209 128 81 204 126 78 219 141 95 218 140 94 226 147 104 221 142 103 211 130 100 222 142 115 226 149 121 222 147 115 224 146 108 223 146 104 208 135 94 188 119 80 168 103 71 133 68 38 147 78 45 132 62 28 145 79 47 188 132 109 163 125 116 65 42 48 145 139 165 122 127 169 117 134 188 114 139 203 113 148 216 114 151 221 107 142 206 111 148 203 109 147 196 106 147 193 105 149 198 107 150 201 109 150 206 111 147 205 118 147 207 123 149 208 115 147 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 112 147 203 112 147 203 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 112 147 203 112 147 205 111 146 204 111 146 204 110 145 203 109 143 204 109 143 204 108 142 203 108 142 203 108 142 203 108 142 203 108 142 203 108 142 203 108 142 205 108 142 205 108 142 205 108 142 205 18 15 44 21 17 44 23 17 45 21 16 39 19 11 32 15 11 25 13 14 19 12 15 20 12 18 30 22 29 47 17 22 51 11 12 43 20 15 45 27 17 41 29 15 30 43 11 22 82 20 35 104 17 33 124 20 31 138 18 27 144 13 19 142 9 12 132 8 10 129 6 9 133 8 16 142 15 24 149 19 27 126 10 11 161 71 62 211 138 121 214 151 133 187 126 82 203 138 46 255 188 84 208 131 51 189 108 45 181 98 46 175 92 40 201 117 57 191 104 33 208 116 33 210 112 21 210 112 15 230 136 46 184 106 34 140 78 31 60 17 1 206 163 156 216 158 136 203 137 105 174 108 76 141 72 39 133 63 29 149 76 41 150 76 39 167 92 53 199 121 82 207 129 90 204 129 90 201 126 87 204 128 92 191 117 80 190 116 81 183 108 77 188 112 88 201 126 105 203 131 107 195 124 96 195 120 88 197 121 85 181 107 70 156 86 52 128 63 33 114 50 22 103 37 5 118 52 18 175 112 81 203 153 128 149 117 106 28 12 13 57 55 69 114 120 152 126 140 187 119 141 201 116 144 217 114 146 223 119 153 227 109 144 212 109 145 203 108 148 199 109 149 198 109 151 199 109 149 200 111 146 200 115 146 201 118 149 206 115 147 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 114 149 205 113 148 204 112 147 203 111 146 202 111 146 202 111 146 202 112 147 203 112 147 203 112 147 205 112 147 205 111 146 204 110 145 203 109 143 204 108 142 203 108 142 203 107 141 202 108 142 203 108 142 203 108 142 203 108 142 203 108 142 205 108 142 205 108 142 205 108 142 205 20 15 45 21 17 44 23 17 45 23 15 39 19 11 32 15 11 25 14 14 22 12 17 23 11 17 29 17 20 39 14 14 42 18 9 40 30 11 39 35 7 29 42 9 20 70 17 25 106 22 38 123 18 33 134 18 27 141 18 21 144 14 16 141 11 13 134 8 12 130 5 13 134 8 19 142 16 27 140 17 22 131 17 16 134 38 26 206 123 107 210 137 118 203 135 90 206 137 42 253 182 74 206 130 44 188 109 42 180 99 44 168 87 34 193 111 51 197 113 41 215 126 42 192 103 13 208 120 31 206 127 50 159 98 41 55 12 0 53 29 17 250 223 216 186 134 113 208 142 116 194 127 100 146 79 52 105 36 7 93 22 0 107 36 6 124 51 19 154 81 49 174 100 71 178 107 77 170 101 72 156 89 62 140 73 47 143 77 51 148 82 60 158 91 74 157 90 74 152 87 69 150 84 62 153 82 54 153 78 46 143 70 37 131 60 30 105 39 13 87 23 0 103 36 7 171 105 73 194 133 102 197 149 126 146 118 106 19 8 6 22 21 29 29 36 55 94 107 142 125 142 194 114 137 205 115 143 217 112 144 219 117 151 223 112 147 211 112 149 204 112 150 199 113 151 198 113 151 198 113 149 199 113 148 202 115 147 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 113 148 204 115 150 206 114 149 205 112 147 203 111 146 202 110 145 201 111 146 202 111 146 202 112 147 203 112 147 205 112 147 205 111 146 204 110 145 203 109 143 204 108 142 203 108 142 203 107 141 202 109 143 204 109 143 204 109 143 204 109 143 204 109 143 206 109 143 206 109 143 206 109 143 206 19 16 47 22 17 47 23 16 47 20 14 40 17 12 32 14 12 25 12 15 22 13 18 24 12 16 28 21 20 38 24 14 41 30 7 36 42 3 30 55 4 23 77 16 23 111 32 37 122 20 34 133 15 29 140 17 22 142 16 17 141 16 12 138 13 11 133 8 14 128 6 17 127 7 19 132 15 24 131 15 18 130 18 14 126 19 9 196 99 83 212 129 111 201 127 80 217 141 43 255 182 69 209 139 53 180 111 44 173 101 51 161 84 38 197 112 55 199 112 43 208 121 41 182 101 22 196 127 60 167 114 62 50 11 0 29 3 0 128 114 103 214 192 181 158 105 87 202 136 112 194 126 103 180 112 89 138 70 47 85 17 0 90 22 0 96 28 5 110 44 22 121 56 36 112 49 32 102 40 25 88 28 17 83 25 14 93 36 27 104 47 38 107 50 41 96 38 27 84 26 14 86 24 9 99 33 9 113 42 14 124 50 21 127 56 28 111 45 21 107 41 17 167 98 69 197 131 99 198 137 106 197 151 127 132 106 93 21 10 6 19 19 21 8 15 25 12 20 43 79 92 127 131 148 200 124 148 210 117 146 214 105 138 207 114 149 213 113 149 209 115 148 201 116 150 198 118 152 200 117 153 203 115 152 205 113 150 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 115 150 206 114 149 205 113 148 204 111 146 202 111 146 202 111 146 202 111 146 202 111 146 202 112 147 205 112 147 205 111 146 204 111 146 204 110 144 205 109 143 204 108 142 203 108 142 203 109 143 204 109 143 204 109 143 204 109 143 204 109 143 206 109 143 206 109 143 206 109 143 206 18 17 49 21 18 49 22 17 49 19 15 42 14 12 33 12 11 25 11 14 23 13 18 24 19 18 32 25 17 38 31 11 38 38 2 30 58 2 27 89 16 33 114 29 34 127 27 29 130 14 25 138 12 24 143 14 18 144 16 13 140 15 9 135 13 10 130 9 16 124 6 18 121 9 23 124 14 23 129 19 20 123 10 6 137 18 10 181 71 58 221 132 114 184 106 60 226 147 52 255 189 78 222 152 64 169 100 33 162 89 44 158 80 41 198 116 68 179 97 41 190 112 46 177 111 51 141 92 49 54 18 0 31 6 0 21 5 5 171 160 154 193 170 156 128 76 54 207 140 114 193 126 100 192 125 99 170 103 77 120 54 30 116 51 29 109 47 26 105 44 26 100 41 27 73 16 5 63 10 2 56 7 2 57 9 7 56 10 10 56 11 8 59 11 7 58 9 4 54 4 0 61 7 0 90 27 9 122 55 29 140 68 43 140 68 44 125 57 36 156 90 68 199 127 102 191 122 93 205 144 115 195 149 125 137 111 98 26 15 11 11 9 10 13 16 23 11 15 27 12 18 40 71 84 118 125 146 191 122 149 204 127 159 220 116 150 213 117 151 212 120 151 208 119 150 204 119 150 204 116 152 204 111 150 207 107 148 204 113 150 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 115 150 206 115 150 206 114 149 205 113 148 204 112 147 203 111 146 202 111 146 202 111 146 202 112 147 205 112 147 205 112 147 205 111 146 204 111 145 206 110 144 205 110 144 205 109 143 204 110 144 205 110 144 205 110 144 205 110 144 205 110 144 207 110 144 207 110 144 207 110 144 207 17 17 51 18 18 52 18 17 49 15 15 43 11 12 33 9 11 26 10 14 23 13 16 25 18 16 29 24 11 31 43 14 42 63 14 43 82 10 34 112 20 35 132 23 26 127 9 9 131 10 19 139 12 21 148 14 15 148 16 12 141 17 9 131 13 9 124 9 16 118 8 19 112 12 24 119 19 27 128 19 22 130 12 10 148 16 12 164 41 33 225 132 114 170 93 49 227 153 64 255 189 86 234 160 73 165 88 20 159 78 31 153 73 36 191 112 79 146 76 40 165 107 67 85 40 7 36 4 0 34 13 12 25 7 21 22 8 21 200 186 185 200 174 159 104 51 20 215 149 114 200 134 99 185 119 85 174 110 75 150 87 56 139 78 49 136 79 52 127 72 51 117 66 49 84 36 22 71 28 19 58 19 14 54 16 15 43 9 8 39 3 3 42 4 3 54 13 9 65 22 16 81 33 23 115 56 40 150 84 62 159 88 66 149 77 55 144 76 57 168 100 79 188 114 89 200 129 101 193 129 101 202 154 131 160 132 120 16 5 1 24 19 25 10 8 19 14 13 19 13 16 23 0 7 19 37 55 77 107 135 172 117 151 199 110 145 203 116 150 213 124 152 215 124 150 211 120 149 207 116 148 207 108 148 207 105 147 205 113 149 207 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 114 149 205 116 151 207 116 151 207 116 151 207 115 150 206 114 149 205 113 148 204 111 146 202 111 146 202 112 147 205 112 147 205 112 147 205 112 147 205 111 145 206 111 145 206 111 145 206 111 145 206 110 144 205 110 144 205 110 144 205 110 144 205 110 144 207 110 144 207 110 144 207 110 144 207 18 19 50 18 19 50 18 17 49 14 14 42 10 10 34 8 9 27 10 13 22 15 16 21 23 17 27 38 22 35 81 46 68 112 54 78 114 29 50 124 18 30 145 21 23 145 15 15 134 9 15 139 12 19 153 14 17 152 17 14 143 16 10 128 13 8 116 10 12 109 10 15 104 12 17 119 23 27 122 12 13 145 21 21 144 8 8 145 21 13 216 123 106 165 92 51 223 154 77 250 183 92 231 162 84 157 88 23 150 79 35 135 68 39 171 110 89 113 64 47 46 9 0 35 8 0 23 7 8 16 6 17 23 12 29 13 2 16 209 199 198 227 204 188 92 43 11 210 146 108 200 136 98 187 122 84 185 117 82 164 98 64 139 74 42 153 92 64 145 89 64 142 90 69 110 61 46 92 44 32 66 21 15 55 16 11 47 16 13 52 21 16 54 15 10 66 23 16 80 33 25 101 47 35 133 70 52 156 88 67 157 86 64 145 73 51 158 87 65 193 122 100 189 117 92 197 130 103 192 135 108 240 199 179 139 119 110 20 11 12 14 11 20 19 16 27 17 15 18 13 13 13 12 17 20 2 15 23 9 31 52 49 75 108 103 132 176 114 142 192 127 150 204 128 150 207 122 148 205 118 149 206 113 152 209 113 153 212 113 149 207 114 149 205 114 149 205 114 149 205 114 149 207 113 149 207 114 149 207 113 149 207 117 152 210 117 152 208 117 152 208 117 152 208 116 151 207 114 149 205 112 147 203 111 146 202 112 147 203 112 147 203 112 147 205 112 147 205 112 146 207 112 146 207 112 146 207 112 146 207 110 145 203 110 145 203 110 145 203 110 145 203 110 144 205 110 144 207 110 144 207 110 144 208 18 19 40 23 21 45 26 21 51 11 4 35 18 12 38 12 9 28 13 12 20 14 12 15 35 27 25 124 104 105 197 157 165 183 120 129 117 25 36 139 26 32 150 17 18 144 9 6 139 13 16 144 15 20 155 14 22 155 14 20 145 12 15 128 13 10 109 14 8 102 15 8 106 19 12 118 20 17 129 9 10 138 8 10 150 16 17 140 21 17 202 114 100 164 95 62 210 148 87 238 181 110 208 157 94 141 93 44 100 56 21 68 32 10 38 10 0 29 10 6 21 9 9 18 12 14 12 11 17 9 9 17 13 14 19 16 16 16 210 210 202 233 220 201 98 59 28 183 127 90 200 140 104 188 122 88 189 113 81 171 92 61 161 84 54 160 86 59 156 92 65 149 87 64 133 68 50 115 49 35 105 41 31 91 37 27 68 32 18 60 27 12 72 24 12 84 28 13 100 39 20 118 52 30 154 82 58 167 93 68 147 75 50 149 78 50 171 100 72 197 126 96 182 113 82 199 139 111 215 170 149 245 221 209 121 117 118 11 15 24 13 11 22 15 12 21 16 14 19 16 14 17 14 15 17 10 15 19 8 17 26 5 17 33 0 12 33 25 42 70 77 96 129 122 144 185 134 158 204 120 148 196 114 145 199 122 155 209 116 151 207 113 148 206 110 146 204 114 150 208 116 152 212 113 152 211 112 148 208 108 147 206 114 150 210 116 152 210 116 152 210 114 151 206 113 148 204 113 148 202 115 148 202 115 148 202 114 149 203 113 148 202 113 148 204 113 147 208 113 147 210 113 147 210 113 147 208 113 148 206 110 145 201 109 144 198 110 143 196 110 143 197 111 143 202 112 144 205 112 143 208 112 143 210 21 18 37 18 15 36 23 17 45 17 11 39 23 17 43 15 11 28 11 8 15 43 38 35 143 132 126 192 172 165 197 157 155 179 117 118 152 63 67 136 23 27 150 17 18 146 10 10 128 5 7 133 10 13 149 12 22 155 14 23 149 15 22 133 14 16 113 16 10 102 15 6 101 10 5 123 19 18 139 14 18 139 8 14 132 7 11 125 17 17 187 109 99 170 112 88 162 113 70 151 109 59 100 63 21 46 13 0 29 4 0 25 7 0 15 5 3 15 10 16 11 11 19 10 13 22 10 13 22 10 13 18 17 19 18 16 17 12 210 210 200 236 223 206 95 60 32 160 107 75 209 148 117 187 118 87 183 102 72 184 99 70 174 93 63 155 80 49 160 91 62 150 83 57 136 63 44 129 56 41 119 47 35 104 42 29 84 41 24 89 48 28 109 54 34 115 50 30 128 57 35 147 73 48 166 90 64 160 85 56 145 71 42 162 89 57 187 114 81 183 114 81 196 133 102 193 143 116 255 222 205 242 227 220 97 98 103 7 15 26 11 11 21 13 12 20 15 12 19 14 13 18 13 12 17 13 12 17 13 14 18 11 14 21 10 17 27 13 22 37 7 19 41 14 29 58 60 80 115 118 139 182 134 158 204 115 141 190 119 149 203 117 150 204 117 149 206 114 149 205 113 148 204 113 149 207 117 153 211 122 158 218 115 154 213 115 154 211 117 153 211 116 152 210 114 151 206 113 148 202 113 148 202 113 148 202 113 149 201 113 148 202 114 149 207 115 149 210 114 148 212 114 148 212 113 147 210 113 148 206 112 147 201 111 147 199 112 145 198 111 144 197 112 144 201 112 144 205 113 142 210 113 142 212 21 16 38 16 12 35 22 20 44 16 16 40 14 12 33 10 8 21 25 19 21 118 107 103 183 169 160 207 185 174 188 153 147 176 125 121 196 120 120 146 48 49 141 19 18 142 17 15 126 16 15 129 19 20 143 16 25 148 14 25 144 10 21 132 7 15 115 6 11 107 4 7 118 13 18 126 12 20 131 5 19 137 11 25 135 24 33 120 29 34 119 58 53 80 38 24 39 7 0 37 11 0 27 4 0 21 2 0 25 10 3 26 16 15 18 13 17 14 13 19 13 13 23 13 13 23 13 12 20 15 13 18 22 16 18 17 9 7 212 201 197 244 224 213 72 34 11 137 83 55 199 134 106 212 137 108 194 113 83 178 98 63 162 86 50 169 99 63 161 92 59 147 80 53 133 61 39 136 65 47 123 54 39 111 49 34 102 49 31 121 69 48 133 68 48 131 60 38 138 66 42 156 82 57 169 93 67 159 84 53 153 77 45 174 100 65 179 109 75 189 124 92 192 138 110 223 182 162 255 232 221 239 225 222 84 83 88 9 12 21 10 10 18 11 11 19 13 12 18 13 12 17 12 11 16 12 11 16 13 12 17 12 13 18 13 16 25 3 9 21 9 16 34 18 29 51 8 19 49 1 15 50 46 61 100 106 125 167 128 150 197 121 148 195 120 146 195 124 152 202 124 156 207 121 154 207 116 149 203 112 147 203 114 151 206 112 148 206 110 149 206 113 152 209 113 152 209 113 150 205 114 151 206 117 154 209 112 147 201 113 148 202 114 150 208 115 151 211 115 150 214 114 149 213 112 148 210 111 147 207 114 149 205 113 149 201 111 147 199 110 145 199 112 144 201 113 145 206 113 142 208 112 141 209 26 18 42 18 13 36 17 19 42 12 17 37 6 9 24 13 12 20 39 29 28 178 161 153 191 172 158 195 172 158 185 155 144 169 129 121 213 152 147 178 94 92 147 39 37 144 25 27 124 14 17 127 16 22 137 16 25 142 16 28 137 15 26 127 13 23 110 13 20 102 11 18 95 5 14 112 19 30 124 23 37 111 15 29 82 1 10 57 0 0 43 8 6 30 9 4 27 9 5 24 10 9 23 11 11 18 9 10 11 6 10 10 8 13 14 13 19 15 15 23 15 15 23 13 12 20 15 10 17 18 11 18 24 13 17 20 5 8 204 188 189 254 229 224 91 54 36 113 61 39 205 138 112 211 134 106 190 110 77 190 112 74 173 102 60 171 104 62 165 97 62 154 85 56 137 66 44 141 70 52 127 60 43 121 56 38 120 57 39 147 81 59 153 78 55 151 73 50 149 74 51 161 87 62 173 98 69 172 96 64 172 94 58 182 108 71 183 115 80 183 124 94 199 154 133 255 226 213 237 218 212 235 225 224 60 55 59 14 13 19 10 10 18 9 12 19 11 12 17 11 12 17 11 10 15 11 10 15 12 11 16 13 12 18 17 16 24 19 19 29 17 19 34 13 16 35 11 16 38 13 19 45 15 22 51 12 23 55 32 48 84 84 103 143 126 145 187 123 146 188 115 139 185 121 149 196 125 156 203 118 150 201 124 157 210 118 153 207 116 153 208 118 155 210 114 153 208 108 147 204 107 146 203 110 149 206 113 148 204 113 148 204 113 149 209 114 150 212 114 149 213 113 148 214 112 147 211 111 147 207 114 149 205 112 147 201 111 146 200 110 145 199 112 144 201 112 144 205 113 142 208 113 142 208 29 19 46 17 13 38 10 15 37 8 17 34 10 16 28 23 21 26 41 26 21 183 161 148 202 176 159 203 178 158 186 163 147 173 144 130 213 166 158 207 142 138 171 81 80 127 18 21 134 12 23 139 13 27 141 16 32 135 19 32 120 18 29 99 17 23 75 13 14 61 11 12 60 16 17 55 13 15 50 5 10 44 1 8 36 4 9 32 12 13 14 10 7 6 8 7 9 8 16 10 8 21 15 13 26 16 16 26 12 15 24 15 18 25 15 18 25 8 11 16 12 13 17 11 10 15 15 10 14 22 13 18 26 13 20 22 7 12 181 164 172 252 227 230 211 177 167 84 35 18 177 109 90 214 134 109 199 119 84 180 105 63 174 108 60 177 113 69 173 105 68 164 95 64 148 78 53 144 76 55 135 68 49 134 66 45 144 69 50 168 87 66 170 86 62 164 80 54 161 81 58 169 93 67 175 98 68 169 93 59 175 97 59 184 110 73 192 128 93 163 112 85 230 197 182 252 232 225 239 229 230 228 222 226 25 16 19 21 16 20 11 12 17 9 14 20 10 13 18 11 12 17 11 10 16 11 10 16 13 11 16 16 11 17 19 14 21 13 10 19 15 12 23 22 20 33 26 23 40 20 19 37 16 14 35 14 16 39 16 23 51 0 9 39 16 29 61 79 94 127 128 146 182 135 157 196 127 150 192 123 150 195 119 147 195 116 148 197 118 151 202 120 156 208 118 155 208 113 150 205 110 149 206 113 152 209 114 149 205 114 149 205 114 148 209 113 147 210 112 147 211 112 147 213 112 147 211 112 148 210 111 147 205 110 147 202 109 144 200 109 144 200 112 144 203 112 144 205 113 143 207 113 143 207 23 14 43 19 15 40 12 18 40 8 17 34 10 17 27 23 19 20 43 26 18 184 158 143 191 164 143 209 184 162 185 166 149 186 164 150 208 175 166 224 175 170 221 150 148 136 47 51 124 22 33 122 17 31 110 12 25 92 9 17 73 7 11 54 6 6 35 5 3 23 5 1 19 8 6 17 7 6 20 8 10 18 7 11 15 9 13 15 15 17 6 14 16 5 16 20 11 15 27 11 14 29 12 14 27 7 11 22 5 9 18 10 15 19 13 17 20 6 10 11 10 11 13 13 11 12 16 12 13 21 15 17 22 13 18 16 5 13 136 123 132 230 211 217 255 232 228 157 113 102 135 69 53 197 121 97 194 116 80 182 108 63 182 116 66 169 105 57 179 110 71 169 98 66 155 85 60 144 76 53 143 77 55 144 73 51 159 79 56 177 91 68 170 80 54 164 74 48 164 80 54 181 101 74 177 101 69 160 84 48 171 96 57 186 118 81 161 104 74 193 151 129 249 225 215 236 224 224 242 235 242 191 186 192 16 5 9 23 14 17 12 13 17 9 14 18 10 13 18 9 12 17 11 10 16 11 10 16 15 10 16 16 11 17 18 11 19 21 14 22 24 18 28 24 18 28 21 15 27 20 14 26 23 17 31 25 23 37 17 18 38 18 23 45 16 22 46 10 17 43 16 27 55 47 62 93 92 108 142 122 142 179 129 152 194 127 154 197 127 155 202 122 153 200 117 149 198 114 147 198 115 151 203 118 153 207 117 150 204 117 149 206 116 148 209 113 147 210 112 146 210 111 146 210 111 146 210 111 147 209 110 146 206 109 145 203 109 144 202 108 143 201 111 143 202 112 144 205 112 142 204 112 142 206 17 12 42 18 18 44 14 20 44 8 15 33 10 12 24 16 10 10 50 32 22 192 166 149 197 171 148 207 185 162 189 173 157 187 171 158 202 179 173 227 192 190 241 190 189 141 82 84 68 16 18 58 14 13 46 8 7 34 6 3 25 7 3 19 12 6 14 15 9 12 17 11 15 20 16 9 13 12 12 12 14 10 8 13 7 4 11 7 6 12 13 13 21 11 14 21 3 3 11 8 11 18 16 17 22 16 17 21 15 16 18 14 16 15 14 16 13 14 16 13 13 13 11 14 14 12 13 11 12 17 15 18 12 10 15 11 8 15 97 94 103 214 204 212 252 232 233 238 201 195 112 56 43 171 103 80 200 126 91 178 104 59 173 101 53 182 111 65 183 111 73 168 95 63 155 85 59 141 74 48 151 83 60 146 74 49 164 83 56 179 92 65 172 81 54 170 79 52 171 85 58 189 108 78 182 103 70 162 88 51 170 100 64 176 116 82 159 113 87 242 211 193 243 228 223 234 228 232 232 229 236 123 118 125 24 13 17 20 11 14 12 13 17 9 14 17 10 14 17 9 13 16 11 10 16 11 10 16 15 10 17 16 11 18 17 10 18 18 11 19 22 15 23 27 20 28 27 20 28 24 17 25 23 16 24 24 18 28 18 16 29 20 19 35 19 20 38 16 19 38 12 17 39 11 19 42 14 24 51 15 29 58 34 50 84 59 79 114 93 115 154 116 139 181 127 151 195 128 155 200 124 152 199 118 146 194 120 150 202 118 149 204 118 149 206 116 148 207 113 147 208 112 146 209 110 146 208 109 145 207 111 147 209 110 146 206 109 143 204 108 142 203 111 143 204 111 143 204 111 141 203 111 141 203 16 16 44 15 16 44 11 15 42 10 12 33 17 15 28 18 9 10 51 33 21 181 158 140 204 182 159 195 174 153 198 185 169 189 176 167 206 191 186 219 195 195 192 158 159 75 41 42 30 9 8 23 12 8 18 13 7 15 16 10 12 19 12 9 19 11 6 17 11 3 14 10 10 19 18 5 11 11 11 15 18 14 17 22 17 18 23 11 12 17 20 19 27 9 8 14 16 16 18 15 15 15 11 11 9 11 11 9 16 15 11 13 12 8 11 10 6 15 14 10 14 14 12 14 14 12 11 11 11 13 14 16 11 12 16 19 20 25 87 90 97 220 218 223 243 228 231 255 237 234 153 109 98 113 56 36 187 118 85 198 125 84 183 106 62 179 102 58 189 113 77 166 93 60 155 86 57 141 74 48 159 89 64 148 74 47 165 86 56 179 94 65 176 86 60 178 88 62 175 88 60 183 100 68 174 96 60 157 84 49 154 91 58 138 89 59 211 176 156 248 229 215 241 231 230 233 231 236 235 232 239 70 65 72 19 8 14 20 11 16 13 13 15 10 14 17 9 13 16 10 11 15 10 9 15 10 9 15 12 9 16 15 10 17 17 12 19 24 18 28 25 19 29 21 15 25 22 17 24 28 23 30 27 22 29 20 15 22 25 22 31 23 20 31 19 17 30 18 17 31 20 22 37 22 25 44 18 23 43 12 18 42 13 23 50 16 27 55 13 26 58 10 25 58 26 42 78 65 83 119 108 128 165 132 153 196 124 148 196 122 149 202 122 149 204 120 149 205 118 149 206 115 147 206 111 145 206 109 143 204 112 148 210 111 147 209 110 144 207 109 143 204 111 143 204 110 142 203 111 141 203 110 140 202 18 19 49 13 14 42 16 16 44 14 12 34 19 13 27 19 8 12 49 32 22 173 156 138 198 182 159 198 183 162 196 184 172 187 176 170 173 161 161 142 127 130 44 25 29 29 13 14 21 11 12 17 13 12 14 14 12 13 15 14 11 15 14 12 14 13 13 13 13 14 12 15 17 12 16 15 13 16 14 14 16 11 15 18 10 15 18 7 15 17 9 14 17 10 14 13 16 17 12 15 15 7 14 14 6 14 13 8 16 12 9 16 12 9 15 11 10 13 9 10 19 17 20 13 13 15 14 13 18 10 11 15 14 15 20 9 12 17 74 79 83 222 223 227 229 220 225 255 243 241 244 216 205 98 55 36 152 93 63 189 116 81 198 116 78 181 97 60 185 107 71 182 107 75 155 84 56 148 78 52 148 76 51 127 53 24 169 94 62 162 86 54 164 82 58 159 75 49 176 91 60 185 102 68 164 86 50 150 84 50 116 62 34 179 143 121 244 222 209 252 241 235 238 232 232 232 230 233 228 223 229 27 20 28 21 11 20 15 8 15 11 11 13 11 12 14 12 13 15 12 12 14 11 10 15 11 10 16 12 11 17 15 12 21 14 11 20 15 12 23 18 15 26 21 18 29 22 20 31 23 21 32 23 22 30 23 22 30 24 21 30 26 20 30 25 22 31 24 21 32 22 20 33 21 19 33 21 20 36 19 20 38 20 23 42 19 24 44 21 26 48 18 24 48 12 17 46 8 15 43 17 26 55 28 41 75 86 105 147 127 149 196 125 149 197 119 145 194 119 146 199 121 151 205 111 142 197 116 148 207 111 145 206 111 145 208 111 145 208 111 145 208 111 142 206 111 143 204 112 142 204 112 142 202 16 17 45 16 17 45 18 15 44 20 15 38 24 13 30 18 5 12 38 23 16 173 160 143 187 174 155 205 192 176 172 159 151 38 26 26 26 15 21 26 16 24 7 0 3 25 19 23 18 12 16 17 12 18 16 14 19 15 15 17 13 14 18 14 14 16 17 12 16 17 11 15 18 12 16 17 12 16 14 14 16 13 15 14 11 15 14 10 14 13 12 14 13 13 13 11 14 13 8 13 13 5 13 12 7 13 12 8 17 13 14 17 12 16 18 11 18 16 11 17 14 9 16 12 9 16 16 15 23 17 16 22 15 16 21 14 15 19 53 57 60 229 230 232 229 224 228 246 236 235 255 243 234 232 205 186 129 79 54 175 108 79 188 107 77 195 111 77 186 107 74 188 115 82 159 88 60 143 72 44 155 81 54 124 50 21 138 67 35 143 72 40 141 67 42 153 75 52 158 77 47 188 109 76 161 88 55 114 55 25 152 110 88 248 222 209 250 237 231 243 237 237 249 245 246 234 229 233 152 146 150 20 13 20 14 6 17 16 11 18 11 11 13 12 12 12 13 13 15 12 12 14 11 10 15 11 10 15 13 12 20 14 13 21 16 14 25 16 16 26 17 17 29 19 19 31 20 19 33 19 21 34 19 21 33 21 21 31 24 21 30 27 22 29 27 22 29 27 21 31 24 21 30 24 21 32 24 21 32 25 21 35 27 25 38 23 21 35 22 19 36 24 23 41 24 22 43 19 20 40 19 20 41 19 23 50 11 24 59 20 35 76 88 105 148 130 151 194 122 146 192 118 144 193 118 148 200 111 142 197 113 145 204 111 145 208 111 145 209 110 144 208 111 142 207 110 141 205 111 141 203 112 142 202 15 15 39 20 18 42 16 10 36 23 13 38 27 14 32 21 8 17 31 18 12 177 165 153 206 194 180 184 171 162 38 27 25 29 18 22 17 7 16 18 11 19 20 18 23 17 12 18 23 10 20 25 9 20 22 12 21 19 14 20 16 13 20 15 14 19 15 15 17 14 14 16 13 13 13 14 14 14 14 14 14 14 14 12 17 13 12 19 10 11 21 9 9 19 9 8 13 11 12 11 13 12 12 12 14 12 11 16 15 12 21 15 12 23 17 11 25 17 11 25 18 12 26 18 12 26 19 13 25 21 15 25 14 9 16 20 15 21 35 33 34 226 222 223 239 234 240 242 236 238 255 255 246 254 238 222 249 211 192 148 90 68 191 115 91 201 120 93 192 117 86 185 114 82 164 95 64 150 81 50 156 82 53 123 49 20 135 69 37 148 83 53 135 67 44 128 57 35 173 99 72 170 99 69 137 76 47 137 89 66 251 222 208 248 233 226 255 250 252 240 239 244 245 243 246 231 227 228 67 58 61 15 6 11 13 7 17 15 12 21 14 12 15 13 13 13 13 13 13 13 13 15 12 11 16 12 11 16 13 13 21 14 14 22 16 16 26 15 18 27 15 17 29 15 19 31 16 19 34 17 20 35 16 22 36 18 22 34 22 20 31 24 21 30 25 22 31 25 22 31 26 20 30 26 20 30 26 20 30 27 21 31 32 24 35 26 18 31 25 17 30 28 22 36 30 24 38 25 21 35 21 17 32 17 15 36 13 17 46 12 21 54 20 31 63 50 64 99 117 135 173 127 150 192 119 146 193 118 145 198 113 144 201 113 145 206 111 145 209 110 144 208 108 142 206 107 141 204 110 142 203 110 142 201 20 17 36 23 18 38 15 7 30 20 11 32 23 12 28 23 12 20 25 14 12 173 163 154 193 183 173 87 76 70 20 10 11 30 20 28 12 4 15 15 12 21 13 12 17 13 11 16 23 11 21 24 11 21 23 11 21 20 13 21 17 14 21 16 15 20 14 15 17 12 16 17 10 14 13 10 14 13 14 14 12 15 14 12 17 11 11 20 10 9 23 9 9 20 8 10 14 12 17 10 13 18 12 12 20 12 12 22 13 11 24 13 11 25 14 10 25 16 9 25 19 12 28 20 11 28 20 12 25 20 12 23 14 7 15 20 14 18 24 18 20 171 165 167 229 222 229 252 247 251 246 247 241 255 255 243 255 249 234 232 186 170 147 82 64 200 125 102 215 144 116 191 122 91 174 108 76 164 98 66 148 77 47 118 49 18 149 84 54 141 77 50 130 68 45 136 74 53 177 113 88 160 103 76 121 73 51 253 217 201 255 237 230 255 249 250 253 252 255 247 248 253 238 236 239 202 198 199 25 16 19 14 8 12 14 11 18 13 12 18 15 13 18 15 13 16 14 14 16 13 13 15 13 13 15 14 13 18 14 15 20 15 16 21 12 15 22 13 16 25 14 18 29 15 19 31 17 20 35 15 21 37 16 22 38 17 20 35 20 20 32 23 20 31 23 20 31 23 20 31 25 19 29 25 19 29 27 19 30 27 20 28 32 22 31 30 20 29 30 20 31 28 20 31 27 19 30 25 17 28 26 18 31 27 23 38 27 25 46 16 18 43 13 17 42 13 23 50 8 22 51 33 51 87 105 128 170 121 147 196 114 143 199 113 143 205 112 143 208 109 142 209 108 142 206 107 141 204 109 141 202 109 141 200 29 18 32 24 16 31 19 12 28 20 13 29 16 10 24 19 12 20 23 11 11 168 155 149 182 169 163 23 12 10 36 27 30 8 3 10 10 7 16 25 24 30 15 15 17 16 16 18 16 15 21 17 14 21 20 15 22 20 15 21 19 14 20 17 15 20 15 16 18 14 15 17 10 14 13 10 14 13 13 13 13 13 13 11 16 12 11 16 12 11 16 10 10 15 11 12 12 13 18 10 13 22 10 13 22 10 12 24 12 12 24 12 12 24 13 11 24 13 11 24 11 7 21 15 9 21 19 13 25 21 13 24 21 14 22 21 11 19 20 11 16 85 76 81 225 216 221 231 225 229 255 255 253 254 253 248 255 249 241 255 247 236 203 155 141 151 93 73 201 137 110 199 134 104 193 129 94 181 117 82 158 93 61 136 71 41 172 106 80 140 76 51 130 69 48 179 122 102 161 110 89 116 73 56 252 220 207 255 249 243 255 245 245 255 253 255 249 250 255 254 255 255 244 243 248 130 125 129 20 14 18 19 13 17 16 14 19 16 13 20 16 15 20 15 14 19 14 13 18 14 14 16 13 14 16 14 15 17 15 16 20 15 16 20 12 15 20 14 17 22 16 19 28 18 20 32 19 21 34 18 21 36 17 20 37 16 19 34 20 19 33 21 19 32 22 20 33 22 20 33 23 20 31 23 20 29 25 19 29 26 20 30 27 19 30 28 20 31 28 20 31 26 20 30 24 18 30 23 17 29 26 20 32 30 24 36 28 22 36 32 28 43 18 17 33 15 18 37 11 19 42 8 22 51 43 61 99 127 149 196 117 142 198 114 143 203 112 143 208 111 142 209 108 142 206 106 140 203 106 140 201 107 142 200 53 36 46 25 12 22 21 15 27 21 19 32 12 10 21 17 12 19 26 14 16 163 148 145 104 89 86 22 10 10 10 4 8 25 24 30 16 16 24 9 10 15 17 15 16 21 19 20 13 17 18 13 17 20 16 15 20 19 14 20 20 13 20 19 14 20 16 14 17 14 15 17 12 14 13 12 14 13 13 13 13 14 12 13 14 12 13 13 13 13 12 12 12 11 12 14 10 11 16 11 11 19 12 12 20 12 12 20 12 13 18 12 13 18 13 14 19 14 15 19 14 13 19 15 14 20 17 14 21 16 13 20 22 17 24 19 12 20 24 17 25 31 21 30 194 183 189 229 220 225 242 240 243 251 251 251 255 255 251 255 251 243 255 247 236 183 137 121 166 110 85 202 141 110 215 155 119 198 138 102 183 123 89 166 102 74 191 123 102 167 99 80 172 112 88 171 121 98 135 93 77 253 223 213 255 250 246 253 243 244 255 253 255 254 253 255 251 252 255 253 254 255 236 235 240 49 47 50 23 18 24 21 16 20 18 13 17 18 16 21 17 16 22 15 16 21 14 13 19 13 14 18 14 15 17 15 16 18 16 17 19 16 17 19 17 21 22 18 22 25 19 22 27 22 22 32 22 22 34 21 20 34 18 20 35 17 19 34 21 20 36 22 21 35 23 21 35 23 21 35 23 19 33 23 20 31 24 21 32 27 21 31 26 20 32 24 18 30 24 18 30 23 20 31 24 20 34 23 19 33 20 18 31 20 17 28 22 12 21 21 11 20 34 28 38 22 22 34 11 17 33 8 19 41 2 18 51 102 123 166 119 142 196 115 144 204 114 143 209 111 142 209 108 141 208 106 140 203 106 140 201 107 142 200 120 95 101 42 23 29 20 15 22 17 20 29 11 14 21 14 13 19 26 14 16 148 130 130 57 37 38 28 14 14 19 14 18 6 9 14 14 19 23 17 18 22 23 17 17 13 8 5 11 17 15 11 17 17 16 14 17 19 13 17 22 11 17 22 11 17 17 12 16 15 13 16 13 13 15 13 13 15 14 12 15 14 12 15 14 12 15 12 12 14 9 13 14 9 13 14 13 11 14 16 12 13 17 13 14 14 13 11 13 14 9 11 14 7 12 15 8 11 16 9 15 20 14 14 19 15 12 16 15 10 11 15 15 16 21 14 13 21 24 22 33 21 15 25 130 117 126 234 223 229 243 238 244 254 255 255 247 253 253 254 255 250 255 248 239 255 241 226 181 131 108 177 118 88 200 142 105 194 136 98 190 134 99 180 119 91 191 118 101 183 110 93 178 118 94 184 138 114 253 221 208 255 245 241 251 245 249 255 254 255 255 254 255 250 249 255 255 254 255 250 248 253 170 169 174 15 14 19 20 19 25 18 16 21 21 17 18 21 16 20 19 18 26 14 17 26 14 14 22 13 14 19 15 16 20 16 17 19 17 19 18 17 19 18 21 23 22 21 23 22 20 21 23 21 20 26 21 20 28 21 19 30 21 19 32 20 18 32 21 20 36 22 21 37 22 21 37 22 21 35 22 20 34 22 20 33 22 20 33 24 21 32 22 18 32 20 18 31 20 18 31 20 18 32 21 20 36 20 19 35 15 17 32 15 13 26 35 24 30 26 11 14 35 26 29 17 15 20 11 13 25 16 24 43 6 21 50 52 71 111 120 143 193 117 143 202 114 143 209 112 143 210 108 141 208 106 141 205 105 141 203 106 142 202 190 162 161 66 44 46 23 12 16 18 16 21 15 14 19 14 12 17 20 8 12 123 107 110 27 8 10 16 4 6 20 18 21 16 20 23 12 16 19 14 15 17 23 14 15 22 17 14 12 16 15 11 17 15 16 14 15 19 13 15 22 11 17 20 11 16 17 12 16 15 13 16 13 13 15 13 13 15 14 12 17 14 12 17 14 12 17 12 11 16 9 13 16 9 13 14 17 13 14 19 13 13 18 14 11 17 13 10 13 12 7 11 12 6 10 13 4 9 15 5 6 11 4 10 15 9 10 14 13 10 14 15 13 16 21 13 13 21 16 16 26 24 21 32 88 77 85 238 225 232 233 226 234 255 254 255 251 255 255 247 251 250 255 254 248 255 250 240 255 230 214 174 129 108 194 150 123 219 175 148 236 194 169 244 197 177 245 187 176 237 179 168 223 176 158 182 147 128 255 232 224 255 252 251 255 253 255 249 252 255 251 251 255 255 254 255 255 254 255 248 246 251 91 90 95 19 20 25 18 19 24 15 14 19 26 22 23 20 15 19 19 18 26 14 17 26 14 14 22 14 15 20 15 16 20 17 18 20 17 18 20 17 19 18 20 21 23 19 20 22 18 17 22 18 17 23 19 16 25 20 17 28 22 19 30 22 20 33 22 19 36 21 20 36 22 21 37 21 20 34 21 19 33 20 18 32 21 19 32 21 19 32 19 17 30 21 19 32 20 19 33 19 18 32 16 15 31 15 17 32 16 18 33 21 19 32 24 15 20 29 19 20 24 15 18 21 16 22 30 29 37 7 13 25 11 22 44 15 31 65 122 142 192 118 142 202 114 143 209 110 143 210 108 142 206 106 142 204 105 141 201 106 142 202 208 179 171 160 135 128 35 14 11 29 13 13 25 10 13 22 10 14 29 17 21 61 49 53 22 11 15 19 13 15 18 16 19 17 17 19 17 17 19 18 16 19 18 13 17 18 13 17 17 17 19 15 16 18 15 15 17 16 14 17 17 12 16 15 13 16 15 13 16 14 14 16 12 12 14 12 12 14 13 13 15 13 13 15 13 13 15 13 13 15 11 12 14 10 11 13 14 12 15 15 13 14 16 14 15 16 14 15 14 14 14 13 13 13 12 12 12 11 13 12 11 13 12 12 13 15 12 13 15 13 14 18 13 14 19 16 15 21 18 17 25 21 18 25 44 34 42 221 210 218 239 229 237 248 243 249 252 250 255 252 252 254 255 254 255 255 251 252 255 247 246 255 240 236 251 234 227 242 225 218 246 227 223 236 217 213 203 177 178 177 153 153 65 44 41 35 20 15 68 58 57 228 222 224 253 251 255 255 254 255 255 254 255 252 251 255 247 246 251 214 213 218 24 23 28 23 22 27 24 23 28 21 19 22 15 13 14 22 20 23 18 17 22 17 18 23 19 18 24 19 18 24 19 18 24 19 18 24 20 19 25 20 19 25 22 21 29 22 21 29 22 21 29 21 20 28 21 19 30 20 18 29 20 18 29 20 18 29 21 19 32 21 19 33 20 18 32 21 19 32 22 20 33 22 20 33 20 18 31 18 16 29 19 17 30 18 16 29 18 16 29 18 16 29 19 17 31 18 17 31 18 17 31 18 16 27 16 15 21 19 17 22 23 17 27 21 18 27 19 17 22 15 16 21 8 14 26 4 14 41 111 128 174 116 138 198 112 140 205 113 147 211 106 141 199 110 147 202 104 140 198 109 145 203 206 176 165 188 160 148 91 64 57 29 5 1 30 10 9 25 9 10 27 15 19 36 27 32 18 12 16 18 13 17 17 15 16 18 16 17 18 16 19 17 15 18 18 13 20 15 13 18 16 15 20 15 15 17 15 15 17 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 13 13 15 13 13 15 13 13 15 13 13 15 13 13 15 13 13 15 12 12 14 12 12 14 12 12 14 13 13 15 14 14 16 15 15 17 14 13 18 13 12 17 13 12 17 13 12 17 14 13 18 14 13 18 15 14 19 14 13 18 14 13 19 14 13 19 16 15 21 18 15 22 30 23 30 177 167 175 244 234 242 247 240 247 248 243 249 255 253 255 250 248 253 255 253 255 250 248 253 137 135 140 82 77 83 76 71 77 55 48 55 39 32 39 36 26 34 30 21 26 28 22 26 29 23 25 19 14 18 42 37 41 206 204 209 250 248 253 255 254 255 252 251 255 255 254 255 117 116 121 23 22 27 16 15 20 19 17 20 21 19 22 19 17 18 22 20 21 20 19 24 20 19 24 20 19 24 20 19 24 20 19 27 21 20 28 21 19 30 21 19 30 21 19 30 21 19 30 21 19 30 22 20 31 22 20 31 22 20 31 22 21 29 22 21 29 22 20 33 21 19 32 21 19 32 22 20 33 22 20 33 22 20 33 20 18 31 19 17 30 23 21 34 22 20 33 21 19 32 20 18 31 19 17 30 18 16 29 17 15 28 15 15 25 15 15 23 16 16 24 19 17 30 20 17 28 21 16 22 17 15 18 12 15 22 8 14 36 70 83 128 115 135 194 119 147 210 107 139 200 110 145 199 108 145 197 105 142 197 112 148 206 201 169 156 200 171 157 153 126 117 66 41 36 29 8 7 32 13 15 32 20 24 22 13 16 18 12 16 17 12 16 16 14 15 17 15 16 17 15 18 16 14 17 15 12 19 15 13 18 15 14 19 15 15 17 14 14 16 14 14 16 14 14 16 13 13 15 13 13 15 13 13 15 15 15 17 15 15 17 14 14 16 13 13 15 13 13 15 13 13 15 12 12 14 12 12 14 11 11 13 12 12 14 14 14 16 14 14 16 14 13 18 13 12 17 14 13 18 14 13 18 13 12 17 14 13 18 15 14 19 14 13 18 14 13 19 14 13 19 14 13 19 16 13 20 27 20 27 123 116 123 230 223 230 248 241 248 255 253 255 250 245 251 255 254 255 250 248 253 112 110 115 24 22 27 8 6 11 30 28 33 21 16 22 20 15 21 29 22 29 22 16 20 20 14 18 24 18 20 35 30 34 22 17 21 44 42 47 217 215 220 254 253 255 245 244 249 226 225 230 40 39 44 25 24 29 19 18 23 19 17 20 24 22 25 22 20 21 22 20 21 21 20 25 21 20 25 21 20 25 21 20 25 21 20 28 21 20 28 21 19 30 21 19 30 22 20 31 22 20 31 23 21 32 23 21 32 23 21 32 23 21 32 23 22 30 22 21 29 24 22 35 23 21 34 23 21 34 24 22 35 24 22 35 24 22 35 23 21 34 22 20 33 25 23 36 24 22 35 22 20 33 20 18 31 19 17 30 17 15 28 15 13 26 13 13 25 14 14 24 16 16 26 18 16 29 19 16 27 20 15 21 17 15 18 13 16 23 9 16 35 29 42 84 127 146 202 121 147 208 109 139 199 111 146 200 105 142 195 110 146 204 109 145 205 201 168 151 193 161 146 195 165 154 139 112 105 36 12 10 35 17 17 33 18 21 22 13 16 18 12 16 17 12 16 16 14 15 17 15 16 17 15 18 16 14 19 15 12 19 15 13 18 15 14 19 15 15 17 15 15 17 15 15 17 14 14 16 14 14 16 14 14 16 13 13 15 16 16 18 16 16 18 15 15 17 13 13 15 13 13 15 13 13 15 13 13 15 13 13 15 12 12 14 13 13 15 14 14 16 14 14 16 13 12 17 13 12 17 13 12 17 14 13 18 11 10 15 12 11 16 14 13 18 14 13 18 14 13 19 14 13 19 15 14 20 16 13 20 21 16 22 87 80 87 229 222 229 254 249 255 255 252 255 251 249 254 249 247 252 105 103 108 20 18 23 16 14 19 27 25 30 28 26 31 26 21 27 30 25 31 25 18 25 19 13 17 31 25 29 18 12 14 14 9 13 15 10 14 31 29 34 49 47 52 228 227 232 250 249 254 137 136 141 25 24 29 22 21 26 25 24 29 22 20 23 23 21 24 22 20 21 21 19 22 21 20 25 21 20 25 21 20 25 21 20 25 21 20 28 20 19 27 20 18 29 20 18 29 25 23 34 25 23 34 25 23 34 24 22 33 23 21 32 22 20 31 21 20 28 21 20 28 23 21 34 23 21 34 23 21 34 24 22 35 24 22 35 24 22 35 23 21 34 22 20 33 22 20 33 21 19 32 20 18 31 19 17 30 18 16 29 17 15 28 16 14 27 14 14 26 15 15 27 16 16 28 17 15 29 18 15 26 19 14 20 16 14 17 13 16 21 9 16 34 10 22 60 122 140 190 112 137 193 115 146 203 110 143 197 105 142 197 115 151 211 104 140 202 208 173 153 189 156 137 208 176 163 180 152 141 63 38 34 35 15 14 24 10 10 24 14 15 19 13 15 18 14 15 16 14 15 17 15 16 17 15 18 16 14 19 16 13 20 16 14 19 15 14 19 15 15 17 15 15 17 15 15 17 15 15 17 15 15 17 14 14 16 14 14 16 17 17 19 16 16 18 15 15 17 14 14 16 13 13 15 13 13 15 13 13 15 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 13 12 17 12 11 16 12 11 16 13 12 17 11 10 15 12 11 16 13 12 17 14 13 18 14 13 19 14 13 19 15 14 20 16 13 20 23 18 25 39 34 41 204 199 206 239 236 243 249 247 252 255 254 255 100 98 103 14 13 18 17 16 21 24 23 28 22 20 25 12 10 15 18 16 19 23 21 24 20 15 19 31 26 30 24 18 22 24 18 22 27 22 26 25 20 24 20 18 23 16 14 19 84 83 89 230 229 235 49 48 54 22 21 27 19 18 23 22 21 26 21 19 22 19 17 20 23 21 24 19 17 20 22 21 26 22 21 26 22 21 26 21 20 25 21 20 28 21 20 28 21 19 30 21 19 30 26 24 35 26 24 35 25 23 34 25 23 34 24 22 33 22 20 31 21 20 28 20 19 27 20 18 31 21 19 32 21 19 32 21 19 32 21 19 32 21 19 32 21 19 32 21 19 32 21 19 32 20 18 31 19 17 30 18 16 29 19 17 30 18 16 29 17 15 28 15 15 27 16 16 28 16 15 29 17 15 29 17 14 25 18 13 19 15 13 16 13 14 19 9 15 29 3 14 44 74 91 134 117 140 190 116 146 200 109 140 194 110 145 201 113 149 211 108 143 207 209 171 148 195 158 139 200 167 150 173 143 132 106 78 74 38 17 16 20 4 5 23 13 14 19 13 15 18 14 15 16 14 15 16 14 17 16 14 19 16 14 19 16 13 20 16 14 19 14 13 18 15 15 17 15 15 17 16 16 18 16 16 18 15 15 17 15 15 17 14 14 16 16 16 18 15 15 17 15 15 17 14 14 16 13 13 15 13 13 15 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 13 13 15 12 11 16 11 10 15 12 11 16 12 11 16 12 11 16 13 12 17 14 13 18 14 13 18 13 12 18 13 12 18 14 13 19 14 13 19 16 13 20 29 26 33 127 124 131 242 239 246 255 254 255 98 96 101 21 20 25 23 22 27 20 19 24 12 11 16 16 14 19 16 14 19 16 14 17 19 17 20 19 14 18 23 18 22 20 14 18 21 15 19 18 13 17 20 15 19 11 9 14 34 32 37 14 13 19 68 67 73 24 23 29 14 13 19 28 27 32 20 19 24 22 20 23 20 18 21 25 23 26 20 18 21 23 22 27 23 22 27 22 21 26 22 21 26 22 21 29 22 21 29 22 20 31 22 20 31 24 22 33 24 22 33 24 22 33 25 23 34 24 22 33 23 21 32 21 20 28 21 20 28 20 18 31 21 19 32 22 20 33 22 20 33 21 19 32 21 19 32 22 20 33 22 20 33 22 20 33 21 19 32 20 18 31 20 18 31 19 17 30 19 17 30 18 16 29 17 15 28 16 15 29 16 15 29 17 15 29 17 14 25 18 13 19 15 13 16 13 14 18 10 14 26 6 16 41 22 36 73 127 148 193 117 143 194 114 145 199 115 147 206 106 140 204 116 151 217 207 167 142 197 159 136 194 159 140 170 138 125 135 108 101 38 17 14 27 11 11 20 8 10 19 13 15 18 14 15 16 14 17 15 15 17 15 14 19 15 14 19 16 13 20 16 14 19 14 13 18 14 14 16 15 15 17 15 15 17 16 16 18 15 15 17 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 13 13 15 13 13 15 13 13 15 12 12 14 13 13 15 13 13 15 13 13 15 12 11 16 12 11 16 12 11 16 13 12 17 13 12 17 13 12 17 14 13 18 13 12 17 13 12 18 13 12 18 15 14 20 16 15 21 12 11 17 29 28 34 68 67 73 239 238 244 118 117 123 19 18 24 12 11 16 12 11 16 16 15 20 11 10 15 21 21 23 19 19 21 11 9 12 17 15 18 19 17 20 15 13 16 22 17 21 24 19 23 26 21 25 20 15 19 31 29 34 10 8 13 21 20 26 25 24 30 28 27 33 16 15 21 32 31 36 23 22 27 20 20 22 22 22 24 23 21 24 21 19 22 22 21 26 22 21 26 22 21 26 22 21 26 22 21 29 22 21 29 22 20 31 22 20 31 23 21 32 24 22 33 24 22 33 24 22 33 23 21 32 22 20 31 20 19 27 19 18 26 21 19 32 22 20 33 23 21 34 22 20 33 21 19 32 21 19 32 23 21 34 24 22 35 22 20 33 21 19 32 20 18 31 19 17 30 20 18 31 19 17 30 18 16 29 17 15 28 14 13 27 15 14 28 17 15 29 16 14 25 16 14 19 15 13 16 13 14 18 10 14 23 10 17 36 2 16 45 102 121 161 120 144 190 117 147 199 113 145 204 107 140 207 116 151 219 213 169 144 198 158 133 195 157 138 186 153 138 143 114 106 37 13 9 35 17 17 15 3 3 18 12 14 15 13 14 15 13 16 14 14 16 14 13 18 14 13 18 15 12 19 15 13 18 13 12 17 13 13 15 14 14 16 15 15 17 15 15 17 15 15 17 14 14 16 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 14 14 16 14 14 16 13 13 15 12 12 14 11 11 13 12 12 14 13 13 15 12 12 14 12 11 16 12 11 16 13 12 17 14 13 18 11 10 15 12 11 16 12 11 16 13 12 17 13 12 18 15 14 20 17 16 22 19 18 24 22 21 27 11 10 16 33 32 38 106 105 111 16 15 21 20 19 25 19 18 23 11 10 15 15 14 19 13 12 17 17 17 19 14 14 16 13 11 14 15 13 16 14 12 15 21 19 22 20 15 19 20 15 19 25 20 24 21 19 22 13 11 16 31 29 34 21 20 26 24 23 29 19 18 24 23 22 28 21 20 25 22 21 26 15 14 19 23 23 25 18 16 19 22 20 23 20 19 24 20 19 24 21 20 25 21 20 25 21 20 28 21 20 28 22 20 31 22 20 31 25 23 34 25 23 34 25 23 34 24 22 33 23 21 32 20 18 29 18 17 25 17 16 24 18 16 27 19 17 30 20 18 31 20 18 31 18 16 29 18 16 29 20 18 31 21 19 32 19 17 30 18 16 29 18 16 29 18 16 29 19 17 30 19 17 30 19 17 30 18 16 29 13 12 26 14 13 27 16 14 27 16 15 23 16 14 19 15 13 16 14 13 18 11 14 21 5 11 25 7 18 40 57 73 106 123 146 188 116 144 194 111 141 201 116 149 216 112 146 217 216 170 144 197 155 130 201 160 140 182 146 130 147 119 108 48 24 20 25 7 7 24 12 12 17 11 13 14 12 13 13 11 14 13 13 15 13 12 17 15 14 19 17 14 21 18 16 21 14 13 18 13 13 15 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 15 15 17 15 15 17 14 14 16 13 13 15 13 13 15 14 14 16 14 14 16 13 13 15 12 12 14 13 13 15 13 13 15 12 12 14 12 12 14 12 11 16 12 11 16 13 12 17 13 12 17 15 14 19 15 14 19 14 13 18 13 12 17 13 12 18 14 13 19 15 14 20 17 16 22 16 16 24 16 16 24 27 28 33 14 15 20 15 16 21 24 25 30 11 10 15 17 16 21 24 23 28 19 18 23 22 20 23 18 16 19 25 23 26 13 11 14 23 21 22 41 39 40 33 28 32 38 33 37 45 43 48 22 20 25 21 18 25 19 18 24 20 19 25 24 23 29 20 21 26 19 20 25 20 19 25 21 20 25 23 22 27 23 22 27 23 21 24 22 20 23 22 21 26 22 21 26 22 21 26 22 21 26 22 21 29 23 22 30 23 21 32 24 22 33 24 22 33 23 21 32 22 20 31 23 21 32 24 22 33 24 22 33 23 22 30 22 21 29 20 18 29 20 18 29 19 17 28 17 15 26 17 15 26 18 16 27 21 19 30 23 21 32 22 20 31 20 18 29 19 17 28 18 16 27 18 16 27 19 17 28 19 17 28 18 16 29 14 13 27 14 13 27 15 13 24 15 14 22 16 14 19 16 14 19 16 15 20 15 16 21 13 17 28 7 16 33 22 36 63 126 146 183 120 146 195 113 143 203 116 149 216 109 146 216 217 172 143 202 158 131 200 159 139 182 146 130 150 120 110 43 20 14 23 5 5 21 11 12 17 11 13 14 12 13 13 13 15 14 14 16 14 13 18 14 13 18 15 12 19 15 13 18 14 13 18 14 14 16 14 14 16 13 13 15 14 14 16 14 14 16 15 15 17 15 15 17 14 14 16 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 13 13 15 12 12 14 13 13 15 13 13 15 13 13 15 12 12 14 12 11 16 13 12 17 13 12 17 13 12 17 14 13 18 14 13 18 14 13 18 13 12 17 12 11 17 12 11 17 14 13 19 16 15 21 17 17 25 16 16 24 24 25 30 16 17 22 10 11 16 12 13 18 9 8 13 19 18 23 13 12 17 16 15 20 15 13 16 129 127 130 171 169 172 200 198 201 216 214 215 206 204 205 214 209 213 204 199 203 185 183 188 158 156 161 29 28 34 17 16 22 20 19 25 17 16 22 18 19 24 18 19 24 19 18 24 20 19 25 22 21 26 23 22 27 23 21 24 22 20 23 21 20 25 20 19 24 19 18 23 18 17 22 18 17 25 18 17 25 20 18 29 20 18 29 21 19 30 21 19 30 21 19 30 23 21 32 25 23 34 25 23 34 24 23 31 22 21 29 20 18 29 20 18 29 19 17 28 17 15 26 17 15 26 18 16 27 21 19 30 23 21 32 23 21 32 21 19 30 19 17 28 18 16 27 18 16 27 18 16 27 17 15 26 17 15 28 15 14 28 15 15 27 15 13 24 15 14 20 15 12 19 15 12 19 14 13 19 14 13 19 10 13 20 6 12 24 12 23 45 107 123 157 119 141 191 116 145 205 114 147 214 112 149 219 215 170 141 204 160 133 196 155 135 181 145 129 150 122 111 35 15 8 22 6 6 20 10 11 17 11 13 15 13 16 14 14 16 14 15 17 14 13 18 13 12 17 13 10 17 12 10 15 14 13 18 14 14 16 14 14 16 14 14 16 14 14 16 15 15 17 15 15 17 15 15 17 14 14 16 13 13 15 12 12 14 12 12 14 13 13 15 14 14 16 14 14 16 13 13 15 14 14 16 14 14 16 13 13 15 13 13 15 13 12 17 13 12 17 14 13 18 14 13 18 14 13 18 14 13 18 14 13 18 13 12 17 12 11 17 12 11 17 14 13 19 15 14 20 12 12 20 15 18 25 21 21 29 17 17 25 16 15 21 24 23 29 26 24 29 17 15 20 26 24 29 22 20 25 105 103 106 205 203 206 241 239 240 253 251 252 255 254 255 255 254 255 255 250 254 255 250 254 242 240 245 223 221 226 91 90 96 26 25 31 16 17 22 14 15 20 16 17 22 16 17 22 17 18 23 18 19 24 21 20 25 22 21 26 22 22 24 21 21 23 21 20 25 20 19 24 18 17 22 16 15 20 15 14 22 16 15 23 17 15 26 18 16 27 19 17 28 20 18 29 21 19 30 24 22 33 25 23 34 25 23 34 23 22 30 21 20 28 20 18 29 20 18 29 19 17 28 17 15 26 17 15 26 18 16 27 20 18 29 22 20 31 23 21 32 21 19 30 19 17 28 18 16 27 17 15 26 17 15 26 16 14 25 15 13 24 16 16 28 16 16 26 15 15 23 14 15 20 14 13 21 13 12 20 14 11 20 13 12 18 11 10 15 7 12 18 2 11 28 74 89 120 119 139 189 120 146 205 111 145 209 114 151 219 212 167 138 205 161 136 193 152 132 181 145 129 147 119 108 31 10 5 25 9 9 20 11 12 16 12 13 14 14 16 13 14 16 13 14 16 13 12 17 13 12 17 13 10 17 12 10 15 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 15 15 17 15 15 17 15 15 17 13 13 15 12 12 14 12 12 14 12 12 14 13 13 15 14 14 16 14 14 16 13 13 15 14 14 16 14 14 16 14 14 16 14 14 16 14 13 18 14 13 18 14 13 18 14 13 18 15 14 19 16 15 20 15 14 19 14 13 18 13 12 18 13 12 18 14 13 19 14 15 20 15 15 23 16 19 26 16 16 24 17 17 25 17 16 22 21 20 26 27 25 30 14 12 17 18 16 21 29 27 32 176 174 177 222 220 223 255 254 255 255 254 255 249 247 248 255 254 255 253 248 252 252 247 251 237 235 240 222 220 225 116 115 121 25 24 30 14 15 20 20 21 26 16 17 22 16 17 22 17 18 23 18 19 24 21 20 25 21 20 25 22 22 24 22 22 24 22 21 26 21 20 25 19 18 23 17 16 21 17 16 24 17 16 24 18 16 27 18 16 27 19 17 28 21 19 30 22 20 31 24 22 33 24 22 33 23 21 32 20 19 27 18 17 25 19 17 28 19 17 28 18 16 27 17 15 26 16 14 25 16 14 25 18 16 27 20 18 29 21 19 30 20 18 29 18 16 27 17 15 26 17 15 26 17 15 26 16 14 25 15 13 24 16 16 26 15 15 23 14 15 19 13 14 18 13 12 20 13 11 22 13 9 23 13 10 19 15 13 16 11 12 16 2 10 23 43 57 84 125 143 191 121 147 206 111 145 209 113 151 216 212 167 138 206 162 137 191 153 132 180 147 132 134 107 98 27 8 2 25 11 10 21 12 13 17 13 14 14 14 16 12 13 15 11 12 14 12 11 16 13 12 17 14 12 17 14 12 17 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 13 13 15 12 12 14 12 12 14 13 13 15 13 13 15 14 14 16 13 13 15 14 14 16 14 14 16 14 14 16 14 14 16 14 13 18 14 13 18 14 13 18 14 13 18 15 14 19 15 14 19 15 14 19 15 14 19 13 12 18 13 12 18 14 13 19 14 15 20 17 17 25 12 15 22 13 13 21 23 23 31 23 22 28 17 16 22 25 23 28 31 26 32 24 19 25 105 100 106 205 200 204 252 247 251 255 254 255 255 253 254 255 254 255 253 251 252 255 253 255 255 250 255 239 237 242 226 224 229 64 63 69 18 17 23 18 18 26 16 16 24 17 17 25 18 18 26 19 20 25 20 21 26 21 20 25 22 21 26 22 21 26 23 22 27 21 20 25 21 20 25 21 20 25 21 20 25 21 20 28 20 19 27 20 18 29 20 18 29 22 20 31 23 21 32 24 22 33 24 22 33 22 20 31 19 17 28 16 15 23 15 14 22 19 17 28 19 17 28 18 16 27 16 14 25 15 13 24 15 13 24 17 15 26 18 16 27 20 18 29 18 16 27 17 15 26 17 15 26 17 15 26 17 15 26 16 14 25 15 13 24 14 14 22 14 15 20 13 14 16 13 14 16 13 11 22 13 11 24 14 10 25 16 10 20 18 14 15 12 12 14 10 14 25 21 31 58 127 145 191 121 146 203 113 147 210 111 149 212 213 168 139 203 161 136 193 155 134 181 148 133 115 88 79 26 7 3 24 10 10 19 10 13 15 13 16 14 14 16 12 13 15 11 12 14 12 11 16 13 12 17 15 13 18 16 14 19 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 14 14 16 14 14 16 14 14 16 17 17 19 15 15 17 13 13 15 12 12 14 13 13 15 13 13 15 13 13 15 13 13 15 14 14 16 14 14 16 14 14 16 14 14 16 14 13 18 14 13 18 14 13 18 14 13 18 13 12 17 14 13 18 14 13 18 14 13 18 13 12 18 13 12 18 13 12 18 13 14 19 12 12 20 14 17 24 17 17 25 18 18 26 27 26 32 27 24 31 22 17 23 22 17 23 55 50 56 166 161 167 225 220 224 255 253 255 255 251 252 252 248 249 255 254 255 255 253 254 252 247 251 242 237 243 231 229 234 172 170 175 22 21 27 19 18 24 26 26 34 13 13 21 19 19 27 20 20 28 21 22 27 21 22 27 22 21 26 22 21 26 22 21 26 23 22 27 21 20 25 21 20 25 22 21 26 23 22 27 23 22 30 22 21 29 22 20 31 21 19 30 25 23 34 25 23 34 24 22 33 22 20 31 19 17 28 16 14 25 14 13 21 14 13 21 19 17 28 19 17 28 18 16 27 17 15 26 15 13 24 15 13 24 17 15 26 18 16 27 18 16 27 17 15 26 17 15 26 17 15 26 17 15 26 17 15 26 16 14 25 15 14 22 13 14 19 13 14 18 12 14 13 12 13 15 13 11 22 13 11 25 16 9 25 16 10 22 17 13 12 11 10 8 12 15 24 7 17 42 118 135 179 122 147 204 115 149 210 111 149 211 211 167 140 200 158 133 194 156 135 180 148 133 96 69 62 27 8 4 21 9 9 17 11 13 15 13 16 14 13 18 13 14 18 13 14 18 13 12 17 14 13 18 15 13 18 15 13 18 13 13 15 13 13 15 14 14 16 14 14 16 15 15 17 14 14 16 14 14 16 14 14 16 19 19 21 17 17 19 15 15 17 13 13 15 13 13 15 13 13 15 12 12 14 12 12 14 13 13 15 13 13 15 14 14 16 14 14 16 14 13 18 14 13 18 13 12 17 13 12 17 13 12 17 13 12 17 14 13 18 14 13 18 13 12 18 13 12 18 14 13 19 13 14 19 14 14 22 17 20 29 21 21 29 13 13 21 20 19 25 21 18 25 19 14 21 45 40 46 152 145 152 210 204 208 252 246 250 255 250 254 255 254 255 255 254 255 255 254 253 253 249 250 255 254 255 236 234 239 210 208 213 76 74 79 21 20 26 22 21 27 23 23 31 21 21 29 21 21 29 22 22 30 23 24 29 22 23 28 22 21 26 21 20 25 21 20 25 22 21 26 23 22 27 23 22 27 23 22 27 23 22 27 23 22 30 23 22 30 23 21 32 23 21 32 25 23 34 25 23 34 23 21 32 20 18 29 16 14 25 15 13 24 15 14 22 16 15 23 21 19 30 20 18 29 20 18 29 18 16 27 17 15 26 16 14 25 17 15 26 18 16 27 18 16 27 18 16 27 17 15 26 16 14 25 16 14 25 16 14 25 15 13 24 13 12 20 14 13 19 14 14 16 12 14 11 12 13 15 13 11 22 13 10 27 16 9 27 16 10 22 18 12 12 13 12 10 10 13 22 6 14 37 100 114 159 123 148 204 114 149 207 113 152 211 210 165 136 196 154 129 194 156 135 180 148 133 82 57 50 27 9 7 21 11 12 19 13 17 15 13 18 14 13 18 15 14 19 15 14 19 15 14 19 14 13 18 13 12 17 12 11 16 13 13 15 14 14 14 14 14 16 15 15 15 15 15 17 15 15 15 15 15 17 14 14 14 20 20 22 18 18 20 15 15 17 14 14 16 13 13 15 12 12 14 12 12 14 11 11 13 13 13 15 13 13 15 14 14 16 14 14 16 14 13 18 14 13 18 13 12 17 13 12 17 13 12 17 14 13 18 15 14 19 15 14 19 14 13 19 14 13 19 15 14 20 16 15 21 17 17 25 9 9 17 20 20 28 26 26 34 27 26 34 16 15 21 48 45 52 149 144 151 208 203 209 237 231 235 255 251 255 255 253 255 251 247 248 255 252 253 255 251 252 255 254 255 246 239 246 211 201 209 179 172 179 24 19 25 27 24 31 29 28 34 16 16 24 23 26 33 20 23 30 23 24 29 24 25 30 23 24 28 22 21 26 21 21 23 21 21 23 22 22 24 26 25 30 26 25 30 25 24 29 24 23 28 23 24 29 23 23 31 24 22 33 25 23 34 24 22 33 24 22 33 24 18 30 19 16 25 15 13 24 13 13 21 17 14 23 19 16 25 23 20 29 23 20 31 21 19 30 19 17 28 17 17 27 16 16 26 17 17 27 18 18 28 18 18 28 17 17 27 16 16 26 16 14 25 17 14 23 16 13 22 13 12 20 12 11 19 14 13 19 14 13 18 13 13 13 13 13 15 12 10 21 12 10 24 13 9 26 13 9 23 19 14 18 17 15 18 10 10 20 10 16 40 85 98 142 126 149 203 114 146 205 114 152 214 206 157 127 203 157 131 194 156 135 181 152 138 68 45 39 22 8 8 22 13 16 13 8 14 17 15 20 16 14 19 15 13 16 14 12 15 14 12 15 13 12 17 13 12 17 14 13 18 11 11 11 10 10 8 19 19 19 5 5 3 24 24 24 16 16 14 10 10 10 17 17 15 16 16 16 12 12 12 13 11 12 20 18 19 9 7 8 15 13 14 15 13 14 11 9 10 9 7 10 23 21 24 16 14 17 13 11 14 14 12 17 15 13 18 19 17 22 13 11 16 17 16 21 17 16 21 16 15 20 15 14 19 14 13 19 14 13 19 15 14 20 16 15 21 20 19 25 12 11 17 25 25 33 23 23 31 24 24 32 21 21 29 153 152 160 193 192 198 231 226 232 255 250 254 253 247 249 255 253 255 255 253 255 255 252 255 252 250 255 253 246 253 222 205 215 202 182 193 72 56 66 33 23 31 29 26 33 22 23 28 16 19 24 22 27 31 24 29 33 22 26 29 22 23 27 23 23 25 22 20 23 19 17 18 19 17 18 22 20 21 24 24 26 15 16 18 25 24 29 20 21 25 17 20 25 22 25 30 20 19 27 19 18 26 22 22 30 17 16 24 27 15 27 17 7 16 22 22 30 11 14 19 22 17 24 23 16 24 24 17 25 17 11 21 21 18 29 15 13 24 16 21 27 18 23 29 15 20 26 16 19 26 17 20 27 14 17 24 14 17 26 16 15 23 19 14 21 19 14 20 13 10 17 14 13 19 15 15 23 14 15 20 16 14 19 15 13 18 15 12 19 14 12 23 13 12 26 11 13 25 16 16 26 11 11 21 13 12 26 14 16 39 70 79 118 128 148 199 123 154 218 113 150 218 210 159 128 202 157 128 192 154 133 181 152 138 55 34 29 19 7 7 20 14 18 15 10 16 16 14 19 16 14 17 17 12 16 16 12 13 14 12 15 13 13 15 13 12 18 13 14 18 22 22 22 10 9 7 13 12 10 27 26 24 12 11 9 12 11 9 32 31 29 13 12 10 18 17 15 17 16 14 19 15 16 25 21 22 13 9 10 18 14 15 19 15 16 19 15 16 14 9 13 9 7 10 13 11 14 15 13 16 23 21 26 24 22 27 5 3 8 25 23 28 16 15 20 16 15 20 16 15 20 14 13 18 13 12 18 13 12 18 14 13 19 15 14 20 15 13 18 20 19 25 18 17 25 17 17 25 21 24 33 30 33 42 127 127 135 196 195 201 225 220 226 255 253 255 255 253 255 255 251 253 255 252 255 255 253 255 244 242 247 219 212 220 168 152 163 125 107 119 43 30 40 24 17 25 24 21 28 25 26 31 16 21 27 20 25 29 23 26 31 23 24 28 22 21 26 24 22 25 23 21 24 20 18 19 20 18 19 22 20 21 24 22 27 19 16 23 24 21 28 30 27 34 25 26 31 22 23 28 28 25 32 29 24 31 26 18 29 28 15 25 29 9 21 27 7 19 14 4 15 21 11 22 29 13 24 31 13 25 37 19 31 23 9 22 30 27 36 8 11 18 8 17 22 7 16 21 15 18 25 19 18 26 17 17 25 13 13 21 13 13 21 15 14 22 21 14 22 22 15 22 16 14 19 15 16 21 12 15 22 14 15 20 15 13 18 17 12 18 14 11 18 14 13 21 11 13 25 10 14 26 13 15 27 11 13 25 15 13 27 13 14 35 58 66 102 131 149 197 123 153 217 115 152 222 212 163 133 199 153 127 189 151 132 179 151 139 39 20 14 18 6 6 20 14 18 16 14 19 15 13 18 14 12 15 16 11 15 16 12 13 14 12 15 13 13 15 13 12 18 13 12 17 9 7 8 17 13 12 134 130 129 141 137 136 143 139 140 125 121 122 114 110 111 95 91 92 87 83 84 75 71 72 57 51 53 45 39 41 24 18 22 22 16 20 17 11 15 14 8 12 23 17 21 19 14 18 22 17 21 18 13 17 11 6 12 24 19 25 10 8 13 15 13 18 16 14 19 16 14 19 15 14 19 14 13 18 13 12 18 12 11 17 13 12 18 14 13 19 13 10 17 25 24 30 11 10 18 16 16 24 17 20 29 27 30 39 76 76 84 184 183 189 220 215 221 246 240 244 255 246 249 255 252 255 255 252 255 251 245 249 214 207 214 153 148 155 116 111 118 51 45 55 23 20 29 23 22 30 22 22 30 27 30 37 19 22 31 22 22 30 24 23 31 24 21 30 26 20 30 26 21 28 26 21 27 22 20 23 21 19 22 22 20 25 23 17 27 31 23 36 26 15 29 27 16 30 23 17 27 23 18 25 23 18 25 23 10 19 35 7 22 46 9 27 44 10 27 46 13 32 34 0 21 44 9 31 43 8 32 41 6 28 35 0 20 38 11 28 20 17 24 12 22 24 13 28 33 15 24 31 17 11 21 22 10 22 25 14 28 21 13 24 17 14 21 18 15 22 20 12 23 16 8 19 11 10 15 9 13 16 10 13 18 12 13 18 14 12 17 16 11 17 14 11 18 14 13 21 11 13 25 10 14 26 13 15 27 14 16 28 16 14 28 13 14 35 41 49 85 134 152 200 122 152 214 120 155 223 211 162 132 196 152 125 188 151 132 174 146 134 29 10 4 19 9 8 19 14 18 16 14 19 13 11 16 14 12 15 16 12 13 17 13 14 15 13 16 13 13 15 13 12 18 13 12 17 32 28 29 13 7 7 140 131 132 193 184 185 255 252 255 255 252 255 251 242 245 150 141 144 153 144 147 160 151 154 167 158 161 186 175 179 190 179 185 193 182 188 177 166 172 163 154 159 140 131 136 132 126 130 104 98 102 81 76 80 18 13 19 6 1 7 28 23 29 18 16 21 16 14 19 16 14 19 15 14 19 14 13 18 13 12 18 13 12 18 13 12 18 14 13 19 15 12 19 20 19 25 11 11 19 22 22 30 17 20 29 19 19 29 31 30 38 153 150 157 198 191 198 241 232 237 255 251 255 255 247 251 248 237 243 224 215 220 163 153 161 105 100 106 84 84 92 22 25 34 22 25 34 24 27 36 22 24 36 24 24 34 23 21 34 22 20 31 23 19 33 25 19 31 26 20 32 27 21 31 25 22 29 22 21 27 20 21 26 21 20 28 18 11 27 32 23 42 31 18 36 30 17 34 29 21 32 26 19 27 30 24 28 91 68 76 97 38 58 115 39 65 117 43 68 116 43 70 117 42 72 116 41 71 115 40 71 117 42 75 120 46 81 147 94 120 26 17 22 7 22 19 5 19 22 13 20 28 16 6 15 30 14 25 21 8 18 20 10 19 16 11 15 19 17 22 18 15 26 12 10 21 14 15 17 14 18 19 9 12 17 11 12 17 14 12 17 16 11 15 14 12 17 14 13 21 11 13 25 11 15 26 12 14 26 15 17 29 16 14 27 11 12 32 25 34 67 133 150 196 121 150 210 119 154 220 206 159 131 195 151 126 190 153 135 159 133 120 26 9 2 21 11 10 17 12 16 14 12 17 13 11 14 14 12 15 16 12 13 17 13 14 15 13 16 13 13 15 13 12 18 13 12 17 14 8 12 38 29 32 150 139 143 204 193 197 255 246 252 255 249 255 217 206 212 130 119 125 154 143 149 159 148 154 158 147 153 169 156 163 175 162 171 190 177 186 191 178 187 194 181 190 186 175 181 185 176 181 175 166 171 175 169 173 101 94 101 21 16 22 9 4 10 12 7 13 16 14 19 16 14 19 16 15 20 15 14 19 14 13 19 14 13 19 14 15 20 14 15 20 16 15 23 13 12 20 17 17 27 24 24 34 23 23 35 22 22 32 19 18 26 103 98 105 187 177 185 226 215 221 255 240 245 246 231 234 223 208 213 162 150 154 115 102 109 126 119 126 46 49 58 14 21 31 18 25 35 19 23 34 24 23 37 16 14 27 23 19 34 25 19 33 24 17 33 25 19 33 26 20 34 25 22 33 24 22 33 20 23 30 18 23 29 17 21 30 30 29 45 20 15 35 19 10 29 24 16 31 34 26 37 20 14 18 24 15 16 166 129 136 150 52 75 177 46 78 176 38 72 174 39 72 174 49 81 173 48 80 180 41 80 190 53 97 172 45 98 233 147 186 44 24 26 10 23 13 14 21 27 19 19 29 24 15 20 26 14 18 26 17 20 23 17 17 17 9 6 18 17 15 10 17 27 0 10 20 8 14 14 8 12 11 9 13 16 12 13 18 14 12 17 17 12 16 15 13 18 15 14 20 12 15 24 11 15 26 10 13 22 13 16 25 14 12 23 9 10 28 16 23 52 120 137 180 119 148 206 116 150 214 204 157 131 194 152 128 191 156 137 135 108 97 28 10 6 22 14 12 16 11 15 14 12 17 15 13 16 14 12 15 16 12 13 16 12 13 14 12 15 13 13 15 13 12 17 13 12 17 13 4 9 50 39 43 176 165 169 194 183 187 222 209 216 235 222 229 211 198 205 170 157 164 152 139 146 174 161 168 184 171 178 192 179 186 190 174 184 193 177 187 189 173 183 194 178 188 191 178 185 181 170 176 177 166 172 181 172 177 160 150 158 83 76 83 23 16 23 17 12 18 17 12 18 16 14 19 17 15 20 16 15 20 15 14 20 15 14 20 15 16 21 16 17 22 19 18 26 13 12 20 19 19 29 17 17 27 21 21 33 28 26 37 23 20 31 46 41 48 185 175 183 202 190 194 235 218 224 230 214 217 180 163 169 140 125 130 150 135 142 143 133 141 19 22 31 15 21 33 19 23 35 20 19 33 26 24 38 17 13 28 26 17 34 26 17 34 25 18 34 26 20 34 25 21 35 22 22 32 20 23 32 19 24 30 16 23 29 15 22 30 14 18 29 21 20 34 37 26 40 25 13 25 24 17 25 28 19 24 32 16 19 184 138 149 150 54 81 170 46 80 178 44 81 178 44 81 176 48 83 177 47 85 185 44 86 184 47 93 177 56 110 229 147 185 57 36 33 20 22 9 25 14 22 26 2 16 50 9 17 44 0 4 34 0 1 33 9 5 25 11 0 27 24 15 16 20 31 2 8 22 14 15 17 15 15 13 12 13 17 12 13 17 15 13 16 18 13 17 16 14 19 16 15 21 13 16 25 12 16 25 13 16 25 16 16 24 18 17 25 12 14 29 13 20 48 104 119 160 123 149 206 118 152 215 202 156 132 197 155 133 191 155 139 105 81 69 28 13 8 22 13 14 15 10 14 14 13 18 16 14 17 16 14 17 17 13 14 16 12 11 14 12 13 13 13 15 13 12 17 14 13 18 28 19 24 66 55 61 179 168 174 219 208 214 214 203 209 201 190 196 231 218 227 158 145 154 148 135 144 179 166 175 197 184 193 209 196 205 208 195 204 209 196 205 203 187 198 209 193 203 206 193 200 199 188 194 195 184 190 187 178 183 197 187 195 156 149 156 86 79 86 11 6 12 16 11 17 16 14 19 17 15 20 17 15 20 16 15 21 16 15 21 16 17 22 17 18 23 19 18 26 17 16 24 17 17 27 17 17 27 19 19 31 23 21 32 26 23 34 21 14 22 141 130 138 207 195 199 229 212 218 197 178 182 148 129 135 167 150 156 205 188 196 86 75 83 20 17 28 21 19 32 25 21 35 23 17 31 28 19 36 26 17 34 26 17 34 25 18 34 24 20 34 25 23 36 24 24 36 20 23 32 18 22 31 17 24 30 16 23 29 14 22 24 18 28 27 19 19 19 31 12 18 43 20 28 100 89 95 167 151 161 127 93 109 142 94 116 133 79 105 119 63 92 158 99 131 140 75 109 152 74 114 143 57 102 172 85 130 130 53 97 162 107 147 162 128 152 106 87 80 142 122 111 132 90 102 138 69 90 148 45 62 145 35 46 137 46 55 145 81 79 129 104 82 112 105 89 71 58 75 23 9 32 21 8 15 22 12 13 14 13 18 14 15 19 17 15 18 19 14 18 18 16 21 16 17 22 12 16 25 12 16 25 15 18 27 16 16 24 23 22 30 16 15 29 12 19 45 82 97 136 125 152 205 124 156 217 202 156 133 198 157 135 189 156 139 86 62 52 28 13 10 19 13 13 13 11 14 16 15 20 17 15 18 16 14 15 17 13 14 16 12 11 14 12 13 14 12 15 13 12 17 14 13 18 19 13 17 82 73 78 135 126 131 180 171 176 232 221 227 225 214 220 156 145 153 129 118 126 142 131 139 161 150 158 163 152 160 168 157 165 171 160 168 181 168 177 179 166 176 188 175 184 187 174 183 183 170 177 179 168 174 175 164 170 178 168 176 171 161 169 157 150 157 38 31 38 16 11 17 17 12 18 16 14 19 17 15 20 16 15 21 16 15 21 16 17 22 17 18 23 15 14 22 18 17 25 13 13 23 25 25 35 22 22 34 16 14 25 29 26 37 29 22 30 80 69 77 206 191 196 199 182 188 168 149 153 151 132 138 152 133 139 167 150 158 25 12 21 26 18 29 17 11 23 24 16 31 20 12 27 21 13 28 29 21 36 23 17 31 21 17 31 24 22 35 25 25 35 22 25 34 20 23 30 19 22 29 22 23 28 22 21 27 21 19 20 21 17 8 39 20 14 72 30 34 114 67 77 190 154 166 255 235 253 214 155 183 144 87 119 163 124 153 117 91 118 184 164 191 130 104 133 160 114 151 134 79 118 198 146 184 123 83 118 192 177 208 114 106 121 125 102 94 225 182 173 184 113 127 199 95 118 186 43 61 204 50 62 175 39 49 207 105 103 204 154 129 177 151 134 103 76 93 27 3 29 19 2 10 25 15 16 16 14 19 15 16 20 17 15 18 18 16 19 18 16 21 16 17 22 13 17 26 10 17 25 13 16 23 14 15 20 23 22 28 14 14 26 8 16 39 63 79 115 124 150 201 125 157 214 206 161 140 191 153 132 190 158 143 56 34 23 25 11 8 22 16 16 14 12 15 11 12 16 12 12 14 14 12 13 16 12 13 16 12 11 15 11 12 13 11 14 13 12 17 14 13 18 20 15 19 57 51 55 98 91 98 117 110 117 117 107 115 117 107 115 131 121 129 148 138 146 149 139 147 154 144 152 160 150 159 165 155 164 169 159 168 174 162 172 180 168 178 184 173 181 175 162 171 178 165 172 171 160 166 171 160 166 169 159 167 164 154 162 159 152 159 37 30 37 15 10 16 16 11 17 15 13 18 16 14 19 15 14 20 14 13 19 12 13 18 11 12 17 17 16 22 17 16 24 16 16 26 16 16 26 17 17 29 19 17 28 20 17 28 24 17 25 23 12 20 190 175 180 186 169 175 127 108 112 91 72 78 156 137 143 53 36 44 23 10 19 26 16 27 22 14 25 19 11 24 20 12 25 21 15 27 22 19 30 22 20 31 21 21 31 25 28 37 18 23 29 16 19 26 22 21 27 27 22 29 28 17 25 31 15 25 41 16 20 52 11 7 89 30 26 135 52 62 164 75 95 239 160 182 255 227 255 231 146 185 153 78 117 168 118 153 120 87 116 196 173 199 138 116 139 160 132 155 139 106 127 193 154 175 125 97 119 182 182 206 120 118 132 129 96 89 223 158 152 206 112 126 214 87 108 209 44 58 216 39 47 205 35 44 216 76 75 212 125 105 199 148 131 110 71 89 32 8 32 21 10 18 18 16 17 16 15 20 17 16 21 19 17 20 18 16 19 18 16 21 15 15 23 13 17 26 12 19 27 15 18 25 17 18 23 21 20 26 13 13 25 14 20 42 45 61 95 123 150 197 126 157 212 202 160 138 192 154 135 180 148 135 48 25 17 20 9 5 21 15 15 12 12 14 13 14 18 14 14 16 14 14 14 18 14 13 17 13 12 17 13 14 15 13 14 16 14 19 16 15 20 11 9 12 17 12 16 19 14 20 15 10 16 10 5 11 13 8 14 24 19 25 35 30 36 42 37 43 50 45 51 62 55 63 75 68 76 89 82 90 102 95 103 115 108 116 125 115 123 141 130 136 151 140 146 154 143 149 156 147 152 159 149 157 158 151 158 144 137 144 33 28 34 15 10 16 14 12 17 14 12 17 14 12 17 14 13 19 14 13 19 13 14 19 13 14 19 17 16 22 17 16 22 16 16 24 16 16 26 16 16 28 17 15 26 19 16 25 22 15 23 30 19 27 99 86 93 154 137 143 81 65 68 70 53 59 106 89 97 23 7 17 27 15 25 23 18 25 20 17 24 19 16 25 18 15 24 18 17 25 19 19 27 17 20 27 17 22 28 14 21 27 15 20 24 20 21 26 27 22 28 31 15 25 32 9 19 39 9 21 56 11 16 99 33 21 146 64 50 167 76 73 170 66 75 168 52 75 197 79 113 159 50 91 148 63 102 149 100 130 171 143 168 149 132 151 128 114 131 135 119 132 164 142 155 129 95 109 130 103 118 135 133 155 113 105 120 121 73 71 160 74 73 170 47 68 201 47 71 207 31 42 205 19 24 208 24 32 192 34 33 166 55 38 171 101 89 65 24 40 19 2 21 14 9 15 16 18 17 14 15 19 17 16 22 19 17 22 20 18 23 19 16 23 16 16 24 12 16 27 10 17 25 14 19 25 16 17 22 20 19 24 17 17 27 13 19 41 32 47 80 128 152 200 127 158 212 198 157 137 192 155 137 161 131 120 36 16 9 18 7 5 19 15 16 11 11 13 14 15 19 12 12 14 13 13 13 16 12 11 16 12 11 15 11 12 14 12 13 15 13 18 15 14 19 15 15 17 15 13 16 14 12 15 15 13 16 16 14 17 16 14 17 15 13 18 14 12 17 17 15 20 17 15 20 18 13 19 16 11 17 14 9 15 13 8 14 15 10 16 18 11 18 22 13 18 22 11 17 27 16 22 38 29 34 42 32 40 49 42 49 58 51 58 18 13 19 15 10 16 13 11 16 13 11 16 12 11 16 13 12 18 14 13 19 15 16 21 16 17 22 17 16 21 17 16 22 15 15 23 15 15 23 15 15 25 16 14 25 17 14 23 19 14 21 25 15 23 34 23 29 90 78 82 69 54 59 72 57 64 42 29 36 19 8 16 21 14 22 15 16 21 12 17 20 14 17 22 15 18 23 16 19 24 16 21 25 16 24 27 17 25 28 16 26 28 18 23 26 22 21 26 28 17 23 34 11 21 48 12 24 72 25 41 100 40 42 182 105 77 237 155 115 232 159 124 223 130 112 179 33 46 186 27 57 168 39 70 196 107 135 187 150 167 195 185 196 79 71 84 151 143 154 188 176 186 243 221 233 92 54 67 115 78 96 182 164 188 169 141 163 161 94 103 164 54 65 194 36 69 209 28 61 208 26 41 207 26 33 207 30 38 189 30 34 170 46 38 192 112 105 52 22 30 17 15 28 14 12 17 20 20 22 14 15 20 16 15 21 19 17 22 20 18 23 19 16 23 16 16 24 10 17 27 10 17 27 14 18 27 15 16 21 18 17 22 22 22 32 12 17 37 15 30 61 132 156 202 127 157 209 195 154 136 191 156 137 141 113 101 27 8 1 17 7 5 19 15 16 10 10 12 15 16 18 11 11 13 12 12 10 15 11 10 15 11 8 15 11 12 13 11 12 14 12 17 14 13 18 11 11 13 12 12 14 13 13 15 15 15 17 15 15 17 15 15 17 15 14 19 15 14 19 15 13 18 16 14 19 16 14 19 15 13 18 14 12 17 13 11 16 15 13 18 18 13 19 20 11 16 14 5 10 19 10 15 21 15 19 12 5 12 11 6 12 15 10 16 15 10 16 13 11 16 13 11 16 12 11 16 12 11 16 13 12 18 14 13 19 14 15 20 16 15 21 18 16 21 17 15 20 16 15 21 14 14 22 14 14 24 14 14 24 15 14 22 16 13 20 17 10 17 21 12 17 38 27 31 71 60 64 64 53 59 15 5 13 28 18 27 16 13 20 8 16 19 8 18 19 11 19 21 13 18 21 14 17 22 15 20 24 16 24 27 19 27 30 21 29 31 20 25 28 22 20 25 27 14 21 39 13 24 60 22 35 89 41 57 118 59 61 191 124 95 232 168 124 209 160 119 205 133 109 173 42 50 177 33 58 157 45 69 165 92 112 180 152 164 194 191 198 98 98 106 149 148 154 160 153 161 221 204 212 112 80 91 132 100 115 178 159 181 161 129 150 151 88 99 157 59 72 188 51 81 185 35 64 175 34 43 178 42 44 185 46 51 175 47 48 169 61 58 181 110 106 53 32 37 14 17 24 7 8 12 10 11 15 14 15 20 17 16 24 19 16 23 19 16 23 19 16 25 16 16 26 10 16 28 10 17 27 14 18 27 17 18 23 18 17 23 22 22 32 11 16 36 6 19 51 136 158 205 125 155 207 191 152 135 188 152 136 122 94 83 25 6 0 20 10 8 19 15 16 9 10 12 14 15 17 13 13 15 14 14 12 17 13 12 17 13 10 16 12 13 14 12 13 15 13 18 15 14 19 15 16 18 16 18 17 16 16 18 13 13 15 10 10 12 9 9 11 11 11 13 13 13 15 12 12 14 13 13 15 15 13 18 15 13 18 14 12 17 13 11 16 13 11 16 15 10 16 16 10 14 18 12 16 24 18 22 15 10 14 15 10 16 28 23 29 19 14 20 10 8 13 13 11 16 13 11 16 13 12 17 13 12 17 13 12 18 13 12 18 13 12 18 13 12 18 17 15 20 17 15 20 15 14 20 14 15 20 13 13 21 13 13 21 13 13 21 14 13 19 20 15 21 17 11 15 22 16 20 44 35 40 30 23 30 21 16 22 15 10 17 19 20 25 10 20 22 9 20 22 11 19 22 13 16 21 15 16 21 16 17 22 17 20 25 20 23 28 18 23 27 17 21 24 19 18 23 26 17 22 38 17 26 52 20 31 68 26 40 76 34 38 95 59 45 108 77 56 88 61 42 90 52 43 87 16 24 93 16 34 80 21 39 71 32 50 71 51 63 65 62 71 24 29 35 33 41 44 38 35 42 75 66 71 43 31 35 47 34 41 56 45 59 50 32 46 54 14 23 61 8 16 63 6 15 66 9 15 59 8 4 57 4 0 70 7 2 64 0 0 63 5 3 66 25 23 24 9 12 8 9 13 14 18 21 17 22 25 14 17 24 17 16 24 19 16 23 18 15 22 17 14 23 15 15 25 11 17 29 12 18 30 13 17 26 16 19 24 20 17 24 20 18 29 13 16 35 5 19 48 128 151 195 125 153 203 192 153 136 183 147 133 103 76 65 27 8 2 22 12 10 19 15 16 10 11 13 11 15 16 12 12 14 13 13 11 16 12 11 16 12 9 15 11 10 15 11 12 14 12 15 14 14 16 14 14 16 13 15 14 15 15 17 17 17 19 19 19 21 18 18 20 16 16 18 14 14 16 15 15 17 16 16 18 18 16 21 19 17 22 19 17 22 19 17 22 18 16 21 17 15 20 22 17 21 19 14 18 25 20 24 12 7 11 12 7 13 18 13 19 12 10 15 13 11 16 14 12 17 14 12 17 13 12 17 13 12 17 13 12 18 13 12 18 12 11 17 12 11 16 18 13 17 16 14 17 16 14 19 14 15 20 14 14 22 12 15 22 14 14 22 15 16 21 18 16 21 19 14 18 23 18 22 19 14 18 13 8 14 24 21 28 11 10 18 13 16 23 8 17 22 8 18 20 12 17 21 15 16 21 19 16 23 23 18 25 24 21 30 25 24 30 18 21 26 15 20 24 15 18 23 19 18 23 29 18 26 36 16 25 39 11 23 38 11 18 33 17 17 31 20 16 29 15 14 39 20 22 42 15 24 43 13 25 43 17 30 42 20 33 35 21 34 26 20 30 21 26 32 26 31 37 32 27 34 28 21 29 23 22 27 27 26 31 26 23 30 30 18 28 39 16 24 49 24 30 27 12 15 28 20 18 26 21 15 26 17 12 40 19 18 38 9 11 37 8 10 22 0 3 24 13 17 11 9 12 15 19 20 12 17 20 15 18 25 18 16 27 18 15 24 17 14 23 16 13 24 15 15 27 11 17 29 12 20 31 13 17 26 16 19 26 21 18 25 16 14 25 14 17 36 7 18 48 106 129 173 126 154 204 194 157 139 176 143 128 89 62 51 29 10 4 23 13 12 15 13 14 12 13 15 11 15 16 11 11 13 11 11 9 15 11 10 14 10 7 14 10 9 14 10 11 13 11 14 13 13 15 19 19 21 14 14 14 9 9 9 7 7 7 10 10 10 13 13 13 16 14 17 16 14 17 15 13 16 15 13 16 15 13 16 15 13 16 15 13 16 14 12 15 13 11 14 12 10 13 11 9 12 4 2 5 20 18 21 18 16 19 21 19 24 8 6 11 12 10 15 19 17 22 14 13 18 13 12 17 13 12 17 12 11 16 12 11 17 12 11 17 12 11 17 13 12 17 17 12 16 15 13 16 15 13 18 14 15 20 15 15 23 13 16 23 14 17 22 17 18 23 15 14 19 21 19 24 17 15 18 19 17 20 14 13 18 15 16 21 18 21 28 8 13 19 9 16 22 11 16 22 17 16 24 23 15 26 29 17 29 30 18 30 30 19 33 27 21 31 20 20 28 14 22 25 12 22 24 12 20 22 19 20 24 22 20 23 28 19 24 28 19 24 28 25 32 24 21 30 34 24 35 38 28 37 27 26 32 24 21 28 30 14 24 35 18 28 33 23 32 34 31 38 27 26 34 21 18 27 30 19 33 38 30 43 28 31 40 12 17 23 21 22 26 24 19 25 30 17 27 38 25 35 25 18 26 20 17 24 20 19 27 27 24 33 24 12 24 29 15 28 34 20 33 24 11 21 26 15 21 16 10 12 13 13 13 11 12 16 16 16 24 17 15 26 18 15 24 18 15 24 17 14 25 15 15 27 11 17 29 11 19 30 14 18 29 13 16 23 21 18 25 14 12 23 15 18 37 5 16 46 78 99 142 131 157 206 197 160 142 173 140 125 81 54 43 29 12 5 23 13 12 13 11 12 13 14 18 11 15 16 12 12 14 13 13 11 16 12 11 16 12 9 15 11 10 16 12 13 15 13 16 16 14 17 13 13 15 14 14 14 15 15 15 15 15 15 15 15 15 16 16 16 17 15 18 17 15 18 17 15 18 16 14 17 16 14 17 16 14 17 16 14 17 16 14 17 16 14 17 16 14 17 19 17 20 10 8 11 19 17 20 8 6 9 19 17 22 14 12 17 22 20 25 13 11 16 15 14 19 13 12 17 12 11 16 11 10 15 11 10 16 12 11 17 13 12 18 14 13 18 17 12 16 15 13 14 15 13 18 14 15 19 15 15 23 14 17 24 15 18 23 16 19 24 22 21 26 11 10 15 14 12 15 21 21 23 9 8 14 13 16 21 16 19 28 15 19 28 17 22 28 18 21 28 24 18 28 29 19 30 34 17 33 32 15 31 27 13 28 20 12 25 14 14 22 12 19 25 13 24 26 13 24 26 13 23 24 17 22 25 23 23 25 25 24 30 21 23 35 20 19 35 28 19 36 25 21 35 8 21 27 21 32 34 31 19 29 35 18 28 25 18 25 24 22 27 26 20 30 39 31 42 34 20 37 26 15 31 16 20 32 19 28 35 21 22 26 29 27 30 29 19 30 20 9 23 22 19 30 23 21 34 18 17 33 17 14 33 20 16 33 22 15 33 24 15 32 23 15 28 16 5 13 23 14 17 17 15 16 20 20 22 15 15 25 17 15 26 19 16 27 19 16 25 18 14 28 16 16 28 11 17 31 10 18 29 15 19 30 9 12 19 21 18 25 14 12 23 16 19 38 2 13 43 56 77 120 134 160 209 \ No newline at end of file diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 0e4f1ba6804..40ff08c9996 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -3,9 +3,14 @@ import pytest -from PIL import Image, UnidentifiedImageError +from PIL import Image, PpmImagePlugin -from .helper import assert_image_equal_tofile, assert_image_similar, hopper +from .helper import ( + assert_image_equal, + assert_image_equal_tofile, + assert_image_similar, + hopper, +) # sample ppm stream TEST_FILE = "Tests/images/hopper.ppm" @@ -50,20 +55,147 @@ def test_pnm(tmp_path): assert_image_equal_tofile(im, f) -def test_magic(tmp_path): +def test_plain_pbm(tmp_path): + with Image.open("Tests/images/hopper_1bit_plain.pbm") as im1, Image.open( + "Tests/images/hopper_1bit.pbm" + ) as im2: + assert_image_equal(im1, im2) + + +def test_8bit_plain_pgm(tmp_path): + with Image.open("Tests/images/hopper_8bit_plain.pgm") as im1, Image.open( + "Tests/images/hopper_8bit.pgm" + ) as im2: + assert_image_equal(im1, im2) + + +def test_8bit_plain_ppm(tmp_path): + with Image.open("Tests/images/hopper_8bit_plain.ppm") as im1, Image.open( + "Tests/images/hopper_8bit.ppm" + ) as im2: + assert_image_equal(im1, im2) + + +def test_16bit_plain_pgm(tmp_path): + with Image.open("Tests/images/hopper_16bit_plain.pgm") as im1, Image.open( + "Tests/images/hopper_16bit.pgm" + ) as im2: + assert im1.mode == "I" + assert im1.size == (128, 128) + assert im1.get_format_mimetype() == "image/x-portable-graymap" + + assert_image_equal(im1, im2) + + +def test_32bit_plain_pgm(tmp_path): + with Image.open("Tests/images/hopper_32bit_plain.pgm") as im1, Image.open( + "Tests/images/hopper_32bit.pgm" + ) as im2: + assert im1.mode == "I" + assert im1.size == (128, 128) + assert im1.get_format_mimetype() == "image/x-portable-graymap" + + assert_image_equal(im1, im2) + + +def test_plain_pbm_data_with_comments(tmp_path): + path1 = str(tmp_path / "temp1.ppm") + path2 = str(tmp_path / "temp2.ppm") + comment = b"# veeery long comment" * 10 ** 6 + with open(path1, "wb") as f1, open(path2, "wb") as f2: + f1.write(b"P1\n2 2\n\n1010") + f2.write(b"P1\n2 2\n" + comment + b"\n1010" + comment) + + with Image.open(path1) as im1, Image.open(path2) as im2: + assert_image_equal(im1, im2) + + +def test_plain_pbm_truncated_data(tmp_path): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: - f.write(b"PyInvalid") + f.write(b"P1\n128 128\n") - with pytest.raises(UnidentifiedImageError): - with Image.open(path): - pass + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_plain_pbm_invalid_data(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P1\n128 128\n1009") + + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_plain_ppm_data_with_comments(tmp_path): + path1 = str(tmp_path / "temp1.ppm") + path2 = str(tmp_path / "temp2.ppm") + comment = b"# veeery long comment" * 10 ** 6 + with open(path1, "wb") as f1, open(path2, "wb") as f2: + f1.write(b"P3\n2 2\n255\n0 0 0 001 1 1 2 2 2 255 255 255") + f2.write( + b"P3\n2 2\n255\n" + comment + b"\n0 0 0 001 1 1 2 2 2 255 255 255" + comment + ) + + with Image.open(path1) as im1, Image.open(path2) as im2: + assert_image_equal(im1, im2) + + +def test_plain_ppm_truncated_data(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P3\n128 128\n255\n") + + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_plain_ppm_invalid_data(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P3\n128 128\n255\n100A") + + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_plain_ppm_half_token_too_long(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P3\n128 128\n255\n012345678910") + + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_plain_ppm_token_too_long(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P3\n128 128\n255\n012345678910 0") + + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_plain_ppm_value_too_large(tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "wb") as f: + f.write(b"P3\n128 128\n255\n256") + + with pytest.raises(ValueError): + Image.open(path).load() + + +def test_magic(tmp_path): + with pytest.raises(SyntaxError): + PpmImagePlugin.PpmImageFile(fp=BytesIO(b"PyInvalid")) def test_header_with_comments(tmp_path): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: - f.write(b"P6 #comment\n#comment\r12#comment\r8\n128 #comment\n255\n") + f.write(b"P6 #comment\n#comment\r 12#comment\r8\n128 #comment\n255\n") with Image.open(path) as im: assert im.size == (128, 128) @@ -79,7 +211,7 @@ def test_non_integer_token(tmp_path): pass -def test_token_too_long(tmp_path): +def test_header_token_too_long(tmp_path): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: f.write(b"P6\n 01234567890") @@ -103,7 +235,7 @@ def test_too_many_colors(tmp_path): assert str(e.value) == "Too many colors for band: 1000" -def test_truncated_file(tmp_path): +def test_truncated_header(tmp_path): path = str(tmp_path / "temp.pgm") with open(path, "w") as f: f.write("P6") From be97a851a81f7879fed590f9529587a6e56eb176 Mon Sep 17 00:00:00 2001 From: Piolie Date: Thu, 4 Feb 2021 21:25:50 -0300 Subject: [PATCH 034/326] Update src/PIL/PpmImagePlugin.py Remove commented line. Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/PpmImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 86616b89f16..c26d86362ee 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -147,7 +147,6 @@ class PpmPlainDecoder(ImageFile.PyDecoder): def _read_block(self, block_size=10 ** 6): return bytearray(self.fd.read(block_size)) - # return self.fd.read(block_size) def _find_comment_end(self, block, start=0): a = block.find(b"\n", start) From 7aa7d850ee1cca5ada616f93a852cc8de5f8fe23 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 4 Mar 2022 17:08:10 +1100 Subject: [PATCH 035/326] Added context managers --- Tests/test_file_ppm.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 40ff08c9996..d34aa738b13 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -115,8 +115,9 @@ def test_plain_pbm_truncated_data(tmp_path): with open(path, "wb") as f: f.write(b"P1\n128 128\n") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_plain_pbm_invalid_data(tmp_path): @@ -124,8 +125,9 @@ def test_plain_pbm_invalid_data(tmp_path): with open(path, "wb") as f: f.write(b"P1\n128 128\n1009") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_plain_ppm_data_with_comments(tmp_path): @@ -147,8 +149,9 @@ def test_plain_ppm_truncated_data(tmp_path): with open(path, "wb") as f: f.write(b"P3\n128 128\n255\n") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_plain_ppm_invalid_data(tmp_path): @@ -156,8 +159,9 @@ def test_plain_ppm_invalid_data(tmp_path): with open(path, "wb") as f: f.write(b"P3\n128 128\n255\n100A") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_plain_ppm_half_token_too_long(tmp_path): @@ -165,8 +169,9 @@ def test_plain_ppm_half_token_too_long(tmp_path): with open(path, "wb") as f: f.write(b"P3\n128 128\n255\n012345678910") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_plain_ppm_token_too_long(tmp_path): @@ -174,8 +179,9 @@ def test_plain_ppm_token_too_long(tmp_path): with open(path, "wb") as f: f.write(b"P3\n128 128\n255\n012345678910 0") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_plain_ppm_value_too_large(tmp_path): @@ -183,8 +189,9 @@ def test_plain_ppm_value_too_large(tmp_path): with open(path, "wb") as f: f.write(b"P3\n128 128\n255\n256") - with pytest.raises(ValueError): - Image.open(path).load() + with Image.open(path) as im: + with pytest.raises(ValueError): + im.load() def test_magic(tmp_path): From 2922a00e9c857db36a9de3706ff73a026b2f72c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 21 Mar 2021 14:33:05 +1100 Subject: [PATCH 036/326] Renamed decoder --- src/PIL/PpmImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index c26d86362ee..4123769820b 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -24,9 +24,9 @@ MODES = { # standard, plain - b"P1": ("plain", "1"), - b"P2": ("plain", "L"), - b"P3": ("plain", "RGB"), + b"P1": ("ppm_plain", "1"), + b"P2": ("ppm_plain", "L"), + b"P3": ("ppm_plain", "RGB"), # standard, raw b"P4": ("raw", "1"), b"P5": ("raw", "L"), @@ -339,7 +339,7 @@ def _save(im, fp, filename): # # -------------------------------------------------------------------- -Image.register_decoder("plain", PpmPlainDecoder) +Image.register_decoder("ppm_plain", PpmPlainDecoder) Image.register_open(PpmImageFile.format, PpmImageFile, _accept) Image.register_save(PpmImageFile.format, _save) From 97982cf703de79552f522e8f2df272a1bc58f046 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Mar 2021 21:40:18 +1100 Subject: [PATCH 037/326] Replaced assert_image_equal with assert_image_equal_tofile --- Tests/test_file_ppm.py | 57 ++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index d34aa738b13..1832e3e4681 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -5,12 +5,7 @@ from PIL import Image, PpmImagePlugin -from .helper import ( - assert_image_equal, - assert_image_equal_tofile, - assert_image_similar, - hopper, -) +from .helper import assert_image_equal_tofile, assert_image_similar, hopper # sample ppm stream TEST_FILE = "Tests/images/hopper.ppm" @@ -56,46 +51,36 @@ def test_pnm(tmp_path): def test_plain_pbm(tmp_path): - with Image.open("Tests/images/hopper_1bit_plain.pbm") as im1, Image.open( - "Tests/images/hopper_1bit.pbm" - ) as im2: - assert_image_equal(im1, im2) + with Image.open("Tests/images/hopper_1bit_plain.pbm") as im: + assert_image_equal_tofile(im, "Tests/images/hopper_1bit.pbm") def test_8bit_plain_pgm(tmp_path): - with Image.open("Tests/images/hopper_8bit_plain.pgm") as im1, Image.open( - "Tests/images/hopper_8bit.pgm" - ) as im2: - assert_image_equal(im1, im2) + with Image.open("Tests/images/hopper_8bit_plain.pgm") as im: + assert_image_equal_tofile(im, "Tests/images/hopper_8bit.pgm") def test_8bit_plain_ppm(tmp_path): - with Image.open("Tests/images/hopper_8bit_plain.ppm") as im1, Image.open( - "Tests/images/hopper_8bit.ppm" - ) as im2: - assert_image_equal(im1, im2) + with Image.open("Tests/images/hopper_8bit_plain.ppm") as im: + assert_image_equal_tofile(im, "Tests/images/hopper_8bit.ppm") def test_16bit_plain_pgm(tmp_path): - with Image.open("Tests/images/hopper_16bit_plain.pgm") as im1, Image.open( - "Tests/images/hopper_16bit.pgm" - ) as im2: - assert im1.mode == "I" - assert im1.size == (128, 128) - assert im1.get_format_mimetype() == "image/x-portable-graymap" + with Image.open("Tests/images/hopper_16bit_plain.pgm") as im: + assert im.mode == "I" + assert im.size == (128, 128) + assert im.get_format_mimetype() == "image/x-portable-graymap" - assert_image_equal(im1, im2) + assert_image_equal_tofile(im, "Tests/images/hopper_16bit.pgm") def test_32bit_plain_pgm(tmp_path): - with Image.open("Tests/images/hopper_32bit_plain.pgm") as im1, Image.open( - "Tests/images/hopper_32bit.pgm" - ) as im2: - assert im1.mode == "I" - assert im1.size == (128, 128) - assert im1.get_format_mimetype() == "image/x-portable-graymap" + with Image.open("Tests/images/hopper_32bit_plain.pgm") as im: + assert im.mode == "I" + assert im.size == (128, 128) + assert im.get_format_mimetype() == "image/x-portable-graymap" - assert_image_equal(im1, im2) + assert_image_equal_tofile(im, "Tests/images/hopper_32bit.pgm") def test_plain_pbm_data_with_comments(tmp_path): @@ -106,8 +91,8 @@ def test_plain_pbm_data_with_comments(tmp_path): f1.write(b"P1\n2 2\n\n1010") f2.write(b"P1\n2 2\n" + comment + b"\n1010" + comment) - with Image.open(path1) as im1, Image.open(path2) as im2: - assert_image_equal(im1, im2) + with Image.open(path1) as im: + assert_image_equal_tofile(im, path2) def test_plain_pbm_truncated_data(tmp_path): @@ -140,8 +125,8 @@ def test_plain_ppm_data_with_comments(tmp_path): b"P3\n2 2\n255\n" + comment + b"\n0 0 0 001 1 1 2 2 2 255 255 255" + comment ) - with Image.open(path1) as im1, Image.open(path2) as im2: - assert_image_equal(im1, im2) + with Image.open(path1) as im: + assert_image_equal_tofile(im, path2) def test_plain_ppm_truncated_data(tmp_path): From 0215175e1db6f3a7dba5910388d4bf5b7d9e8dc5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 6 Mar 2022 02:39:06 +0000 Subject: [PATCH 038/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_ppm.py | 4 ++-- src/PIL/PpmImagePlugin.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 1832e3e4681..ddeded6fb14 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -86,7 +86,7 @@ def test_32bit_plain_pgm(tmp_path): def test_plain_pbm_data_with_comments(tmp_path): path1 = str(tmp_path / "temp1.ppm") path2 = str(tmp_path / "temp2.ppm") - comment = b"# veeery long comment" * 10 ** 6 + comment = b"# veeery long comment" * 10**6 with open(path1, "wb") as f1, open(path2, "wb") as f2: f1.write(b"P1\n2 2\n\n1010") f2.write(b"P1\n2 2\n" + comment + b"\n1010" + comment) @@ -118,7 +118,7 @@ def test_plain_pbm_invalid_data(tmp_path): def test_plain_ppm_data_with_comments(tmp_path): path1 = str(tmp_path / "temp1.ppm") path2 = str(tmp_path / "temp2.ppm") - comment = b"# veeery long comment" * 10 ** 6 + comment = b"# veeery long comment" * 10**6 with open(path1, "wb") as f1, open(path2, "wb") as f2: f1.write(b"P3\n2 2\n255\n0 0 0 001 1 1 2 2 2 255 255 255") f2.write( diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 9a5cac7d0dc..cae078d02a8 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -145,7 +145,7 @@ def _open(self): class PpmPlainDecoder(ImageFile.PyDecoder): _pulls_fd = True - def _read_block(self, block_size=10 ** 6): + def _read_block(self, block_size=10**6): return bytearray(self.fd.read(block_size)) def _find_comment_end(self, block, start=0): @@ -218,9 +218,9 @@ def _decode_bitonal(self): def _decode_blocks(self, channels=1, depth=8): decoded_data = bytearray() if depth == 32: - maxval = 2 ** 31 - 1 # HACK: 32-bit grayscale uses signed int + maxval = 2**31 - 1 # HACK: 32-bit grayscale uses signed int else: - maxval = 2 ** depth - 1 # FIXME: should be passed by _open + maxval = 2**depth - 1 # FIXME: should be passed by _open max_len = 10 bytes_per_sample = depth // 8 total_tokens = self.size * channels From 073acd4c82ae77d8e5f052d73e1d73dde0a989d0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Mar 2022 17:32:15 +1100 Subject: [PATCH 039/326] Moved decoder names out of MODES --- Tests/test_file_ppm.py | 12 ++++++- src/PIL/PpmImagePlugin.py | 74 +++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index ddeded6fb14..8fe68d33e2c 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -51,35 +51,45 @@ def test_pnm(tmp_path): def test_plain_pbm(tmp_path): + # P1 with Image.open("Tests/images/hopper_1bit_plain.pbm") as im: + # P4 assert_image_equal_tofile(im, "Tests/images/hopper_1bit.pbm") def test_8bit_plain_pgm(tmp_path): + # P2 with Image.open("Tests/images/hopper_8bit_plain.pgm") as im: + # P5 assert_image_equal_tofile(im, "Tests/images/hopper_8bit.pgm") def test_8bit_plain_ppm(tmp_path): + # P3 with Image.open("Tests/images/hopper_8bit_plain.ppm") as im: + # P6 assert_image_equal_tofile(im, "Tests/images/hopper_8bit.ppm") def test_16bit_plain_pgm(tmp_path): + # P2 with maxval 2 ** 16 - 1 with Image.open("Tests/images/hopper_16bit_plain.pgm") as im: assert im.mode == "I" assert im.size == (128, 128) assert im.get_format_mimetype() == "image/x-portable-graymap" + # P5 with maxval 2 ** 16 - 1 assert_image_equal_tofile(im, "Tests/images/hopper_16bit.pgm") def test_32bit_plain_pgm(tmp_path): + # P2 with maxval 2 ** 31 - 1 with Image.open("Tests/images/hopper_32bit_plain.pgm") as im: assert im.mode == "I" assert im.size == (128, 128) assert im.get_format_mimetype() == "image/x-portable-graymap" + # P5 with maxval 2 ** 31 - 1 assert_image_equal_tofile(im, "Tests/images/hopper_32bit.pgm") @@ -187,7 +197,7 @@ def test_magic(tmp_path): def test_header_with_comments(tmp_path): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: - f.write(b"P6 #comment\n#comment\r 12#comment\r8\n128 #comment\n255\n") + f.write(b"P6 #comment\n#comment\r12#comment\r8\n128 #comment\n255\n") with Image.open(path) as im: assert im.size == (128, 128) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index cae078d02a8..497b98395dc 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -23,20 +23,19 @@ b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" MODES = { - # standard, plain - b"P1": ("ppm_plain", "1"), - b"P2": ("ppm_plain", "L"), - b"P3": ("ppm_plain", "RGB"), - # standard, raw - b"P4": ("raw", "1"), - b"P5": ("raw", "L"), - b"P6": ("raw", "RGB"), + # standard + b"P1": "1", + b"P2": "L", + b"P3": "RGB", + b"P4": "1", + b"P5": "L", + b"P6": "RGB", # extensions - b"P0CMYK": ("raw", "CMYK"), + b"P0CMYK": "CMYK", # PIL extensions (for test purposes only) - b"PyP": ("raw", "P"), - b"PyRGBA": ("raw", "RGBA"), - b"PyCMYK": ("raw", "CMYK"), + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK", } @@ -90,18 +89,16 @@ def _read_token(self): def _open(self): magic_number = self._read_magic() try: - decoder, mode = MODES[magic_number] + mode = MODES[magic_number] except KeyError: raise SyntaxError("not a PPM file") - self.custom_mimetype = { - b"P1": "image/x-portable-bitmap", - b"P2": "image/x-portable-graymap", - b"P3": "image/x-portable-pixmap", - b"P4": "image/x-portable-bitmap", - b"P5": "image/x-portable-graymap", - b"P6": "image/x-portable-pixmap", - }.get(magic_number) + if magic_number in (b"P1", b"P4"): + self.custom_mimetype = "image/x-portable-bitmap" + elif magic_number in (b"P2", b"P5"): + self.custom_mimetype = "image/x-portable-graymap" + elif magic_number in (b"P3", b"P6"): + self.custom_mimetype = "image/x-portable-pixmap" for ix in range(3): token = int(self._read_token()) @@ -127,14 +124,12 @@ def _open(self): self.mode = "I" rawmode = "I;32B" + decoder_name = "raw" + if magic_number in (b"P1", b"P2", b"P3"): + decoder_name = "ppm_plain" self._size = xsize, ysize self.tile = [ - ( - decoder, # decoder - (0, 0, xsize, ysize), # region: whole image - self.fp.tell(), # offset to image data - (rawmode, 0, 1), # parameters for decoder - ) + (decoder_name, (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1)) ] @@ -145,8 +140,8 @@ def _open(self): class PpmPlainDecoder(ImageFile.PyDecoder): _pulls_fd = True - def _read_block(self, block_size=10**6): - return bytearray(self.fd.read(block_size)) + def _read_block(self): + return self.fd.read(10**6) def _find_comment_end(self, block, start=0): a = block.find(b"\n", start) @@ -155,10 +150,9 @@ def _find_comment_end(self, block, start=0): def _ignore_comments(self, block): """ - Deletes comments from block. If comment does not end in this - block, raises a flag. + Deletes comments from block. + If comment does not end in this block, raises a flag. """ - comment_spans = False while True: comment_start = block.find(b"#") # look for next comment @@ -166,9 +160,8 @@ def _ignore_comments(self, block): break comment_end = self._find_comment_end(block, comment_start) if comment_end != -1: # comment ends in this block - block = ( - block[:comment_start] + block[comment_end + 1 :] - ) # delete comment + # delete comment + block = block[:comment_start] + block[comment_end + 1 :] else: # last comment continues to next block(s) block = block[:comment_start] comment_spans = True @@ -177,9 +170,8 @@ def _ignore_comments(self, block): def _decode_bitonal(self): """ - The reason this is a separate method is that in the plain PBM - format all data tokens are exactly one byte, and so the - inter-token whitespace is optional. + This is a separate method because the plain PBM format all data tokens + are exactly one byte, and so the inter-token whitespace is optional. """ decoded_data = bytearray() total_tokens = self.size @@ -217,10 +209,8 @@ def _decode_bitonal(self): def _decode_blocks(self, channels=1, depth=8): decoded_data = bytearray() - if depth == 32: - maxval = 2**31 - 1 # HACK: 32-bit grayscale uses signed int - else: - maxval = 2**depth - 1 # FIXME: should be passed by _open + # HACK: 32-bit grayscale uses signed int + maxval = 2 ** (31 if depth == 32 else depth) - 1 max_len = 10 bytes_per_sample = depth // 8 total_tokens = self.size * channels From 9e04416c6cf337249ac7a8d47f0b6d487b33b047 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Mar 2022 17:40:08 +1100 Subject: [PATCH 040/326] Removed token_spans variable --- src/PIL/PpmImagePlugin.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 497b98395dc..0a71b6fb6c8 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -196,16 +196,13 @@ def _decode_bitonal(self): tokens = b"".join(block.split()) for token in tokens: - if token in (48, 49): - tokens_read += 1 - else: + if token not in (48, 49): raise ValueError(f"Invalid token for this mode: {bytes([token])}") - + tokens_read += 1 decoded_data.append(token) if tokens_read == total_tokens: # finished! invert = bytes.maketrans(b"01", b"\xFF\x00") - decoded_data = decoded_data.translate(invert) - return decoded_data + return decoded_data.translate(invert) def _decode_blocks(self, channels=1, depth=8): decoded_data = bytearray() @@ -215,14 +212,13 @@ def _decode_blocks(self, channels=1, depth=8): bytes_per_sample = depth // 8 total_tokens = self.size * channels - token_spans = False comment_spans = False half_token = False tokens_read = 0 while True: block = self._read_block() # read next block if not block: - if token_spans: + if half_token: block = bytearray(b" ") # flush half_token else: raise ValueError("Reached EOF while reading data") @@ -237,14 +233,12 @@ def _decode_blocks(self, channels=1, depth=8): block, comment_spans = self._ignore_comments(block) - if token_spans: + if half_token: block = half_token + block # stitch half_token to new block - token_spans = False tokens = block.split() if block and not block[-1:].isspace(): # block might split token - token_spans = True half_token = tokens.pop() # save half token for later if len(half_token) > max_len: # prevent buildup of half_token raise ValueError( From 4fcef0d1635bfc4f000005cb1d5df6b6f91c8007 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Mar 2022 17:44:32 +1100 Subject: [PATCH 041/326] Removed re-raising of exception --- src/PIL/PpmImagePlugin.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 0a71b6fb6c8..0f3cbcc4b8a 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -250,12 +250,7 @@ def _decode_blocks(self, channels=1, depth=8): raise ValueError( f"Token too long found in data: {token[:max_len + 1]}" ) - try: - token = int(token) - except ValueError: - raise ValueError( - f"Non-decimal-ASCII found in data: {token}" - ) from None + token = int(token) tokens_read += 1 if token > maxval: raise ValueError(f"Channel value too large for this mode: {token}") From e32a94e835771b1b93f57236e0b674c1cdc0bff5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 13 Mar 2022 13:36:26 +1100 Subject: [PATCH 042/326] Removed tokens_read variable --- src/PIL/PpmImagePlugin.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 0f3cbcc4b8a..c73a814eb35 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -174,11 +174,10 @@ def _decode_bitonal(self): are exactly one byte, and so the inter-token whitespace is optional. """ decoded_data = bytearray() - total_tokens = self.size + total_bytes = self.size comment_spans = False - tokens_read = 0 - while True: + while len(decoded_data) != total_bytes: block = self._read_block() # read next block if not block: raise ValueError("Reached EOF while reading data") @@ -194,15 +193,12 @@ def _decode_bitonal(self): block, comment_spans = self._ignore_comments(block) tokens = b"".join(block.split()) - for token in tokens: if token not in (48, 49): raise ValueError(f"Invalid token for this mode: {bytes([token])}") - tokens_read += 1 - decoded_data.append(token) - if tokens_read == total_tokens: # finished! - invert = bytes.maketrans(b"01", b"\xFF\x00") - return decoded_data.translate(invert) + decoded_data = (decoded_data + tokens)[:total_bytes] + invert = bytes.maketrans(b"01", b"\xFF\x00") + return decoded_data.translate(invert) def _decode_blocks(self, channels=1, depth=8): decoded_data = bytearray() @@ -210,12 +206,11 @@ def _decode_blocks(self, channels=1, depth=8): maxval = 2 ** (31 if depth == 32 else depth) - 1 max_len = 10 bytes_per_sample = depth // 8 - total_tokens = self.size * channels + total_bytes = self.size * channels * bytes_per_sample comment_spans = False half_token = False - tokens_read = 0 - while True: + while len(decoded_data) != total_bytes: block = self._read_block() # read next block if not block: if half_token: @@ -251,12 +246,12 @@ def _decode_blocks(self, channels=1, depth=8): f"Token too long found in data: {token[:max_len + 1]}" ) token = int(token) - tokens_read += 1 if token > maxval: raise ValueError(f"Channel value too large for this mode: {token}") decoded_data += token.to_bytes(bytes_per_sample, "big") - if tokens_read == total_tokens: # finished! - return decoded_data + if len(decoded_data) == total_bytes: # finished! + break + return decoded_data def decode(self, buffer): self.size = self.state.xsize * self.state.ysize From 0d5a2d2b83f3ad5d1c477c8b9f60c4f1706059e6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 13 Mar 2022 14:11:28 +1100 Subject: [PATCH 043/326] Allow PyDecoder to pass truncated data to C decoder --- src/PIL/PpmImagePlugin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index c73a814eb35..0e19cab07d8 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -174,13 +174,14 @@ def _decode_bitonal(self): are exactly one byte, and so the inter-token whitespace is optional. """ decoded_data = bytearray() - total_bytes = self.size + total_bytes = self.state.xsize * self.state.ysize comment_spans = False while len(decoded_data) != total_bytes: block = self._read_block() # read next block if not block: - raise ValueError("Reached EOF while reading data") + # eof + break while block and comment_spans: comment_end = self._find_comment_end(block) @@ -206,7 +207,7 @@ def _decode_blocks(self, channels=1, depth=8): maxval = 2 ** (31 if depth == 32 else depth) - 1 max_len = 10 bytes_per_sample = depth // 8 - total_bytes = self.size * channels * bytes_per_sample + total_bytes = self.state.xsize * self.state.ysize * channels * bytes_per_sample comment_spans = False half_token = False @@ -216,7 +217,8 @@ def _decode_blocks(self, channels=1, depth=8): if half_token: block = bytearray(b" ") # flush half_token else: - raise ValueError("Reached EOF while reading data") + # eof + break while block and comment_spans: comment_end = self._find_comment_end(block) @@ -254,7 +256,6 @@ def _decode_blocks(self, channels=1, depth=8): return decoded_data def decode(self, buffer): - self.size = self.state.xsize * self.state.ysize rawmode = self.args[0] if self.mode == "1": From e3b53dc7e0b3bab2d7fa203fd11f270788cf71bd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Mar 2022 21:23:29 +1100 Subject: [PATCH 044/326] Use SAFEBLOCK instead of arbitrary large number --- src/PIL/PpmImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 0e19cab07d8..d238cd26cbe 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -141,7 +141,7 @@ class PpmPlainDecoder(ImageFile.PyDecoder): _pulls_fd = True def _read_block(self): - return self.fd.read(10**6) + return self.fd.read(ImageFile.SAFEBLOCK) def _find_comment_end(self, block, start=0): a = block.find(b"\n", start) @@ -186,8 +186,8 @@ def _decode_bitonal(self): while block and comment_spans: comment_end = self._find_comment_end(block) if comment_end != -1: # comment ends in this block - comment_spans = False block = block[comment_end + 1 :] # delete tail of previous comment + comment_spans = False else: # comment spans whole block block = self._read_block() From edc6c3d2f00c8a1afe36c991f7eb69e2afa3cd51 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Mar 2022 22:10:47 +1100 Subject: [PATCH 045/326] Use maxval instead of unreliable 32-bit specific behaviour --- src/PIL/PpmImagePlugin.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index d238cd26cbe..73f796b94e9 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -100,6 +100,7 @@ def _open(self): elif magic_number in (b"P3", b"P6"): self.custom_mimetype = "image/x-portable-pixmap" + maxval = None for ix in range(3): token = int(self._read_token()) if ix == 0: # token is the x size @@ -127,10 +128,9 @@ def _open(self): decoder_name = "raw" if magic_number in (b"P1", b"P2", b"P3"): decoder_name = "ppm_plain" + args = (rawmode, 0, 1) if decoder_name == "raw" else (rawmode, maxval) self._size = xsize, ysize - self.tile = [ - (decoder_name, (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1)) - ] + self.tile = [(decoder_name, (0, 0, xsize, ysize), self.fp.tell(), args)] # @@ -201,10 +201,8 @@ def _decode_bitonal(self): invert = bytes.maketrans(b"01", b"\xFF\x00") return decoded_data.translate(invert) - def _decode_blocks(self, channels=1, depth=8): + def _decode_blocks(self, channels, depth, maxval): decoded_data = bytearray() - # HACK: 32-bit grayscale uses signed int - maxval = 2 ** (31 if depth == 32 else depth) - 1 max_len = 10 bytes_per_sample = depth // 8 total_bytes = self.state.xsize * self.state.ysize * channels * bytes_per_sample @@ -256,20 +254,20 @@ def _decode_blocks(self, channels=1, depth=8): return decoded_data def decode(self, buffer): - rawmode = self.args[0] + rawmode, maxval = self.args if self.mode == "1": decoded_data = self._decode_bitonal() rawmode = "1;8" elif self.mode == "L": - decoded_data = self._decode_blocks(channels=1, depth=8) + decoded_data = self._decode_blocks(1, 8, maxval) elif self.mode == "I": if rawmode == "I;16B": - decoded_data = self._decode_blocks(channels=1, depth=16) + decoded_data = self._decode_blocks(1, 16, maxval) elif rawmode == "I;32B": - decoded_data = self._decode_blocks(channels=1, depth=32) + decoded_data = self._decode_blocks(1, 32, maxval) elif self.mode == "RGB": - decoded_data = self._decode_blocks(channels=3, depth=8) + decoded_data = self._decode_blocks(3, 8, maxval) self.set_as_raw(bytes(decoded_data), rawmode) return -1, 0 From 6b2da2f1236f774ac6d6526cf479020b68776f95 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 2 Apr 2022 21:04:22 +1100 Subject: [PATCH 046/326] Consider transparency from each frame when saving --- Tests/test_file_gif.py | 11 ++++++++++- src/PIL/GifImagePlugin.py | 6 +++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index dffd1006f2f..9caea84a85c 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -619,7 +619,7 @@ def test_dispose2_background(tmp_path): assert im.getpixel((0, 0)) == (255, 0, 0) -def test_transparency_in_second_frame(): +def test_transparency_in_second_frame(tmp_path): with Image.open("Tests/images/different_transparency.gif") as im: assert im.info["transparency"] == 0 @@ -629,6 +629,15 @@ def test_transparency_in_second_frame(): assert_image_equal_tofile(im, "Tests/images/different_transparency_merged.png") + out = str(tmp_path / "temp.gif") + im.save(out, save_all=True) + + with Image.open(out) as reread: + reread.seek(reread.tell() + 1) + assert_image_equal_tofile( + reread, "Tests/images/different_transparency_merged.png" + ) + def test_no_transparency_in_second_frame(): with Image.open("Tests/images/iss634.gif") as img: diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b798bb96991..fad6cdf1bbe 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -573,10 +573,14 @@ def _write_multiple_frames(im, fp, palette): im_frame = _normalize_mode(im_frame.copy()) if frame_count == 0: for k, v in im_frame.info.items(): + if k == "transparency": + continue im.encoderinfo.setdefault(k, v) - im_frame = _normalize_palette(im_frame, palette, im.encoderinfo) encoderinfo = im.encoderinfo.copy() + if "transparency" in im_frame.info: + encoderinfo.setdefault("transparency", im_frame.info["transparency"]) + im_frame = _normalize_palette(im_frame, palette, encoderinfo) if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] if isinstance(disposal, (list, tuple)): From 950d0ad1d37a8b68b13fef9dbab47bf5a76b552a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Apr 2022 23:12:45 +1000 Subject: [PATCH 047/326] Fixed behaviour change from #5901 endian fix --- Tests/images/issue_6194.j2k | Bin 0 -> 318 bytes Tests/test_file_jpeg2k.py | 5 +++++ src/libImaging/Jpeg2KDecode.c | 6 ++++-- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 Tests/images/issue_6194.j2k diff --git a/Tests/images/issue_6194.j2k b/Tests/images/issue_6194.j2k new file mode 100644 index 0000000000000000000000000000000000000000..b1b8851670bb5764c080a41d3e9ea0285ba22ba6 GIT binary patch literal 318 zcmezG|38pHlK})cpcu>n5-d;*;WP3xGX4)@-~sX&8JJjD7#RP@FmO3EbaYGrqW>uj zstktDsYS(^`FRRPdM0`X|0ghTfsHc&YM*@mj{y%C z&*TFPKN$`%2pnLLWnpJ#2J#pWFjxuje6;1!2CHWT>M%yAuLr4Lz#z||z{q}pp+?Dp z!AgXIp@Yf&e?50US3Ot@6HtpOiWZ>pKx8N2z|717u|m*+fun)v&wOrsUWg`Upe8ef jCVQaa3P6|eFfc!4VPpo{^^*x=R||JSd_8wEimage[y0 + y] + x0; + UINT16 *row = (UINT16 *)im->image[y0 + y] + x0; for (x = 0; x < w; ++x) { UINT16 pixel = j2ku_shift(offset + *data++, shift); + #ifdef WORDS_BIGENDIAN + pixel = (pixel >> 8) | (pixel << 8); + #endif *row++ = pixel; - *row++ = pixel >> 8; } } break; From 1d1c22e1d4979710b9e5fe951ac174308d12ebfb Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 15 Apr 2022 20:45:19 -0400 Subject: [PATCH 048/326] CI: Update versions of actions used by Cygwin CI As suggested by code review. Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/test-cygwin.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 3b133254206..c85876ad115 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -21,10 +21,10 @@ jobs: git config --global core.autocrlf input - name: Checkout Pillow - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Cygwin - uses: cygwin/cygwin-install-action@v1 + uses: cygwin/cygwin-install-action@v2 with: platform: ${{ matrix.architecture }} packages: > @@ -78,7 +78,7 @@ jobs: dash.exe -c "mkdir -p Tests/errors" - name: Upload errors - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: errors @@ -89,7 +89,7 @@ jobs: bash.exe .ci/after_success.sh - name: Upload coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: file: ./coverage.xml flags: GHA_Cygwin From 635f8cf327aea361120a9f6a344355ed1caea03b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Apr 2022 15:42:09 +1000 Subject: [PATCH 049/326] Added Python 3.9 --- .github/workflows/test-cygwin.yml | 2 +- docs/installation.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c85876ad115..5597b2b34c4 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-minor-version: [7, 8] + python-minor-version: [7, 8, 9] architecture: ["x86", "x86_64"] timeout-minutes: 40 diff --git a/docs/installation.rst b/docs/installation.rst index e5f09dad48b..f2c08488f01 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -478,9 +478,9 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 | | +----------------------------+---------------------+ -| | 3.9/MinGW | x86, x86-64 | +| | 3.9 (MinGW) | x86, x86-64 | | +----------------------------+---------------------+ -| | 3.8/Cygwin | x86-64 | +| | 3.7, 3.8, 3.9 (Cygwin) | x86, x86-64 | +----------------------------------+----------------------------+---------------------+ From c8118438ae4ad477f8b4f7c969e2c16b892a0d8f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Apr 2022 17:07:39 +1000 Subject: [PATCH 050/326] Do not update frame position until local image is found --- Tests/images/comment_after_last_frame.gif | Bin 0 -> 1625 bytes Tests/test_file_gif.py | 27 ++++++++++++++-------- src/PIL/GifImagePlugin.py | 4 ++-- 3 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 Tests/images/comment_after_last_frame.gif diff --git a/Tests/images/comment_after_last_frame.gif b/Tests/images/comment_after_last_frame.gif new file mode 100644 index 0000000000000000000000000000000000000000..9f5c7b8da471dfe3f29d4578c83d145b81dc69dc GIT binary patch literal 1625 zcmZ?wbhEHbWMp7uXlED&qaiS&LqPFAx1VcBu(M-;tC5}oGb1AdgAOP_K-q(VgN1>S jg@r-!AA53sZfRu-WQPD|-mnG$b$}1h literal 0 HcmV?d00001 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index dffd1006f2f..d177520e2cb 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -341,16 +341,23 @@ def test_seek_rewind(): assert_image_equal(im, expected) -def test_n_frames(): - for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]: - # Test is_animated before n_frames - with Image.open(path) as im: - assert im.is_animated == (n_frames != 1) - - # Test is_animated after n_frames - with Image.open(path) as im: - assert im.n_frames == n_frames - assert im.is_animated == (n_frames != 1) +@pytest.mark.parametrize( + "path, n_frames", + ( + (TEST_GIF, 1), + ("Tests/images/comment_after_last_frame.gif", 2), + ("Tests/images/iss634.gif", 42), + ), +) +def test_n_frames(path, n_frames): + # Test is_animated before n_frames + with Image.open(path) as im: + assert im.is_animated == (n_frames != 1) + + # Test is_animated after n_frames + with Image.open(path) as im: + assert im.n_frames == n_frames + assert im.is_animated == (n_frames != 1) def test_no_change(): diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b798bb96991..5c8016839d6 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -183,8 +183,6 @@ def _seek(self, frame, update_image=True): if not s or s == b";": raise EOFError - self.__frame = frame - self.tile = [] palette = None @@ -283,6 +281,8 @@ def _seek(self, frame, update_image=True): if interlace is None: # self.__fp = None raise EOFError + + self.__frame = frame if not update_image: return From cdfe08bc5e06f0ac2debbf3dd1db69584623db2d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Apr 2022 19:14:00 +1000 Subject: [PATCH 051/326] Only install NumPy through pip on 64-bit --- .github/workflows/test-cygwin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 5597b2b34c4..8683e72d71c 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,6 +53,7 @@ jobs: bash.exe .ci/install.sh - name: Install a different NumPy + if: matrix.architecture == 'x86_64' run: | bash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*'" From 2ae52552f787d5916763b7fc970a27d72d612e9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 Apr 2022 09:54:17 +1000 Subject: [PATCH 052/326] Renamed __fp to _fp --- src/PIL/DcxImagePlugin.py | 12 ++++++------ src/PIL/FliImagePlugin.py | 14 +++++++------- src/PIL/GifImagePlugin.py | 16 ++++++++-------- src/PIL/ImImagePlugin.py | 12 ++++++------ src/PIL/Image.py | 8 ++++---- src/PIL/MicImagePlugin.py | 10 +++++----- src/PIL/MpoImagePlugin.py | 16 ++++++++-------- src/PIL/PngImagePlugin.py | 18 +++++++++--------- src/PIL/PsdImagePlugin.py | 12 ++++++------ src/PIL/SpiderImagePlugin.py | 12 ++++++------ src/PIL/TiffImagePlugin.py | 12 ++++++------ 11 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index de21db8f082..d5c7482261f 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -57,7 +57,7 @@ def _open(self): break self._offset.append(offset) - self.__fp = self.fp + self._fp = self.fp self.frame = None self.n_frames = len(self._offset) self.is_animated = self.n_frames > 1 @@ -67,21 +67,21 @@ def seek(self, frame): if not self._seek_check(frame): return self.frame = frame - self.fp = self.__fp + self.fp = self._fp self.fp.seek(self._offset[frame]) PcxImageFile._open(self) def tell(self): return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None Image.register_open(DcxImageFile.format, DcxImageFile, _accept) diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index ea950330555..7df301904c8 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -91,7 +91,7 @@ def _open(self): # set things up to decode first frame self.__frame = -1 - self.__fp = self.fp + self._fp = self.fp self.__rewind = self.fp.tell() self.seek(0) @@ -125,7 +125,7 @@ def seek(self, frame): def _seek(self, frame): if frame == 0: self.__frame = -1 - self.__fp.seek(self.__rewind) + self._fp.seek(self.__rewind) self.__offset = 128 else: # ensure that the previous frame was loaded @@ -136,7 +136,7 @@ def _seek(self, frame): self.__frame = frame # move to next frame - self.fp = self.__fp + self.fp = self._fp self.fp.seek(self.__offset) s = self.fp.read(4) @@ -153,14 +153,14 @@ def _seek(self, frame): def tell(self): return self.__frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index d36d8c61a8c..cfb6c0355b2 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -102,7 +102,7 @@ def _open(self): p = ImagePalette.raw("RGB", p) self.global_palette = self.palette = p - self.__fp = self.fp # FIXME: hack + self._fp = self.fp # FIXME: hack self.__rewind = self.fp.tell() self._n_frames = None self._is_animated = None @@ -161,7 +161,7 @@ def _seek(self, frame, update_image=True): self.__offset = 0 self.dispose = None self.__frame = -1 - self.__fp.seek(self.__rewind) + self._fp.seek(self.__rewind) self.disposal_method = 0 else: # ensure that the previous frame was loaded @@ -171,7 +171,7 @@ def _seek(self, frame, update_image=True): if frame != self.__frame + 1: raise ValueError(f"cannot seek to frame {frame}") - self.fp = self.__fp + self.fp = self._fp if self.__offset: # backup to last frame self.fp.seek(self.__offset) @@ -281,7 +281,7 @@ def _seek(self, frame, update_image=True): s = None if interlace is None: - # self.__fp = None + # self._fp = None raise EOFError if not update_image: return @@ -443,14 +443,14 @@ def load_end(self): def tell(self): return self.__frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # -------------------------------------------------------------------- diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index f7e690b355d..3c5739f3da8 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -245,7 +245,7 @@ def _open(self): self.__offset = offs = self.fp.tell() - self.__fp = self.fp # FIXME: hack + self._fp = self.fp # FIXME: hack if self.rawmode[:2] == "F;": @@ -294,21 +294,21 @@ def seek(self, frame): size = ((self.size[0] * bits + 7) // 8) * self.size[1] offs = self.__offset + frame * size - self.fp = self.__fp + self.fp = self._fp self.tile = [("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1))] def tell(self): return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3c36178bd0d..16fad61c116 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -544,8 +544,8 @@ def __enter__(self): def __exit__(self, *args): if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): - if hasattr(self, "_close__fp"): - self._close__fp() + if hasattr(self, "_close_fp"): + self._close_fp() if self.fp: self.fp.close() self.fp = None @@ -563,8 +563,8 @@ def close(self): more information. """ try: - if hasattr(self, "_close__fp"): - self._close__fp() + if hasattr(self, "_close_fp"): + self._close_fp() if self.fp: self.fp.close() self.fp = None diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 9248b1b6550..324c8eff415 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -62,7 +62,7 @@ def _open(self): if not self.images: raise SyntaxError("not an MIC file; no image entries") - self.__fp = self.fp + self._fp = self.fp self.frame = None self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 @@ -89,14 +89,14 @@ def seek(self, frame): def tell(self): return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 88c1bfcc540..0d61746b73e 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -58,20 +58,20 @@ def _after_jpeg_open(self, mpheader=None): 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._fp = self.fp # FIXME: hack + self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame self.__frame = 0 self.offset = 0 # for now we can only handle reading and individual frame extraction self.readonly = 1 def load_seek(self, pos): - self.__fp.seek(pos) + self._fp.seek(pos) def seek(self, frame): if not self._seek_check(frame): return - self.fp = self.__fp + self.fp = self._fp self.offset = self.__mpoffsets[frame] self.fp.seek(self.offset + 2) # skip SOI marker @@ -97,14 +97,14 @@ def seek(self, frame): def tell(self): return self.__frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None @staticmethod def adopt(jpeg_instance, mpheader=None): diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c939b86e776..313090e8d6d 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -689,7 +689,7 @@ def _open(self): if not _accept(self.fp.read(8)): raise SyntaxError("not a PNG file") - self.__fp = self.fp + self._fp = self.fp self.__frame = 0 # @@ -746,7 +746,7 @@ def _open(self): self._close_exclusive_fp_after_loading = False self.png.save_rewind() self.__rewind_idat = self.__prepare_idat - self.__rewind = self.__fp.tell() + self.__rewind = self._fp.tell() if self.default_image: # IDAT chunk contains default image and not first animation frame self.n_frames += 1 @@ -801,7 +801,7 @@ def seek(self, frame): def _seek(self, frame, rewind=False): if frame == 0: if rewind: - self.__fp.seek(self.__rewind) + self._fp.seek(self.__rewind) self.png.rewind() self.__prepare_idat = self.__rewind_idat self.im = None @@ -809,7 +809,7 @@ def _seek(self, frame, rewind=False): self.pyaccess = None self.info = self.png.im_info self.tile = self.png.im_tile - self.fp = self.__fp + self.fp = self._fp self._prev_im = None self.dispose = None self.default_image = self.info.get("default_image", False) @@ -828,7 +828,7 @@ def _seek(self, frame, rewind=False): self.im.paste(self.dispose, self.dispose_extent) self._prev_im = self.im.copy() - self.fp = self.__fp + self.fp = self._fp # advance to the next frame if self.__prepare_idat: @@ -1006,14 +1006,14 @@ def getxmp(self): else {} ) - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # -------------------------------------------------------------------- diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 2832195798c..3be9aa2906b 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -132,7 +132,7 @@ def _open(self): self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) # keep the file open - self.__fp = self.fp + self._fp = self.fp self.frame = 1 self._min_frame = 1 @@ -146,7 +146,7 @@ def seek(self, layer): self.mode = mode self.tile = tile self.frame = layer - self.fp = self.__fp + self.fp = self._fp return name, bbox except IndexError as e: raise EOFError("no such layer") from e @@ -155,14 +155,14 @@ def tell(self): # return layer number (0=image, 1..max=layers) return self.frame - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None def _layerinfo(fp, ct_bytes): diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 1a72f5c0451..0a65c286cd9 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -149,7 +149,7 @@ def _open(self): self.mode = "F" self.tile = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))] - self.__fp = self.fp # FIXME: hack + self._fp = self.fp # FIXME: hack @property def n_frames(self): @@ -172,7 +172,7 @@ def seek(self, frame): if not self._seek_check(frame): return self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) - self.fp = self.__fp + self.fp = self._fp self.fp.seek(self.stkoffset) self._open() @@ -191,14 +191,14 @@ def tkPhotoImage(self): return ImageTk.PhotoImage(self.convert2byte(), palette=256) - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # -------------------------------------------------------------------- diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 000429991d6..ee737cb5935 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1073,7 +1073,7 @@ def _open(self): # setup frame pointers self.__first = self.__next = self.tag_v2.next self.__frame = -1 - self.__fp = self.fp + self._fp = self.fp self._frame_pos = [] self._n_frames = None @@ -1106,7 +1106,7 @@ def seek(self, frame): self.im = Image.core.new(self.mode, self.size) def _seek(self, frame): - self.fp = self.__fp + self.fp = self._fp # reset buffered io handle in case fp # was passed to libtiff, invalidating the buffer @@ -1515,14 +1515,14 @@ def _setup(self): self._tile_orientation = self.tag_v2.get(0x0112) - def _close__fp(self): + def _close_fp(self): try: - if self.__fp != self.fp: - self.__fp.close() + if self._fp != self.fp: + self._fp.close() except AttributeError: pass finally: - self.__fp = None + self._fp = None # From 4e075adcc5e13ffe67d25f6fab2e9b548a1d86d7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Apr 2022 20:31:23 +1000 Subject: [PATCH 053/326] Merged _close_fp into close and __exit__ --- src/PIL/DcxImagePlugin.py | 9 --------- src/PIL/FliImagePlugin.py | 9 --------- src/PIL/GifImagePlugin.py | 9 --------- src/PIL/ImImagePlugin.py | 9 --------- src/PIL/Image.py | 12 ++++++++---- src/PIL/MicImagePlugin.py | 9 --------- src/PIL/MpoImagePlugin.py | 9 --------- src/PIL/PngImagePlugin.py | 9 --------- src/PIL/PsdImagePlugin.py | 9 --------- src/PIL/SpiderImagePlugin.py | 9 --------- src/PIL/TiffImagePlugin.py | 9 --------- 11 files changed, 8 insertions(+), 94 deletions(-) diff --git a/src/PIL/DcxImagePlugin.py b/src/PIL/DcxImagePlugin.py index d5c7482261f..aeed1e7c7ba 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -74,15 +74,6 @@ def seek(self, frame): def tell(self): return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - Image.register_open(DcxImageFile.format, DcxImageFile, _accept) diff --git a/src/PIL/FliImagePlugin.py b/src/PIL/FliImagePlugin.py index 7df301904c8..e13b1779ccc 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -153,15 +153,6 @@ def _seek(self, frame): def tell(self): return self.__frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # registry diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index cfb6c0355b2..33c76586b93 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -443,15 +443,6 @@ def load_end(self): def tell(self): return self.__frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # -------------------------------------------------------------------- # Write GIF files diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index 3c5739f3da8..ee95a94cb55 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -301,15 +301,6 @@ def seek(self, frame): def tell(self): return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # -------------------------------------------------------------------- diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 16fad61c116..1409a20d213 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -544,8 +544,10 @@ def __enter__(self): def __exit__(self, *args): if hasattr(self, "fp") and getattr(self, "_exclusive_fp", False): - if hasattr(self, "_close_fp"): - self._close_fp() + if getattr(self, "_fp", False): + if self._fp != self.fp: + self._fp.close() + self._fp = None if self.fp: self.fp.close() self.fp = None @@ -563,8 +565,10 @@ def close(self): more information. """ try: - if hasattr(self, "_close_fp"): - self._close_fp() + if getattr(self, "_fp", False): + if self._fp != self.fp: + self._fp.close() + self._fp = None if self.fp: self.fp.close() self.fp = None diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 324c8eff415..0de37cf37c2 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -89,15 +89,6 @@ def seek(self, frame): def tell(self): return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # -------------------------------------------------------------------- diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 0d61746b73e..fc3f8556ff7 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -97,15 +97,6 @@ def seek(self, frame): def tell(self): return self.__frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - @staticmethod def adopt(jpeg_instance, mpheader=None): """ diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 313090e8d6d..856c218027f 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1006,15 +1006,6 @@ def getxmp(self): else {} ) - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # -------------------------------------------------------------------- # PNG writer diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 3be9aa2906b..9622e648a88 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -155,15 +155,6 @@ def tell(self): # return layer number (0=image, 1..max=layers) return self.frame - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - def _layerinfo(fp, ct_bytes): # read layerinfo block diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 0a65c286cd9..154008c08dd 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -191,15 +191,6 @@ def tkPhotoImage(self): return ImageTk.PhotoImage(self.convert2byte(), palette=256) - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # -------------------------------------------------------------------- # Image series diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index ee737cb5935..c871072adf7 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1515,15 +1515,6 @@ def _setup(self): self._tile_orientation = self.tag_v2.get(0x0112) - def _close_fp(self): - try: - if self._fp != self.fp: - self._fp.close() - except AttributeError: - pass - finally: - self._fp = None - # # -------------------------------------------------------------------- From f18688e84e1197872d2e724857573f7bd2ce4f2b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 Apr 2022 08:28:28 +1000 Subject: [PATCH 054/326] Removed unused variable --- src/PIL/MicImagePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/MicImagePlugin.py b/src/PIL/MicImagePlugin.py index 0de37cf37c2..d4f6c90f778 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -62,7 +62,6 @@ def _open(self): if not self.images: raise SyntaxError("not an MIC file; no image entries") - self._fp = self.fp self.frame = None self._n_frames = len(self.images) self.is_animated = self._n_frames > 1 From e62449f94cc150584c3192061feb3cca7cd5d4c9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 Apr 2022 12:14:53 +1000 Subject: [PATCH 055/326] Added DeferredError to _fp --- Tests/test_file_apng.py | 9 +++++++++ Tests/test_file_fli.py | 9 +++++++++ Tests/test_file_gif.py | 13 +++++++++++++ Tests/test_file_mpo.py | 8 ++++++++ Tests/test_file_tiff.py | 9 +++++++++ src/PIL/Image.py | 4 ++-- 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index d1d5c85c1ae..ad61a07ccc5 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -637,6 +637,15 @@ def test_apng_save_blend(tmp_path): assert im.getpixel((0, 0)) == (0, 255, 0, 255) +def test_seek_after_close(): + im = Image.open("Tests/images/apng/delay.png") + im.seek(1) + im.close() + + with pytest.raises(ValueError): + im.seek(0) + + def test_constants_deprecation(): for enum, prefix in { PngImagePlugin.Disposal: "APNG_DISPOSE_", diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index c1ad4a7f0f9..a7d43d2e922 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -46,6 +46,15 @@ def test_closed_file(): im.close() +def test_seek_after_close(): + im = Image.open(animated_test_file) + im.seek(1) + im.close() + + with pytest.raises(ValueError): + im.seek(0) + + def test_context_manager(): with warnings.catch_warnings(): with Image.open(static_test_file) as im: diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index fd30cded07d..db64dd1aff0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -46,6 +46,19 @@ def test_closed_file(): im.close() +def test_seek_after_close(): + im = Image.open("Tests/images/iss634.gif") + im.load() + im.close() + + with pytest.raises(ValueError): + im.is_animated + with pytest.raises(ValueError): + im.n_frames + with pytest.raises(ValueError): + im.seek(1) + + def test_context_manager(): with warnings.catch_warnings(): with Image.open(TEST_GIF) as im: diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index ca3ea84198f..d9b59321be4 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -48,6 +48,14 @@ def test_closed_file(): im.close() +def test_seek_after_close(): + im = Image.open(test_files[0]) + im.close() + + with pytest.raises(ValueError): + im.seek(1) + + def test_context_manager(): with warnings.catch_warnings(): with Image.open(test_files[0]) as im: diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 16c43b00f27..8fdae4f1371 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -70,6 +70,15 @@ def test_closed_file(self): im.load() im.close() + def test_seek_after_close(self): + im = Image.open("Tests/images/multipage.tiff") + im.close() + + with pytest.raises(ValueError): + im.n_frames + with pytest.raises(ValueError): + im.seek(1) + def test_context_manager(self): with warnings.catch_warnings(): with Image.open("Tests/images/multipage.tiff") as im: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 1409a20d213..3de2c8cd2cc 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -547,7 +547,7 @@ def __exit__(self, *args): if getattr(self, "_fp", False): if self._fp != self.fp: self._fp.close() - self._fp = None + self._fp = DeferredError(ValueError("Operation on closed image")) if self.fp: self.fp.close() self.fp = None @@ -568,7 +568,7 @@ def close(self): if getattr(self, "_fp", False): if self._fp != self.fp: self._fp.close() - self._fp = None + self._fp = DeferredError(ValueError("Operation on closed image")) if self.fp: self.fp.close() self.fp = None From e8b2b9baca9d4ad8d5d4d21028e29e8a862d3503 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 18 Apr 2022 12:53:55 +1000 Subject: [PATCH 056/326] Use python3 --- .github/workflows/test-windows.yml | 4 ++-- winbuild/build.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 6ed8bb0c5ae..30e262eebc6 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -43,8 +43,8 @@ jobs: - name: Print build system information run: python .github/workflows/system-info.py - - name: python -m pip install wheel pytest pytest-cov pytest-timeout defusedxml - run: python -m pip install wheel pytest pytest-cov pytest-timeout defusedxml + - name: python3 -m pip install wheel pytest pytest-cov pytest-timeout defusedxml + run: python3 -m pip install wheel pytest pytest-cov pytest-timeout defusedxml - name: Install dependencies id: install diff --git a/winbuild/build.rst b/winbuild/build.rst index 661c5a5ecea..6e496b9cb2a 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -91,7 +91,7 @@ Some binary dependencies (e.g. ``fribidi.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`` +Build and install Pillow, then run ``python3 -m pytest Tests`` from the root Pillow directory. Example From 232df4734e22d9373274e1c500b5f7d9fb4d4381 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 18 Apr 2022 12:28:41 +1000 Subject: [PATCH 057/326] Tests directory does not need to be specified --- winbuild/build.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/winbuild/build.rst b/winbuild/build.rst index 6e496b9cb2a..992fda58bfc 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -91,8 +91,8 @@ Some binary dependencies (e.g. ``fribidi.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 ``python3 -m pytest Tests`` -from the root Pillow directory. +Build and install Pillow, then run ``python3 -m pytest`` from the root Pillow +directory. Example ------- From 23560348c1ae359f94de2703d74ddaf58050ac87 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 18 Apr 2022 14:37:00 +1000 Subject: [PATCH 058/326] Use python3 in build system information Co-authored-by: Hugo van Kemenade --- .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 30e262eebc6..f18a6041b4b 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -41,7 +41,7 @@ jobs: cache-dependency-path: ".github/workflows/test-windows.yml" - name: Print build system information - run: python .github/workflows/system-info.py + run: python3 .github/workflows/system-info.py - name: python3 -m pip install wheel pytest pytest-cov pytest-timeout defusedxml run: python3 -m pip install wheel pytest pytest-cov pytest-timeout defusedxml From 189c9362fcfd5d0a49b0b4a82a2351a7c085299c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 18 Apr 2022 14:39:12 +1000 Subject: [PATCH 059/326] pre-commit autoupdate --- .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 353dd0c1911..fb860bbb0e9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.1.0 + rev: v4.2.0 hooks: - id: check-merge-conflict - id: check-yaml From 97e6197e9f8b1e4740528a2362299ff9fef21bef Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:17:45 -0400 Subject: [PATCH 060/326] CI: Set up cache for Cygwin pip Copied from the MIT-licensed: https://github.com/actions/cache/blob/main/examples.md#using-pip-to-get-cache-location --- .github/workflows/test-cygwin.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 8683e72d71c..e68275e4250 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -43,6 +43,19 @@ jobs: uses: egor-tensin/cleanup-path@v1 with: dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' + + - name: Get pip cache dir + id: pip-cache + run: | + bash.exe -c 'echo "::set-output name=dir::$(pip cache dir)"' + + - name: pip cache + uses: actions/cache@v3 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- - name: Build system information run: | From bd61d1ef1b1c43bececd873c8ecb346bbbd4c786 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:28:02 -0400 Subject: [PATCH 061/326] FIX, CI: Actually run pip --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index e68275e4250..0874bc01a4a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -47,7 +47,7 @@ jobs: - name: Get pip cache dir id: pip-cache run: | - bash.exe -c 'echo "::set-output name=dir::$(pip cache dir)"' + bash.exe -c 'echo "::set-output name=dir::$(python -m pip cache dir)"' - name: pip cache uses: actions/cache@v3 From 2c5e5049db72b4f6f2c1dbf28237b9fa4f565d1d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Apr 2022 07:58:12 +1000 Subject: [PATCH 062/326] Ignore compression value from BMP info dictionary --- Tests/test_file_tiff.py | 7 +++++++ src/PIL/TiffImagePlugin.py | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 16c43b00f27..1bdc4639a25 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -690,6 +690,13 @@ def test_save_icc_profile(self, tmp_path): with Image.open(outfile) as reloaded: assert reloaded.info["icc_profile"] == icc_profile + def test_save_bmp_compression(self, tmp_path): + with Image.open("Tests/images/hopper.bmp") as im: + assert im.info["compression"] == 0 + + outfile = str(tmp_path / "temp.tif") + im.save(outfile) + def test_discard_icc_profile(self, tmp_path): outfile = str(tmp_path / "temp.tif") diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 000429991d6..3a3e5c43017 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1568,7 +1568,13 @@ def _save(im, fp, filename): encoderinfo = im.encoderinfo encoderconfig = im.encoderconfig - compression = encoderinfo.get("compression", im.info.get("compression")) + try: + compression = encoderinfo["compression"] + except KeyError: + compression = im.info.get("compression") + if isinstance(compression, int): + # compression value may be from BMP. Ignore it + compression = None if compression is None: compression = "raw" elif compression == "tiff_jpeg": From be6dd712ba6a14b38f5cd609110041564ffca10d Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 20 Apr 2022 20:48:57 -0400 Subject: [PATCH 063/326] Ci: Simplify Cygwin pip cache This might pave the way for caching the Cygwin install, which might speed things up a bit. --- .github/workflows/test-cygwin.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 0874bc01a4a..70667bce60d 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -47,15 +47,16 @@ jobs: - name: Get pip cache dir id: pip-cache run: | - bash.exe -c 'echo "::set-output name=dir::$(python -m pip cache dir)"' + bash.exe -c 'cygpath -wa $(python -m pip cache dir)' + bash.exe -c 'echo "::set-output name=dir::$(cygpath -wa $(python -m pip cache dir))"' - name: pip cache uses: actions/cache@v3 with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + path: 'C:\cygwin\home\runner\.cache\pip' + key: ${{ runner.os }}-cygwin-pip-${{ hashFiles('.ci/install.sh') }} restore-keys: | - ${{ runner.os }}-pip- + ${{ runner.os }}-cygwin-pip- - name: Build system information run: | From 1e3fdb30556a2858f4740b2789f8a90e5b4bcaf2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Apr 2022 11:26:34 +1000 Subject: [PATCH 064/326] Pad COLORMAP to 768 items --- Tests/test_file_libtiff.py | 4 ++-- src/PIL/TiffImagePlugin.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index d83c584b5da..a43548ae0f3 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -497,8 +497,8 @@ def test_cmyk_save(self, tmp_path): im.save(out, compression="tiff_adobe_deflate") assert_image_equal_tofile(im, out) - def test_palette_save(self, tmp_path): - im = hopper("P") + @pytest.mark.parametrize("im", (hopper("P"), Image.new("P", (1, 1), "#000"))) + def test_palette_save(self, im, tmp_path): out = str(tmp_path / "temp.tif") TiffImagePlugin.WRITE_LIBTIFF = True diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 000429991d6..6966467d66e 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1676,7 +1676,12 @@ def _save(im, fp, filename): if im.mode in ["P", "PA"]: lut = im.im.getpalette("RGB", "RGB;L") - ifd[COLORMAP] = tuple(v * 256 for v in lut) + colormap = [] + colors = len(lut) // 3 + for i in range(3): + colormap += [v * 256 for v in lut[colors * i : colors * (i + 1)]] + colormap += [0] * (256 - colors) + ifd[COLORMAP] = colormap # data orientation stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) # aim for given strip size (64 KB by default) when using libtiff writer From c1d3bac917217b8f051ca8ad3576b18fd787b9fd Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Thu, 21 Apr 2022 10:26:12 -0400 Subject: [PATCH 065/326] CI: Fix runner username for pip cache --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 70667bce60d..afddb85518a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,7 @@ jobs: - name: pip cache uses: actions/cache@v3 with: - path: 'C:\cygwin\home\runner\.cache\pip' + path: 'C:\cygwin\home\runneradmin\.cache\pip' key: ${{ runner.os }}-cygwin-pip-${{ hashFiles('.ci/install.sh') }} restore-keys: | ${{ runner.os }}-cygwin-pip- From a40c7a6bea437a6b6aa745c6f4752d5f62df684a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 Apr 2022 07:31:20 +1000 Subject: [PATCH 066/326] If font is file-like object, do not re-read from object to get variant --- Tests/test_imagefont.py | 5 ++++- src/PIL/ImageFont.py | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0e1d1e637ad..0c50303f902 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -65,9 +65,12 @@ def _font_as_bytes(self): return font_bytes def test_font_with_filelike(self): - ImageFont.truetype( + ttf = ImageFont.truetype( self._font_as_bytes(), FONT_SIZE, layout_engine=self.LAYOUT_ENGINE ) + ttf_copy = ttf.font_variant() + assert ttf_copy.font_bytes == ttf.font_bytes + self._render(self._font_as_bytes()) # Usage note: making two fonts from the same buffer fails. # shared_bytes = self._font_as_bytes() diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 81ac03fe6b5..4799d71fb1c 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -711,8 +711,13 @@ def font_variant( :return: A FreeTypeFont object. """ + if font is None: + try: + font = BytesIO(self.font_bytes) + except AttributeError: + font = self.path return FreeTypeFont( - font=self.path if font is None else font, + font=font, size=self.size if size is None else size, index=self.index if index is None else index, encoding=self.encoding if encoding is None else encoding, From 805e8f10bad8bf032fea26148a85d942a2d66e2f Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 22 Apr 2022 08:25:46 -0400 Subject: [PATCH 067/326] CI: Try to fix Cygwin pip cache --- .github/workflows/test-cygwin.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index afddb85518a..8353714af51 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,19 +44,13 @@ jobs: with: dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' - - name: Get pip cache dir - id: pip-cache - run: | - bash.exe -c 'cygpath -wa $(python -m pip cache dir)' - bash.exe -c 'echo "::set-output name=dir::$(cygpath -wa $(python -m pip cache dir))"' - - name: pip cache uses: actions/cache@v3 with: path: 'C:\cygwin\home\runneradmin\.cache\pip' - key: ${{ runner.os }}-cygwin-pip-${{ hashFiles('.ci/install.sh') }} + key: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}-${{ hashFiles('.ci/install.sh') }} restore-keys: | - ${{ runner.os }}-cygwin-pip- + ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- - name: Build system information run: | From 7605e4601161da7f392d5db55559aade4d82565b Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 23 Apr 2022 08:22:33 -0400 Subject: [PATCH 068/326] CI: Set shell for Cygwin CI steps Allows me to avoid .ci/build_cygwin.sh --- .ci/build_cygwin.sh | 8 -------- .github/workflows/test-cygwin.yml | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 .ci/build_cygwin.sh diff --git a/.ci/build_cygwin.sh b/.ci/build_cygwin.sh deleted file mode 100644 index d621a53364f..00000000000 --- a/.ci/build_cygwin.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -set -e - -python3 -m coverage erase -make clean -CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . -python3 selftest.py diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 8353714af51..d05250a8d28 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,8 +53,9 @@ jobs: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- - name: Build system information + shell: dash.exe -l "{0}" run: | - bash.exe -c "python3 .github/workflows/system-info.py" + python3 .github/workflows/system-info.py - name: Install dependencies run: | @@ -62,20 +63,27 @@ jobs: - name: Install a different NumPy if: matrix.architecture == 'x86_64' + shell: dash.exe -l "{0}" run: | - bash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*'" + python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*' - name: Check imports + shell: dash.exe -l "{0}" run: | - bash.exe -c "python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)'" + python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)' - name: Build + shell: bash.exe --login -eo pipefail "{0}" run: | - bash.exe .ci/build_cygwin.sh + python3 -m coverage erase + make clean + CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . + python3 selftest.py - name: Rebase dlls + shell: dash.exe -l "{0}" run: | - bash.exe -c '/usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe)' + /usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe) - name: Test run: | From 1674e425a7bad3f710769c23b5bd515e66459c66 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 23 Apr 2022 08:25:58 -0400 Subject: [PATCH 069/326] CI: Sort the Cygwin requirements. --- .github/workflows/test-cygwin.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index d05250a8d28..a82c7446a09 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -28,16 +28,18 @@ jobs: with: platform: ${{ matrix.architecture }} packages: > - ImageMagick jpeg python3${{ matrix.python-minor-version }}-cffi + ImageMagick gcc-g++ ghostscript jpeg libfreetype-devel + libimagequant-devel libjpeg-devel liblapack-devel + liblcms2-devel libopenjp2-devel libraqm-devel + libtiff-devel libwebp-devel libxcb-devel libxcb-xinerama0 + make netpbm perl + python3${{ matrix.python-minor-version }}-cffi + python3${{ matrix.python-minor-version }}-cython python3${{ matrix.python-minor-version }}-devel python3${{ matrix.python-minor-version }}-numpy - python3${{ matrix.python-minor-version }}-cython liblapack-devel gcc-g++ python3${{ matrix.python-minor-version }}-sip - python3${{ matrix.python-minor-version }}-tkinter ghostscript - libfreetype-devel libimagequant-devel libjpeg-devel liblcms2-devel - libopenjp2-devel libraqm-devel libtiff-devel libwebp-devel libxcb-devel - libxcb-xinerama0 netpbm perl qt5-devel-tools xorg-server-extra zlib-devel - subversion make + python3${{ matrix.python-minor-version }}-tkinter + qt5-devel-tools subversion xorg-server-extra zlib-devel - name: Add Lapack to PATH uses: egor-tensin/cleanup-path@v1 From 46c1f9389e6d33e4e2dccf6a732c4e4ccfb0dd84 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 23 Apr 2022 08:28:57 -0400 Subject: [PATCH 070/326] CI: Update pip before installing dependencies. Avoid pip warnings. --- .github/workflows/test-cygwin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index a82c7446a09..f4e98adebe9 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -61,6 +61,7 @@ jobs: - name: Install dependencies run: | + dash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U pip" bash.exe .ci/install.sh - name: Install a different NumPy From 9fb79513a256e422c73efd19205cd0f4e8b0368f Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 23 Apr 2022 08:51:09 -0400 Subject: [PATCH 071/326] FIX, CI: Change shell to stay in repository. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f4e98adebe9..b037d33439b 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -55,7 +55,7 @@ jobs: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- - name: Build system information - shell: dash.exe -l "{0}" + shell: bash.exe --login -eo pipefail "{0}" run: | python3 .github/workflows/system-info.py From fdbab82f9eaffd232a6d8b68ef2cab16c3da6f1d Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 09:54:23 -0400 Subject: [PATCH 072/326] CI: Use dash to check system-info.py This may be a problem with using a login shell (starts in ~) rather than with using bash over dash. We'll see in a bit. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index b037d33439b..f4e98adebe9 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -55,7 +55,7 @@ jobs: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- - name: Build system information - shell: bash.exe --login -eo pipefail "{0}" + shell: dash.exe -l "{0}" run: | python3 .github/workflows/system-info.py From 91e6457360cf8bd384ce32cedd49fb2d75cfbd37 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 10:53:19 -0400 Subject: [PATCH 073/326] CI: Stop using login shell for build info I should probably avoid login shells in any step that uses a relative path. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f4e98adebe9..4d1065b489a 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -55,7 +55,7 @@ jobs: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- - name: Build system information - shell: dash.exe -l "{0}" + shell: dash.exe "{0}" run: | python3 .github/workflows/system-info.py From ed0c37d528295c31a25f786349adb0456c2807df Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:27:01 -0400 Subject: [PATCH 074/326] CI: Don't use shell for build information --- .github/workflows/test-cygwin.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 4d1065b489a..8f5dbe252ef 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -55,9 +55,8 @@ jobs: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- - name: Build system information - shell: dash.exe "{0}" run: | - python3 .github/workflows/system-info.py + dash.exe "python3 .github/workflows/system-info.py" - name: Install dependencies run: | From 41976ae125e6a7ffb4bde7b8b4e817029e4de647 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:34:13 -0400 Subject: [PATCH 075/326] FIX: Fix syntax for shell call --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 8f5dbe252ef..3205f2f68bf 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -56,7 +56,7 @@ jobs: - name: Build system information run: | - dash.exe "python3 .github/workflows/system-info.py" + dash.exe -c "python3 .github/workflows/system-info.py" - name: Install dependencies run: | From e0f4c21d0150f0b605e05ac485e3fba8a8dc33e4 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 12:36:05 -0400 Subject: [PATCH 076/326] CI: Remind bash to ignore CR in workflow --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 3205f2f68bf..fa99e04b691 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -75,7 +75,7 @@ jobs: python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)' - name: Build - shell: bash.exe --login -eo pipefail "{0}" + shell: bash.exe --login -eo pipefail -o igncr "{0}" run: | python3 -m coverage erase make clean From 88c406eac9deffcb2ad73d6c1841196268c5cf81 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 12:57:52 -0400 Subject: [PATCH 077/326] CI: Stop using login shell for build step --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index fa99e04b691..65cdb2039f2 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -75,7 +75,7 @@ jobs: python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)' - name: Build - shell: bash.exe --login -eo pipefail -o igncr "{0}" + shell: bash.exe -eo pipefail -o igncr "{0}" run: | python3 -m coverage erase make clean From 08e0d0b81766ca1f4416bbff97f9e809153a55fe Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:15:04 -0400 Subject: [PATCH 078/326] CI: Use bash for rebase step Hopefully this makes sure rebase finds all of pillow's C extension modules so rebase failures are less likely. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 65cdb2039f2..499d1f75a16 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -83,7 +83,7 @@ jobs: python3 selftest.py - name: Rebase dlls - shell: dash.exe -l "{0}" + shell: bash.exe -eo pipefail -o igncr "{0}" run: | /usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe) From 2d25e668e71c1ac6c83097953587330b67d24adc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 30 Apr 2022 10:19:48 +1000 Subject: [PATCH 079/326] Do not install NumPy on Python 3.11 --- .ci/install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index fc7d9fc1893..856cbe5f97b 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -35,12 +35,11 @@ python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma python3 -m pip install test-image-results -# TODO Remove condition when NumPy supports 3.11 -if ! [ "$GHA_PYTHON_VERSION" == "3.11-dev" ]; then python3 -m pip install numpy ; fi if [[ $(uname) != CYGWIN* ]]; then PYTHONOPTIMIZE=0 python3 -m pip install cffi - python3 -m pip install numpy + # TODO Remove condition when NumPy supports 3.11 + if ! [ "$GHA_PYTHON_VERSION" == "3.11-dev" ]; then python3 -m pip install numpy ; fi # PyQt6 doesn't support PyPy3 if [[ $GHA_PYTHON_VERSION == 3.* ]]; then From 143e57b6cddc7effc2b74e9ccd7a330520295ceb Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 29 Apr 2022 20:40:57 -0400 Subject: [PATCH 080/326] CI: Specify python minor version everywhere on Cygwin Finish the last few of these. It's usually unnecessary, but I ran into trouble once with NumPy, and will likely run into more problems if I start submitting packages. A different option would be to use alternatives to point python3 at the right python. --- .github/workflows/test-cygwin.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 499d1f75a16..aec937aa7a8 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -56,7 +56,7 @@ jobs: - name: Build system information run: | - dash.exe -c "python3 .github/workflows/system-info.py" + dash.exe -c "python3.${{ matrix.python-minor-version }} .github/workflows/system-info.py" - name: Install dependencies run: | @@ -77,10 +77,10 @@ jobs: - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | - python3 -m coverage erase + python3.${{ matrix.python-minor-version }} -m coverage erase make clean - CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . - python3 selftest.py + CFLAGS="-coverage -Werror=implicit-function-declaration" python3.${{ matrix.python-minor-version }} -m pip install -v --global-option="build_ext" . + python3.${{ matrix.python-minor-version }} selftest.py - name: Rebase dlls shell: bash.exe -eo pipefail -o igncr "{0}" From b299d7cfc27a3a3365e8a01098ff830c696dc385 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 May 2022 22:33:49 +1000 Subject: [PATCH 081/326] Simplified code by using unsigned int instead of union --- src/libImaging/Quant.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 1c6b9d6a2d7..69cbcd08674 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -1519,7 +1519,7 @@ quantize( typedef struct { Pixel new; - Pixel furthest; + uint32_t furthestV; uint32_t furthestDistance; int secondPixel; } DistanceData; @@ -1536,7 +1536,7 @@ compute_distances(const HashTable *h, const Pixel pixel, uint32_t *dist, void *u } if (oldDist > data->furthestDistance) { data->furthestDistance = oldDist; - data->furthest.v = pixel.v; + data->furthestV = pixel.v; } } @@ -1579,8 +1579,8 @@ quantize2( data.furthestDistance = 0; data.secondPixel = (i == 1) ? 1 : 0; hashtable_foreach_update(h, compute_distances, &data); - p[i].v = data.furthest.v; - data.new.v = data.furthest.v; + p[i].v = data.furthestV; + data.new.v = data.furthestV; } hashtable_free(h); From 44494a11710e4df780ae0147f7a9ef2c0a806ade Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 May 2022 23:26:54 +1000 Subject: [PATCH 082/326] Set furthestV to first v in case compute_distances does not assign it --- src/libImaging/Quant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 69cbcd08674..dfa6d842d3f 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -1577,6 +1577,7 @@ quantize2( data.new.c.b = (int)(.5 + (double)mean[2] / (double)nPixels); for (i = 0; i < nQuantPixels; i++) { data.furthestDistance = 0; + data.furthestV = pixelData[0].v; data.secondPixel = (i == 1) ? 1 : 0; hashtable_foreach_update(h, compute_distances, &data); p[i].v = data.furthestV; From ce7489884fa7951793d0d10118520017eaae214a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 2 May 2022 17:54:49 -0400 Subject: [PATCH 083/326] CI: Update Cygwin pip, but not on 32-bit * CI: Update Cygwin pip, but not on 32-bit 32-bit Cygwin pip>=22 fails to install coverage. * CI: Let .ci/install.sh handle updating pip * CI Combine pip update conditions * CI: Don't try to upgrade pip on 32-bit Cygwin --- .ci/install.sh | 3 +++ .github/workflows/test-cygwin.yml | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 856cbe5f97b..02e1474bd3f 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -23,6 +23,9 @@ if [[ $(uname) != CYGWIN* ]]; then sudo apt-get -qq install libfreetype6-dev liblcms2-dev python3-tk\ ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\ cmake meson imagemagick libharfbuzz-dev libfribidi-dev +fi + +if [[ $(uname -mo) != "i*86 Cygwin" ]]; then python3 -m pip install --upgrade pip fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index aec937aa7a8..0c175a6d4e7 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -60,7 +60,6 @@ jobs: - name: Install dependencies run: | - dash.exe -c "python3.${{ matrix.python-minor-version }} -m pip install -U pip" bash.exe .ci/install.sh - name: Install a different NumPy From 0b134250faebec08b87ccf5e51c600972f84629a Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Mon, 2 May 2022 20:38:12 -0400 Subject: [PATCH 084/326] CI: Use alternatives to select python version (#4) * CI: Use alternatives to select python version * CI: Specify full path to alternatives /usr/sbin isn't in PATH * DBG: Print uname to try to avoid bad pip install. pip>=22 on 32-bit Cygwin seems to cause problems installing coverage. I have no idea why this is, so I just skip upgrading pip there. * FIX: Fix syntax of conditional for 32-bit Cygwin I have no idea if it needs to be able to accept i386, but it will if it comes up. * FIX: Revert earlier debug change. --- .ci/install.sh | 2 +- .github/workflows/test-cygwin.yml | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index 02e1474bd3f..7364e4a196a 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -25,7 +25,7 @@ if [[ $(uname) != CYGWIN* ]]; then cmake meson imagemagick libharfbuzz-dev libfribidi-dev fi -if [[ $(uname -mo) != "i*86 Cygwin" ]]; then +if [[ $(uname -mo) != i*86" Cygwin" ]]; then python3 -m pip install --upgrade pip fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 0c175a6d4e7..c2b4159b4f4 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,10 +53,18 @@ jobs: key: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}-${{ hashFiles('.ci/install.sh') }} restore-keys: | ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- + + - name: Ensure correct python minor version used in scripts + shell: bash.exe -eo pipefail -o igncr "{0}" + run: | + /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} + /usr/sbin/alternatives --display python3 + /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} + /usr/sbin/alternatives --display python - name: Build system information run: | - dash.exe -c "python3.${{ matrix.python-minor-version }} .github/workflows/system-info.py" + dash.exe -c "python3 .github/workflows/system-info.py" - name: Install dependencies run: | @@ -66,20 +74,20 @@ jobs: if: matrix.architecture == 'x86_64' shell: dash.exe -l "{0}" run: | - python3.${{ matrix.python-minor-version }} -m pip install -U 'numpy!=1.21.*' + python3 -m pip install -U 'numpy!=1.21.*' - name: Check imports shell: dash.exe -l "{0}" run: | - python3.${{ matrix.python-minor-version }} -c 'import numpy as np; print(np.__version__)' + python3 -c 'import numpy as np; print(np.__version__)' - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | - python3.${{ matrix.python-minor-version }} -m coverage erase + python3 -m coverage erase make clean - CFLAGS="-coverage -Werror=implicit-function-declaration" python3.${{ matrix.python-minor-version }} -m pip install -v --global-option="build_ext" . - python3.${{ matrix.python-minor-version }} selftest.py + CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . + python3 selftest.py - name: Rebase dlls shell: bash.exe -eo pipefail -o igncr "{0}" From 4e12ccc63e40a9b567af3b2e1ac821f5157cddc6 Mon Sep 17 00:00:00 2001 From: Ben Rudiak-Gould Date: Sat, 30 Apr 2022 22:58:44 -0700 Subject: [PATCH 085/326] Support more affine expression forms in Image.point In modes I and F, Image.point only supported affine expressions of the forms (lambda x:) x * a, x + a, and x * a + b. Expressions like 1 - x had to be written x * -1 + 1. This rewrite, though still limited to affine transformations, supports far more expression forms, including 1 - x, (2 * x + 1) / 3, etc. --- Tests/test_image_point.py | 12 ++++++-- src/PIL/Image.py | 61 +++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 428ad116b3d..2a4218bf854 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -18,10 +18,18 @@ def test_sanity(): im.point(lambda x: x * 1) im.point(lambda x: x + 1) im.point(lambda x: x * 1 + 1) + im.point(lambda x: 0.1 + 0.2 * x) + im.point(lambda x: -x) + im.point(lambda x: x - 0.5) + im.point(lambda x: 1 - x / 2) + im.point(lambda x: (2 + x) / 3) + im.point(lambda x: 0.5) with pytest.raises(TypeError): - im.point(lambda x: x - 1) + im.point(lambda x: x * x) with pytest.raises(TypeError): - im.point(lambda x: x / 1) + im.point(lambda x: 1 / x) + with pytest.raises(TypeError): + im.point(lambda x: x // 2) def test_16bit_lut(): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 99c7ba0d1a1..e3a1eac70cb 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -431,45 +431,44 @@ def _getencoder(mode, encoder_name, args, extra=()): # Simple expression analyzer -def coerce_e(value): - return value if isinstance(value, _E) else _E(value) +# _Affine(m, b) represents the polynomial m x + b +class _Affine: + def __init__(self, m, b): + self.m = m + self.b = b - -class _E: - def __init__(self, data): - self.data = data + def __neg__(self): + return _Affine(-self.m, -self.b) def __add__(self, other): - return _E((self.data, "__add__", coerce_e(other).data)) + if isinstance(other, _Affine): + return _Affine(self.m + other.m, self.b + other.b) + return _Affine(self.m, self.b + other) + + __radd__ = __add__ + + def __sub__(self, other): + return self + -other + + def __rsub__(self, other): + return other + -self def __mul__(self, other): - return _E((self.data, "__mul__", coerce_e(other).data)) + if isinstance(other, _Affine): + return NotImplemented + return _Affine(self.m * other, self.b * other) + + __rmul__ = __mul__ + + def __truediv__(self, other): + if isinstance(other, _Affine): + return NotImplemented + return _Affine(self.m / other, self.b / other) def _getscaleoffset(expr): - stub = ["stub"] - data = expr(_E(stub)).data - try: - (a, b, c) = data # simplified syntax - if a is stub and b == "__mul__" and isinstance(c, numbers.Number): - return c, 0.0 - if a is stub and b == "__add__" and isinstance(c, numbers.Number): - return 1.0, c - except TypeError: - pass - try: - ((a, b, c), d, e) = data # full syntax - if ( - a is stub - and b == "__mul__" - and isinstance(c, numbers.Number) - and d == "__add__" - and isinstance(e, numbers.Number) - ): - return c, e - except TypeError: - pass - raise ValueError("illegal expression") + a = expr(_Affine(1.0, 0.0)) + return (a.m, a.b) if isinstance(a, _Affine) else (0.0, a) # -------------------------------------------------------------------- From 46802d5def59b6694e5243e428b6419dc8a5ab43 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 3 May 2022 09:01:23 +1000 Subject: [PATCH 086/326] Removed unused import and restored existing checks (#1) * Removed unused import * Restored existing checks * Restored coerce_e, _E and data property * Deprecated coerce_e Co-authored-by: Andrew Murray --- Tests/test_image_point.py | 9 +++++++++ src/PIL/Image.py | 35 +++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 2a4218bf854..140b7a3c916 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -1,5 +1,7 @@ import pytest +from PIL import Image + from .helper import assert_image_equal, hopper @@ -17,6 +19,7 @@ def test_sanity(): im.point(list(range(256))) im.point(lambda x: x * 1) im.point(lambda x: x + 1) + im.point(lambda x: x - 1) im.point(lambda x: x * 1 + 1) im.point(lambda x: 0.1 + 0.2 * x) im.point(lambda x: -x) @@ -24,6 +27,7 @@ def test_sanity(): im.point(lambda x: 1 - x / 2) im.point(lambda x: (2 + x) / 3) im.point(lambda x: 0.5) + im.point(lambda x: x / 1) with pytest.raises(TypeError): im.point(lambda x: x * x) with pytest.raises(TypeError): @@ -55,3 +59,8 @@ def test_f_mode(): im = hopper("F") with pytest.raises(ValueError): im.point(None) + + +def test_coerce_e_deprecation(): + with pytest.warns(DeprecationWarning): + assert Image.coerce_e(2).data == 2 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index e3a1eac70cb..114f4adb3eb 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -29,7 +29,6 @@ import io import logging import math -import numbers import os import re import struct @@ -431,19 +430,23 @@ def _getencoder(mode, encoder_name, args, extra=()): # Simple expression analyzer -# _Affine(m, b) represents the polynomial m x + b -class _Affine: - def __init__(self, m, b): - self.m = m - self.b = b +def coerce_e(value): + deprecate("coerce_e", 10) + return value if isinstance(value, _E) else _E(1, value) + + +class _E: + def __init__(self, scale, data): + self.scale = scale + self.data = data def __neg__(self): - return _Affine(-self.m, -self.b) + return _E(-self.scale, -self.data) def __add__(self, other): - if isinstance(other, _Affine): - return _Affine(self.m + other.m, self.b + other.b) - return _Affine(self.m, self.b + other) + if isinstance(other, _E): + return _E(self.scale + other.scale, self.data + other.data) + return _E(self.scale, self.data + other) __radd__ = __add__ @@ -454,21 +457,21 @@ def __rsub__(self, other): return other + -self def __mul__(self, other): - if isinstance(other, _Affine): + if isinstance(other, _E): return NotImplemented - return _Affine(self.m * other, self.b * other) + return _E(self.scale * other, self.data * other) __rmul__ = __mul__ def __truediv__(self, other): - if isinstance(other, _Affine): + if isinstance(other, _E): return NotImplemented - return _Affine(self.m / other, self.b / other) + return _E(self.scale / other, self.data / other) def _getscaleoffset(expr): - a = expr(_Affine(1.0, 0.0)) - return (a.m, a.b) if isinstance(a, _Affine) else (0.0, a) + a = expr(_E(1, 0)) + return (a.scale, a.data) if isinstance(a, _E) else (0, a) # -------------------------------------------------------------------- From 88f46f3c998e15ebec8b93df32316b95f7639432 Mon Sep 17 00:00:00 2001 From: Ben Rudiak-Gould Date: Tue, 3 May 2022 13:42:04 -0700 Subject: [PATCH 087/326] Add a comment --- src/PIL/Image.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 114f4adb3eb..09214e2f93b 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -435,6 +435,9 @@ def coerce_e(value): return value if isinstance(value, _E) else _E(1, value) +# _E(scale, offset) represents the affine transformation scale * x + offset. +# The "data" field is named for compatibility with the old implementation, +# and should be renamed once coerce_e is removed. class _E: def __init__(self, scale, data): self.scale = scale From 48f763a3785933aaed8d52a983ddae287e8f235c Mon Sep 17 00:00:00 2001 From: Ben Rudiak-Gould Date: Tue, 3 May 2022 13:53:50 -0700 Subject: [PATCH 088/326] Manually merge radarhere's additional tests --- Tests/test_image_point.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 140b7a3c916..157ecb120f0 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -28,8 +28,11 @@ def test_sanity(): im.point(lambda x: (2 + x) / 3) im.point(lambda x: 0.5) im.point(lambda x: x / 1) + im.point(lambda x: x + x) with pytest.raises(TypeError): im.point(lambda x: x * x) + with pytest.raises(TypeError): + im.point(lambda x: x / x) with pytest.raises(TypeError): im.point(lambda x: 1 / x) with pytest.raises(TypeError): From 941ff61f17ed7b6a309128fdefd9bf72937afb54 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 4 May 2022 09:23:24 -0400 Subject: [PATCH 089/326] CI: Drop 32-bit Cygwin from regular testing Cygwin recommends using 64-bit if at all possible, and will discontinue support entirely within the next year or so. This also reduces CI load, which I suppose is polite to those who provide it at no cost. --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index c2b4159b4f4..103fb10f4e6 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -9,7 +9,7 @@ jobs: fail-fast: false matrix: python-minor-version: [7, 8, 9] - architecture: ["x86", "x86_64"] + architecture: ["x86_64"] timeout-minutes: 40 From 2067f6040930e5c86bd396c4605c39b5eb5a4893 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Thu, 5 May 2022 15:21:22 -0400 Subject: [PATCH 090/326] STY: Commit suggestions from PR review. --- .ci/install.sh | 5 +---- .github/workflows/test-cygwin.yml | 12 +++++------- docs/installation.rst | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index 7364e4a196a..6d689456605 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -25,10 +25,7 @@ if [[ $(uname) != CYGWIN* ]]; then cmake meson imagemagick libharfbuzz-dev libfribidi-dev fi -if [[ $(uname -mo) != i*86" Cygwin" ]]; then - python3 -m pip install --upgrade pip -fi - +python3 -m pip install --upgrade pip python3 -m pip install --upgrade wheel python3 -m pip install coverage python3 -m pip install defusedxml diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 103fb10f4e6..61e745ea5c5 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -9,11 +9,10 @@ jobs: fail-fast: false matrix: python-minor-version: [7, 8, 9] - architecture: ["x86_64"] timeout-minutes: 40 - name: Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} + name: Python 3.${{ matrix.python-minor-version }} steps: - name: Fix line endings @@ -26,7 +25,7 @@ jobs: - name: Install Cygwin uses: cygwin/cygwin-install-action@v2 with: - platform: ${{ matrix.architecture }} + platform: x86_64 packages: > ImageMagick gcc-g++ ghostscript jpeg libfreetype-devel libimagequant-devel libjpeg-devel liblapack-devel @@ -50,9 +49,9 @@ jobs: uses: actions/cache@v3 with: path: 'C:\cygwin\home\runneradmin\.cache\pip' - key: ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}-${{ hashFiles('.ci/install.sh') }} + key: ${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}-${{ hashFiles('.ci/install.sh') }} restore-keys: | - ${{ runner.os }}-cygwin-${{ matrix.architecture }}-pip3.${{ matrix.python-minor-version }}- + ${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}- - name: Ensure correct python minor version used in scripts shell: bash.exe -eo pipefail -o igncr "{0}" @@ -71,7 +70,6 @@ jobs: bash.exe .ci/install.sh - name: Install a different NumPy - if: matrix.architecture == 'x86_64' shell: dash.exe -l "{0}" run: | python3 -m pip install -U 'numpy!=1.21.*' @@ -119,7 +117,7 @@ jobs: with: file: ./coverage.xml flags: GHA_Cygwin - name: Cygwin Python 3.${{ matrix.python-minor-version }} ${{ matrix.architecture }} + name: Cygwin Python 3.${{ matrix.python-minor-version }} success: needs: build diff --git a/docs/installation.rst b/docs/installation.rst index 48e5b94c296..8cc03f28edf 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -485,7 +485,7 @@ These platforms are built and tested for every change. | +----------------------------+---------------------+ | | 3.9 (MinGW) | x86, x86-64 | | +----------------------------+---------------------+ -| | 3.7, 3.8, 3.9 (Cygwin) | x86, x86-64 | +| | 3.7, 3.8, 3.9 (Cygwin) | x86-64 | +----------------------------------+----------------------------+---------------------+ From c824ab048fc44f7cd2f843f45b68169688bb1c9d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 6 May 2022 22:35:26 +1000 Subject: [PATCH 091/326] Fixed drawing translucent 1px high polygons --- .../imagedraw_polygon_1px_high_translucent.png | Bin 0 -> 76 bytes Tests/test_imagedraw.py | 14 ++++++++++++++ src/libImaging/Draw.c | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 Tests/images/imagedraw_polygon_1px_high_translucent.png diff --git a/Tests/images/imagedraw_polygon_1px_high_translucent.png b/Tests/images/imagedraw_polygon_1px_high_translucent.png new file mode 100644 index 0000000000000000000000000000000000000000..8bbf9397c72a4352cf74c16b0fdae378d56065f3 GIT binary patch literal 76 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c6!2~3&r&&$}Qo^1tjv*Cuk`o-5dU$wvzVh5) ZVBpqfXcTz0dNNR&!PC{xWt~$(69E385JCU| literal 0 HcmV?d00001 diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 6755d94b895..7f4a18c47bf 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -655,6 +655,20 @@ def test_polygon_1px_high(): assert_image_equal_tofile(im, expected) +def test_polygon_1px_high_translucent(): + # Test drawing a translucent 1px high polygon + # Arrange + im = Image.new("RGB", (4, 3)) + draw = ImageDraw.Draw(im, "RGBA") + expected = "Tests/images/imagedraw_polygon_1px_high_translucent.png" + + # Act + draw.polygon([(1, 1), (1, 1), (3, 1), (3, 1)], (255, 0, 0, 127)) + + # Assert + assert_image_equal_tofile(im, expected) + + def test_polygon_translucent(): # Arrange im = Image.new("RGB", (W, H)) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 86cd6c3a058..543651f7fa6 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -419,7 +419,7 @@ draw_horizontal_lines( if (e[i].ymin == y && e[i].ymin == e[i].ymax) { int xmax; int xmin = e[i].xmin; - if (*x_pos < xmin) { + if (*x_pos != -1 && *x_pos < xmin) { // Line would be after the current position continue; } @@ -540,7 +540,7 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h } qsort(xx, j, sizeof(float), x_cmp); if (hasAlpha == 1) { - int x_pos = 0; + int x_pos = j == 0 ? -1 : 0; for (i = 1; i < j; i += 2) { int x_end = ROUND_DOWN(xx[i]); if (x_end < x_pos) { From b670df828d03d410db8c2f8bb4d5346ad31ff9e7 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Fri, 6 May 2022 22:43:03 -0400 Subject: [PATCH 092/326] STY: Fix English in .github/workflows/test-cygwin.yml Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 61e745ea5c5..42f5f297595 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,7 +53,7 @@ jobs: restore-keys: | ${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}- - - name: Ensure correct python minor version used in scripts + - name: Ensure correct Python minor version is used in scripts shell: bash.exe -eo pipefail -o igncr "{0}" run: | /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} From eee481176dab05a64608079b0373cf2a3259c497 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 7 May 2022 14:52:28 +1000 Subject: [PATCH 093/326] Removed DLL rebase --- .github/workflows/test-cygwin.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 42f5f297595..2aa03b88e59 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -44,7 +44,7 @@ jobs: uses: egor-tensin/cleanup-path@v1 with: dirs: 'C:\cygwin\bin;C:\cygwin\lib\lapack' - + - name: pip cache uses: actions/cache@v3 with: @@ -52,7 +52,7 @@ jobs: key: ${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}-${{ hashFiles('.ci/install.sh') }} restore-keys: | ${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}- - + - name: Ensure correct Python minor version is used in scripts shell: bash.exe -eo pipefail -o igncr "{0}" run: | @@ -87,11 +87,6 @@ jobs: CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . python3 selftest.py - - name: Rebase dlls - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - /usr/bin/rebase --database $(find /usr{,/local}/lib/python3.${{ matrix.python-minor-version }}/site-packages src/PIL ${HOME}/.local/lib/ /usr/lib/lapack /usr/bin -name \*.dll -o -name \*.exe) - - name: Test run: | bash.exe xvfb-run -s '-screen 0 1024x768x24' .ci/test.sh From a5b20f7a3468248d7ca718cd066ebb9f9bcbdb0e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 7 May 2022 15:29:49 +1000 Subject: [PATCH 094/326] Removed setting alternative --- .github/workflows/test-cygwin.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 2aa03b88e59..ae1c0fb8b6c 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -53,14 +53,6 @@ jobs: restore-keys: | ${{ runner.os }}-cygwin-pip3.${{ matrix.python-minor-version }}- - - name: Ensure correct Python minor version is used in scripts - shell: bash.exe -eo pipefail -o igncr "{0}" - run: | - /usr/sbin/alternatives --set python3 /usr/bin/python3.${{ matrix.python-minor-version }} - /usr/sbin/alternatives --display python3 - /usr/sbin/alternatives --set python /usr/bin/python3.${{ matrix.python-minor-version }} - /usr/sbin/alternatives --display python - - name: Build system information run: | dash.exe -c "python3 .github/workflows/system-info.py" From 7c97d8457621ed205406d1abddcef739aabc2f25 Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Sat, 7 May 2022 07:42:40 -0400 Subject: [PATCH 095/326] CI: Stop testing numpy imports Install seems to be working now. Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/test-cygwin.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 42f5f297595..3ff3a15af9c 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -73,12 +73,6 @@ jobs: shell: dash.exe -l "{0}" run: | python3 -m pip install -U 'numpy!=1.21.*' - - - name: Check imports - shell: dash.exe -l "{0}" - run: | - python3 -c 'import numpy as np; print(np.__version__)' - - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | From 740695e802b50758b319e4371d79228c6dd1cf3e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 7 May 2022 22:29:18 +1000 Subject: [PATCH 096/326] Updated macOS tested Pillow versions --- docs/installation.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 75c1cb9a2c2..ad2414c840a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -501,13 +501,13 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.0.1 |arm | +| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.1.0 |arm | +----------------------------------+---------------------------+------------------+--------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | | +---------------------------+------------------+--------------+ -| | 3.7, 3.8, 3.9, 3.10 | 9.0.1 |x86-64 | -| +---------------------------+------------------+--------------+ -| | 3.6 | 8.4.0 |x86-64 | +| | 3.7, 3.8, 3.9, 3.10 | 9.1.0 |x86-64 | +| +---------------------------+------------------+ | +| | 3.6 | 8.4.0 | | +----------------------------------+---------------------------+------------------+--------------+ | macOS 10.15 Catalina | 3.6, 3.7, 3.8, 3.9 | 8.3.2 |x86-64 | | +---------------------------+------------------+ | From c6cdd4dd528bd616f84f4cbe11edc4a063ccb22a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 7 May 2022 23:13:41 +1000 Subject: [PATCH 097/326] Adjust formatting --- .github/workflows/test-cygwin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 80729efa13c..a656d64c5a0 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -65,6 +65,7 @@ jobs: shell: dash.exe -l "{0}" run: | python3 -m pip install -U 'numpy!=1.21.*' + - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | From c919db11a8d074a03e904dd9919d520d8d4b1584 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 7 May 2022 22:32:46 +1000 Subject: [PATCH 098/326] Install cffi --- .ci/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 6d689456605..c588af42ffa 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -27,6 +27,7 @@ fi python3 -m pip install --upgrade pip python3 -m pip install --upgrade wheel +PYTHONOPTIMIZE=0 python3 -m pip install cffi python3 -m pip install coverage python3 -m pip install defusedxml python3 -m pip install olefile @@ -37,7 +38,6 @@ python3 -m pip install pyroma python3 -m pip install test-image-results if [[ $(uname) != CYGWIN* ]]; then - PYTHONOPTIMIZE=0 python3 -m pip install cffi # TODO Remove condition when NumPy supports 3.11 if ! [ "$GHA_PYTHON_VERSION" == "3.11-dev" ]; then python3 -m pip install numpy ; fi From 035e95551d0221083998d7cb4d52f02b03beadb1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 8 May 2022 21:30:42 +1000 Subject: [PATCH 099/326] Use build.sh --- .ci/build.sh | 2 +- .github/workflows/test-cygwin.yml | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index a2e3041bd27..e678f68ec85 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -2,7 +2,7 @@ set -e -coverage erase +python3 -m coverage erase if [ $(uname) == "Darwin" ]; then export CPPFLAGS="-I/usr/local/miniconda/include"; fi diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index a656d64c5a0..a2795b0881f 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -69,10 +69,7 @@ jobs: - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | - python3 -m coverage erase - make clean - CFLAGS="-coverage -Werror=implicit-function-declaration" python3 -m pip install -v --global-option="build_ext" . - python3 selftest.py + bash.exe .ci/build.sh - name: Test run: | From 0c7868b8cde901092c6dc08615c7cb2d9b2be2d9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 8 May 2022 21:40:46 +1000 Subject: [PATCH 100/326] Added Cygwin badge --- README.md | 3 +++ docs/index.rst | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 7bff737a289..5e9adaf7e95 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ As of 2019, Pillow development is GitHub Actions build status (Test MinGW) + GitHub Actions build status (Test Cygwin) GitHub Actions build status (Test Docker) diff --git a/docs/index.rst b/docs/index.rst index f1a721c6ac1..5e886c2e82a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,6 +29,10 @@ Pillow for enterprise is available via the Tidelift Subscription. `Learn more Date: Mon, 9 May 2022 18:50:54 +1000 Subject: [PATCH 101/326] Populate Python palette in fromarray() --- Tests/test_image_array.py | 12 ++++++++++++ src/PIL/Image.py | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 5c9cdd7e0e0..7168c4265bb 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -80,3 +80,15 @@ def test(mode): with pytest.raises(TypeError): wrapped = Wrapper(test("L"), {"shape": (100, 128)}) Image.fromarray(wrapped) + + +def test_fromarray_palette(): + # Arrange + i = im.convert("L") + a = numpy.array(i) + + # Act + out = Image.fromarray(a, "P") + + # Assert that the Python and C palettes match + assert len(out.palette.colors) == len(out.im.getpalette()) / 3 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 99c7ba0d1a1..c141da09f49 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2838,6 +2838,10 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): if args[0] in _MAPMODES: im = new(mode, (1, 1)) im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) + if mode == "P": + from . import ImagePalette + + im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB")) im.readonly = 1 return im From 18e1005fbf0bfb4a37ea9742e1745c3791dbb4bf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 9 May 2022 21:35:28 +1000 Subject: [PATCH 102/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index de5bd8b8d21..c5bf6b5f824 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Populate Python palette in fromarray() #6283 + [radarhere] + - Raise ValueError if PNG chunks are truncated #6253 [radarhere] From 3bce17175c4b1fb55d4ae4d8cc1532e3233e46a5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 9 May 2022 22:30:04 +1000 Subject: [PATCH 103/326] Replaced test image to fix xfail --- Tests/images/decompression_bomb.ico | Bin 58 -> 198 bytes Tests/test_decompression_bomb.py | 1 - 2 files changed, 1 deletion(-) diff --git a/Tests/images/decompression_bomb.ico b/Tests/images/decompression_bomb.ico index 0efc9eaf74bc15bd5fd4babe445ff49035cd140e..2ecfa8586e8ea407f6f8232ac0d726670eaea6e6 100644 GIT binary patch literal 198 vcmZQzU<5(|0VXiLfq{WR42U&=`2T+h2Fc?C|3M0ofcy^z24XOh7K)hw6`&IK literal 58 vcmZQzU<5)1CIKL+!SI5KfkBLc0mxzi@);Pc7#Qjq83ceV28N$NIb;9;XITSG diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index 1572328de2c..d85d1f3c266 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -51,7 +51,6 @@ def test_exception(self): with Image.open(TEST_FILE): pass - @pytest.mark.xfail(reason="different exception") def test_exception_ico(self): with pytest.raises(Image.DecompressionBombError): with Image.open("Tests/images/decompression_bomb.ico"): From 1dff85a7ee0b75c10e267e88eaf0d79582dac940 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 10 May 2022 20:57:36 +1000 Subject: [PATCH 104/326] Added Fedora 36 --- .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 497b994db28..b3cfb99bb36 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -24,6 +24,7 @@ jobs: debian-10-buster-x86, debian-11-bullseye-x86, fedora-35-amd64, + fedora-36-amd64, gentoo, ubuntu-18.04-bionic-amd64, ubuntu-20.04-focal-amd64, diff --git a/docs/installation.rst b/docs/installation.rst index ad2414c840a..59d828405c3 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -463,6 +463,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Fedora 35 | 3.10 | x86-64 | +----------------------------------+----------------------------+---------------------+ +| Fedora 36 | 3.10 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 | From 30db27c3aa22f687cbc140b2dd03d0a768faf06c Mon Sep 17 00:00:00 2001 From: DWesl <22566757+DWesl@users.noreply.github.com> Date: Wed, 11 May 2022 08:01:02 -0400 Subject: [PATCH 105/326] CI: Only invoke bash once for build step. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's see if this works. Co-authored-by: Ondrej Baranovič --- .github/workflows/test-cygwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index a2795b0881f..2e8fc9c09f0 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -69,7 +69,7 @@ jobs: - name: Build shell: bash.exe -eo pipefail -o igncr "{0}" run: | - bash.exe .ci/build.sh + .ci/build.sh - name: Test run: | From b3d29e946aa4ea468922b00cdc1b41ac788ef263 Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Fri, 13 May 2022 11:33:33 -0600 Subject: [PATCH 106/326] Always use GIF89a for long comments Fix bug that allows GIFs with long comments to be written as GIF87a. --- Tests/test_file_gif.py | 3 ++- src/PIL/GifImagePlugin.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 3c2fab72278..07b5592e8b0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -804,8 +804,9 @@ def test_comment_over_255(tmp_path): im.info["comment"] = comment im.save(out) with Image.open(out) as reread: - assert reread.info["comment"] == comment + # Test that GIF89a is used for long comment + assert reread.info["version"] == b"GIF89a" def test_zero_comment_subblocks(): diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9b34a3b0e1b..3376cccaef9 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -915,7 +915,7 @@ def _get_global_header(im, info): for extensionKey in ["transparency", "duration", "loop", "comment"]: if info and extensionKey in info: if (extensionKey == "duration" and info[extensionKey] == 0) or ( - extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255) + extensionKey == "comment" and len(info[extensionKey]) == 0 ): continue version = b"89a" From 44c6467400502b69e48bbbbb0a20a70c564538fb Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Fri, 13 May 2022 11:38:39 -0600 Subject: [PATCH 107/326] Multiple GIF comments in a frame are separated If more than one comment is in a GIF frame, separate them with \r\n in the info dict. --- Tests/images/multiple_comments.gif | Bin 0 -> 1540 bytes Tests/test_file_gif.py | 6 ++++++ src/PIL/GifImagePlugin.py | 13 +++++++++---- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 Tests/images/multiple_comments.gif diff --git a/Tests/images/multiple_comments.gif b/Tests/images/multiple_comments.gif new file mode 100644 index 0000000000000000000000000000000000000000..88b2af800e89f7771c48fa9cb667097608802df0 GIT binary patch literal 1540 zcmZY8eKga190%~%R5o(^^+qKG_ROcP@hGRr((Hc!KJqRi7g z0)t9H`2P?t?onT_wMnUwpc?WA3wkkcn1K= z^xfnqJmjvVqm_%Zg&EEYql*#&0RVv9k#_Nqh(!2a#pC_MA`$34Hy8l94F(HZ0UhBc z{1Di;laW>RcunSRar0Dq(@Ym%hBjxDzm!_O3Tj^G)nOzjHjSBEPwux*NQ&K1C=IvC zg)<-dQK5@QheoO+gYN}j9jj&X`Dr2T4Y2#CIdN4jii?@7xyS>5nRZNA#C)l!-x6;d zKr6KQOePmByY-Ps?;-BUs-T~Yj3_TF+qtke+fZ3Mh@Ho0LfUGHcE$6K7trgQr#S(^ z_QpjV)?f&Y&zINfvbq#X%z(4IhMvET1uGrbcX;g^_rRHhKk~|R_=n8nrrn-lVHS+| zkHgXy+jYt(R3hB3I%WNF;WkC4r1bJxa?&IStq}x=B*iu_hJQj$!2=2ODoyoyH<<24 z%K#!W>1cWd9iq)}dAEFoYNj369$dHv8?x~TP!A2%t9WwDcP z7;HjD;`RLMo7w0h6+SfoF4YG>NK1)La~FD2u-3jC1v;wX!xCvhN4=`XI46xMcF74m zBmzt09=NN25Bh8(>NFqE$a#AsnjDj0O_0EvC6Lsp0Qp9;lMYA?3Izj9DCO3n9C2nL zv#6L|9FlBcRtPSuFRp7SKU`ZYYAFhccChLc5kR2`wynLbi(P#zz61WKv*V-zRuRG~ z6B!*d=mpPCvL5%$F20%9o>?keURi8>IPJ(=8&lHSaNPR1iQHCk|1>_Sfl@%95(O#O zY&fZO%gF8>OuhdR)d1zMh=~Fp0bh4_w?Iq4Mo6H46BBu5*fG6>$^_2RCn>6yAa?}< z_>E`j0i3)9#9u2R*x!if`gTi^)jYT!!NJEjLAigzLcJTFCY{rayThyy*Z&NvIjB-< z-j>u}N zpB0f@s%Lz^^dqo`8NQV+vsY zlM#prQvZzw(OCU$q(pWEGNwQmbTQ2pgv5-)#lV(`Yp(}FFIsn7cEVepHP=8U*6V28;QeuWRf&R z>`R(Ojx0Z(_ZiUT&SiPG{8qN+SmSMy2Y`@))9vZ&+RcEN4aB;8lR(4)R@tg;3 zvN9|*sq+JpM_dI(iuFo*W6Rz}N*$)RWT0)wCbB%F{L=gS8SmE;^G3bOx8Yb0gT0;P zs?-{>|5V0PKl{a;F>D|q@b~kO)(!O!Om$q~Gfjx9w;{^t#b`y!wS$%Z)+B|gyqp#1 z`NDC{-0GvQFXhGB^HUBEtSpmPiVm&zi#3o+iWE^pC&_Y>_M4sD5hKmPV+OCjIPXo2 HK4APe#uKGI literal 0 HcmV?d00001 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 3c2fab72278..9fb171411d2 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -813,6 +813,12 @@ def test_zero_comment_subblocks(): assert_image_equal_tofile(im, TEST_GIF) +def test_read_multiple_comments(): + with Image.open("Tests/images/multiple_comments.gif") as im: + # Multiple comments in a frame are separated not concatenated + assert im.info["comment"] == b"Test comment 1\r\nTest comment 2" + + def test_version(tmp_path): out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9b34a3b0e1b..6c2b1dedfb8 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -228,12 +228,17 @@ def _seek(self, frame, update_image=True): # # comment extension # + # Collect one comment block + comment = b"" while block: - if "comment" in info: - info["comment"] += block - else: - info["comment"] = block + comment += block block = self.data() + + # If multiple comments in frame, separate in info with \r\n + if "comment" in info: + info["comment"] += b"\r\n" + comment + else: + info["comment"] = comment s = None continue elif s[0] == 255: From 22d9095e5c74217331f6552d06ac3fef3dea0bae Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Fri, 13 May 2022 12:45:01 -0600 Subject: [PATCH 108/326] Correct placement of GIF comment Place GIF comment after Global Color table. Should go after "NETSCAPE" looping extension after pull #6211. --- Tests/test_file_gif.py | 23 +++++++++++++++++++++++ src/PIL/GifImagePlugin.py | 23 +++++++++++++---------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 3c2fab72278..1bd772dc63e 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -813,6 +813,29 @@ def test_zero_comment_subblocks(): assert_image_equal_tofile(im, TEST_GIF) +def test_write_comment(tmp_path): + out = str(tmp_path / "temp.gif") + with Image.open("Tests/images/multiple_comments.gif") as im: + im.save(out, save_all=True, comment="Test") + with Image.open(out) as reread: + # Comments written should appear only in first frame + assert reread.info["comment"] == b"Test" + for i, frame in enumerate(ImageSequence.Iterator(reread)): + assert (i == 0 and frame.info["comment"] == b"Test" or + i != 0 and "comment" not in frame.info) + + +def test_write_no_comment(tmp_path): + out = str(tmp_path / "temp.gif") + with Image.open("Tests/images/multiple_comments.gif") as im: + # Empty comment="" arg should suppress all comments + im.save(out, save_all=True, comment="") + with Image.open(out) as reread: + assert "comment" not in reread.info + for frame in ImageSequence.Iterator(reread): + assert "comment" not in frame.info + + def test_version(tmp_path): out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9b34a3b0e1b..4d785d83463 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -715,15 +715,6 @@ def _write_local_header(fp, im, offset, flags): + o8(0) ) - if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]): - fp.write(b"!" + o8(254)) # extension intro - comment = im.encoderinfo["comment"] - if isinstance(comment, str): - comment = comment.encode() - for i in range(0, len(comment), 255): - subblock = comment[i : i + 255] - fp.write(o8(len(subblock)) + subblock) - fp.write(o8(0)) if "loop" in im.encoderinfo: number_of_loops = im.encoderinfo["loop"] fp.write( @@ -929,7 +920,7 @@ def _get_global_header(im, info): palette_bytes = _get_palette_bytes(im) color_table_size = _get_color_table_size(palette_bytes) - return [ + header = [ b"GIF" # signature + version # version + o16(im.size[0]) # canvas width @@ -943,6 +934,18 @@ def _get_global_header(im, info): _get_header_palette(palette_bytes), ] + if "comment" in info and len(info["comment"]): + comment = info["comment"] + if isinstance(comment, str): + comment = comment.encode() + header.append(b"!" + o8(254)) # extension intro + for i in range(0, len(comment), 255): + subblock = comment[i : i + 255] + header.append(o8(len(subblock)) + subblock) + header.append(o8(0)) + + return header + def _write_frame_data(fp, im_frame, offset, params): try: From 815839631e7262960b0369da7661de3adc1ad3ba Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 14 May 2022 10:11:42 +1000 Subject: [PATCH 109/326] Updated openjpeg to 2.5.0 --- depends/install_openjpeg.sh | 2 +- docs/installation.rst | 3 ++- winbuild/build_prepare.py | 10 +++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/depends/install_openjpeg.sh b/depends/install_openjpeg.sh index 914e71e5396..4f4b81a628b 100755 --- a/depends/install_openjpeg.sh +++ b/depends/install_openjpeg.sh @@ -1,7 +1,7 @@ #!/bin/bash # install openjpeg -archive=openjpeg-2.4.0 +archive=openjpeg-2.5.0 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/docs/installation.rst b/docs/installation.rst index 1807ecf9fe4..199a3e2729d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -181,7 +181,8 @@ Many of Pillow's features require external libraries: * **openjpeg** provides JPEG 2000 functionality. - * Pillow has been tested with openjpeg **2.0.0**, **2.1.0**, **2.3.1** and **2.4.0**. + * Pillow has been tested with openjpeg **2.0.0**, **2.1.0**, **2.3.1**, + **2.4.0** and **2.5.0**. * Pillow does **not** support the earlier **1.5** series which ships with Debian Jessie. diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index c5fcd62ff73..1c09b6002f2 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -246,15 +246,15 @@ def cmd_msbuild( "libs": [r"Lib\MS\*.lib"], }, "openjpeg": { - "url": "https://github.com/uclouvain/openjpeg/archive/v2.4.0.tar.gz", - "filename": "openjpeg-2.4.0.tar.gz", - "dir": "openjpeg-2.4.0", + "url": "https://github.com/uclouvain/openjpeg/archive/v2.5.0.tar.gz", + "filename": "openjpeg-2.5.0.tar.gz", + "dir": "openjpeg-2.5.0", "build": [ cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")), cmd_nmake(target="clean"), cmd_nmake(target="openjp2"), - cmd_mkdir(r"{inc_dir}\openjpeg-2.4.0"), - cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.4.0"), + cmd_mkdir(r"{inc_dir}\openjpeg-2.5.0"), + cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.5.0"), ], "libs": [r"bin\*.lib"], }, From a0fa540b0b630e1c32676c200cdc17c02ef3eced Mon Sep 17 00:00:00 2001 From: Yulv-git Date: Sat, 14 May 2022 12:46:46 +0800 Subject: [PATCH 110/326] Fix some typos. --- docs/releasenotes/8.0.0.rst | 2 +- src/PIL/Image.py | 2 +- src/PIL/SpiderImagePlugin.py | 2 +- src/libImaging/GifDecode.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/releasenotes/8.0.0.rst b/docs/releasenotes/8.0.0.rst index 2ff9b3799ba..fe26580474d 100644 --- a/docs/releasenotes/8.0.0.rst +++ b/docs/releasenotes/8.0.0.rst @@ -174,7 +174,7 @@ Previously, if a BMP file was too large, an ``OSError`` would be raised. Now, Dark theme for docs ^^^^^^^^^^^^^^^^^^^ -The https://pillow.readthedocs.io documentation will use a dark theme if the the user has requested the system use one. Uses the ``prefers-color-scheme`` CSS media query. +The https://pillow.readthedocs.io documentation will use a dark theme if the user has requested the system use one. Uses the ``prefers-color-scheme`` CSS media query. diff --git a/src/PIL/Image.py b/src/PIL/Image.py index c141da09f49..fead48b29d9 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1324,7 +1324,7 @@ def getdata(self, band=None): def getextrema(self): """ - Gets the the minimum and maximum pixel values for each band in + Gets the minimum and maximum pixel values for each band in the image. :returns: For a single-band image, a 2-tuple containing the diff --git a/src/PIL/SpiderImagePlugin.py b/src/PIL/SpiderImagePlugin.py index 1a72f5c0451..d5d6f7b1842 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -15,7 +15,7 @@ # ## -# Image plugin for the Spider image format. This format is is used +# Image plugin for the Spider image format. This format is used # by the SPIDER software, in processing image data from electron # microscopy and tomography. ## diff --git a/src/libImaging/GifDecode.c b/src/libImaging/GifDecode.c index 0be4771cdeb..92b2607b4d9 100644 --- a/src/libImaging/GifDecode.c +++ b/src/libImaging/GifDecode.c @@ -125,7 +125,7 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t context->blocksize--; - /* New bits are shifted in from from the left. */ + /* New bits are shifted in from the left. */ context->bitbuffer |= (INT32)c << context->bitcount; context->bitcount += 8; From 416de882e418d64ff3e058903cd4fafabcff0d52 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 14 May 2022 15:36:51 +0000 Subject: [PATCH 111/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_gif.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 1bd772dc63e..eee432116da 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -821,8 +821,12 @@ def test_write_comment(tmp_path): # Comments written should appear only in first frame assert reread.info["comment"] == b"Test" for i, frame in enumerate(ImageSequence.Iterator(reread)): - assert (i == 0 and frame.info["comment"] == b"Test" or - i != 0 and "comment" not in frame.info) + assert ( + i == 0 + and frame.info["comment"] == b"Test" + or i != 0 + and "comment" not in frame.info + ) def test_write_no_comment(tmp_path): From 6257e788adac28f004313816b7a15b677754947b Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Sat, 14 May 2022 11:57:41 -0600 Subject: [PATCH 112/326] Update test_file_gif.py Changed to use a test image already in Images folder --- Tests/test_file_gif.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index eee432116da..762dab8dff4 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -815,7 +815,7 @@ def test_zero_comment_subblocks(): def test_write_comment(tmp_path): out = str(tmp_path / "temp.gif") - with Image.open("Tests/images/multiple_comments.gif") as im: + with Image.open("Tests/images/dispose_prev.gif") as im: im.save(out, save_all=True, comment="Test") with Image.open(out) as reread: # Comments written should appear only in first frame @@ -831,7 +831,7 @@ def test_write_comment(tmp_path): def test_write_no_comment(tmp_path): out = str(tmp_path / "temp.gif") - with Image.open("Tests/images/multiple_comments.gif") as im: + with Image.open("Tests/images/dispose_prev.gif") as im: # Empty comment="" arg should suppress all comments im.save(out, save_all=True, comment="") with Image.open(out) as reread: From b9a5944058a35e43479b726e448cec3bd54fd2ca Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 16 May 2022 08:56:45 +1000 Subject: [PATCH 113/326] Added pytest-timeout to test dependencies --- Tests/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/README.rst b/Tests/README.rst index 55464578702..2d014e5a46e 100644 --- a/Tests/README.rst +++ b/Tests/README.rst @@ -8,7 +8,7 @@ Dependencies Install:: - python3 -m pip install pytest pytest-cov + python3 -m pip install pytest pytest-cov pytest-timeout Execution --------- From 62c7ee0f00c715e51e73b338a40d4a438f6bc5ca Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 16 May 2022 22:31:49 +1000 Subject: [PATCH 114/326] Only try to connect discontiguous corners at the end of edges --- Tests/test_imagedraw.py | 6 ++++++ src/libImaging/Draw.c | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 6755d94b895..5db4fbf6134 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1452,3 +1452,9 @@ def test_discontiguous_corners_polygon(): ) expected = os.path.join(IMAGES_PATH, "discontiguous_corners_polygon.png") assert_image_similar_tofile(img, expected, 1) + + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + draw.polygon([(18, 30), (19, 31), (18, 30), (85, 30), (60, 72)], "red") + expected = "Tests/images/imagedraw_outline_polygon_RGB.png" + assert_image_similar_tofile(im, expected, 1) diff --git a/src/libImaging/Draw.c b/src/libImaging/Draw.c index 86cd6c3a058..2fcd380480a 100644 --- a/src/libImaging/Draw.c +++ b/src/libImaging/Draw.c @@ -513,7 +513,9 @@ polygon_generic(Imaging im, int n, Edge *e, int ink, int eofill, hline_handler h continue; } // Check if the two edges join to make a corner - if (xx[j-1] == (ymin - other_edge->y0) * other_edge->dx + other_edge->x0) { + if (((ymin == current->ymin && ymin == other_edge->ymin) || + (ymin == current->ymax && ymin == other_edge->ymax)) && + xx[j-1] == (ymin - other_edge->y0) * other_edge->dx + other_edge->x0) { // Determine points from the edges on the next row // Or if this is the last row, check the previous row int offset = ymin == ymax ? -1 : 1; From c7f5b4c2daa8882b95564127f174d94575c420d2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 17 May 2022 17:31:18 +1000 Subject: [PATCH 115/326] Documented deprecation --- docs/deprecations.rst | 8 ++++++++ docs/releasenotes/9.2.0.rst | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index ad030acd071..8c5b8a748d7 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -170,6 +170,14 @@ in Pillow 10 (2023-07-01). Upgrade to `PyQt6 `_ or `PySide6 `_ instead. +Image.coerce_e +~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 + +This undocumented method has been deprecated and will be removed in Pillow 10 +(2023-07-01). + Removed features ---------------- diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index c38944b10e9..db051d1881f 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -31,6 +31,14 @@ FreeTypeFont.getmask2 fill parameter The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been deprecated and will be removed in Pillow 10 (2023-07-01). +Image.coerce_e +~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 + +This undocumented method has been deprecated and will be removed in Pillow 10 +(2023-07-01). + API Changes =========== From f0353c599676d694692174e32dc3acee2912b4a0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 15 May 2022 13:51:31 +1000 Subject: [PATCH 116/326] When reading past the end of a scan line, reduce bytes left --- Tests/images/cross_scan_line_truncated.tga | Bin 0 -> 4881 bytes Tests/test_file_tga.py | 4 ++++ src/libImaging/TgaRleDecode.c | 1 + 3 files changed, 5 insertions(+) create mode 100644 Tests/images/cross_scan_line_truncated.tga diff --git a/Tests/images/cross_scan_line_truncated.tga b/Tests/images/cross_scan_line_truncated.tga new file mode 100644 index 0000000000000000000000000000000000000000..cec4357e3acb85ebfc34e88bdbd4d4621670c456 GIT binary patch literal 4881 zcmeIzyA8k~3`NmLmu!%2$YxnRe8dDPC~}O1Tv>4CYp0w^%GIvi?HoN32>h17v-{`u m>jM->Fubuffer + state->x, ptr, n); ptr += n; + bytes -= n; extra_bytes -= n; } } From 43e2ee0433779eedc0514922af896540d7bd98d6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 15 May 2022 16:18:24 +1000 Subject: [PATCH 117/326] Added release notes for 9.1.1 --- CHANGES.rst | 13 +++++++++---- docs/releasenotes/9.1.1.rst | 16 ++++++++++++++++ docs/releasenotes/index.rst | 1 + 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 docs/releasenotes/9.1.1.rst diff --git a/CHANGES.rst b/CHANGES.rst index c5bf6b5f824..b7b6fbfc696 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,10 +15,6 @@ Changelog (Pillow) [radarhere] - Adjust BITSPERSAMPLE to match SAMPLESPERPIXEL when opening TIFFs #6270 - [radarhere] - -- Do not open images with zero or negative height #6269 - [radarhere] - Search pkgconf system libs/cflags #6138 [jameshilliard, radarhere] @@ -50,6 +46,15 @@ Changelog (Pillow) - Deprecated PhotoImage.paste() box parameter #6178 [radarhere] +9.1.1 (2022-05-17) +------------------ + +- When reading past the end of a TGA scan line, reduce bytes left. CVE-2022-30595 + [radarhere] + +- Do not open images with zero or negative height #6269 + [radarhere] + 9.1.0 (2022-04-01) ------------------ diff --git a/docs/releasenotes/9.1.1.rst b/docs/releasenotes/9.1.1.rst new file mode 100644 index 00000000000..f8b155f3d6a --- /dev/null +++ b/docs/releasenotes/9.1.1.rst @@ -0,0 +1,16 @@ +9.1.1 +----- + +Security +======== + +This release addresses several security problems. + +:cve:`CVE-2022-30595`: When reading a TGA file with RLE packets that cross scan lines, +Pillow reads the information past the end of the first line without deducting that +from the length of the remaining file data. This vulnerability was introduced in Pillow +9.1.0, and can cause a heap buffer overflow. + +Opening an image with a zero or negative height has been found to bypass a +decompression bomb check. This will now raise a :py:exc:`SyntaxError` instead, in turn +raising a ``PIL.UnidentifiedImageError``. diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index db578bdb781..597c804f861 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -15,6 +15,7 @@ expected to be backported to earlier versions. :maxdepth: 2 9.2.0 + 9.1.1 9.1.0 9.0.1 9.0.0 From 1a286627b4fccda1ba30826c9cb2913a3860ec58 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 15 May 2022 16:15:29 +1000 Subject: [PATCH 118/326] Skip test_realloc_overflow unless libtiff 4.0.4 or higher --- Tests/test_file_libtiff.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index d83c584b5da..588b9b70377 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -18,6 +18,7 @@ hopper, mark_if_feature_version, skip_unless_feature, + skip_unless_feature_version, ) @@ -991,6 +992,7 @@ def test_sampleformat_not_corrupted(self): with Image.open(out) as im: im.load() + @skip_unless_feature_version("libtiff", "4.0.4") def test_realloc_overflow(self): TiffImagePlugin.READ_LIBTIFF = True with Image.open("Tests/images/tiff_overflow_rows_per_strip.tif") as im: From 385af47cf1baa850b77f022bb2c6bd4d4a3c8fb5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 17 May 2022 22:47:01 +0300 Subject: [PATCH 119/326] Include 'twine check' in 'make sdist' --- Makefile | 1 + RELEASING.md | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 437050ed4e5..ff929fee722 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,7 @@ release-test: sdist: python3 -m build --help > /dev/null 2>&1 || python3 -m pip install build python3 -m build --sdist + python3 -m twine check --strict dist/* .PHONY: test test: diff --git a/RELEASING.md b/RELEASING.md index a6049b68585..aa7511c8a41 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -24,7 +24,6 @@ Released quarterly on January 2nd, April 1st, July 1st and October 15th. * [ ] Create and check source distribution: ```bash make sdist - python3 -m twine check --strict dist/* ``` * [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions) * [ ] Check and upload all binaries and source distributions e.g.: @@ -61,7 +60,6 @@ Released as needed for security, installation or critical bug fixes. * [ ] Create and check source distribution: ```bash make sdist - python3 -m twine check --strict dist/* ``` * [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions) * [ ] Check and upload all binaries and source distributions e.g.: @@ -91,7 +89,6 @@ Released as needed privately to individual vendors for critical security-related * [ ] Create and check source distribution: ```bash make sdist - python3 -m twine check --strict dist/* ``` * [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/main/RELEASING.md#binary-distributions) * [ ] Publish the [release on GitHub](https://github.com/python-pillow/Pillow/releases) From 77aad732d162cf752f5ed267a2d92dab0ed899e7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 18 May 2022 01:11:31 +0300 Subject: [PATCH 120/326] Ensure twine is installed Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index ff929fee722..219dda1de50 100644 --- a/Makefile +++ b/Makefile @@ -85,6 +85,7 @@ release-test: sdist: python3 -m build --help > /dev/null 2>&1 || python3 -m pip install build python3 -m build --sdist + python3 -m twine --help > /dev/null 2>&1 || python3 -m pip install twine python3 -m twine check --strict dist/* .PHONY: test From 69e07c2bf95de48a8f1a36476f39540129d5e850 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 18 May 2022 10:11:52 +1000 Subject: [PATCH 121/326] Upgrade non-amd64 Ubuntu jobs to Jammy --- .github/workflows/test-docker.yml | 12 ++++++------ docs/installation.rst | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index b3cfb99bb36..2b4dc6b5232 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -11,9 +11,9 @@ jobs: matrix: docker: [ # Run slower jobs first to give them a headstart and reduce waiting time - ubuntu-20.04-focal-arm64v8, - ubuntu-20.04-focal-ppc64le, - ubuntu-20.04-focal-s390x, + ubuntu-22.04-jammy-arm64v8, + ubuntu-22.04-jammy-ppc64le, + ubuntu-22.04-jammy-s390x, # Then run the remainder alpine, amazon-2-amd64, @@ -32,11 +32,11 @@ jobs: ] dockerTag: [main] include: - - docker: "ubuntu-20.04-focal-arm64v8" + - docker: "ubuntu-22.04-jammy-arm64v8" qemu-arch: "aarch64" - - docker: "ubuntu-20.04-focal-ppc64le" + - docker: "ubuntu-22.04-jammy-ppc64le" qemu-arch: "ppc64le" - - docker: "ubuntu-20.04-focal-s390x" + - docker: "ubuntu-22.04-jammy-s390x" qemu-arch: "s390x" name: ${{ matrix.docker }} diff --git a/docs/installation.rst b/docs/installation.rst index 199a3e2729d..efde6e931df 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -475,11 +475,9 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 | | | PyPy3 | | -| +----------------------------+---------------------+ -| | 3.8 | arm64v8, ppc64le, | -| | | s390x | +----------------------------------+----------------------------+---------------------+ -| Ubuntu Linux 22.04 LTS (Jammy) | 3.10 | x86-64 | +| Ubuntu Linux 22.04 LTS (Jammy) | 3.10 | arm64v8, ppc64le, | +| | | s390x, x86-64 | +----------------------------------+----------------------------+---------------------+ | Windows Server 2016 | 3.7 | x86-64 | +----------------------------------+----------------------------+---------------------+ From 98329354e0328d1a2206b108b3d3b150821447bc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 19 May 2022 20:59:16 +1000 Subject: [PATCH 122/326] Simplified version check --- src/PIL/GifImagePlugin.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 3376cccaef9..e8551ca33f9 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -912,17 +912,16 @@ def _get_global_header(im, info): # https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp version = b"87a" - for extensionKey in ["transparency", "duration", "loop", "comment"]: - if info and extensionKey in info: - if (extensionKey == "duration" and info[extensionKey] == 0) or ( - extensionKey == "comment" and len(info[extensionKey]) == 0 - ): - continue - version = b"89a" - break - else: - if im.info.get("version") == b"89a": - version = b"89a" + if im.info.get("version") == b"89a" or ( + info + and ( + "transparency" in info + or "loop" in info + or info.get("duration") + or info.get("comment") + ) + ): + version = b"89a" background = _get_background(im, info.get("background")) From 138bd280e4768d1e7b59852c72c33ea6019e5c8e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 19 May 2022 20:59:32 +1000 Subject: [PATCH 123/326] Added check to test_comment as well --- Tests/test_file_gif.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 07b5592e8b0..a404387cbec 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -794,6 +794,9 @@ def test_comment(tmp_path): with Image.open(out) as reread: assert reread.info["comment"] == im.info["comment"].encode() + # Test that GIF89a is used for comments + assert reread.info["version"] == b"GIF89a" + def test_comment_over_255(tmp_path): out = str(tmp_path / "temp.gif") @@ -805,7 +808,8 @@ def test_comment_over_255(tmp_path): im.save(out) with Image.open(out) as reread: assert reread.info["comment"] == comment - # Test that GIF89a is used for long comment + + # Test that GIF89a is used for comments assert reread.info["version"] == b"GIF89a" From 5246a179e045d7f895125ab0f5f85af5244d9e60 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 19 May 2022 21:07:21 +1000 Subject: [PATCH 124/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b7b6fbfc696..eef1a75d0fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Support more affine expression forms in im.point() #6254 + [benrg, radarhere] + - Populate Python palette in fromarray() #6283 [radarhere] @@ -15,6 +18,7 @@ Changelog (Pillow) [radarhere] - Adjust BITSPERSAMPLE to match SAMPLESPERPIXEL when opening TIFFs #6270 + [radarhere] - Search pkgconf system libs/cflags #6138 [jameshilliard, radarhere] From 89f5a7d5af6bec020590778d31cde22560937056 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 20 May 2022 08:51:57 +1000 Subject: [PATCH 125/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index eef1a75d0fb..311404f12d8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- If font is file-like object, do not re-read from object to get variant #6234 + [radarhere] + +- Raise ValueError when trying to access internal fp after close #6213 + [radarhere] + - Support more affine expression forms in im.point() #6254 [benrg, radarhere] From 3efe34463ef8b5c9a3157867e1281b14f4b94408 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 20 May 2022 19:10:51 +1000 Subject: [PATCH 126/326] Clarified that LZW encoding is always used for GIFs [ci skip] --- docs/handbook/image-file-formats.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index c02965a05fc..6e87db561c5 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -101,8 +101,8 @@ GIF ^^^ Pillow reads GIF87a and GIF89a versions of the GIF file format. The library -writes LZW encoded files in GIF87a by default, unless GIF89a features -are used or GIF89a is already in use. +writes files in GIF87a by default, unless GIF89a features are used or GIF89a is +already in use. Files are written with LZW encoding. GIF files are initially read as grayscale (``L``) or palette mode (``P``) images. Seeking to later frames in a ``P`` image will change the image to From 533c907329b51699e70953643ba36c4a229b5c09 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 20 May 2022 19:21:12 +1000 Subject: [PATCH 127/326] Updated example in light of #3203 --- docs/handbook/image-file-formats.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 6e87db561c5..bb2f854bcad 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -245,17 +245,14 @@ Reading local images The GIF loader creates an image memory the same size as the GIF file’s *logical screen size*, and pastes the actual pixel data (the *local image*) into this -image. If you only want the actual pixel rectangle, you can manipulate the -:py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.ImageFile.ImageFile.tile` -attributes before loading the file:: +image. If you only want the actual pixel rectangle, you can crop the image:: im = Image.open(...) if im.tile[0][0] == "gif": # only read the first "local image" from this GIF file - tag, (x0, y0, x1, y1), offset, extra = im.tile[0] - im.size = (x1 - x0, y1 - y0) - im.tile = [(tag, (0, 0) + im.size, offset, extra)] + box = im.tile[0][1] + im = im.crop(box) ICNS ^^^^ From 7c031e9279084db08b9c3d8168fa6d52927bd763 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 20 May 2022 19:30:50 +1000 Subject: [PATCH 128/326] Clarified sentence [ci skip] --- docs/handbook/image-file-formats.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index bb2f854bcad..b38efaccdb6 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -17,9 +17,9 @@ When an image is opened from a file, only that instance of the image is consider have the format. Copies of the image will contain data loaded from the file, but not the file itself, meaning that it can no longer be considered to be in the original format. So if :py:meth:`~PIL.Image.Image.copy` is called on an image, or another method -internally creates a copy of the image, the ``fp`` (file pointer), along with any -methods and attributes specific to a format. The :py:attr:`~PIL.Image.Image.format` -attribute will be ``None``. +internally creates a copy of the image, then any methods or attributes specific to the +format will no longer be present. The ``fp`` (file pointer) attribute will no longer be +present, and the :py:attr:`~PIL.Image.Image.format` attribute will be ``None``. Fully supported formats ----------------------- From 5cb007fdf13f50d238e9bf51ad6e80aa0c78796b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 00:07:12 +1000 Subject: [PATCH 129/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 311404f12d8..b22d2936838 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Ignore compression value from BMP info dictionary when saving as TIFF #6231 + [radarhere] + - If font is file-like object, do not re-read from object to get variant #6234 [radarhere] From bf65544d2ecf4458b8ee9d920a9b009ee9dadc88 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 10:15:00 +1000 Subject: [PATCH 130/326] Updated harfbuzz to 4.3.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 1c09b6002f2..3d9391321a6 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -280,9 +280,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/4.2.1.zip", - "filename": "harfbuzz-4.2.1.zip", - "dir": "harfbuzz-4.2.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/4.3.0.zip", + "filename": "harfbuzz-4.3.0.zip", + "dir": "harfbuzz-4.3.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 91f0927e6f375ca2ad792d47fbe897daf55e76f7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 10:25:19 +1000 Subject: [PATCH 131/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b22d2936838..9127d19347b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Always use GIF89a for comments #6292 + [raygard, radarhere] + - Ignore compression value from BMP info dictionary when saving as TIFF #6231 [radarhere] From 7921e19a27568007cd0ec9cc060a55fb3e6b8e73 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 15:10:08 +1000 Subject: [PATCH 132/326] Updated macOS tested Pillow versions [ci skip] --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index efde6e931df..04903071f15 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -504,11 +504,11 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.1.0 |arm | +| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.1.1 |arm | +----------------------------------+---------------------------+------------------+--------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | | +---------------------------+------------------+--------------+ -| | 3.7, 3.8, 3.9, 3.10 | 9.1.0 |x86-64 | +| | 3.7, 3.8, 3.9, 3.10 | 9.1.1 |x86-64 | | +---------------------------+------------------+ | | | 3.6 | 8.4.0 | | +----------------------------------+---------------------------+------------------+--------------+ From 46a80d144a16836af304a7aaa8e620962d91ac23 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 16:35:01 +1000 Subject: [PATCH 133/326] Update transparency when remapping the palette --- Tests/test_file_gif.py | 28 ++++++++++++++++++++++------ src/PIL/GifImagePlugin.py | 2 +- src/PIL/Image.py | 3 +++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 9caea84a85c..49dc3d841bc 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -620,6 +620,7 @@ def test_dispose2_background(tmp_path): def test_transparency_in_second_frame(tmp_path): + out = str(tmp_path / "temp.gif") with Image.open("Tests/images/different_transparency.gif") as im: assert im.info["transparency"] == 0 @@ -629,14 +630,13 @@ def test_transparency_in_second_frame(tmp_path): assert_image_equal_tofile(im, "Tests/images/different_transparency_merged.png") - out = str(tmp_path / "temp.gif") im.save(out, save_all=True) - with Image.open(out) as reread: - reread.seek(reread.tell() + 1) - assert_image_equal_tofile( - reread, "Tests/images/different_transparency_merged.png" - ) + with Image.open(out) as reread: + reread.seek(reread.tell() + 1) + assert_image_equal_tofile( + reread, "Tests/images/different_transparency_merged.png" + ) def test_no_transparency_in_second_frame(): @@ -649,6 +649,22 @@ def test_no_transparency_in_second_frame(): assert img.histogram()[255] == 0 +def test_remapped_transparency(tmp_path): + out = str(tmp_path / "temp.gif") + + im = Image.new("P", (1, 2)) + im2 = im.copy() + + # Add transparency at a higher index + # so that it will be optimized to a lower index + im.putpixel((0, 1), 5) + im.info["transparency"] = 5 + im.save(out, save_all=True, append_images=[im2]) + + with Image.open(out) as reloaded: + assert reloaded.info["transparency"] == reloaded.getpixel((0, 1)) + + def test_duration(tmp_path): duration = 1000 diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index fad6cdf1bbe..cead48c19c0 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -578,9 +578,9 @@ def _write_multiple_frames(im, fp, palette): im.encoderinfo.setdefault(k, v) encoderinfo = im.encoderinfo.copy() + im_frame = _normalize_palette(im_frame, palette, encoderinfo) if "transparency" in im_frame.info: encoderinfo.setdefault("transparency", im_frame.info["transparency"]) - im_frame = _normalize_palette(im_frame, palette, encoderinfo) if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] if isinstance(disposal, (list, tuple)): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 813ac52aab4..086b2b196f9 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1934,6 +1934,9 @@ def remap_palette(self, dest_map, source_palette=None): m_im.putpalette(new_palette_bytes) m_im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes) + if "transparency" in self.info: + m_im.info["transparency"] = new_positions[self.info["transparency"]] + return m_im def _get_safe_box(self, size, resample, box): From 99f4623a8d4ca0b2117497ac5c13d4832527f2bd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 17:38:44 +1000 Subject: [PATCH 134/326] Remove transparency if it cannot be remapped --- Tests/test_image.py | 14 ++++++++++++++ src/PIL/Image.py | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 07cf6eb92b3..0a951af8aaf 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -607,6 +607,20 @@ def test_remap_palette(self): with pytest.raises(ValueError): im.remap_palette(None) + def test_remap_palette_transparency(self): + im = Image.new("P", (1, 2)) + im.putpixel((0, 1), 1) + im.info["transparency"] = 0 + + im_remapped = im.remap_palette([1, 0]) + assert im_remapped.info["transparency"] == 1 + + # Test unused transparency + im.info["transparency"] = 2 + + im_remapped = im.remap_palette([1, 0]) + assert "transparency" not in im_remapped.info + def test__new(self): im = hopper("RGB") im_p = hopper("P") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 086b2b196f9..e226f140089 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1935,7 +1935,11 @@ def remap_palette(self, dest_map, source_palette=None): m_im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes) if "transparency" in self.info: - m_im.info["transparency"] = new_positions[self.info["transparency"]] + try: + m_im.info["transparency"] = dest_map.index(self.info["transparency"]) + except ValueError: + if "transparency" in m_im.info: + del m_im.info["transparency"] return m_im From 62d5817e29b0df5dd2f86652aaa4ca4c5e415075 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 22 May 2022 14:11:11 +1000 Subject: [PATCH 135/326] Changed delimiter to \n --- Tests/test_file_gif.py | 6 +++--- src/PIL/GifImagePlugin.py | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 9fb171411d2..96fbfd2a19e 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -813,10 +813,10 @@ def test_zero_comment_subblocks(): assert_image_equal_tofile(im, TEST_GIF) -def test_read_multiple_comments(): +def test_read_multiple_comment_blocks(): with Image.open("Tests/images/multiple_comments.gif") as im: - # Multiple comments in a frame are separated not concatenated - assert im.info["comment"] == b"Test comment 1\r\nTest comment 2" + # Multiple comment blocks in a frame are separated not concatenated + assert im.info["comment"] == b"Test comment 1\nTest comment 2" def test_version(tmp_path): diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 6c2b1dedfb8..d914a0a21df 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -228,15 +228,16 @@ def _seek(self, frame, update_image=True): # # comment extension # - # Collect one comment block comment = b"" + + # Collect one comment block while block: comment += block block = self.data() - # If multiple comments in frame, separate in info with \r\n if "comment" in info: - info["comment"] += b"\r\n" + comment + # If multiple comment blocks in frame, separate with \n + info["comment"] += b"\n" + comment else: info["comment"] = comment s = None From 30a0e448c181a81b435b77655d2c86549fbb4fec Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 22 May 2022 14:54:47 +1000 Subject: [PATCH 136/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9127d19347b..4bcd9d5ff9a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Separate multiple GIF comment blocks with newlines #6294 + [raygard, radarhere] + - Always use GIF89a for comments #6292 [raygard, radarhere] From 67f5e5d272c95e6e8f61547dba022fb2d3c4daa8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 23:29:55 +1000 Subject: [PATCH 137/326] Test an empty string comment in arguments removes existing comment --- Tests/test_file_gif.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 3f7b7aeb95d..268a2349d73 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -853,15 +853,17 @@ def test_write_comment(tmp_path): ) -def test_write_no_comment(tmp_path): +def test_empty_string_comment(tmp_path): out = str(tmp_path / "temp.gif") - with Image.open("Tests/images/dispose_prev.gif") as im: - # Empty comment="" arg should suppress all comments + with Image.open("Tests/images/chi.gif") as im: + assert "comment" in im.info + + # Empty string comment should suppress existing comment im.save(out, save_all=True, comment="") - with Image.open(out) as reread: - assert "comment" not in reread.info - for frame in ImageSequence.Iterator(reread): - assert "comment" not in frame.info + + with Image.open(out) as reread: + for frame in ImageSequence.Iterator(reread): + assert "comment" not in frame.info def test_version(tmp_path): From 62d0f0e38d8a4395b33627b24454da384b78ca77 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 22 May 2022 15:30:16 +1000 Subject: [PATCH 138/326] Once comment is loaded, keep it for subsequent frames --- Tests/images/second_frame_comment.gif | Bin 0 -> 2400 bytes Tests/test_file_gif.py | 47 +++++++++++++++++--------- src/PIL/GifImagePlugin.py | 17 ++++++---- 3 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 Tests/images/second_frame_comment.gif diff --git a/Tests/images/second_frame_comment.gif b/Tests/images/second_frame_comment.gif new file mode 100644 index 0000000000000000000000000000000000000000..c8fc957911e79693933aa77329561f557f0a6cee GIT binary patch literal 2400 zcmZ?wbh9u|WMp7uXlED&qaiS&LqG@Qdr)3r;9y~3WMN@Y{3q?4pPQSSSE7)ar%;lS zs!*JooS&DXkXDqKo5}!J56nA)BRb?pJwLKT0GeNrGlbH}4v|sckLVD9W(aEl_3RI! literal 0 HcmV?d00001 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 268a2349d73..c4f634faea2 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -837,22 +837,6 @@ def test_read_multiple_comment_blocks(): assert im.info["comment"] == b"Test comment 1\nTest comment 2" -def test_write_comment(tmp_path): - out = str(tmp_path / "temp.gif") - with Image.open("Tests/images/dispose_prev.gif") as im: - im.save(out, save_all=True, comment="Test") - with Image.open(out) as reread: - # Comments written should appear only in first frame - assert reread.info["comment"] == b"Test" - for i, frame in enumerate(ImageSequence.Iterator(reread)): - assert ( - i == 0 - and frame.info["comment"] == b"Test" - or i != 0 - and "comment" not in frame.info - ) - - def test_empty_string_comment(tmp_path): out = str(tmp_path / "temp.gif") with Image.open("Tests/images/chi.gif") as im: @@ -866,6 +850,37 @@ def test_empty_string_comment(tmp_path): assert "comment" not in frame.info +def test_retain_comment_in_subsequent_frames(tmp_path): + # Test that a comment block at the beginning is kept + with Image.open("Tests/images/chi.gif") as im: + for frame in ImageSequence.Iterator(im): + assert frame.info["comment"] == b"Created with GIMP" + + with Image.open("Tests/images/second_frame_comment.gif") as im: + assert "comment" not in im.info + + # Test that a comment in the middle is read + im.seek(1) + assert im.info["comment"] == b"Comment in the second frame" + + # Test that it is still present in a later frame + im.seek(2) + assert im.info["comment"] == b"Comment in the second frame" + + # Test that rewinding removes the comment + im.seek(0) + assert "comment" not in im.info + + # Test that a saved image keeps the comment + out = str(tmp_path / "temp.gif") + with Image.open("Tests/images/dispose_prev.gif") as im: + im.save(out, save_all=True, comment="Test") + + with Image.open(out) as reread: + for frame in ImageSequence.Iterator(reread): + assert frame.info["comment"] == b"Test" + + def test_version(tmp_path): out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index f5ec610cb80..c91c1fbffb0 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -163,6 +163,8 @@ def _seek(self, frame, update_image=True): self.__frame = -1 self._fp.seek(self.__rewind) self.disposal_method = 0 + if "comment" in self.info: + del self.info["comment"] else: # ensure that the previous frame was loaded if self.tile and update_image: @@ -230,7 +232,7 @@ def _seek(self, frame, update_image=True): # comment = b"" - # Collect one comment block + # Read this comment block while block: comment += block block = self.data() @@ -395,7 +397,9 @@ def _rgb(color): ) ] - for k in ["duration", "comment", "extension", "loop"]: + if info.get("comment"): + self.info["comment"] = info["comment"] + for k in ["duration", "extension", "loop"]: if k in info: self.info[k] = info[k] elif k in self.info: @@ -929,17 +933,18 @@ def _get_global_header(im, info): # Global Color Table _get_header_palette(palette_bytes), ] + if info.get("comment"): + comment_block = b"!" + o8(254) # extension intro - if "comment" in info and len(info["comment"]): comment = info["comment"] if isinstance(comment, str): comment = comment.encode() - header.append(b"!" + o8(254)) # extension intro for i in range(0, len(comment), 255): subblock = comment[i : i + 255] - header.append(o8(len(subblock)) + subblock) - header.append(o8(0)) + comment_block += o8(len(subblock)) + subblock + comment_block += o8(0) + header.append(comment_block) return header From 9188c81e5fd36c03ba1244e054662bf34b90d40b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 24 May 2022 08:50:51 +1000 Subject: [PATCH 139/326] Once a GIF comment is loaded, it is kept for subsequent frames --- docs/handbook/image-file-formats.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index b38efaccdb6..8591ab2a600 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -156,7 +156,8 @@ The :py:meth:`~PIL.Image.open` method sets the following it will loop forever. **comment** - May not be present. A comment about the image. + May not be present. A comment about the image. This is the last comment found + before the current frame's image. **extension** May not be present. Contains application specific information. From 26c79343ec864d4b326100c543c4a1eb21c5adb5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 24 May 2022 08:55:45 +1000 Subject: [PATCH 140/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4bcd9d5ff9a..b28b0fc6ad4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Only write GIF comments at the beginning of the file #6300 + [raygard, radarhere] + - Separate multiple GIF comment blocks with newlines #6294 [raygard, radarhere] From 5cf02f816f102bcce0c76e0e57fd582bd113228e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Apr 2022 16:46:33 +1000 Subject: [PATCH 141/326] Moved Netscape extension after global color table when saving --- src/PIL/GifImagePlugin.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index c91c1fbffb0..1b317d23350 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -716,18 +716,6 @@ def _write_local_header(fp, im, offset, flags): + o8(0) ) - if "loop" in im.encoderinfo: - number_of_loops = im.encoderinfo["loop"] - fp.write( - b"!" - + o8(255) # extension intro - + o8(11) - + b"NETSCAPE2.0" - + o8(3) - + o8(1) - + o16(number_of_loops) # number of loops - + o8(0) - ) include_color_table = im.encoderinfo.get("include_color_table") if include_color_table: palette_bytes = _get_palette_bytes(im) @@ -933,6 +921,17 @@ def _get_global_header(im, info): # Global Color Table _get_header_palette(palette_bytes), ] + if "loop" in info: + header.append( + b"!" + + o8(255) # extension intro + + o8(11) + + b"NETSCAPE2.0" + + o8(3) + + o8(1) + + o16(info["loop"]) # number of loops + + o8(0) + ) if info.get("comment"): comment_block = b"!" + o8(254) # extension intro From 2457eafabdcb617628a4bb9bee9cafda332636a3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Apr 2022 16:44:23 +1000 Subject: [PATCH 142/326] Only read the number of loops from the first frame --- Tests/images/duplicate_number_of_loops.gif | Bin 0 -> 1622 bytes Tests/test_file_gif.py | 9 ++++++++- src/PIL/GifImagePlugin.py | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 Tests/images/duplicate_number_of_loops.gif diff --git a/Tests/images/duplicate_number_of_loops.gif b/Tests/images/duplicate_number_of_loops.gif new file mode 100644 index 0000000000000000000000000000000000000000..ac315ee99f312dec957aaa08cc3aa065acad4993 GIT binary patch literal 1622 zcmZ?wbhEHbWMp7uXlED&qaiS&LqPFAx1VcBu(M-;tC5}oGb0lNgAOP_K-q(VgN1>S ag@plK4KtFO_WvU~_(nZH(nA26N2~$y{0yQ1 literal 0 HcmV?d00001 diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index c4f634faea2..dcdf2179afa 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -772,9 +772,16 @@ def test_number_of_loops(tmp_path): im = Image.new("L", (100, 100), "#000") im.save(out, loop=number_of_loops) with Image.open(out) as reread: - assert reread.info["loop"] == number_of_loops + # Check that even if a subsequent GIF frame has the number of loops specified, + # only the value from the first frame is used + with Image.open("Tests/images/duplicate_number_of_loops.gif") as im: + assert im.info["loop"] == 2 + + im.seek(1) + assert im.info["loop"] == 2 + def test_background(tmp_path): out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 1b317d23350..33b968a8f3f 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -244,7 +244,7 @@ def _seek(self, frame, update_image=True): info["comment"] = comment s = None continue - elif s[0] == 255: + elif s[0] == 255 and frame == 0: # # application extension # @@ -252,7 +252,7 @@ def _seek(self, frame, update_image=True): if block[:11] == b"NETSCAPE2.0": block = self.data() if len(block) >= 3 and block[0] == 1: - info["loop"] = i16(block, 1) + self.info["loop"] = i16(block, 1) while self.data(): pass @@ -399,7 +399,7 @@ def _rgb(color): if info.get("comment"): self.info["comment"] = info["comment"] - for k in ["duration", "extension", "loop"]: + for k in ["duration", "extension"]: if k in info: self.info[k] = info[k] elif k in self.info: From 961e16fa24e288e55240a62146b7a489cb1fc82e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 25 May 2022 22:14:29 +1000 Subject: [PATCH 143/326] Link to GitHub discussions [ci skip] --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index bc958774497..ba2b7d8ed26 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v ## Bug fixes, feature additions, etc. -Please send a pull request to the `main` branch. Please include [documentation](https://pillow.readthedocs.io) and [tests](../Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new), [Gitter](https://gitter.im/python-pillow/Pillow) or irc://irc.freenode.net#pil +Please send a pull request to the `main` branch. Please include [documentation](https://pillow.readthedocs.io) and [tests](../Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new), [discussions](https://github.com/python-pillow/Pillow/discussions/new), [Gitter](https://gitter.im/python-pillow/Pillow) or irc://irc.freenode.net#pil - Fork the Pillow repository. - Create a branch from `main`. From b1885779a9cec6b2855c1cef09b015163241c5f0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 27 May 2022 07:54:54 +1000 Subject: [PATCH 144/326] Once exif data is parsed, do not reload unless it changes --- Tests/test_file_mpo.py | 9 +++++++++ Tests/test_file_tiff.py | 20 ++++++++++++++++++++ src/PIL/Image.py | 10 ++++++++++ src/PIL/MpoImagePlugin.py | 2 ++ src/PIL/TiffImagePlugin.py | 1 + 5 files changed, 42 insertions(+) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index d9b59321be4..d093f26ccd8 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -124,6 +124,15 @@ def test_parallax(): assert exif.get_ifd(0x927C)[0xB211] == -3.125 +def test_reload_exif_after_seek(): + with Image.open("Tests/images/sugarshack.mpo") as im: + exif = im.getexif() + del exif[296] + + im.seek(1) + assert 296 in exif + + def test_mp(): for test_file in test_files: with Image.open(test_file) as im: diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index d03f7c736de..8706cb950dd 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -497,6 +497,26 @@ def check_exif(exif): exif = im.getexif() check_exif(exif) + def test_modify_exif(self, tmp_path): + outfile = str(tmp_path / "temp.tif") + with Image.open("Tests/images/ifd_tag_type.tiff") as im: + exif = im.getexif() + exif[256] = 100 + + im.save(outfile, exif=exif) + + with Image.open(outfile) as im: + exif = im.getexif() + assert exif[256] == 100 + + def test_reload_exif_after_seek(self): + with Image.open("Tests/images/multipage.tiff") as im: + exif = im.getexif() + del exif[256] + im.seek(1) + + assert 256 in exif + def test_exif_frames(self): # Test that EXIF data can change across frames with Image.open("Tests/images/g4-multi.tiff") as im: diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 5b7a50e1806..6b7a4dc3b6c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1383,6 +1383,10 @@ def get_value(element): def getexif(self): if self._exif is None: self._exif = Exif() + self._exif._loaded = False + elif self._exif._loaded: + return self._exif + self._exif._loaded = True exif_info = self.info.get("exif") if exif_info is None: @@ -1407,6 +1411,12 @@ def getexif(self): return self._exif + def _reload_exif(self): + if self._exif is None or not self._exif._loaded: + return + self._exif._loaded = False + self.getexif() + def getim(self): """ Returns a capsule that points to the internal image memory. diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index fc3f8556ff7..27c30958c9c 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -82,6 +82,7 @@ def seek(self, frame): if i16(segment) == 0xFFE1: # APP1 n = i16(self.fp.read(2)) - 2 self.info["exif"] = ImageFile._safe_read(self.fp, n) + self._reload_exif() mptype = self.mpinfo[0xB002][frame]["Attribute"]["MPType"] if mptype.startswith("Large Thumbnail"): @@ -90,6 +91,7 @@ def seek(self, frame): self._size = (exif[40962], exif[40963]) elif "exif" in self.info: del self.info["exif"] + self._reload_exif() self.tile = [("jpeg", (0, 0) + self.size, self.offset, (self.mode, ""))] self.__frame = frame diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 7cfd76af0db..78a7fd14d71 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1136,6 +1136,7 @@ def _seek(self, frame): self.__frame += 1 self.fp.seek(self._frame_pos[frame]) self.tag_v2.load(self.fp) + self._reload_exif() # fill the legacy tag/ifd entries self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) self.__frame = frame From 8b84e4c3d0d5adad0126c6ed6e67d0afa1378c24 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 27 May 2022 19:02:43 +1000 Subject: [PATCH 145/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b28b0fc6ad4..6ef953eda02 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,15 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Improve transparency handling when saving GIF images #6176 + [radarhere] + +- Do not update GIF frame position until local image is found #6219 + [radarhere] + +- Netscape GIF extension belongs after the global color table #6211 + [radarhere] + - Only write GIF comments at the beginning of the file #6300 [raygard, radarhere] From 6a071f81d798bd53dbff7910c2165a876281278a Mon Sep 17 00:00:00 2001 From: Davide Consalvo Date: Fri, 27 May 2022 12:24:19 +0200 Subject: [PATCH 146/326] fixed p2pa conversion --- src/libImaging/Convert.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index ba57deca192..5dc17db60d0 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1031,7 +1031,7 @@ p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; int rgb = strcmp(palette->mode, "RGB"); for (x = 0; x < xsize; x++, in++) { - const UINT8 *rgba = &palette->palette[in[0]]; + const UINT8 *rgba = &palette->palette[in[0] * 4]; *out++ = in[0]; *out++ = in[0]; *out++ = in[0]; From 9a14be898c8b4f25706f6bc85f9f74d66a42cf0a Mon Sep 17 00:00:00 2001 From: Davide Consalvo Date: Fri, 27 May 2022 12:34:05 +0200 Subject: [PATCH 147/326] added p2pa test --- Tests/images/tiny.png | Bin 0 -> 16911 bytes Tests/test_image_convert.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 Tests/images/tiny.png diff --git a/Tests/images/tiny.png b/Tests/images/tiny.png new file mode 100644 index 0000000000000000000000000000000000000000..3d9ff56e7ef6d2ddad44cf18ec81a89a8b56f398 GIT binary patch literal 16911 zcmeI4%WvaE9LHU;iuZW9fx&6^(Dxw+NTZ{E-qqj06ZT$`v6V1xpVos5RIuTEAAQCt=5u$V8f zkxOu6wa^F~WOqAVw&{3?l~>A~E*7h7MOhJLr7D$PXC*-_^Mb;QB~B1kK~V*fP5uh? zWw2_VVXE7$jifoaS}hEMz*TvEJRYx%iz|-T=S4+PctPSNi31PLKehvH!rA_%I7k|& zg?!z!+`w{dHjJwsIETS%p%6AS{Y%C*ayy-AQ?{Sb1CIPeb9r$^;721qPw5BiM-VjW zWUkWh9lMC%M!s|C>1h23*}cAvtCch~CO;ASEzg1_ ztPQcTl;z?^RTSloH9@H~iX}y8HmhQ@*ic%6B$w9GusPBb;WsTmaJ*xT9T1Eb<_`K` zzBH{ETG0Y?HP1)kWM3_$vwgZErwcCBM2kk%^l)j`eeDPtHGZya)~Ts35m_g>8j@TQ zrIOB>qE_Jy5iCWPt6+&jwN$PYE1HnT$K;FK7!QzLmju{e1eufM9&A)4K`oVE6C_ob zhKjrv2k{*<7;7GC^kKTyalRJ@R^!HA57P1lUap7$zpNw$4 zK{}^m>m^MaPJ6s%g(PFb3wCjRB8nh8Ae2ih}!Q;zZ+dyq@C>Pm{?^ z2bM^%XkkjPw!>5Zx@81|x+qm4p~{x#3a%l$A1Xvi#uZ6%R(ZP z&u~S~FX(uajw&Eis*-Z1hxf?eO``EH>TxZqjZ#NQojgl#+mJxH9^9`ef0hy&i#=we zk7L180OX{xT1?b7~Puc~-X@J_QU9qh3}d0seR~{7&R}bym7} ze<@y_mClrJ+g4Cd@nniIRrtK9uEW8OX0-|ywl;QFLmaZ#WYcQ%*Jd$k2x?N zjy8hRAleic5fSb{ap5$GHpN9mgga1NI1QpraS;*W4ipzogJ@G+L`1j)#f8%#+7uTN z5$-^7;WUUg#YIGfJ5XFW4WdnP5fR}I6cA6E zMMQ)q$s;SLlRPJ?JuTtq~;1I2~YAleic5fSb{ap5$GHpN9mgga1NI1QpraS;*W4ipzo zgJ|Cu*YfOV5|9nQjWC8ELAdwX)&zb);VE}>&u5tPPlul+=JprQL*a|vYrXZW?zLyD z%)OuPetGxvH@jtFkNM`SA8viGRM h` Date: Sat, 28 May 2022 00:13:50 +1000 Subject: [PATCH 148/326] Added EMF and SUN [ci skip] --- docs/handbook/image-file-formats.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 8591ab2a600..1826d965f7a 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1233,6 +1233,11 @@ PSD Pillow identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0. +SUN +^^^ + +Pillow identifies and reads Sun raster files. + WAL ^^^ @@ -1247,13 +1252,13 @@ this format. By default, a Quake2 standard palette is attached to the texture. To override the palette, use the putpalette method. -WMF -^^^ +WMF, EMF +^^^^^^^^ -Pillow can identify WMF files. +Pillow can identify WMF and EMF files. -On Windows, it can read WMF files. By default, it will load the image at 72 -dpi. To load it at another resolution: +On Windows, it can read WMF and EMF files. By default, it will load the image +at 72 dpi. To load it at another resolution: .. code-block:: python @@ -1263,7 +1268,8 @@ dpi. To load it at another resolution: im.load(dpi=144) To add other read or write support, use -:py:func:`PIL.WmfImagePlugin.register_handler` to register a WMF handler. +:py:func:`PIL.WmfImagePlugin.register_handler` to register a WMF and EMF +handler. .. code-block:: python From b0bc74a2974031c64f1bd7232812672d72f45bc7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 May 2022 09:24:42 +1000 Subject: [PATCH 149/326] Only test alpha channel values --- Tests/test_image_convert.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 6b877592a94..e5639e10533 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -222,18 +222,18 @@ def test_p_la(): assert_image_similar(alpha, comparable, 5) -def test_p_pa(): +def test_p2pa_alpha(): with Image.open("Tests/images/tiny.png") as im: assert im.mode == "P" im_pa = im.convert("PA") - assert im_pa.mode == "PA" + assert im_pa.mode == "PA" - assert ( - im_pa.tobytes() == b"\x00\x00\x00\x00\x08\xff\x04\xff\x00\x00\x00\x00" - b"\x06\xff\x07\xff\x00\x00\x00\x00\x02\xff\x03\xff\x00" - b"\x00\x00\x00\x01\xff\x05\xff" - ) + im_a = im_pa.getchannel("A") + for x in range(4): + alpha = 255 if x > 1 else 0 + for y in range(4): + assert im_a.getpixel((x, y)) == alpha def test_matrix_illegal_conversion(): From cb4b5f212d5a81cc8300d5438287b5a7b9d9661d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 May 2022 15:25:19 +1000 Subject: [PATCH 150/326] Separated test --- Tests/test_imagedraw.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 5db4fbf6134..59af64bbac7 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1453,6 +1453,8 @@ def test_discontiguous_corners_polygon(): expected = os.path.join(IMAGES_PATH, "discontiguous_corners_polygon.png") assert_image_similar_tofile(img, expected, 1) + +def test_polygon(): im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) draw.polygon([(18, 30), (19, 31), (18, 30), (85, 30), (60, 72)], "red") From dc5a2a1fc9b619e6e16941e0620f6edd2a3bd9fe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 May 2022 18:36:46 +1000 Subject: [PATCH 151/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6ef953eda02..cc8996377ac 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Only try to connect discontiguous corners at the end of edges #6303 + [radarhere] + - Improve transparency handling when saving GIF images #6176 [radarhere] From 0a4a7722b532489a71a16072d4d1d73334dbce71 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 21 May 2022 10:13:10 +1000 Subject: [PATCH 152/326] Updated libtiff to 4.4.0 --- 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 efde6e931df..0b04b134e9b 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -162,7 +162,7 @@ Many of Pillow's features require external libraries: * **libtiff** provides compressed TIFF functionality - * Pillow has been tested with libtiff versions **3.x** and **4.0-4.3** + * Pillow has been tested with libtiff versions **3.x** and **4.0-4.4** * **libfreetype** provides type related services diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 3d9391321a6..a963205c156 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -143,9 +143,9 @@ def cmd_msbuild( "libs": [r"*.lib"], }, "libtiff": { - "url": "https://download.osgeo.org/libtiff/tiff-4.3.0.tar.gz", - "filename": "tiff-4.3.0.tar.gz", - "dir": "tiff-4.3.0", + "url": "https://download.osgeo.org/libtiff/tiff-4.4.0.tar.gz", + "filename": "tiff-4.4.0.tar.gz", + "dir": "tiff-4.4.0", "build": [ cmd_cmake("-DBUILD_SHARED_LIBS:BOOL=OFF"), cmd_nmake(target="clean"), From 40a918d274182b7d7c063d7797fb77d967982c4a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 28 May 2022 20:14:05 +1000 Subject: [PATCH 153/326] Set readcount to TIFF_VARIABLE for a variable number of values --- src/libImaging/TiffDecode.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index f818f19d502..3bb444c804c 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -815,11 +815,11 @@ ImagingLibTiffMergeFieldInfo( // custom fields added with ImagingLibTiffMergeFieldInfo are only used for // decoding, ignore readcount; - int readcount = 1; + int readcount = is_var_length ? TIFF_VARIABLE : 1; // we support writing a single value, or a variable number of values - int writecount = 1; + int writecount = is_var_length ? TIFF_VARIABLE : 1; // whether the first value should encode the number of values. - int passcount = 0; + int passcount = (is_var_length && field_type != TIFF_ASCII) ? 1 : 0; TIFFFieldInfo info[] = { {key, @@ -831,14 +831,6 @@ ImagingLibTiffMergeFieldInfo( passcount, "CustomField"}}; - if (is_var_length) { - info[0].field_writecount = -1; - } - - if (is_var_length && field_type != TIFF_ASCII) { - info[0].field_passcount = 1; - } - n = sizeof(info) / sizeof(info[0]); // Test for libtiff 4.0 or later, excluding libtiff 3.9.6 and 3.9.7 From 8aa1a9d291b70185146fb5bc9d202074a6eea301 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 29 May 2022 13:57:19 +0300 Subject: [PATCH 154/326] Autoupdate pre-commit monthly --- .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 353dd0c1911..d975ccf0446 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,4 +43,4 @@ repos: - id: check-yaml ci: - autoupdate_schedule: quarterly + autoupdate_schedule: monthly From a5ceb746b04f55b61b3483d3282de836c523f03e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 29 May 2022 19:10:35 +0300 Subject: [PATCH 155/326] Add sphinx-lint to pre-commit --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d975ccf0446..1837f5736f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,5 +42,10 @@ repos: - id: check-merge-conflict - id: check-yaml + - repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v0.6 + hooks: + - id: sphinx-lint + ci: autoupdate_schedule: monthly From 880dee7e1d1f0b856ae6ec32649fe35a70586ce9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 29 May 2022 19:11:21 +0300 Subject: [PATCH 156/326] Fix missing inline literal (escaped) space after literal (missing-space-after-literal) --- winbuild/build.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winbuild/build.rst b/winbuild/build.rst index 661c5a5ecea..e8dc627715c 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -42,7 +42,7 @@ behaviour of ``build_prepare.py``: If ``PYTHON`` is unset, the version of Python used to run ``build_prepare.py`` will be used. If only ``PYTHON`` is set, ``EXECUTABLE`` defaults to ``python.exe``. -* ``ARCHITECTURE`` is used to select a ``x86``, ``x64`` or ``ARM64``build. +* ``ARCHITECTURE`` is used to select a ``x86``, ``x64`` or ``ARM64`` build. By default, uses same architecture as the version of Python used to run ``build_prepare.py``. is used. * ``PILLOW_BUILD`` can be used to override the ``winbuild\build`` directory From f90caabdbc66f81b8b90700eca0a535ebdda21b7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 29 May 2022 19:23:39 +0300 Subject: [PATCH 157/326] Remove sentence fragment --- winbuild/build.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/winbuild/build.rst b/winbuild/build.rst index e8dc627715c..af7c558cf67 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -44,7 +44,6 @@ behaviour of ``build_prepare.py``: ``EXECUTABLE`` defaults to ``python.exe``. * ``ARCHITECTURE`` is used to select a ``x86``, ``x64`` or ``ARM64`` build. By default, uses same architecture as the version of Python used to run ``build_prepare.py``. - is used. * ``PILLOW_BUILD`` can be used to override the ``winbuild\build`` directory path, used to store generated build scripts and compiled libraries. **Warning:** This directory is wiped when ``build_prepare.py`` is run. From e96177451e2c0e408d7e7f1c3531f0c998dcf17c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 30 May 2022 19:36:36 +1000 Subject: [PATCH 158/326] Only import ImageFont when necessary --- src/PIL/ImageDraw.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index f9782bc501a..3824626bd6a 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -33,7 +33,7 @@ import math import numbers -from . import Image, ImageColor, ImageFont +from . import Image, ImageColor """ A simple 2D drawing interface for PIL images. @@ -667,6 +667,8 @@ def textbbox( if font is None: font = self.getfont() + from . import ImageFont + if not isinstance(font, ImageFont.FreeTypeFont): raise ValueError("Only supported for TrueType fonts") mode = "RGBA" if embedded_color else self.fontmode From 561d7ca9e05ed131a18dfd87491041ed73b08afe Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 2 Jun 2022 08:02:42 +1000 Subject: [PATCH 159/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cc8996377ac..30e1058e3e1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,15 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Pad COLORMAP to 768 items when saving TIFF #6232 + [radarhere] + +- Fix P -> PA conversion #6337 + [RedShy, radarhere] + +- Once exif data is parsed, do not reload unless it changes #6335 + [radarhere] + - Only try to connect discontiguous corners at the end of edges #6303 [radarhere] From 80b3ee79bd1791b7c867affc2b8515cf5d34f83f Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Wed, 1 Jun 2022 16:25:27 -0600 Subject: [PATCH 160/326] Update build_prepare.py Use SourceForge auto mirror capability. --- winbuild/build_prepare.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index a963205c156..09310a90403 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -89,7 +89,7 @@ def cmd_msbuild( ) -SF_MIRROR = "https://iweb.dl.sourceforge.net" +SF_PROJECTS = "https://sourceforge.net/projects" architectures = { "x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"}, @@ -107,7 +107,7 @@ def cmd_msbuild( # dependencies, listed in order of compilation deps = { "libjpeg": { - "url": SF_MIRROR + "/project/libjpeg-turbo/2.1.3/libjpeg-turbo-2.1.3.tar.gz", + "url": SF_PROJECTS + "/libjpeg-turbo/files/2.1.3/libjpeg-turbo-2.1.3.tar.gz/download", "filename": "libjpeg-turbo-2.1.3.tar.gz", "dir": "libjpeg-turbo-2.1.3", "build": [ @@ -172,7 +172,7 @@ def cmd_msbuild( "libs": [r"output\release-static\{architecture}\lib\*.lib"], }, "libpng": { - "url": SF_MIRROR + "/project/libpng/libpng16/1.6.37/lpng1637.zip", + "url": SF_PROJECTS + "/libpng/files/libpng16/1.6.37/lpng1637.zip/download", "filename": "lpng1637.zip", "dir": "lpng1637", "build": [ @@ -221,7 +221,7 @@ def cmd_msbuild( # "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"], }, "lcms2": { - "url": SF_MIRROR + "/project/lcms/lcms/2.13/lcms2-2.13.1.tar.gz", + "url": SF_PROJECTS + "/lcms/files/lcms/2.13/lcms2-2.13.1.tar.gz/download", "filename": "lcms2-2.13.1.tar.gz", "dir": "lcms2-2.13.1", "patch": { From 86e49f42eb0c134b860d687ae0b25be635267e27 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jun 2022 22:29:24 +0000 Subject: [PATCH 161/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- winbuild/build_prepare.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 09310a90403..d7e88ca4075 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -107,7 +107,8 @@ def cmd_msbuild( # dependencies, listed in order of compilation deps = { "libjpeg": { - "url": SF_PROJECTS + "/libjpeg-turbo/files/2.1.3/libjpeg-turbo-2.1.3.tar.gz/download", + "url": SF_PROJECTS + + "/libjpeg-turbo/files/2.1.3/libjpeg-turbo-2.1.3.tar.gz/download", "filename": "libjpeg-turbo-2.1.3.tar.gz", "dir": "libjpeg-turbo-2.1.3", "build": [ From b9df9662d6e71ca15f0be1df0e9130f7060b4745 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 3 Jun 2022 19:01:36 +1000 Subject: [PATCH 162/326] Include #6178 in release notes --- docs/releasenotes/9.2.0.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index db051d1881f..eddbd07a2d7 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -31,8 +31,15 @@ FreeTypeFont.getmask2 fill parameter The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been deprecated and will be removed in Pillow 10 (2023-07-01). +PhotoImage.paste box parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 9.2.0 + +The ``box`` parameter is unused. It will be removed in Pillow 10.0.0 (2023-07-01). + Image.coerce_e -~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^ .. deprecated:: 9.2.0 From 3b2f076b6f7ec4dce16caf5de72db8eb43835d05 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 4 Jun 2022 09:38:06 +0300 Subject: [PATCH 163/326] Fix WARNING: Invalid configuration value found: 'language = None'. Update your configuration to a valid langauge code. Falling back to 'en' (English). --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 2ed236b18af..bc67d936893 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,7 +68,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: From 6a5692483c936b2b87cfb8253b14935d490bfd94 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 4 Jun 2022 16:42:26 +1000 Subject: [PATCH 164/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 30e1058e3e1..a3accd4e3d9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Fixed drawing translucent 1px high polygons #6278 + [radarhere] + - Pad COLORMAP to 768 items when saving TIFF #6232 [radarhere] From 4c8aff9798b6fd2501dd96a01941dd73c7bbebf4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Jun 2022 00:12:48 +1000 Subject: [PATCH 165/326] Allow remapping P images with RGBA palettes --- Tests/test_image.py | 9 +++++++++ src/PIL/Image.py | 27 +++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 7922505185b..0f73795363d 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -604,6 +604,15 @@ def test_remap_palette(self): with Image.open("Tests/images/hopper.gif") as im: assert_image_equal(im, im.remap_palette(list(range(256)))) + # Test identity transform with an RGBA palette + im = Image.new("P", (256, 1)) + for x in range(256): + im.putpixel((x, 0), x) + im.putpalette(list(range(256)) * 4, "RGBA") + im_remapped = im.remap_palette(list(range(256))) + assert_image_equal(im, im_remapped) + assert im.palette.palette == im_remapped.palette.palette + # Test illegal image mode with hopper() as im: with pytest.raises(ValueError): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0ba2808f851..eb9239bec8c 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1867,10 +1867,15 @@ def remap_palette(self, dest_map, source_palette=None): if self.mode not in ("L", "P"): raise ValueError("illegal image mode") + bands = 3 + palette_mode = "RGB" if source_palette is None: if self.mode == "P": self.load() - source_palette = self.im.getpalette("RGB")[:768] + palette_mode = self.im.getpalettemode() + if palette_mode == "RGBA": + bands = 4 + source_palette = self.im.getpalette(palette_mode, palette_mode) else: # L-mode source_palette = bytearray(i // 3 for i in range(768)) @@ -1879,7 +1884,9 @@ def remap_palette(self, dest_map, source_palette=None): # pick only the used colors from the palette for i, oldPosition in enumerate(dest_map): - palette_bytes += source_palette[oldPosition * 3 : oldPosition * 3 + 3] + palette_bytes += source_palette[ + oldPosition * bands : oldPosition * bands + bands + ] new_positions[oldPosition] = i # replace the palette color id of all pixel with the new id @@ -1905,19 +1912,23 @@ def remap_palette(self, dest_map, source_palette=None): m_im = self.copy() m_im.mode = "P" - m_im.palette = ImagePalette.ImagePalette("RGB", palette=mapping_palette * 3) + m_im.palette = ImagePalette.ImagePalette( + palette_mode, palette=mapping_palette * bands + ) # possibly set palette dirty, then # m_im.putpalette(mapping_palette, 'L') # converts to 'P' # or just force it. # UNDONE -- this is part of the general issue with palettes - m_im.im.putpalette("RGB;L", m_im.palette.tobytes()) + m_im.im.putpalette(palette_mode + ";L", m_im.palette.tobytes()) m_im = m_im.convert("L") - # Internally, we require 768 bytes for a palette. - new_palette_bytes = palette_bytes + (768 - len(palette_bytes)) * b"\x00" - m_im.putpalette(new_palette_bytes) - m_im.palette = ImagePalette.ImagePalette("RGB", palette=palette_bytes) + # Internally, we require 256 palette entries. + new_palette_bytes = ( + palette_bytes + ((256 * bands) - len(palette_bytes)) * b"\x00" + ) + m_im.putpalette(new_palette_bytes, palette_mode) + m_im.palette = ImagePalette.ImagePalette(palette_mode, palette=palette_bytes) if "transparency" in self.info: try: From 11be1631433f252b816802aef1a3cd109bd308c7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 6 Jun 2022 22:47:58 +1000 Subject: [PATCH 166/326] Added apply_transparency() --- Tests/test_image.py | 29 +++++++++++++++++++++++++++++ src/PIL/Image.py | 22 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/Tests/test_image.py b/Tests/test_image.py index 7922505185b..24289bf517d 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -840,6 +840,35 @@ def test_zero_tobytes(self, size): im = Image.new("RGB", size) assert im.tobytes() == b"" + def test_apply_transparency(self): + im = Image.new("P", (1, 1)) + im.putpalette((0, 0, 0, 1, 1, 1)) + assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1} + + # Test that no transformation is applied without transparency + im.apply_transparency() + assert im.palette.colors == {(0, 0, 0): 0, (1, 1, 1): 1} + + # Test that a transparency index is applied + im.info["transparency"] = 0 + im.apply_transparency() + assert "transparency" not in im.info + assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 255): 1} + + # Test that existing transparency is kept + im = Image.new("P", (1, 1)) + im.putpalette((0, 0, 0, 255, 1, 1, 1, 128), "RGBA") + im.info["transparency"] = 0 + im.apply_transparency() + assert im.palette.colors == {(0, 0, 0, 0): 0, (1, 1, 1, 128): 1} + + # Test that transparency bytes are applied + with Image.open("Tests/images/pil123p.png") as im: + assert isinstance(im.info["transparency"], bytes) + assert im.palette.colors[(27, 35, 6)] == 24 + im.apply_transparency() + assert im.palette.colors[(27, 35, 6, 214)] == 24 + def test_categories_deprecation(self): with pytest.warns(DeprecationWarning): assert hopper().category == 0 diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0ba2808f851..7edb9a4c293 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1449,6 +1449,28 @@ def getpalette(self, rawmode="RGB"): rawmode = mode return list(self.im.getpalette(mode, rawmode)) + def apply_transparency(self): + """ + If a P mode image has a "transparency" key in the info dictionary, + remove the key and apply the transparency to the palette instead. + """ + if self.mode != "P" or "transparency" not in self.info: + return + + from . import ImagePalette + + palette = self.getpalette("RGBA") + transparency = self.info["transparency"] + if isinstance(transparency, bytes): + for i, alpha in enumerate(transparency): + palette[i * 4 + 3] = alpha + else: + palette[transparency * 4 + 3] = 0 + self.palette = ImagePalette.ImagePalette("RGBA", bytes(palette)) + self.palette.dirty = 1 + + del self.info["transparency"] + def getpixel(self, xy): """ Returns the pixel value at a given position. From 2394ff564cd7b3edad163a7108106b404a6d470d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jun 2022 17:31:04 +0000 Subject: [PATCH 167/326] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/Lucas-C/pre-commit-hooks: v1.1.13 → v1.2.0](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.1.13...v1.2.0) --- .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 9153202fe98..1806db54cdb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - id: yesqa - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.13 + rev: v1.2.0 hooks: - id: remove-tabs exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) From 1c0bc81a6513f9c9f93de024167699cf37f600be Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 8 Jun 2022 17:51:44 +1000 Subject: [PATCH 168/326] Revert "Skip test_realloc_overflow unless libtiff 4.0.4 or higher" --- Tests/test_file_libtiff.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index a26cd9b469f..a43548ae0f3 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -18,7 +18,6 @@ hopper, mark_if_feature_version, skip_unless_feature, - skip_unless_feature_version, ) @@ -992,7 +991,6 @@ def test_sampleformat_not_corrupted(self): with Image.open(out) as im: im.load() - @skip_unless_feature_version("libtiff", "4.0.4") def test_realloc_overflow(self): TiffImagePlugin.READ_LIBTIFF = True with Image.open("Tests/images/tiff_overflow_rows_per_strip.tif") as im: From 8e37d4c8499dd2026b51b4fbfa57bd1e3295361b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Jun 2022 09:25:27 +1000 Subject: [PATCH 169/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a3accd4e3d9..098e0abf50a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Fixed behaviour change from endian fix #6197 + [radarhere] + +- Allow remapping P images with RGBA palettes #6350 + [radarhere] + - Fixed drawing translucent 1px high polygons #6278 [radarhere] From 70a060e7658469acf33485cb0a6ed198e2d0be2f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 11 Jun 2022 22:31:07 +1000 Subject: [PATCH 170/326] Document apply_transparency() --- docs/reference/Image.rst | 1 + docs/releasenotes/9.2.0.rst | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst index 2613b6585b3..ed37521fdae 100644 --- a/docs/reference/Image.rst +++ b/docs/reference/Image.rst @@ -123,6 +123,7 @@ methods. Unless otherwise stated, all methods return a new instance of the .. automethod:: PIL.Image.Image.alpha_composite +.. automethod:: PIL.Image.Image.apply_transparency .. automethod:: PIL.Image.Image.convert The following example converts an RGB image (linearly calibrated according to diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index eddbd07a2d7..424fd487a29 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -57,10 +57,13 @@ TODO API Additions ============= -TODO -^^^^ +Image.apply_transparency +^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +Added :py:meth:`~PIL.Image.Image.apply_transparency`, a method to take a P mode image +with "transparency" in ``im.info``, and apply the transparency to the palette instead. +The image's palette mode will become "RGBA", and "transparency" will be removed from +``im.info``. Security ======== From c083eadccbc257fa37c20a7beb890664f2f614d8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 12 Jun 2022 12:14:21 +1000 Subject: [PATCH 171/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 098e0abf50a..7f998cea184 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Added apply_transparency() #6352 + [radarhere] + - Fixed behaviour change from endian fix #6197 [radarhere] From a48d95061da0a7e23d24b7b8501a90a9fd027e52 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 12 Jun 2022 16:00:31 +1000 Subject: [PATCH 172/326] Use gnome-screenshot on Linux if available --- src/PIL/ImageGrab.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index eb21ac39948..54e01aa03e9 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -15,15 +15,14 @@ # See the README file for information on usage and redistribution. # +import os +import shutil +import subprocess import sys +import tempfile from . import Image -if sys.platform == "darwin": - import os - import subprocess - import tempfile - def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=None): if xdisplay is None: @@ -62,6 +61,18 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N left, top, right, bottom = bbox im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) return im + elif not Image.core.HAVE_XCB and shutil.which("gnome-screenshot"): + fh, filepath = tempfile.mkstemp(".png") + os.close(fh) + subprocess.call(["gnome-screenshot", "-f", filepath]) + im = Image.open(filepath) + im.load() + os.unlink(filepath) + if bbox: + im_cropped = im.crop(bbox) + im.close() + return im_cropped + return im # use xdisplay=None for default display on non-win32/macOS systems if not Image.core.HAVE_XCB: raise OSError("Pillow was built without XCB support") From b1ba0909edb8f4c2c7815397c5e39c6a36e3bbb3 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 13 Jun 2022 09:56:52 +1000 Subject: [PATCH 173/326] Prefer gnome-screenshot if xdisplay is None MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondrej Baranovič --- src/PIL/ImageGrab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageGrab.py b/src/PIL/ImageGrab.py index 54e01aa03e9..38074cb1b0d 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -61,7 +61,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N left, top, right, bottom = bbox im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) return im - elif not Image.core.HAVE_XCB and shutil.which("gnome-screenshot"): + elif shutil.which("gnome-screenshot"): fh, filepath = tempfile.mkstemp(".png") os.close(fh) subprocess.call(["gnome-screenshot", "-f", filepath]) From 463d18191a5f3e41003f2dc1530dd1a9b139dacc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jun 2022 10:16:30 +1000 Subject: [PATCH 174/326] Document use of gnome-screenshot --- docs/reference/ImageGrab.rst | 5 ++++- docs/releasenotes/9.2.0.rst | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst index ac83b225522..3086ba8c311 100644 --- a/docs/reference/ImageGrab.rst +++ b/docs/reference/ImageGrab.rst @@ -15,7 +15,10 @@ or the clipboard to a PIL image memory. 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)) + On Linux, if ``xdisplay`` is ``None`` then ``gnome-screenshot`` will be used if it + is installed. To capture the default X11 display instead, pass ``xdisplay=""``. + + .. versionadded:: 1.1.3 (Windows), 3.0.0 (macOS), 7.1.0 (Linux) :param bbox: What region to copy. Default is the entire screen. Note that on Windows OS, the top-left point may be negative if ``all_screens=True`` is used. diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index 424fd487a29..20e6cfa950f 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -76,7 +76,9 @@ TODO Other Changes ============= -TODO -^^^^ +Using gnome-screenshot on Linux +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +In :py:meth:`~PIL.ImageGrab.grab` on Linux, if ``xdisplay`` is ``None`` then +``gnome-screenshot`` will be used to capture the display if it is installed. To capture +the default X11 display instead, pass ``xdisplay=""``. From c4d51fb2681c2434fd324098d116a66013549de7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jun 2022 19:55:25 +1000 Subject: [PATCH 175/326] Added support for PPM arbitrary maxval in plain formats --- Tests/test_file_ppm.py | 16 +++++++- src/PIL/PpmImagePlugin.py | 83 +++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 627ed9d0fbf..4e5729598a6 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -22,6 +22,21 @@ def test_sanity(): @pytest.mark.parametrize( "data, mode, pixels", ( + (b"P2 3 1 4 0 2 4", "L", (0, 128, 255)), + (b"P2 3 1 257 0 128 257", "I", (0, 32640, 65535)), + # P3 with maxval < 255 + ( + b"P3 3 1 17 0 1 2 8 9 10 15 16 17", + "RGB", + ((0, 15, 30), (120, 135, 150), (225, 240, 255)), + ), + # P3 with maxval > 255 + # Scale down to 255, since there is no RGB mode with more than 8-bit + ( + b"P3 3 1 257 0 1 2 128 129 130 256 257 257", + "RGB", + ((0, 1, 2), (127, 128, 129), (254, 255, 255)), + ), (b"P5 3 1 4 \x00\x02\x04", "L", (0, 128, 255)), (b"P5 3 1 257 \x00\x00\x00\x80\x01\x01", "I", (0, 32640, 65535)), # P6 with maxval < 255 @@ -35,7 +50,6 @@ def test_sanity(): ), ), # P6 with maxval > 255 - # Scale down to 255, since there is no RGB mode with more than 8-bit ( b"P6 3 1 257 \x00\x00\x00\x01\x00\x02" b"\x00\x80\x00\x81\x00\x82\x01\x00\x01\x01\xFF\xFF", diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index b512701d32e..7df75fb386a 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -105,6 +105,8 @@ def _open(self): maxval = None decoder_name = "raw" + if magic_number in (b"P1", b"P2", b"P3"): + decoder_name = "ppm_plain" for ix in range(3): token = int(self._read_token()) if ix == 0: # token is the x size @@ -126,14 +128,13 @@ def _open(self): if maxval > 255 and mode == "L": self.mode = "I" - # If maxval matches a bit depth, use the raw decoder directly - if maxval == 65535 and mode == "L": - rawmode = "I;16B" - elif maxval != 255: - decoder_name = "ppm" + if decoder_name != "ppm_plain": + # If maxval matches a bit depth, use the raw decoder directly + if maxval == 65535 and mode == "L": + rawmode = "I;16B" + elif maxval != 255: + decoder_name = "ppm" - if magic_number in (b"P1", b"P2", b"P3"): - decoder_name = "ppm_plain" args = (rawmode, 0, 1) if decoder_name == "raw" else (rawmode, maxval) self._size = xsize, ysize self.tile = [(decoder_name, (0, 0, xsize, ysize), self.fp.tell(), args)] @@ -156,7 +157,7 @@ def _find_comment_end(self, block, start=0): def _ignore_comments(self, block): """ - Deletes comments from block. + Delete comments from block. If comment does not end in this block, raises a flag. """ comment_spans = False @@ -176,14 +177,14 @@ def _ignore_comments(self, block): def _decode_bitonal(self): """ - This is a separate method because the plain PBM format all data tokens - are exactly one byte, and so the inter-token whitespace is optional. + This is a separate method because in the plain PBM format, all data tokens are + exactly one byte, so the inter-token whitespace is optional. """ - decoded_data = bytearray() + data = bytearray() total_bytes = self.state.xsize * self.state.ysize comment_spans = False - while len(decoded_data) != total_bytes: + while len(data) != total_bytes: block = self._read_block() # read next block if not block: # eof @@ -193,7 +194,7 @@ def _decode_bitonal(self): comment_end = self._find_comment_end(block) if comment_end != -1: # comment ends in this block block = block[comment_end + 1 :] # delete tail of previous comment - comment_spans = False + break else: # comment spans whole block block = self._read_block() @@ -203,19 +204,21 @@ def _decode_bitonal(self): for token in tokens: if token not in (48, 49): raise ValueError(f"Invalid token for this mode: {bytes([token])}") - decoded_data = (decoded_data + tokens)[:total_bytes] + data = (data + tokens)[:total_bytes] invert = bytes.maketrans(b"01", b"\xFF\x00") - return decoded_data.translate(invert) + return data.translate(invert) - def _decode_blocks(self, channels, depth, maxval): - decoded_data = bytearray() + def _decode_blocks(self, maxval): + data = bytearray() max_len = 10 - bytes_per_sample = depth // 8 - total_bytes = self.state.xsize * self.state.ysize * channels * bytes_per_sample + out_byte_count = 4 if self.mode == "I" else 1 + out_max = 65535 if self.mode == "I" else 255 + bands = Image.getmodebands(self.mode) + total_bytes = self.state.xsize * self.state.ysize * bands * out_byte_count comment_spans = False half_token = False - while len(decoded_data) != total_bytes: + while len(data) != total_bytes: block = self._read_block() # read next block if not block: if half_token: @@ -251,31 +254,24 @@ def _decode_blocks(self, channels, depth, maxval): raise ValueError( f"Token too long found in data: {token[:max_len + 1]}" ) - token = int(token) - if token > maxval: - raise ValueError(f"Channel value too large for this mode: {token}") - decoded_data += token.to_bytes(bytes_per_sample, "big") - if len(decoded_data) == total_bytes: # finished! + value = int(token) + if value > maxval: + raise ValueError(f"Channel value too large for this mode: {value}") + value = round(value / maxval * out_max) + data += o32(value) if self.mode == "I" else o8(value) + if len(data) == total_bytes: # finished! break - return decoded_data + return data def decode(self, buffer): - rawmode, maxval = self.args - if self.mode == "1": - decoded_data = self._decode_bitonal() + data = self._decode_bitonal() rawmode = "1;8" - elif self.mode == "L": - decoded_data = self._decode_blocks(1, 8, maxval) - elif self.mode == "I": - if rawmode == "I;16B": - decoded_data = self._decode_blocks(1, 16, maxval) - elif rawmode == "I;32B": - decoded_data = self._decode_blocks(1, 32, maxval) - elif self.mode == "RGB": - decoded_data = self._decode_blocks(3, 8, maxval) - - self.set_as_raw(bytes(decoded_data), rawmode) + else: + maxval = self.args[-1] + data = self._decode_blocks(maxval) + rawmode = "I;32" if self.mode == "I" else self.mode + self.set_as_raw(bytes(data), rawmode) return -1, 0 @@ -284,7 +280,7 @@ class PpmDecoder(ImageFile.PyDecoder): def decode(self, buffer): data = bytearray() - maxval = min(self.args[-1], 65535) + maxval = self.args[-1] in_byte_count = 1 if maxval < 256 else 2 out_byte_count = 4 if self.mode == "I" else 1 out_max = 65535 if self.mode == "I" else 255 @@ -301,7 +297,7 @@ def decode(self, buffer): value = min(out_max, round(value / maxval * out_max)) data += o32(value) if self.mode == "I" else o8(value) rawmode = "I;32" if self.mode == "I" else self.mode - self.set_as_raw(bytes(data), (rawmode, 0, 1)) + self.set_as_raw(bytes(data), rawmode) return -1, 0 @@ -337,11 +333,12 @@ def _save(im, fp, filename): # # -------------------------------------------------------------------- -Image.register_decoder("ppm_plain", PpmPlainDecoder) + Image.register_open(PpmImageFile.format, PpmImageFile, _accept) Image.register_save(PpmImageFile.format, _save) Image.register_decoder("ppm", PpmDecoder) +Image.register_decoder("ppm_plain", PpmPlainDecoder) Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm"]) From 216cd374dd5a3ab3af0537c3021de5eeffa1ea51 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 14 Jun 2022 21:39:26 +1000 Subject: [PATCH 176/326] Parametrized tests --- Tests/test_file_ppm.py | 120 ++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 75 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 4e5729598a6..40495bf10b4 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -99,28 +99,29 @@ def test_pnm(tmp_path): assert_image_equal_tofile(im, f) -def test_plain_pbm(tmp_path): - # P1 - with Image.open("Tests/images/hopper_1bit_plain.pbm") as im: - # P4 - assert_image_equal_tofile(im, "Tests/images/hopper_1bit.pbm") - - -def test_8bit_plain_pgm(tmp_path): - # P2 - with Image.open("Tests/images/hopper_8bit_plain.pgm") as im: - # P5 - assert_image_equal_tofile(im, "Tests/images/hopper_8bit.pgm") - - -def test_8bit_plain_ppm(tmp_path): - # P3 - with Image.open("Tests/images/hopper_8bit_plain.ppm") as im: - # P6 - assert_image_equal_tofile(im, "Tests/images/hopper_8bit.ppm") +@pytest.mark.parametrize( + "plain_path, raw_path", + ( + ( + "Tests/images/hopper_1bit_plain.pbm", # P1 + "Tests/images/hopper_1bit.pbm", # P4 + ), + ( + "Tests/images/hopper_8bit_plain.pgm", # P2 + "Tests/images/hopper_8bit.pgm", # P5 + ), + ( + "Tests/images/hopper_8bit_plain.ppm", # P3 + "Tests/images/hopper_8bit.ppm", # P6 + ), + ), +) +def test_plain(plain_path, raw_path): + with Image.open(plain_path) as im: + assert_image_equal_tofile(im, raw_path) -def test_16bit_plain_pgm(tmp_path): +def test_16bit_plain_pgm(): # P2 with maxval 2 ** 16 - 1 with Image.open("Tests/images/hopper_16bit_plain.pgm") as im: assert im.mode == "I" @@ -131,86 +132,55 @@ def test_16bit_plain_pgm(tmp_path): assert_image_equal_tofile(im, "Tests/images/hopper_16bit.pgm") -def test_plain_pbm_data_with_comments(tmp_path): - path1 = str(tmp_path / "temp1.ppm") - path2 = str(tmp_path / "temp2.ppm") - comment = b"# veeery long comment" * 10**6 - with open(path1, "wb") as f1, open(path2, "wb") as f2: - f1.write(b"P1\n2 2\n\n1010") - f2.write(b"P1\n2 2\n" + comment + b"\n1010" + comment) - - with Image.open(path1) as im: - assert_image_equal_tofile(im, path2) - - -def test_plain_pbm_truncated_data(tmp_path): - path = str(tmp_path / "temp.ppm") - with open(path, "wb") as f: - f.write(b"P1\n128 128\n") - - with Image.open(path) as im: - with pytest.raises(ValueError): - im.load() - - -def test_plain_pbm_invalid_data(tmp_path): - path = str(tmp_path / "temp.ppm") - with open(path, "wb") as f: - f.write(b"P1\n128 128\n1009") - - with Image.open(path) as im: - with pytest.raises(ValueError): - im.load() - - -def test_plain_ppm_data_with_comments(tmp_path): +@pytest.mark.parametrize( + "header, data", + ((b"P1\n2 2", b"1010"), (b"P3\n2 2\n255", b"0 0 0 001 1 1 2 2 2 255 255 255")), +) +def test_plain_data_with_comments(tmp_path, header, data): path1 = str(tmp_path / "temp1.ppm") path2 = str(tmp_path / "temp2.ppm") comment = b"# veeery long comment" * 10**6 with open(path1, "wb") as f1, open(path2, "wb") as f2: - f1.write(b"P3\n2 2\n255\n0 0 0 001 1 1 2 2 2 255 255 255") - f2.write( - b"P3\n2 2\n255\n" + comment + b"\n0 0 0 001 1 1 2 2 2 255 255 255" + comment - ) + f1.write(header + b"\n\n" + data) + f2.write(header + b"\n" + comment + b"\n" + data + comment) with Image.open(path1) as im: assert_image_equal_tofile(im, path2) -def test_plain_ppm_truncated_data(tmp_path): +@pytest.mark.parametrize("data", (b"P1\n128 128\n", b"P3\n128 128\n255\n")) +def test_plain_truncated_data(tmp_path, data): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: - f.write(b"P3\n128 128\n255\n") + f.write(data) with Image.open(path) as im: with pytest.raises(ValueError): im.load() -def test_plain_ppm_invalid_data(tmp_path): +@pytest.mark.parametrize("data", (b"P1\n128 128\n1009", b"P3\n128 128\n255\n100A")) +def test_plain_invalid_data(tmp_path, data): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: - f.write(b"P3\n128 128\n255\n100A") + f.write(data) with Image.open(path) as im: with pytest.raises(ValueError): im.load() -def test_plain_ppm_half_token_too_long(tmp_path): - path = str(tmp_path / "temp.ppm") - with open(path, "wb") as f: - f.write(b"P3\n128 128\n255\n012345678910") - - with Image.open(path) as im: - with pytest.raises(ValueError): - im.load() - - -def test_plain_ppm_token_too_long(tmp_path): +@pytest.mark.parametrize( + "data", + ( + b"P3\n128 128\n255\n012345678910", # half token too long + b"P3\n128 128\n255\n012345678910 0", # token too long + ), +) +def test_plain_ppm_token_too_long(tmp_path, data): path = str(tmp_path / "temp.ppm") with open(path, "wb") as f: - f.write(b"P3\n128 128\n255\n012345678910 0") + f.write(data) with Image.open(path) as im: with pytest.raises(ValueError): @@ -227,7 +197,7 @@ def test_plain_ppm_value_too_large(tmp_path): im.load() -def test_magic(tmp_path): +def test_magic(): with pytest.raises(SyntaxError): PpmImagePlugin.PpmImageFile(fp=BytesIO(b"PyInvalid")) @@ -263,7 +233,7 @@ def test_header_token_too_long(tmp_path): assert str(e.value) == "Token too long in file header: 01234567890" -def test_truncated_header(tmp_path): +def test_truncated_file(tmp_path): # Test EOF in header path = str(tmp_path / "temp.pgm") with open(path, "w") as f: From 6eb6232f04a742440152704af47f0cc41fd03231 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jun 2022 20:32:36 +1000 Subject: [PATCH 177/326] Test comment that ends in the same block --- Tests/test_file_ppm.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 40495bf10b4..5c6376caf66 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -133,13 +133,17 @@ def test_16bit_plain_pgm(): @pytest.mark.parametrize( - "header, data", - ((b"P1\n2 2", b"1010"), (b"P3\n2 2\n255", b"0 0 0 001 1 1 2 2 2 255 255 255")), + "header, data, comment_count", + ( + (b"P1\n2 2", b"1010", 10**6), + (b"P2\n3 1\n4", b"0 2 4", 1), + (b"P3\n2 2\n255", b"0 0 0 001 1 1 2 2 2 255 255 255", 10**6), + ), ) -def test_plain_data_with_comments(tmp_path, header, data): +def test_plain_data_with_comment(tmp_path, header, data, comment_count): path1 = str(tmp_path / "temp1.ppm") path2 = str(tmp_path / "temp2.ppm") - comment = b"# veeery long comment" * 10**6 + comment = b"# comment" * comment_count with open(path1, "wb") as f1, open(path2, "wb") as f2: f1.write(header + b"\n\n" + data) f2.write(header + b"\n" + comment + b"\n" + data + comment) From 1bac1cf6f5a21b5d86f00a375d4b90322f25de52 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 14 Jun 2022 19:38:51 +1000 Subject: [PATCH 178/326] Moved all comments logic into _ignore_comments() --- src/PIL/PpmImagePlugin.py | 61 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index 7df75fb386a..392771d3e96 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -156,24 +156,38 @@ def _find_comment_end(self, block, start=0): return min(a, b) if a * b > 0 else max(a, b) # lowest nonnegative index (or -1) def _ignore_comments(self, block): - """ - Delete comments from block. - If comment does not end in this block, raises a flag. - """ - comment_spans = False + if self._comment_spans: + # Finish current comment + while block: + comment_end = self._find_comment_end(block) + if comment_end != -1: + # Comment ends in this block + # Delete tail of comment + block = block[comment_end + 1 :] + break + else: + # Comment spans whole block + # So read the next block, looking for the end + block = self._read_block() + + # Search for any further comments + self._comment_spans = False while True: - comment_start = block.find(b"#") # look for next comment - if comment_start == -1: # no comment found + comment_start = block.find(b"#") + if comment_start == -1: + # No comment found break comment_end = self._find_comment_end(block, comment_start) - if comment_end != -1: # comment ends in this block - # delete comment + if comment_end != -1: + # Comment ends in this block + # Delete comment block = block[:comment_start] + block[comment_end + 1 :] - else: # last comment continues to next block(s) + else: + # Comment continues to next block(s) block = block[:comment_start] - comment_spans = True + self._comment_spans = True break - return block, comment_spans + return block def _decode_bitonal(self): """ @@ -183,22 +197,13 @@ def _decode_bitonal(self): data = bytearray() total_bytes = self.state.xsize * self.state.ysize - comment_spans = False while len(data) != total_bytes: block = self._read_block() # read next block if not block: # eof break - while block and comment_spans: - comment_end = self._find_comment_end(block) - if comment_end != -1: # comment ends in this block - block = block[comment_end + 1 :] # delete tail of previous comment - break - else: # comment spans whole block - block = self._read_block() - - block, comment_spans = self._ignore_comments(block) + block = self._ignore_comments(block) tokens = b"".join(block.split()) for token in tokens: @@ -216,7 +221,6 @@ def _decode_blocks(self, maxval): bands = Image.getmodebands(self.mode) total_bytes = self.state.xsize * self.state.ysize * bands * out_byte_count - comment_spans = False half_token = False while len(data) != total_bytes: block = self._read_block() # read next block @@ -227,15 +231,7 @@ def _decode_blocks(self, maxval): # eof break - while block and comment_spans: - comment_end = self._find_comment_end(block) - if comment_end != -1: # comment ends in this block - block = block[comment_end + 1 :] # delete tail of previous comment - break - else: # comment spans whole block - block = self._read_block() - - block, comment_spans = self._ignore_comments(block) + block = self._ignore_comments(block) if half_token: block = half_token + block # stitch half_token to new block @@ -264,6 +260,7 @@ def _decode_blocks(self, maxval): return data def decode(self, buffer): + self._comment_spans = False if self.mode == "1": data = self._decode_bitonal() rawmode = "1;8" From e80ddfd2b6009847fc9756cc2c6985e588f1a7d5 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 14 Jun 2022 22:26:50 +1000 Subject: [PATCH 179/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7f998cea184..658a950dd8b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Added support for decoding plain PPM formats #5242 + [Piolie, radarhere] + - Added apply_transparency() #6352 [radarhere] From b47bcc246ff5506bec4f3b0079bb28c65e792966 Mon Sep 17 00:00:00 2001 From: Jingxuan He Date: Wed, 15 Jun 2022 17:39:37 +0200 Subject: [PATCH 180/326] Fix a potential wrong operator bug --- 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 50ec3b5efa3..282c314201a 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -377,7 +377,7 @@ def profileToProfile( raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + raise PyCMSError("flags must be an integer between 0 and %s" % _MAX_FLAG) try: if not isinstance(inputProfile, ImageCmsProfile): From 6c889d10883d264bd946e37ba081631590a44353 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 15 Jun 2022 21:34:16 +0300 Subject: [PATCH 181/326] Test ImageCms.profileToProfile with invalid flags --- Tests/test_imagecms.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 66a72a90eb2..0c0eb92c47e 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -210,6 +210,15 @@ def test_invalid_color_temperature(): ImageCms.createProfile("LAB", "invalid") +@pytest.mark.parametrize("flag", ("my string", -1)) +def test_invalid_flag(flag): + with hopper() as im: + with pytest.raises( + ImageCms.PyCMSError, match="flags must be an integer between 0 and " + ): + ImageCms.profileToProfile(im, "foo", "bar", flags=flag) + + def test_simple_lab(): i = Image.new("RGB", (10, 10), (128, 128, 128)) From 983a6139d57b37a883344972c6b1de50bb757de0 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 15 Jun 2022 21:42:04 +0300 Subject: [PATCH 182/326] Check other exception messages --- Tests/test_imagecms.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 0c0eb92c47e..ce6ec0bacf4 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -174,19 +174,25 @@ def test_exceptions(): psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="mode mismatch"): t.apply_in_place(hopper("RGBA")) # the procedural pyCMS API uses PyCMSError for all sorts of errors with hopper() as im: - with pytest.raises(ImageCms.PyCMSError): + with pytest.raises(ImageCms.PyCMSError, match="cannot open profile file"): ImageCms.profileToProfile(im, "foo", "bar") - with pytest.raises(ImageCms.PyCMSError): + + with pytest.raises(ImageCms.PyCMSError, match="cannot open profile file"): ImageCms.buildTransform("foo", "bar", "RGB", "RGB") - with pytest.raises(ImageCms.PyCMSError): + + with pytest.raises(ImageCms.PyCMSError, match="Invalid type for Profile"): ImageCms.getProfileName(None) skip_missing() - with pytest.raises(ImageCms.PyCMSError): + + with pytest.raises( + ImageCms.PyCMSError, + match="'NoneType' object cannot be interpreted as an integer", + ): ImageCms.isIntentSupported(SRGB, None, None) @@ -201,12 +207,20 @@ def test_lab_color_profile(): def test_unsupported_color_space(): - with pytest.raises(ImageCms.PyCMSError): + with pytest.raises( + ImageCms.PyCMSError, + match=re.escape( + "Color space not supported for on-the-fly profile creation (unsupported)" + ), + ): ImageCms.createProfile("unsupported") def test_invalid_color_temperature(): - with pytest.raises(ImageCms.PyCMSError): + with pytest.raises( + ImageCms.PyCMSError, + match='Color temperature must be numeric, "invalid" not valid', + ): ImageCms.createProfile("LAB", "invalid") @@ -470,9 +484,9 @@ def test_profile_typesafety(): prepatch, these would segfault, postpatch they should emit a typeerror """ - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="Invalid type for Profile"): ImageCms.ImageCmsProfile(0).tobytes() - with pytest.raises(TypeError): + with pytest.raises(TypeError, match="Invalid type for Profile"): ImageCms.ImageCmsProfile(1).tobytes() From 959e576dd2a8fd86d6bc0b5ebebdb7f73daef829 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 15 Jun 2022 21:43:18 +0300 Subject: [PATCH 183/326] Use f-string --- 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 282c314201a..605252d5d4c 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -377,7 +377,7 @@ def profileToProfile( raise PyCMSError("renderingIntent must be an integer between 0 and 3") if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - raise PyCMSError("flags must be an integer between 0 and %s" % _MAX_FLAG) + raise PyCMSError(f"flags must be an integer between 0 and {_MAX_FLAG}") try: if not isinstance(inputProfile, ImageCmsProfile): From 6ae6a241f29b8823996b22dde9a516b3e6c560a4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 15 Jun 2022 21:57:20 +0300 Subject: [PATCH 184/326] More generic match text --- Tests/test_imagecms.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index ce6ec0bacf4..6dd38894177 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -189,10 +189,9 @@ def test_exceptions(): ImageCms.getProfileName(None) skip_missing() - with pytest.raises( - ImageCms.PyCMSError, - match="'NoneType' object cannot be interpreted as an integer", - ): + # macOS/Ubuntu: "'NoneType' object cannot be interpreted as an integer" + # Windows: "an integer is required (got type NoneType)" + with pytest.raises(ImageCms.PyCMSError, match="integer"): ImageCms.isIntentSupported(SRGB, None, None) From 4d6e5a0d095cc1567f286e470d7af286a3e8d50e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 16 Jun 2022 08:36:43 +1000 Subject: [PATCH 185/326] Limit tile size to avoid extending outside image --- Tests/images/input_bw_one_band.fpx | Bin 0 -> 33792 bytes Tests/images/input_bw_one_band.png | Bin 0 -> 477 bytes Tests/test_file_fpx.py | 11 +++++++++++ src/PIL/FpxImagePlugin.py | 9 ++++++--- 4 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 Tests/images/input_bw_one_band.fpx create mode 100644 Tests/images/input_bw_one_band.png diff --git a/Tests/images/input_bw_one_band.fpx b/Tests/images/input_bw_one_band.fpx new file mode 100644 index 0000000000000000000000000000000000000000..9bdc53763fe9459ecc35cd45feabf15d06d6d124 GIT binary patch literal 33792 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 literal 0 HcmV?d00001 diff --git a/Tests/images/input_bw_one_band.png b/Tests/images/input_bw_one_band.png new file mode 100644 index 0000000000000000000000000000000000000000..6b4c1f37696a847dc9859da93af8c6e1eec52eb7 GIT binary patch literal 477 zcmV<30V4j1P)jUe z_XDE!potni8@oajKyE?{?8Z%ECs8g#)@9yoW@kcW;BZE9i>uIBPxe}^*M_-BtxHm) z#&%_c(lJ}*c-uLY21#*P)t4(WiBB@7Z5?@LleG0V##ncOo~JrrDW-9e3m8SXEG41k zQ1ooVStIZKToK&(#R4kiwC;vq;Z`ffXh z2d$enH~Se(8Tk+^-q;G|m{7a}n>3st$)Emhao@qT5)o=Y?C2a(a)+nVz8CffiPcI% TjOZMM00000NkvXXu0mjf^4ivS literal 0 HcmV?d00001 diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 818565f88b3..fa22e90f660 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -2,11 +2,22 @@ from PIL import Image +from .helper import assert_image_equal_tofile + FpxImagePlugin = pytest.importorskip( "PIL.FpxImagePlugin", reason="olefile not installed" ) +def test_sanity(): + with Image.open("Tests/images/input_bw_one_band.fpx") as im: + assert im.mode == "L" + assert im.size == (70, 46) + assert im.format == "FPX" + + assert_image_equal_tofile(im, "Tests/images/input_bw_one_band.png") + + def test_invalid_file(): # Test an invalid OLE file invalid_file = "Tests/images/flower.jpg" diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index f955b234769..a55376d0e08 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -154,13 +154,16 @@ def _open_subimage(self, index=1, subimage=0): for i in range(0, len(s), length): + x1 = min(xsize, x + xtile) + y1 = min(ysize, y + ytile) + compression = i32(s, i + 8) if compression == 0: self.tile.append( ( "raw", - (x, y, x + xtile, y + ytile), + (x, y, x1, y1), i32(s, i) + 28, (self.rawmode,), ) @@ -172,7 +175,7 @@ def _open_subimage(self, index=1, subimage=0): self.tile.append( ( "fill", - (x, y, x + xtile, y + ytile), + (x, y, x1, y1), i32(s, i) + 28, (self.rawmode, s[12:16]), ) @@ -201,7 +204,7 @@ def _open_subimage(self, index=1, subimage=0): self.tile.append( ( "jpeg", - (x, y, x + xtile, y + ytile), + (x, y, x1, y1), i32(s, i) + 28, (rawmode, jpegmode), ) From 765d66c069fb35442a01457955c15a8db898f734 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 16 Jun 2022 19:24:36 +1000 Subject: [PATCH 186/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 658a950dd8b..8d5d7001a86 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Limit FPX tile size to avoid extending outside image #6368 + [radarhere] + - Added support for decoding plain PPM formats #5242 [Piolie, radarhere] From c2047b8293ec9091970c09abb113d5de586d1b7b Mon Sep 17 00:00:00 2001 From: nulano Date: Fri, 17 Jun 2022 17:13:11 +0100 Subject: [PATCH 187/326] fix null check for fribidi_version_info in fribidi shim --- src/thirdparty/fribidi-shim/fribidi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/thirdparty/fribidi-shim/fribidi.c b/src/thirdparty/fribidi-shim/fribidi.c index 04491e17f8b..5663da86b92 100644 --- a/src/thirdparty/fribidi-shim/fribidi.c +++ b/src/thirdparty/fribidi-shim/fribidi.c @@ -33,6 +33,7 @@ static void fribidi_get_bracket_types_compat( int load_fribidi(void) { int error = 0; + const char **p_fribidi_version_info = 0; p_fribidi = 0; @@ -87,20 +88,21 @@ int load_fribidi(void) { LOAD_FUNCTION(fribidi_get_par_embedding_levels); #ifndef _WIN32 - fribidi_version_info = *(const char**)dlsym(p_fribidi, "fribidi_version_info"); - if (error || (fribidi_version_info == 0)) { + p_fribidi_version_info = (const char**)dlsym(p_fribidi, "fribidi_version_info"); + if (error || (p_fribidi_version_info == 0) || (*p_fribidi_version_info == 0)) { dlclose(p_fribidi); p_fribidi = 0; return 2; } #else - fribidi_version_info = *(const char**)GetProcAddress(p_fribidi, "fribidi_version_info"); - if (error || (fribidi_version_info == 0)) { + p_fribidi_version_info = (const char**)GetProcAddress(p_fribidi, "fribidi_version_info"); + if (error || (p_fribidi_version_info == 0) || (*p_fribidi_version_info == 0)) { FreeLibrary(p_fribidi); p_fribidi = 0; return 2; } #endif + fribidi_version_info = *p_fribidi_version_info; return 0; } From 9f9e26522eddbd4db3012b761ec6754dab5605d5 Mon Sep 17 00:00:00 2001 From: William Jacobs Date: Sat, 18 Jun 2022 04:09:41 -0400 Subject: [PATCH 188/326] Fixed bug with rounding pixels to palette This fixes a bug with rounding pixels to the nearest palette color. Specifically, there is a typo with computing the minimum distance from a palette color to a given cache box. This is causing palette colors to be incorrectly excluded from consideration. --- Tests/images/palette_negative.png | Bin 17070 -> 16984 bytes Tests/images/palette_sepia.png | Bin 20339 -> 20364 bytes Tests/images/palette_wedge.png | Bin 17065 -> 16984 bytes Tests/test_image_entropy.py | 2 +- Tests/test_image_getcolors.py | 2 +- Tests/test_image_histogram.py | 2 +- docs/releasenotes/9.2.0.rst | 8 +++++--- src/libImaging/Palette.c | 6 +++--- 8 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Tests/images/palette_negative.png b/Tests/images/palette_negative.png index 938a7285fd75af8fcdb5e5857e05e9674b9ae337..7fcfd29a0db44940bf69e76a9aa4a06b8e39f660 100644 GIT binary patch literal 16984 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�i8(H2WE)WEyW#bL`2xuYh zbVMEf%;H}&9EQgLl5RB&k*D@o2^_8DhD7V#JHS5SG#vcht+OM&sl@3QY57_@A+F;# z>ueT-YQuNwQTI-_*}K%YK%tjXCE5aYf`0t9>2EUb{cs1O;QjKy+cO+&BVAuoA~ndB zoX84fvn-F}!z71?xC-)q*7-uuD$~{HR$4!J1JVn2QQyMqYA!1MX~S{c*#u0+JanE0 z6$X*%AJiO(sj2B{W7ZpnYRVd5#?e9fDq`3}nUTar1Pp+@O}Xno%m3pecPCBh{ZpHj zu}gyq=zUH?Z?d9`Wr8aWXw%77lzhFs@GT3cNWEb7TBqzyJg@^TmvBT9>6a?{woAXO zDLXe7FMJ0^!8`?f=-40o$%tJ#km#dq98Frs_~AP>x~%}|_wxP$`~YEne^CyNw>r{m zCCrp23(y5xIyw|JxRUd6gxqGk=9WuEGMAY1YX3qlrDICmSruni8$Rg_A5-5CC!g{9 zwRVWz;xkMo1=n|p8&=@O{iO^}og0azRZ9m|iX0l5GG literal 17070 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%0DN|MLC=0`L9H+oFLFil*`C(bS9|Oy`Ir!0kQo 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 F{{cuUG`Ii& diff --git a/Tests/images/palette_sepia.png b/Tests/images/palette_sepia.png index f3fc932531f9b73c8d214e7c236c556ab3775d22..9e7d6b0345b2c9193b97d61c97a9dd4bdee668df 100644 GIT binary patch literal 20364 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 M07*qoM6N<$f@Pis-~a#s literal 20339 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 diff --git a/Tests/images/palette_wedge.png b/Tests/images/palette_wedge.png index 23fb7940d6d61c42e69ad3c80fca5e77c26994d3..4b3d9ff3a219dea6f7ee2fb9fdc9382c341804bb 100644 GIT binary patch literal 16984 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 literal 17065 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 mp1g?hKH_PmYNYWMwhbk94palette[i * 4 + 0]; - tmin = (r < r0) ? RDIST(r, r1) : (r > r1) ? RDIST(r, r0) : 0; + tmin = (r < r0) ? RDIST(r, r0) : (r > r1) ? RDIST(r, r1) : 0; tmax = (r <= rc) ? RDIST(r, r1) : RDIST(r, r0); g = palette->palette[i * 4 + 1]; - tmin += (g < g0) ? GDIST(g, g1) : (g > g1) ? GDIST(g, g0) : 0; + tmin += (g < g0) ? GDIST(g, g0) : (g > g1) ? GDIST(g, g1) : 0; tmax += (g <= gc) ? GDIST(g, g1) : GDIST(g, g0); b = palette->palette[i * 4 + 2]; - tmin += (b < b0) ? BDIST(b, b1) : (b > b1) ? BDIST(b, b0) : 0; + tmin += (b < b0) ? BDIST(b, b0) : (b > b1) ? BDIST(b, b1) : 0; tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0); dmin[i] = tmin; From 128ed189e58e2679ad5cdbc22937a3109282e340 Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Sat, 18 Jun 2022 18:07:58 -0600 Subject: [PATCH 189/326] Improve test in _get_optimize() Palette can be optimized if number of colors can be reduced by half or more. --- Tests/test_file_gif.py | 15 +++++++++++++++ src/PIL/GifImagePlugin.py | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index c261cfb97bb..2bfaef4ee77 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -180,6 +180,21 @@ def test_optimize_full_l(): assert im.mode == "L" +def test_optimize_if_palette_can_be_reduced_by_half(): + with Image.open("Tests/images/test.colors.gif") as im: + # Reduce because original is too big for _get_optimize() + im = im.resize((591, 443)) + imrgb = im.convert("RGB") + out = BytesIO() + imrgb.save(out, "GIF", optimize=False) + with Image.open(out) as reloaded: + assert len(reloaded.palette.palette) // 3 == 256 + out = BytesIO() + imrgb.save(out, "GIF", optimize=True) + with Image.open(out) as reloaded: + assert len(reloaded.palette.palette) // 3 == 8 + + def test_roundtrip(tmp_path): out = str(tmp_path / "temp.gif") im = hopper() diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 3469199cad3..f58146c7ed3 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -824,9 +824,14 @@ def _get_optimize(im, info): if count: used_palette_colors.append(i) + num_palette_colors = len(im.palette.palette) // 4 if im.palette.mode == 'RGBA' else len(im.palette.palette) // 3 + # Round up to power of 2 but at least 4 + num_palette_colors = max(4, 1 << (num_palette_colors - 1).bit_length()) if optimise or ( + len(used_palette_colors) <= 128 - and max(used_palette_colors) > len(used_palette_colors) + and max(used_palette_colors) >= len(used_palette_colors) + or len(used_palette_colors) <= num_palette_colors // 2 ): return used_palette_colors From f656711c80d039e60f86923b05dff4866815f2e2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 19 Jun 2022 00:20:25 +0000 Subject: [PATCH 190/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/GifImagePlugin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index f58146c7ed3..b30ed1728e3 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -824,11 +824,14 @@ def _get_optimize(im, info): if count: used_palette_colors.append(i) - num_palette_colors = len(im.palette.palette) // 4 if im.palette.mode == 'RGBA' else len(im.palette.palette) // 3 + num_palette_colors = ( + len(im.palette.palette) // 4 + if im.palette.mode == "RGBA" + else len(im.palette.palette) // 3 + ) # Round up to power of 2 but at least 4 num_palette_colors = max(4, 1 << (num_palette_colors - 1).bit_length()) if optimise or ( - len(used_palette_colors) <= 128 and max(used_palette_colors) >= len(used_palette_colors) or len(used_palette_colors) <= num_palette_colors // 2 From 5f6bc0e1a6598e375542de80d59cfd69a9c21fa9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 19 Jun 2022 14:07:50 +1000 Subject: [PATCH 191/326] Removed #6377 from release notes --- docs/releasenotes/9.2.0.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index 83ba28df85b..424fd487a29 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -76,9 +76,7 @@ TODO Other Changes ============= -Fixed bug with rounding pixels to palette -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +TODO +^^^^ -Fixed a bug when rounding image pixels to palette colors. This affects the results of -``Image.quantize`` and of ``Image.convert`` when converting from mode "RGB" to "P", -"L", or "1", regardless of whether dithering is enabled. +TODO From 709744432a2a290170d2b01e4c39fe2789f505f8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 19 Jun 2022 16:47:50 +1000 Subject: [PATCH 192/326] Optimise palettes with more than 128 colors --- Tests/test_file_gif.py | 32 ++++++++++++++------------------ src/PIL/GifImagePlugin.py | 15 ++++++--------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 2bfaef4ee77..dbbd3bf9de9 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -158,6 +158,9 @@ def check(colors, size, expected_palette_length): assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) # These do optimize the palette + check(256, 511, 256) + check(255, 511, 255) + check(129, 511, 129) check(128, 511, 128) check(64, 511, 64) check(4, 511, 4) @@ -167,11 +170,6 @@ def check(colors, size, expected_palette_length): check(64, 513, 256) check(4, 513, 256) - # Other limits that don't optimize the palette - check(129, 511, 256) - check(255, 511, 256) - check(256, 511, 256) - def test_optimize_full_l(): im = Image.frombytes("L", (16, 16), bytes(range(256))) @@ -182,17 +180,15 @@ def test_optimize_full_l(): def test_optimize_if_palette_can_be_reduced_by_half(): with Image.open("Tests/images/test.colors.gif") as im: - # Reduce because original is too big for _get_optimize() + # Reduce dimensions because original is too big for _get_optimize() im = im.resize((591, 443)) - imrgb = im.convert("RGB") - out = BytesIO() - imrgb.save(out, "GIF", optimize=False) - with Image.open(out) as reloaded: - assert len(reloaded.palette.palette) // 3 == 256 + im_rgb = im.convert("RGB") + + for (optimize, colors) in ((False, 256), (True, 8)): out = BytesIO() - imrgb.save(out, "GIF", optimize=True) + im_rgb.save(out, "GIF", optimize=optimize) with Image.open(out) as reloaded: - assert len(reloaded.palette.palette) // 3 == 8 + assert len(reloaded.palette.palette) // 3 == colors def test_roundtrip(tmp_path): @@ -997,8 +993,8 @@ def im_generator(ims): def test_transparent_optimize(tmp_path): # From issue #2195, if the transparent color is incorrectly optimized out, GIF loses # transparency. - # Need a palette that isn't using the 0 color, and one that's > 128 items where the - # transparent color is actually the top palette entry to trigger the bug. + # Need a palette that isn't using the 0 color, + # where the transparent color is actually the top palette entry to trigger the bug. data = bytes(range(1, 254)) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) @@ -1008,10 +1004,10 @@ def test_transparent_optimize(tmp_path): im.putpalette(palette) out = str(tmp_path / "temp.gif") - im.save(out, transparency=253) - with Image.open(out) as reloaded: + im.save(out, transparency=im.getpixel((252, 0))) - assert reloaded.info["transparency"] == 253 + with Image.open(out) as reloaded: + assert reloaded.info["transparency"] == reloaded.getpixel((252, 0)) def test_rgb_transparency(tmp_path): diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b30ed1728e3..dd659c95905 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -824,18 +824,15 @@ def _get_optimize(im, info): if count: used_palette_colors.append(i) - num_palette_colors = ( - len(im.palette.palette) // 4 - if im.palette.mode == "RGBA" - else len(im.palette.palette) // 3 + if optimise or max(used_palette_colors) >= len(used_palette_colors): + return used_palette_colors + + num_palette_colors = len(im.palette.palette) // Image.getmodebands( + im.palette.mode ) # Round up to power of 2 but at least 4 num_palette_colors = max(4, 1 << (num_palette_colors - 1).bit_length()) - if optimise or ( - len(used_palette_colors) <= 128 - and max(used_palette_colors) >= len(used_palette_colors) - or len(used_palette_colors) <= num_palette_colors // 2 - ): + if len(used_palette_colors) <= num_palette_colors // 2: return used_palette_colors From 607f5080808d38dbc5e1d89c18ffec5d77f6f4b9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 19 Jun 2022 12:29:39 +0300 Subject: [PATCH 193/326] Update comment We get "an integer is required (got type NoneType)" with Python 3.7-3.9 on Windows. We get "'NoneType' object cannot be interpreted as an integer" with Python 3.10 on Windows and all versions on macOS and Ubuntu. --- Tests/test_imagecms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 6dd38894177..0ff8fc7d225 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -189,8 +189,8 @@ def test_exceptions(): ImageCms.getProfileName(None) skip_missing() - # macOS/Ubuntu: "'NoneType' object cannot be interpreted as an integer" - # Windows: "an integer is required (got type NoneType)" + # Windows, Python <= 3.9: "an integer is required (got type NoneType)" + # Others: "'NoneType' object cannot be interpreted as an integer" with pytest.raises(ImageCms.PyCMSError, match="integer"): ImageCms.isIntentSupported(SRGB, None, None) From 1229110fb2f9acd7a0bf7eb21c256b534211a2b6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 19 Jun 2022 15:22:02 +0300 Subject: [PATCH 194/326] Update comment Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_imagecms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index 0ff8fc7d225..3d8dbe6bbf5 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -189,8 +189,8 @@ def test_exceptions(): ImageCms.getProfileName(None) skip_missing() - # Windows, Python <= 3.9: "an integer is required (got type NoneType)" - # Others: "'NoneType' object cannot be interpreted as an integer" + # Python <= 3.9: "an integer is required (got type NoneType)" + # Python > 3.9: "'NoneType' object cannot be interpreted as an integer" with pytest.raises(ImageCms.PyCMSError, match="integer"): ImageCms.isIntentSupported(SRGB, None, None) From 406fe59242ad288bcd9f9fe663b227620eacd344 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 25 May 2022 01:57:13 +0100 Subject: [PATCH 195/326] deprecate font.getsize and related functions --- src/PIL/ImageDraw.py | 66 +++++++++++++++++++++++++++++++++++--------- src/PIL/ImageFont.py | 27 +++++++++++++++--- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 3824626bd6a..4e5ee24b71c 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -34,6 +34,7 @@ import numbers from . import Image, ImageColor +from ._deprecate import deprecate """ A simple 2D drawing interface for PIL images. @@ -372,6 +373,18 @@ def _multiline_split(self, text): return text.split(split_character) + def _multiline_spacing(self, font, spacing, stroke_width): + # this can be replaced with self.textbbox(...)[3] when textsize is removed + return ( + self.textsize( + "A", + font=font, + stroke_width=stroke_width, + __internal__=True, + )[1] + + spacing + ) + def text( self, xy, @@ -511,9 +524,7 @@ def multiline_text( widths = [] max_width = 0 lines = self._multiline_split(text) - line_spacing = ( - self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing - ) + line_spacing = self._multiline_spacing(font, spacing, stroke_width) for line in lines: line_width = self.textlength( line, font, direction=direction, features=features, language=language @@ -571,16 +582,33 @@ def textsize( features=None, language=None, stroke_width=0, + __internal__=False, ): """Get the size of a given string, in pixels.""" + if not __internal__: + deprecate("textsize", 10, "textbbox or textlength") if self._multiline_check(text): return self.multiline_textsize( - text, font, spacing, direction, features, language, stroke_width + text, + font, + spacing, + direction, + features, + language, + stroke_width, + __internal__=True, ) if font is None: font = self.getfont() - return font.getsize(text, direction, features, language, stroke_width) + return font.getsize( + text, + direction, + features, + language, + stroke_width, + __internal__=True, + ) def multiline_textsize( self, @@ -591,15 +619,23 @@ def multiline_textsize( features=None, language=None, stroke_width=0, + __internal__=False, ): + if not __internal__: + deprecate("multiline_textsize", 10, "multiline_textbbox") max_width = 0 lines = self._multiline_split(text) - line_spacing = ( - self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing - ) + line_spacing = self._multiline_spacing(font, spacing, stroke_width) for line in lines: line_width, line_height = self.textsize( - line, font, spacing, direction, features, language, stroke_width + line, + font, + spacing, + direction, + features, + language, + stroke_width, + __internal__=True, ) max_width = max(max_width, line_width) return max_width, len(lines) * line_spacing - spacing @@ -625,8 +661,14 @@ def textlength( try: return font.getlength(text, mode, direction, features, language) except AttributeError: + deprecate("textlength support for fonts without getlength", 10) size = self.textsize( - text, font, direction=direction, features=features, language=language + text, + font, + direction=direction, + features=features, + language=language, + __internal__=True, ) if direction == "ttb": return size[1] @@ -704,9 +746,7 @@ def multiline_textbbox( widths = [] max_width = 0 lines = self._multiline_split(text) - line_spacing = ( - self.textsize("A", font=font, stroke_width=stroke_width)[1] + spacing - ) + line_spacing = self._multiline_spacing(font, spacing, stroke_width) for line in lines: line_width = self.textlength( line, diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 681b75d448b..38fc24d7481 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -143,6 +143,8 @@ def getsize(self, text, *args, **kwargs): :return: (width, height) """ + if not kwargs.get("__internal__"): + deprecate("getsize", 10, "getbbox or getlength") return self.font.getsize(text) def getmask(self, text, mode="", *args, **kwargs): @@ -386,7 +388,13 @@ def getbbox( return left, top, left + width, top + height def getsize( - self, text, direction=None, features=None, language=None, stroke_width=0 + self, + text, + direction=None, + features=None, + language=None, + stroke_width=0, + __internal__=False, ): """ Returns width and height (in pixels) of given text if rendered in font with @@ -438,6 +446,8 @@ def getsize( :return: (width, height) """ + if not __internal__: + deprecate("getsize", 10, "getbbox or getlength") # vertical offset is added for historical reasons # see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929 size, offset = self.font.getsize(text, "L", direction, features, language) @@ -495,12 +505,15 @@ def getsize_multiline( :return: (width, height) """ + deprecate("getsize_multiline", 10) max_width = 0 lines = self._multiline_split(text) - line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing + line_spacing = ( + self.getsize("A", stroke_width=stroke_width, __internal__=True)[1] + spacing + ) for line in lines: line_width, line_height = self.getsize( - line, direction, features, language, stroke_width + line, direction, features, language, stroke_width, __internal__=True ) max_width = max(max_width, line_width) @@ -516,6 +529,7 @@ def getoffset(self, text): :return: A tuple of the x and y offset """ + deprecate("getoffset", 10, "getbbox") return self.font.getsize(text)[1] def getmask( @@ -796,7 +810,12 @@ def __init__(self, font, orientation=None): self.orientation = orientation # any 'transpose' argument, or None def getsize(self, text, *args, **kwargs): - w, h = self.font.getsize(text) + if not kwargs.get("__internal__"): + deprecate("getsize", 10, "getbbox or getlength") + try: + w, h = self.font.getsize(text, __internal__=True) + except TypeError: + w, h = self.font.getsize(text) if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): return h, w return w, h From c854bf8d1c05022bec4309fbf6b547e494db9373 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 25 May 2022 18:06:20 +0100 Subject: [PATCH 196/326] add getbbox and getlength to basic ImageFont and update related tests --- Tests/test_imagedraw.py | 28 +++++++++++++++++++++++----- Tests/test_imagefont.py | 7 +++++-- src/PIL/ImageDraw.py | 4 ---- src/PIL/ImageFont.py | 27 +++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 69d1ac9fad3..dca7bfe6a58 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1232,9 +1232,10 @@ def test_textsize_empty_string(): # Act # Should not cause 'SystemError: returned NULL without setting an error' - draw.textsize("") - draw.textsize("\n") - draw.textsize("test\n") + draw.textbbox((0, 0), "") + draw.textbbox((0, 0), "\n") + draw.textbbox((0, 0), "test\n") + draw.textlength("") @skip_unless_feature("freetype2") @@ -1245,8 +1246,25 @@ def test_textsize_stroke(): font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20) # Act / Assert - assert draw.textsize("A", font, stroke_width=2) == (16, 20) - assert draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2) == (52, 44) + assert draw.textbbox((2, 2), "A", font, stroke_width=2) == (0, 4, 16, 20) + assert draw.textbbox((2, 2), "A", font, stroke_width=4) == (-2, 2, 18, 22) + assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=2) == (0, 4, 52, 44) + assert draw.textbbox((2, 2), "ABC\nAaaa", font, stroke_width=4) == (-2, 2, 54, 50) + + +def test_textsize_deprecation(): + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + + with pytest.warns(DeprecationWarning) as log: + draw.textsize("Hello") + assert len(log) == 1 + with pytest.warns(DeprecationWarning) as log: + draw.textsize("Hello\nWorld") + assert len(log) == 1 + with pytest.warns(DeprecationWarning) as log: + draw.multiline_textsize("Hello\nWorld") + assert len(log) == 1 @skip_unless_feature("freetype2") diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 0c50303f902..fe7d79f82e2 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -720,8 +720,11 @@ def test_textbbox_non_freetypefont(self): im = Image.new("RGB", (200, 200)) d = ImageDraw.Draw(im) default_font = ImageFont.load_default() - with pytest.raises(ValueError): - d.textbbox((0, 0), "test", font=default_font) + with pytest.warns(DeprecationWarning) as log: + width, height = d.textsize("test", font=default_font) + assert len(log) == 1 + assert d.textlength("test", font=default_font) == width + assert d.textbbox((0, 0), "test", font=default_font) == (0, 0, width, height) @pytest.mark.parametrize( "anchor, left, top", diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 4e5ee24b71c..723ec17fdc1 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -709,10 +709,6 @@ def textbbox( if font is None: font = self.getfont() - from . import ImageFont - - if not isinstance(font, ImageFont.FreeTypeFont): - raise ValueError("Only supported for TrueType fonts") mode = "RGBA" if embedded_color else self.fontmode bbox = font.getbbox( text, mode, direction, features, language, stroke_width, anchor diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 38fc24d7481..4e2cb9686df 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -167,6 +167,33 @@ def getmask(self, text, mode="", *args, **kwargs): """ return self.font.getmask(text, mode) + def getbbox(self, text, *args, **kwargs): + """ + Returns bounding box (in pixels) of given text. + + .. versionadded:: 9.2.0 + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + :return: ``(left, top, right, bottom)`` bounding box + """ + width, height = self.font.getsize(text) + return 0, 0, width, height + + def getlength(self, text, *args, **kwargs): + """ + Returns length (in pixels) of given text. + This is the amount by which following text should be offset. + + .. versionadded:: 9.2.0 + """ + width, height = self.font.getsize(text) + return width + ## # Wrapper for FreeType fonts. Application code should use the From f34a6460ef0925679b6c810fa363bcabcf56458b Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 25 May 2022 18:23:37 +0100 Subject: [PATCH 197/326] update test_font_pcf to use getbbox --- Tests/test_font_pcf.py | 8 ++++++-- Tests/test_font_pcf_charsets.py | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 288848f2619..885ef843372 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -68,12 +68,16 @@ def test_textsize(request, tmp_path): tempname = save_font(request, tmp_path) font = ImageFont.load(tempname) for i in range(255): - (dx, dy) = font.getsize(chr(i)) + (ox, oy, dx, dy) = font.getbbox(chr(i)) + assert ox == 0 + assert oy == 0 assert dy == 20 assert dx in (0, 10) + assert font.getlength(chr(i)) == dx for i in range(len(message)): msg = message[: i + 1] - assert font.getsize(msg) == (len(msg) * 10, 20) + assert font.getlength(msg) == len(msg) * 10 + assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20) def _test_high_characters(request, tmp_path, message): diff --git a/Tests/test_font_pcf_charsets.py b/Tests/test_font_pcf_charsets.py index a1036fd28e6..4477ee29d55 100644 --- a/Tests/test_font_pcf_charsets.py +++ b/Tests/test_font_pcf_charsets.py @@ -101,13 +101,17 @@ def _test_textsize(request, tmp_path, encoding): tempname = save_font(request, tmp_path, encoding) font = ImageFont.load(tempname) for i in range(255): - (dx, dy) = font.getsize(bytearray([i])) + (ox, oy, dx, dy) = font.getbbox(bytearray([i])) + assert ox == 0 + assert oy == 0 assert dy == 20 assert dx in (0, 10) + assert font.getlength(bytearray([i])) == dx message = charsets[encoding]["message"].encode(encoding) for i in range(len(message)): msg = message[: i + 1] - assert font.getsize(msg) == (len(msg) * 10, 20) + assert font.getlength(msg) == len(msg) * 10 + assert font.getbbox(msg) == (0, 0, len(msg) * 10, 20) def test_textsize_iso8859_1(request, tmp_path): From 1bf87556ef9953eeea5751714d87bdcc98b49702 Mon Sep 17 00:00:00 2001 From: nulano Date: Wed, 25 May 2022 22:00:13 +0100 Subject: [PATCH 198/326] add textbbox and textlength to ImageDraw2 and update tests --- Tests/test_imagedraw.py | 2 +- Tests/test_imagedraw2.py | 13 +++++++++---- src/PIL/ImageDraw2.py | 26 +++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index dca7bfe6a58..23bc756bb14 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1239,7 +1239,7 @@ def test_textsize_empty_string(): @skip_unless_feature("freetype2") -def test_textsize_stroke(): +def test_textbbox_stroke(): # Arrange im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 3a70176cee5..e4e8a38cb59 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -1,5 +1,7 @@ import os.path +import pytest + from PIL import Image, ImageDraw, ImageDraw2 from .helper import ( @@ -205,7 +207,9 @@ def test_textsize(): font = ImageDraw2.Font("white", FONT_PATH) # Act - size = draw.textsize("ImageDraw2", font) + with pytest.warns(DeprecationWarning) as log: + size = draw.textsize("ImageDraw2", font) + assert len(log) == 1 # Assert assert size[1] == 12 @@ -221,9 +225,10 @@ def test_textsize_empty_string(): # Act # Should not cause 'SystemError: returned NULL without setting an error' - draw.textsize("", font) - draw.textsize("\n", font) - draw.textsize("test\n", font) + draw.textbbox((0, 0), "", font) + draw.textbbox((0, 0), "\n", font) + draw.textbbox((0, 0), "test\n", font) + draw.textlength("", font) @skip_unless_feature("freetype2") diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index 1f63110fd26..6e6a307bde5 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -25,6 +25,7 @@ from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath +from ._deprecate import deprecate class Pen: @@ -176,4 +177,27 @@ def textsize(self, text, font): .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize` """ - return self.draw.textsize(text, font=font.font) + deprecate("textsize", 10, "textbbox or textlength") + return self.draw.textsize(text, font=font.font, __internal__=True) + + def textbbox(self, xy, text, font): + """ + Returns bounding box (in pixels) of given text. + + :return: ``(left, top, right, bottom)`` bounding box + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox` + """ + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + return self.draw.textbbox(xy, text, font=font.font) + + def textlength(self, text, font): + """ + Returns length (in pixels) of given text. + This is the amount by which following text should be offset. + + .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength` + """ + return self.draw.textlength(text, font=font.font) From 5fc3b6e5bbe6396246004b48e32afa1e19be231d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 20 Jun 2022 08:29:37 +1000 Subject: [PATCH 199/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8d5d7001a86..afd66e99ae0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Fixed incorrect operator in ImageCms error #6370 + [LostBenjamin, hugovk, radarhere] + - Limit FPX tile size to avoid extending outside image #6368 [radarhere] From a2eff2aff58e2f10ab29c982d25dad58229e1330 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 20 Jun 2022 09:18:05 +1000 Subject: [PATCH 200/326] Added test --- Tests/test_image_quantize.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index e9afd91189c..981753eb9b7 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -65,6 +65,22 @@ def test_quantize_no_dither(): assert converted.palette.palette == palette.palette.palette +def test_quantize_no_dither2(): + im = Image.new("RGB", (9, 1)) + im.putdata(list((p,) * 3 for p in range(0, 36, 4))) + + palette = Image.new("P", (1, 1)) + data = (0, 0, 0, 32, 32, 32) + palette.putpalette(data) + quantized = im.quantize(dither=Image.Dither.NONE, palette=palette) + + assert tuple(quantized.palette.palette) == data + + px = quantized.load() + for x in range(9): + assert px[x, 0] == (0 if x < 5 else 1) + + def test_quantize_dither_diff(): image = hopper() with Image.open("Tests/images/caption_6_33_22.png") as palette: From e2158344a0b4b4016a39dcf40c7220aa77b60579 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 20 Jun 2022 01:20:56 +0100 Subject: [PATCH 201/326] update test_imagefont to use textbbox --- Tests/images/rectangle_surrounding_text.png | Bin 1520 -> 1558 bytes Tests/test_imagefont.py | 126 ++++++++++++++------ Tests/test_imagefontctl.py | 4 +- 3 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Tests/images/rectangle_surrounding_text.png b/Tests/images/rectangle_surrounding_text.png index 2b75a5e9c7ae1fc828125e2903ea6136d52cd2eb..ca77cea7323afb75186315fcd403b7db2509ef86 100644 GIT binary patch delta 1455 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 Date: Mon, 20 Jun 2022 02:18:16 +0100 Subject: [PATCH 202/326] add getbbox and getlength to TransposedFont with tests --- Tests/test_imagefont.py | 39 +++++++++++++++++++++++++++++++++++---- src/PIL/ImageFont.py | 15 +++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index e004edfaff3..606b9ba0ed4 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -315,16 +315,31 @@ def test_rotated_transposed_font(self): # Original font draw.font = font - box_size_a = draw.textsize(word) + with pytest.warns(DeprecationWarning) as log: + box_size_a = draw.textsize(word) + assert len(log) == 1 + bbox_a = draw.textbbox((10, 10), word) # Rotated font draw.font = transposed_font - box_size_b = draw.textsize(word) + with pytest.warns(DeprecationWarning) as log: + box_size_b = draw.textsize(word) + assert len(log) == 1 + bbox_b = draw.textbbox((20, 20), word) # Check (w,h) of box a is (h,w) of box b assert box_size_a[0] == box_size_b[1] assert box_size_a[1] == box_size_b[0] + # Check bbox b is (20, 20, 20 + h, 20 + w) + assert bbox_b[0] == 20 + assert bbox_b[1] == 20 + assert bbox_b[2] == 20 + bbox_a[3] - bbox_a[1] + assert bbox_b[3] == 20 + bbox_a[2] - bbox_a[0] + + # text length is undefined for vertical text + pytest.raises(ValueError, draw.textlength, word) + def test_unrotated_transposed_font(self): img_grey = Image.new("L", (100, 100)) draw = ImageDraw.Draw(img_grey) @@ -336,15 +351,31 @@ def test_unrotated_transposed_font(self): # Original font draw.font = font - box_size_a = draw.textsize(word) + with pytest.warns(DeprecationWarning) as log: + box_size_a = draw.textsize(word) + assert len(log) == 1 + bbox_a = draw.textbbox((10, 10), word) + length_a = draw.textlength(word) # Rotated font draw.font = transposed_font - box_size_b = draw.textsize(word) + with pytest.warns(DeprecationWarning) as log: + box_size_b = draw.textsize(word) + assert len(log) == 1 + bbox_b = draw.textbbox((20, 20), word) + length_b = draw.textlength(word) # Check boxes a and b are same size assert box_size_a == box_size_b + # Check bbox b is (20, 20, 20 + w, 20 + h) + assert bbox_b[0] == 20 + assert bbox_b[1] == 20 + assert bbox_b[2] == 20 + bbox_a[2] - bbox_a[0] + assert bbox_b[3] == 20 + bbox_a[3] - bbox_a[1] + + assert length_a == length_b + def test_rotated_transposed_font_get_mask(self): # Arrange text = "mask this" diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 4e2cb9686df..ef452778266 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -853,6 +853,21 @@ def getmask(self, text, mode="", *args, **kwargs): return im.transpose(self.orientation) return im + def getbbox(self, text, *args, **kwargs): + # TransposedFont doesn't support getmask2, move top-left point to (0, 0) + # this has no effect on ImageFont and simulates anchor="lt" for FreeTypeFont + left, top, right, bottom = self.font.getbbox(text, *args, **kwargs) + width = right - left + height = bottom - top + if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): + return 0, 0, height, width + return 0, 0, width, height + + def getlength(self, text, *args, **kwargs): + if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): + raise ValueError("text length is undefined for rotated text") + return self.font.getlength(text, *args, **kwargs) + def load(filename): """ From a7baa31de854723ddfe0d08d472196feb0e8acc3 Mon Sep 17 00:00:00 2001 From: nulano Date: Mon, 20 Jun 2022 02:49:26 +0100 Subject: [PATCH 203/326] use getbbox instead of getsize in fuzzers.py --- Tests/oss-fuzz/fuzzers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/oss-fuzz/fuzzers.py b/Tests/oss-fuzz/fuzzers.py index 5786764a64d..10a172b4675 100644 --- a/Tests/oss-fuzz/fuzzers.py +++ b/Tests/oss-fuzz/fuzzers.py @@ -33,9 +33,9 @@ def fuzz_font(data): # different font objects. return - font.getsize_multiline("ABC\nAaaa") + font.getbbox("ABC") font.getmask("test text") with Image.new(mode="RGBA", size=(200, 200)) as im: draw = ImageDraw.Draw(im) - draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2) + draw.multiline_textbbox((10, 10), "ABC\nAaaa", font, stroke_width=2) draw.text((10, 10), "Test Text", font=font, fill="#000") From 3e8a9b2165ec271ade5096e6c59ad642f9f7fd86 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 20 Jun 2022 21:01:10 +1000 Subject: [PATCH 204/326] Fixed loading L mode RLE8 images --- Tests/images/hopper_rle8_greyscale.bmp | Bin 0 -> 6288 bytes Tests/test_file_bmp.py | 3 +++ src/PIL/BmpImagePlugin.py | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 Tests/images/hopper_rle8_greyscale.bmp diff --git a/Tests/images/hopper_rle8_greyscale.bmp b/Tests/images/hopper_rle8_greyscale.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ead32ff95a46d6fc5672974f96c639e680a69830 GIT binary patch literal 6288 zcmeI0_glza7{)(0A=#BAJCeN%SxLhvlD)EbW<=SWgzPOl6lL%1y_J={3k_tiS1#|5 z?_cnKpX;3KxzD-IkI#9Y`@UVgLaq4|K_=jrjs#|Zl4&}`$};`(Z=_$OZ+`u|{v2F9 zeEieF-*EmH{LcS{|2&aIh71|Vm@y+JCMIOcl!?rlGm|At7EDb|$(l7QW@ct&%a)Do z*|U=)M-Fo4%t@|XxyYS6H+k~p!Q9*&3kwS@EiK8LH!oIJR^-c<4{K{{^5@Twjg1Wj z3KXDV!GaVjREWZb3sa;>5sDTqO0iVC2Y=1O^5Y6cog$QKJ|=dNgCkjA889v5Xrxj`8EiGhxC6 zCQh8lq)C&QJb5xxrc7b#)TvCHHjU}ir!!;541$A$nK^SNAt52mnl+2rvu87B&KyER zLzz2wF7xKiWB&a4ELgCBg$oz5Xwf1TFJ8=&B}-VkbScZ0Eo1re<*Zn-f|V;*vTD^T z!otE>y?Qlk)~sRe+O@1(w~qDe*Rx^61~zWoNO*WSn>KA?^XAQL*|LR*hzKGhBiXuj zE8DhhBPuG2=;&y+Z{N<29Xr^$b0@oY?PB-t-R#-3hrN6EvTxr$_V3@%fddCPc<>;H z4jtm~;lsql#Bk)u5sn@`%CTd|IDY&%Cr+H;6<;oSVUcJh-YuC7b{W`I+vBbs2apT4fZr;4fty{Oaefu_d z?%W|hKAyXG?{e?nJ?`JX&w~dKc=+%kj~+eZ@#DuldGds(PoMJa*)yI$f6j{+FL?R# zB?$=$Bqk>E>eVY=zkbb|H*ZKvN+LNqnUs_iQd3iT`}QsG-o4}f`}d@!rSajz2R?rM z$fr-A`26`ZU%q_d>({S*`}U3R-@o(Y#}9t~{K>Chzkauo-@iYDGRj1mlv!D1s;n|o zHf2{1wl)s;(NUDJRuZZPig-)swU8%S8?3DmS^yLk-nPjnzb+@={YZlee17 zM=jJ+t<+j=)K=}(ULDj?o#d;|>Y}ddrta#Yp6aFE>Z88ur~dNO01eb24b~71mA{5* zxB@gnBNeD1jnZh1(O8YscumkmP10md(Ns;-bj?t(W-3IpG+T2Ns=1n{`C6cbTBOBV zqNQ4<W~gAMn`m1$8=mLbW*2uT4!`t=X72dbWxXdSyyyb*K}R6iqj3<)GgiC9mVUe?&-cB z=%F6zv7YFup6R(>=%o^rs8@QeH%d~nQk1H| wfKdTP4={RwF$Ih%VDJEg2N+DyV1kAgFtmW71`Rc6_yL9=U^qhmH%AEl0}b_zNB{r; literal 0 HcmV?d00001 diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index f214fd6bda1..776b499e089 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -134,6 +134,9 @@ def test_rle8(): with Image.open("Tests/images/hopper_rle8.bmp") as im: assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12) + with Image.open("Tests/images/hopper_rle8_greyscale.bmp") as im: + assert_image_equal_tofile(im, "Tests/images/bw_gradient.png") + # This test image has been manually hexedited # to have rows with too much data with Image.open("Tests/images/hopper_rle8_row_overflow.bmp") as im: diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 4dc2b93c392..5aacb10da8c 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -321,7 +321,8 @@ def decode(self, buffer): # align to 16-bit word boundary if self.fd.tell() % 2 != 0: self.fd.seek(1, os.SEEK_CUR) - self.set_as_raw(bytes(data), ("P", 0, self.args[-1])) + rawmode = "L" if self.mode == "L" else "P" + self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1])) return -1, 0 From f160e698a1e650a06f2287821be8d95be0fbb0c2 Mon Sep 17 00:00:00 2001 From: Dawid Crivelli Date: Mon, 20 Jun 2022 16:20:08 +0200 Subject: [PATCH 205/326] Handle PCF fonts files with missing characters --- src/PIL/PcfFontFile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index 0556c2bbc58..686237c12ec 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -241,7 +241,7 @@ def _load_encoding(self): ] if encoding_offset != 0xFFFF: encoding[i] = encoding_offset - except UnicodeDecodeError: + except (UnicodeDecodeError, IndexError): # character is not supported in selected encoding pass From b00b509dd817dfedbc7a0943be5c8aa8272043a7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 Jun 2022 07:54:44 +1000 Subject: [PATCH 206/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index afd66e99ae0..fee35799c52 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Use gnome-screenshot on Linux if available #6361 + [radarhere, nulano] + +- Fixed loading L mode BMP RLE8 images #6384 + [radarhere] + - Fixed incorrect operator in ImageCms error #6370 [LostBenjamin, hugovk, radarhere] From db741bf25f7ad7ec6662f1a813d0505f7882b336 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 Jun 2022 16:55:33 +1000 Subject: [PATCH 207/326] Skip remaining characters after IndexError --- src/PIL/PcfFontFile.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index 686237c12ec..0b43b27acf4 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -241,8 +241,10 @@ def _load_encoding(self): ] if encoding_offset != 0xFFFF: encoding[i] = encoding_offset - except (UnicodeDecodeError, IndexError): + except UnicodeDecodeError: # character is not supported in selected encoding pass + except IndexError: + break return encoding From 317286d260488e1e60a3d25b4d6c3fce83ba12f8 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 Jun 2022 17:27:49 +1000 Subject: [PATCH 208/326] Pad palette to 768 bytes --- Tests/test_file_pcx.py | 5 +++++ src/PIL/PcxImagePlugin.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 61e33a57bb4..ba6663cd3ba 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -20,6 +20,11 @@ def test_sanity(tmp_path): for mode in ("1", "L", "P", "RGB"): _roundtrip(tmp_path, hopper(mode)) + # Test a palette with less than 256 colors + im = Image.new("P", (1, 1)) + im.putpalette((255, 0, 0)) + _roundtrip(tmp_path, im) + # Test an unsupported mode f = str(tmp_path / "temp.pcx") im = hopper("RGBA") diff --git a/src/PIL/PcxImagePlugin.py b/src/PIL/PcxImagePlugin.py index d2e166bdd77..841c18a2200 100644 --- a/src/PIL/PcxImagePlugin.py +++ b/src/PIL/PcxImagePlugin.py @@ -198,7 +198,9 @@ def _save(im, fp, filename): if im.mode == "P": # colour palette fp.write(o8(12)) - fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes + palette = im.im.getpalette("RGB", "RGB") + palette += b"\x00" * (768 - len(palette)) + fp.write(palette) # 768 bytes elif im.mode == "L": # greyscale palette fp.write(o8(12)) From e6b7730c62992d0e7041b3a93ad87152959e76fa Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 22 Jun 2022 19:49:02 +1000 Subject: [PATCH 209/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fee35799c52..34d96b0b29d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Fixed bug with rounding pixels to palette colors #6377 + [btrekkie, radarhere] + - Use gnome-screenshot on Linux if available #6361 [radarhere, nulano] From 2970a5b8b2b6a03e528f75128491a364fe54226a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 23 Jun 2022 16:59:28 +1000 Subject: [PATCH 210/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 34d96b0b29d..18c2b6e2b55 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Pad PCX palette to 768 bytes when saving #6391 + [radarhere] + - Fixed bug with rounding pixels to palette colors #6377 [btrekkie, radarhere] From e5046b1057f24794ea83f694ecd23a404873853b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 9 Apr 2022 08:53:27 +1000 Subject: [PATCH 211/326] Reverted to __array_interface__ with the release of NumPy 1.23 --- Tests/test_image_array.py | 8 +++++--- src/PIL/Image.py | 12 +++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Tests/test_image_array.py b/Tests/test_image_array.py index 7168c4265bb..7e5fd6fe13c 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -1,4 +1,5 @@ import pytest +from packaging.version import parse as parse_version from PIL import Image @@ -34,9 +35,10 @@ def test_with_dtype(dtype): test_with_dtype(numpy.float64) test_with_dtype(numpy.uint8) - with Image.open("Tests/images/truncated_jpeg.jpg") as im_truncated: - with pytest.raises(OSError): - numpy.array(im_truncated) + if parse_version(numpy.__version__) >= parse_version("1.23"): + with Image.open("Tests/images/truncated_jpeg.jpg") as im_truncated: + with pytest.raises(OSError): + numpy.array(im_truncated) def test_fromarray(): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 3cf4297351b..8d1b3e9568a 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -671,14 +671,9 @@ def _repr_png_(self): raise ValueError("Could not save to PNG for display") from e return b.getvalue() - class _ArrayData: - def __init__(self, new): - self.__array_interface__ = new - - def __array__(self, dtype=None): + @property + def __array_interface__(self): # numpy array interface support - import numpy as np - new = {} shape, typestr = _conv_type_shape(self) new["shape"] = shape @@ -690,8 +685,7 @@ def __array__(self, dtype=None): new["data"] = self.tobytes("raw", "L") else: new["data"] = self.tobytes() - - return np.array(self._ArrayData(new), dtype) + return new def __getstate__(self): return [self.info, self.mode, self.size, self.getpalette(), self.tobytes()] From ef93892af8606b20c863c8987b01b328b706aa62 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 25 Jun 2022 17:08:56 +1000 Subject: [PATCH 212/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 18c2b6e2b55..6fdd4bdd4fe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Reverted to __array_interface__ with the release of NumPy 1.23 #6394 + [radarhere] + - Pad PCX palette to 768 bytes when saving #6391 [radarhere] From 847ad8c512cf51c83740c8da621c8205fe693aa4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 26 Jun 2022 20:58:36 +1000 Subject: [PATCH 213/326] Clarify check that palette is not already at its smallest --- src/PIL/GifImagePlugin.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index dd659c95905..ce9fb5dd46c 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -830,9 +830,13 @@ def _get_optimize(im, info): num_palette_colors = len(im.palette.palette) // Image.getmodebands( im.palette.mode ) - # Round up to power of 2 but at least 4 - num_palette_colors = max(4, 1 << (num_palette_colors - 1).bit_length()) - if len(used_palette_colors) <= num_palette_colors // 2: + current_palette_size = 1 << (num_palette_colors - 1).bit_length() + if ( + # check that the palette would become smaller when saved + len(used_palette_colors) <= current_palette_size // 2 + # check that the palette is not already the smallest possible size + and current_palette_size > 2 + ): return used_palette_colors From bc6a02422eed1ef798304f9c96d9410a210c6671 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 Jun 2022 09:13:21 +1000 Subject: [PATCH 214/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6fdd4bdd4fe..4a0a86609b7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Improved GIF optimize condition #6378 + [raygard, radarhere] + - Reverted to __array_interface__ with the release of NumPy 1.23 #6394 [radarhere] From 93805d5257add6439c62992d83f691df9be8c5e6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 27 Jun 2022 14:03:20 +1000 Subject: [PATCH 215/326] Limit expected number of characters to nencoding --- .../fonts/10x20-ISO8859-1-fewer-characters.pcf | Bin 0 -> 25860 bytes Tests/test_font_pcf.py | 8 ++++++++ src/PIL/PcfFontFile.py | 12 ++++-------- 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 Tests/fonts/10x20-ISO8859-1-fewer-characters.pcf diff --git a/Tests/fonts/10x20-ISO8859-1-fewer-characters.pcf b/Tests/fonts/10x20-ISO8859-1-fewer-characters.pcf new file mode 100644 index 0000000000000000000000000000000000000000..c065f59a9daca445b77e06a43daa7a0cdf645b62 GIT binary patch literal 25860 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 Date: Mon, 27 Jun 2022 15:50:31 +1000 Subject: [PATCH 216/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4a0a86609b7..8a37e6dbb56 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Handle PCF fonts files with less than 256 characters #6386 + [dawidcrivelli, radarhere] + - Improved GIF optimize condition #6378 [raygard, radarhere] From 3558f4e5d69074bd7bf424c116ac8244322e1366 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 28 Jun 2022 09:50:03 +1000 Subject: [PATCH 217/326] Updated harfbuzz to 4.4.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index d7e88ca4075..0870cc22511 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -281,9 +281,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/4.3.0.zip", - "filename": "harfbuzz-4.3.0.zip", - "dir": "harfbuzz-4.3.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/4.4.0.zip", + "filename": "harfbuzz-4.4.0.zip", + "dir": "harfbuzz-4.4.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 31bed8cfccd29a0ac19ce8c36b93a91adbaa324b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 29 Jun 2022 09:49:14 +0300 Subject: [PATCH 218/326] Docs: remove redundant search page from table of contents --- docs/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 5e886c2e82a..c731e274600 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -94,4 +94,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` -* :ref:`search` From eebbf6b27174156d1be9aaaa768c316636120fac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 29 Jun 2022 18:59:44 +1000 Subject: [PATCH 219/326] Updated harfbuzz to 4.4.1 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 0870cc22511..2e7c84ad0e2 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -281,9 +281,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/4.4.0.zip", - "filename": "harfbuzz-4.4.0.zip", - "dir": "harfbuzz-4.4.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/4.4.1.zip", + "filename": "harfbuzz-4.4.1.zip", + "dir": "harfbuzz-4.4.1", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From c9f1b35e981075110a23487a8d4a6cbb59a588ea Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jun 2022 12:47:35 +1000 Subject: [PATCH 220/326] Added GIF decompression bomb check --- Tests/images/decompression_bomb_extents.gif | Bin 0 -> 368 bytes Tests/test_decompression_bomb.py | 5 +++++ src/PIL/GifImagePlugin.py | 1 + 3 files changed, 6 insertions(+) create mode 100644 Tests/images/decompression_bomb_extents.gif diff --git a/Tests/images/decompression_bomb_extents.gif b/Tests/images/decompression_bomb_extents.gif new file mode 100644 index 0000000000000000000000000000000000000000..0d5ff03f525904d4bc29e3b22f70b45eb1c2cba7 GIT binary patch literal 368 zcmZ?wbh9u|Okqf2XkcLY&j12CAOa-9z_E~lkweB~!-9j&9Ku>LCpIiR+%BN(HOFJ) zqNCjs##whxY+QW2U%|Oc#&gq>lan=qSH+y%wDk0JgXB|lJU1^pJKLi8)t!@@m!F^S z(99+4wPnS{#U7KjVoz;Zd3kxjVz0SgTUT9O9kDsd1uK)l5 literal 0 HcmV?d00001 diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index d85d1f3c266..63071b78c9c 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -61,6 +61,11 @@ def test_exception_gif(self): with Image.open("Tests/images/decompression_bomb.gif"): pass + def test_exception_gif_extents(self): + with Image.open("Tests/images/decompression_bomb_extents.gif") as im: + with pytest.raises(Image.DecompressionBombError): + im.seek(1) + def test_exception_bmp(self): with pytest.raises(Image.DecompressionBombError): with Image.open("Tests/images/bmp/b/reallybig.bmp"): diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index ce9fb5dd46c..c239a6a2bdb 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -265,6 +265,7 @@ def _seek(self, frame, update_image=True): x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6) if (x1 > self.size[0] or y1 > self.size[1]) and update_image: self._size = max(x1, self.size[0]), max(y1, self.size[1]) + Image._decompression_bomb_check(self._size) frame_dispose_extent = x0, y0, x1, y1 flags = s[8] From 7527964ce94b1018362271a82bd09ee98fd7a455 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jun 2022 18:46:51 +1000 Subject: [PATCH 221/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8a37e6dbb56..a8eabab8752 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Added GIF decompression bomb check #6402 + [radarhere] + - Handle PCF fonts files with less than 256 characters #6386 [dawidcrivelli, radarhere] From 3b8e195db2f7bbee98441849849b845704d7d393 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jun 2022 18:58:38 +1000 Subject: [PATCH 222/326] Added release notes for #6402 --- docs/releasenotes/9.2.0.rst | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index 20e6cfa950f..ca52f6ab952 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -1,12 +1,6 @@ 9.2.0 ----- -Backwards Incompatible Changes -============================== - -TODO -^^^^ - Deprecations ============ @@ -46,14 +40,6 @@ Image.coerce_e This undocumented method has been deprecated and will be removed in Pillow 10 (2023-07-01). -API Changes -=========== - -TODO -^^^^ - -TODO - API Additions ============= @@ -68,10 +54,7 @@ The image's palette mode will become "RGBA", and "transparency" will be removed Security ======== -TODO -^^^^ - -TODO +An additional decompression bomb check has been added for the GIF format. Other Changes ============= From 31009c978094941a75a2f7c6fb269f2ea9d3063f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jun 2022 19:30:01 +1000 Subject: [PATCH 223/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a8eabab8752..c9b4a9f491f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Fixed null check for fribidi_version_info in FriBiDi shim #6376 + [nulano] + - Added GIF decompression bomb check #6402 [radarhere] From f57a9d678c770712320345ed9f71c04b38db1249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Thu, 30 Jun 2022 14:20:56 +0200 Subject: [PATCH 224/326] update TransposedFont.getlength error message Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageFont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index ef452778266..77d2cf0f9da 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -865,7 +865,7 @@ def getbbox(self, text, *args, **kwargs): def getlength(self, text, *args, **kwargs): if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - raise ValueError("text length is undefined for rotated text") + raise ValueError("text length is undefined for text rotated by 90 or 270 degrees") return self.font.getlength(text, *args, **kwargs) From 303ec1a95e8091c1fa3c0a11da7e8603c55e6a69 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 30 Jun 2022 12:21:39 +0000 Subject: [PATCH 225/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/PIL/ImageFont.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 77d2cf0f9da..8a55d517fdc 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -865,7 +865,9 @@ def getbbox(self, text, *args, **kwargs): def getlength(self, text, *args, **kwargs): if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - raise ValueError("text length is undefined for text rotated by 90 or 270 degrees") + raise ValueError( + "text length is undefined for text rotated by 90 or 270 degrees" + ) return self.font.getlength(text, *args, **kwargs) From 838b1f1598f5f7c110874a5341dd01aba611ddd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Thu, 30 Jun 2022 14:22:07 +0200 Subject: [PATCH 226/326] add replacement for getsize_multiline to deprecation warning --- src/PIL/ImageFont.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 8a55d517fdc..0bd92749d58 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -532,7 +532,7 @@ def getsize_multiline( :return: (width, height) """ - deprecate("getsize_multiline", 10) + deprecate("getsize_multiline", 10, "ImageDraw.multiline_textbbox") max_width = 0 lines = self._multiline_split(text) line_spacing = ( From 3c0b8763abb26232bf304a5cdf2b046d5f7e443e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jun 2022 21:58:47 +1000 Subject: [PATCH 227/326] Added documentation and release notes --- docs/deprecations.rst | 16 ++++++++++++++++ docs/reference/ImageFont.rst | 1 + docs/releasenotes/9.2.0.rst | 16 ++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 8c5b8a748d7..af9c35f34a4 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -178,6 +178,22 @@ Image.coerce_e This undocumented method has been deprecated and will be removed in Pillow 10 (2023-07-01). +Font size and offset methods +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 9.2.0 + +=========================================================================== ============================================================================================================= +Deprecated Use instead +=========================================================================== ============================================================================================================= +:py:meth:`.FreeTypeFont.getsize` and :py:meth:`.FreeTypeFont.getoffset` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength` +:py:meth:`.FreeTypeFont.getsize_multiline` :py:meth:`.ImageDraw.multiline_textbbox` +:py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength` +:py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength` +:py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox` +``ImageDraw2.Draw().textsize`` ``ImageDraw2.Draw().textbbox`` and ``ImageDraw2.Draw().textlength`` +=========================================================================== ============================================================================================================= + Removed features ---------------- diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst index 8efef7cfd5c..516fa63a783 100644 --- a/docs/reference/ImageFont.rst +++ b/docs/reference/ImageFont.rst @@ -56,6 +56,7 @@ Methods .. autoclass:: PIL.ImageFont.TransposedFont :members: + :undoc-members: Constants --------- diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index ca52f6ab952..8815e2c0d61 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -40,6 +40,22 @@ Image.coerce_e This undocumented method has been deprecated and will be removed in Pillow 10 (2023-07-01). +Font size and offset methods +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. deprecated:: 9.2.0 + +=========================================================================== ============================================================================================================= +Deprecated Use instead +=========================================================================== ============================================================================================================= +:py:meth:`.FreeTypeFont.getsize` and :py:meth:`.FreeTypeFont.getoffset` :py:meth:`.FreeTypeFont.getbbox` and :py:meth:`.FreeTypeFont.getlength` +:py:meth:`.FreeTypeFont.getsize_multiline` :py:meth:`.ImageDraw.multiline_textbbox` +:py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength` +:py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength` +:py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox` +``ImageDraw2.Draw().textsize`` ``ImageDraw2.Draw().textbbox`` and ``ImageDraw2.Draw().textlength`` +=========================================================================== ============================================================================================================= + API Additions ============= From 9957e0b0d7371685abcc68f6d591277d26348fc9 Mon Sep 17 00:00:00 2001 From: nulano Date: Thu, 30 Jun 2022 14:38:20 +0200 Subject: [PATCH 228/326] link to ImageDraw2 in deprecations and release notes --- docs/deprecations.rst | 2 +- docs/releasenotes/9.2.0.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index af9c35f34a4..3796e3dfbeb 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -191,7 +191,7 @@ Deprecated Use :py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength` :py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength` :py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox` -``ImageDraw2.Draw().textsize`` ``ImageDraw2.Draw().textbbox`` and ``ImageDraw2.Draw().textlength`` +:py:meth:`.ImageDraw2.Draw.textsize` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength` =========================================================================== ============================================================================================================= Removed features diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index 8815e2c0d61..bd6b767e777 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -53,7 +53,7 @@ Deprecated Use :py:meth:`.ImageFont.getsize` :py:meth:`.ImageFont.getbbox` and :py:meth:`.ImageFont.getlength` :py:meth:`.TransposedFont.getsize` :py:meth:`.TransposedFont.getbbox` and :py:meth:`.TransposedFont.getlength` :py:meth:`.ImageDraw.textsize` and :py:meth:`.ImageDraw.multiline_textsize` :py:meth:`.ImageDraw.textbbox`, :py:meth:`.ImageDraw.textlength` and :py:meth:`.ImageDraw.multiline_textbbox` -``ImageDraw2.Draw().textsize`` ``ImageDraw2.Draw().textbbox`` and ``ImageDraw2.Draw().textlength`` +:py:meth:`.ImageDraw2.Draw.textsize` :py:meth:`.ImageDraw2.Draw.textbbox` and :py:meth:`.ImageDraw2.Draw.textlength` =========================================================================== ============================================================================================================= API Additions From 7691231aa7bb74584139c8c50b60e93d56790f14 Mon Sep 17 00:00:00 2001 From: nulano Date: Thu, 30 Jun 2022 14:51:29 +0200 Subject: [PATCH 229/326] Fix heading in deprecations.rst --- docs/deprecations.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 3796e3dfbeb..a0289bb24f2 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -179,7 +179,7 @@ This undocumented method has been deprecated and will be removed in Pillow 10 (2023-07-01). Font size and offset methods -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. deprecated:: 9.2.0 From 65020e7c7c4539e905b3f5a9a1666892ac41ed70 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 30 Jun 2022 22:40:40 +1000 Subject: [PATCH 230/326] Documented deprecation in individual methods --- docs/reference/ImageDraw.rst | 4 ++++ src/PIL/ImageDraw2.py | 2 ++ src/PIL/ImageFont.py | 11 +++++++++++ 3 files changed, 17 insertions(+) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index b95d8d591a7..1ba024eea9d 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -436,6 +436,8 @@ Methods .. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) + .. deprecated:: 9.2.0 + Return the size of the given string, in pixels. Use :py:meth:`textlength()` to measure the offset of following text with @@ -484,6 +486,8 @@ Methods .. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) + .. deprecated:: 9.2.0 + Return the size of the given string, in pixels. Use :py:meth:`textlength()` to measure the offset of following text with diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index 6e6a307bde5..ea9461e92ec 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -173,6 +173,8 @@ def text(self, xy, text, font): def textsize(self, text, font): """ + .. deprecated:: 9.2.0 + Return the size of the given string, in pixels. .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize` diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 0bd92749d58..1f356adefd9 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -137,6 +137,8 @@ def _load_pilfont_data(self, file, image): def getsize(self, text, *args, **kwargs): """ + .. deprecated:: 9.2.0 + Returns width and height (in pixels) of given text. :param text: Text to measure. @@ -424,6 +426,8 @@ def getsize( __internal__=False, ): """ + .. deprecated:: 9.2.0 + Returns width and height (in pixels) of given text if rendered in font with provided direction, features, and language. @@ -493,6 +497,8 @@ def getsize_multiline( stroke_width=0, ): """ + .. deprecated:: 9.2.0 + Returns width and height (in pixels) of given text if rendered in font with provided direction, features, and language, while respecting newline characters. @@ -548,6 +554,8 @@ def getsize_multiline( def getoffset(self, text): """ + .. deprecated:: 9.2.0 + Returns the offset of given text. This is the gap between the starting coordinate and the first marking. Note that this gap is included in the result of :py:func:`~PIL.ImageFont.FreeTypeFont.getsize`. @@ -837,6 +845,9 @@ def __init__(self, font, orientation=None): self.orientation = orientation # any 'transpose' argument, or None def getsize(self, text, *args, **kwargs): + """ + .. deprecated:: 9.2.0 + """ if not kwargs.get("__internal__"): deprecate("getsize", 10, "getbbox or getlength") try: From ad5271d62f65deb67b10c1938fff782e68ab3e62 Mon Sep 17 00:00:00 2001 From: nulano Date: Thu, 30 Jun 2022 15:02:09 +0200 Subject: [PATCH 231/326] Document replacements for individual deprecated font methods --- docs/reference/ImageDraw.rst | 8 ++++---- src/PIL/ImageFont.py | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index 1ba024eea9d..e6e2d15c06b 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -437,13 +437,12 @@ Methods .. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) .. deprecated:: 9.2.0 + Use :py:meth:`textlength()` to measure the offset of following text with + 1/64 pixel precision. + Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor. Return the size of the given string, in pixels. - Use :py:meth:`textlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor. - .. note:: For historical reasons this function measures text height from the ascender line instead of the top, see :ref:`text-anchors`. If you wish to measure text height from the top, it is recommended @@ -487,6 +486,7 @@ Methods .. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) .. deprecated:: 9.2.0 + Use :py:meth:`.multiline_textbbox` instead. Return the size of the given string, in pixels. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 1f356adefd9..8c23b59b6be 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -138,6 +138,7 @@ def _load_pilfont_data(self, file, image): def getsize(self, text, *args, **kwargs): """ .. deprecated:: 9.2.0 + Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. Returns width and height (in pixels) of given text. @@ -427,14 +428,13 @@ def getsize( ): """ .. deprecated:: 9.2.0 + Use :py:meth:`getlength()` to measure the offset of following text with + 1/64 pixel precision. + Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. Returns width and height (in pixels) of given text if rendered in font with provided direction, features, and language. - Use :py:meth:`getlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. - .. note:: For historical reasons this function measures text height from the ascender line instead of the top, see :ref:`text-anchors`. If you wish to measure text height from the top, it is recommended @@ -498,6 +498,7 @@ def getsize_multiline( ): """ .. deprecated:: 9.2.0 + Use :py:meth:`.ImageDraw.multiline_textbbox` instead. Returns width and height (in pixels) of given text if rendered in font with provided direction, features, and language, while respecting @@ -555,6 +556,7 @@ def getsize_multiline( def getoffset(self, text): """ .. deprecated:: 9.2.0 + Use :py:meth:`.getbbox` instead. Returns the offset of given text. This is the gap between the starting coordinate and the first marking. Note that this gap is @@ -847,6 +849,7 @@ def __init__(self, font, orientation=None): def getsize(self, text, *args, **kwargs): """ .. deprecated:: 9.2.0 + Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. """ if not kwargs.get("__internal__"): deprecate("getsize", 10, "getbbox or getlength") From a37c21e136fbd0398a9f767b0cc7ac910ce67daf Mon Sep 17 00:00:00 2001 From: nulano Date: Thu, 30 Jun 2022 15:29:09 +0200 Subject: [PATCH 232/326] document planned removal date for ImageFont deprecations and release notes --- docs/deprecations.rst | 3 +++ docs/releasenotes/9.2.0.rst | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index a0289bb24f2..9be92770ab9 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -183,6 +183,9 @@ Font size and offset methods .. deprecated:: 9.2.0 +Several functions for computing the size and offset of rendered text +have been deprecated and will be removed in Pillow 10 (2023-07-01): + =========================================================================== ============================================================================================================= Deprecated Use instead =========================================================================== ============================================================================================================= diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index bd6b767e777..9c102f1776a 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -45,6 +45,9 @@ Font size and offset methods .. deprecated:: 9.2.0 +Several functions for computing the size and offset of rendered text +have been deprecated and will be removed in Pillow 10 (2023-07-01): + =========================================================================== ============================================================================================================= Deprecated Use instead =========================================================================== ============================================================================================================= From 74e0b954f2010b0e2e19166b1defad2f4ed6307c Mon Sep 17 00:00:00 2001 From: nulano Date: Fri, 1 Jul 2022 11:22:47 +0200 Subject: [PATCH 233/326] test {ImageFont,TransposedFont}.getsize() deprecation --- Tests/test_font_pcf.py | 3 +++ Tests/test_imagefont.py | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 815ef1d9254..c217378fb74 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -82,6 +82,9 @@ def test_textsize(request, tmp_path): assert dy == 20 assert dx in (0, 10) assert font.getlength(chr(i)) == dx + with pytest.warns(DeprecationWarning) as log: + assert font.getsize(chr(i)) == (dx, dy) + assert len(log) == 1 for i in range(len(message)): msg = message[: i + 1] assert font.getlength(msg) == len(msg) * 10 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 606b9ba0ed4..16da87d469a 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -317,14 +317,16 @@ def test_rotated_transposed_font(self): draw.font = font with pytest.warns(DeprecationWarning) as log: box_size_a = draw.textsize(word) - assert len(log) == 1 + assert box_size_a == font.getsize(word) + assert len(log) == 2 bbox_a = draw.textbbox((10, 10), word) # Rotated font draw.font = transposed_font with pytest.warns(DeprecationWarning) as log: box_size_b = draw.textsize(word) - assert len(log) == 1 + assert box_size_b == transposed_font.getsize(word) + assert len(log) == 2 bbox_b = draw.textbbox((20, 20), word) # Check (w,h) of box a is (h,w) of box b From 729fe6f8b0ec7ebea408c526d4c3f8c998c7e6bb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 1 Jul 2022 19:45:36 +1000 Subject: [PATCH 234/326] Updated indentation --- docs/reference/ImageDraw.rst | 10 ++++++---- src/PIL/ImageFont.py | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index e6e2d15c06b..c2d72c804c1 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -437,9 +437,10 @@ Methods .. py:method:: ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) .. deprecated:: 9.2.0 - Use :py:meth:`textlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor. + + Use :py:meth:`textlength()` to measure the offset of following text with + 1/64 pixel precision. + Use :py:meth:`textbbox()` to get the exact bounding box based on an anchor. Return the size of the given string, in pixels. @@ -486,7 +487,8 @@ Methods .. py:method:: ImageDraw.multiline_textsize(text, font=None, spacing=4, direction=None, features=None, language=None, stroke_width=0) .. deprecated:: 9.2.0 - Use :py:meth:`.multiline_textbbox` instead. + + Use :py:meth:`.multiline_textbbox` instead. Return the size of the given string, in pixels. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 8c23b59b6be..1f62468ab3b 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -138,7 +138,8 @@ def _load_pilfont_data(self, file, image): def getsize(self, text, *args, **kwargs): """ .. deprecated:: 9.2.0 - Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. + + Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. Returns width and height (in pixels) of given text. @@ -428,9 +429,10 @@ def getsize( ): """ .. deprecated:: 9.2.0 - Use :py:meth:`getlength()` to measure the offset of following text with - 1/64 pixel precision. - Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. + + Use :py:meth:`getlength()` to measure the offset of following text with + 1/64 pixel precision. + Use :py:meth:`getbbox()` to get the exact bounding box based on an anchor. Returns width and height (in pixels) of given text if rendered in font with provided direction, features, and language. @@ -498,7 +500,8 @@ def getsize_multiline( ): """ .. deprecated:: 9.2.0 - Use :py:meth:`.ImageDraw.multiline_textbbox` instead. + + Use :py:meth:`.ImageDraw.multiline_textbbox` instead. Returns width and height (in pixels) of given text if rendered in font with provided direction, features, and language, while respecting @@ -556,7 +559,8 @@ def getsize_multiline( def getoffset(self, text): """ .. deprecated:: 9.2.0 - Use :py:meth:`.getbbox` instead. + + Use :py:meth:`.getbbox` instead. Returns the offset of given text. This is the gap between the starting coordinate and the first marking. Note that this gap is @@ -849,7 +853,8 @@ def __init__(self, font, orientation=None): def getsize(self, text, *args, **kwargs): """ .. deprecated:: 9.2.0 - Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. + + Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. """ if not kwargs.get("__internal__"): deprecate("getsize", 10, "getbbox or getlength") From 4ca99f74545182b5184fa16ab69db9cbf0fb7d3e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 1 Jul 2022 19:51:29 +1000 Subject: [PATCH 235/326] Install furo if it is not available --- docs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Makefile b/docs/Makefile index 0d352302f93..309fc4458e0 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -43,6 +43,7 @@ clean: install-sphinx: python3 -c "import sphinx" > /dev/null 2>&1 || python3 -m pip install sphinx + python3 -c "import furo" > /dev/null 2>&1 || python3 -m pip install furo html: $(MAKE) install-sphinx From 8a6050ee5bef46f3c77688023d21b680d74b8af2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 1 Jul 2022 20:33:59 +1000 Subject: [PATCH 236/326] Replaced __internal__ argument with warning filters --- src/PIL/ImageDraw.py | 102 +++++++++++++++++++++--------------------- src/PIL/ImageDraw2.py | 6 ++- src/PIL/ImageFont.py | 31 ++++++------- 3 files changed, 70 insertions(+), 69 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 723ec17fdc1..8970471d3b2 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -32,6 +32,7 @@ import math import numbers +import warnings from . import Image, ImageColor from ._deprecate import deprecate @@ -375,15 +376,16 @@ def _multiline_split(self, text): def _multiline_spacing(self, font, spacing, stroke_width): # this can be replaced with self.textbbox(...)[3] when textsize is removed - return ( - self.textsize( - "A", - font=font, - stroke_width=stroke_width, - __internal__=True, - )[1] - + spacing - ) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + return ( + self.textsize( + "A", + font=font, + stroke_width=stroke_width, + )[1] + + spacing + ) def text( self, @@ -582,34 +584,34 @@ def textsize( features=None, language=None, stroke_width=0, - __internal__=False, ): """Get the size of a given string, in pixels.""" - if not __internal__: - deprecate("textsize", 10, "textbbox or textlength") + deprecate("textsize", 10, "textbbox or textlength") if self._multiline_check(text): - return self.multiline_textsize( + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + return self.multiline_textsize( + text, + font, + spacing, + direction, + features, + language, + stroke_width, + ) + + if font is None: + font = self.getfont() + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + return font.getsize( text, - font, - spacing, direction, features, language, stroke_width, - __internal__=True, ) - if font is None: - font = self.getfont() - return font.getsize( - text, - direction, - features, - language, - stroke_width, - __internal__=True, - ) - def multiline_textsize( self, text, @@ -619,25 +621,24 @@ def multiline_textsize( features=None, language=None, stroke_width=0, - __internal__=False, ): - if not __internal__: - deprecate("multiline_textsize", 10, "multiline_textbbox") + deprecate("multiline_textsize", 10, "multiline_textbbox") max_width = 0 lines = self._multiline_split(text) line_spacing = self._multiline_spacing(font, spacing, stroke_width) - for line in lines: - line_width, line_height = self.textsize( - line, - font, - spacing, - direction, - features, - language, - stroke_width, - __internal__=True, - ) - max_width = max(max_width, line_width) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + for line in lines: + line_width, line_height = self.textsize( + line, + font, + spacing, + direction, + features, + language, + stroke_width, + ) + max_width = max(max_width, line_width) return max_width, len(lines) * line_spacing - spacing def textlength( @@ -662,14 +663,15 @@ def textlength( return font.getlength(text, mode, direction, features, language) except AttributeError: deprecate("textlength support for fonts without getlength", 10) - size = self.textsize( - text, - font, - direction=direction, - features=features, - language=language, - __internal__=True, - ) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + size = self.textsize( + text, + font, + direction=direction, + features=features, + language=language, + ) if direction == "ttb": return size[1] return size[0] diff --git a/src/PIL/ImageDraw2.py b/src/PIL/ImageDraw2.py index ea9461e92ec..2667b77dd43 100644 --- a/src/PIL/ImageDraw2.py +++ b/src/PIL/ImageDraw2.py @@ -24,6 +24,8 @@ """ +import warnings + from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath from ._deprecate import deprecate @@ -180,7 +182,9 @@ def textsize(self, text, font): .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textsize` """ deprecate("textsize", 10, "textbbox or textlength") - return self.draw.textsize(text, font=font.font, __internal__=True) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + return self.draw.textsize(text, font=font.font) def textbbox(self, xy, text, font): """ diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 1f62468ab3b..a3b711c6077 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -147,8 +147,7 @@ def getsize(self, text, *args, **kwargs): :return: (width, height) """ - if not kwargs.get("__internal__"): - deprecate("getsize", 10, "getbbox or getlength") + deprecate("getsize", 10, "getbbox or getlength") return self.font.getsize(text) def getmask(self, text, mode="", *args, **kwargs): @@ -425,7 +424,6 @@ def getsize( features=None, language=None, stroke_width=0, - __internal__=False, ): """ .. deprecated:: 9.2.0 @@ -479,8 +477,7 @@ def getsize( :return: (width, height) """ - if not __internal__: - deprecate("getsize", 10, "getbbox or getlength") + deprecate("getsize", 10, "getbbox or getlength") # vertical offset is added for historical reasons # see https://github.com/python-pillow/Pillow/pull/4910#discussion_r486682929 size, offset = self.font.getsize(text, "L", direction, features, language) @@ -545,14 +542,14 @@ def getsize_multiline( deprecate("getsize_multiline", 10, "ImageDraw.multiline_textbbox") max_width = 0 lines = self._multiline_split(text) - line_spacing = ( - self.getsize("A", stroke_width=stroke_width, __internal__=True)[1] + spacing - ) - for line in lines: - line_width, line_height = self.getsize( - line, direction, features, language, stroke_width, __internal__=True - ) - max_width = max(max_width, line_width) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + line_spacing = self.getsize("A", stroke_width=stroke_width)[1] + spacing + for line in lines: + line_width, line_height = self.getsize( + line, direction, features, language, stroke_width + ) + max_width = max(max_width, line_width) return max_width, len(lines) * line_spacing - spacing @@ -856,11 +853,9 @@ def getsize(self, text, *args, **kwargs): Use :py:meth:`.getbbox` or :py:meth:`.getlength` instead. """ - if not kwargs.get("__internal__"): - deprecate("getsize", 10, "getbbox or getlength") - try: - w, h = self.font.getsize(text, __internal__=True) - except TypeError: + deprecate("getsize", 10, "getbbox or getlength") + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) w, h = self.font.getsize(text) if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): return h, w From dc518ac25e912200dd885728f5dbc885c812f8f2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 1 Jul 2022 16:16:26 +0300 Subject: [PATCH 237/326] 9.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 66cb160645f..3b67ed5770a 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "9.2.0.dev0" +__version__ = "9.2.0" From 58acec3312fb8671c9d84829197e1c8150085589 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 1 Jul 2022 16:19:51 +0300 Subject: [PATCH 238/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index c9b4a9f491f..d8419f7120a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,9 +2,12 @@ Changelog (Pillow) ================== -9.2.0 (unreleased) +9.2.0 (2022-07-01) ------------------ +- Deprecate ImageFont.getsize and related functions #6381 + [nulano, radarhere] + - Fixed null check for fribidi_version_info in FriBiDi shim #6376 [nulano] From 26e2c7e030bac7c28ff0bd63dc7bfdaecd99f83c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 1 Jul 2022 21:29:53 +0300 Subject: [PATCH 239/326] 9.3.0.dev0 version bump --- src/PIL/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/_version.py b/src/PIL/_version.py index 3b67ed5770a..8e736a4326f 100644 --- a/src/PIL/_version.py +++ b/src/PIL/_version.py @@ -1,2 +1,2 @@ # Master version for Pillow -__version__ = "9.2.0" +__version__ = "9.3.0.dev0" From 43b185e1c1b011792770ebcacdc68ca7d58ee291 Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Mon, 4 Jul 2022 01:19:58 +0000 Subject: [PATCH 240/326] chore: Set permissions for GitHub actions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- .github/workflows/lint.yml | 3 +++ .github/workflows/release-drafter.yml | 6 ++++++ .github/workflows/test-cygwin.yml | 2 ++ .github/workflows/test-docker.yml | 5 +++++ .github/workflows/test-mingw.yml | 5 +++++ .github/workflows/test-valgrind.yml | 3 +++ .github/workflows/test-windows.yml | 5 +++++ .github/workflows/test.yml | 5 +++++ .github/workflows/tidelift.yml | 3 +++ 9 files changed, 37 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4540fb5afd2..527f26d35d4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,6 +2,9 @@ name: Lint on: [push, pull_request, workflow_dispatch] +permissions: + contents: read + jobs: build: diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index ad66117b187..7ee76c4ac8c 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -7,8 +7,14 @@ on: - main workflow_dispatch: +permissions: + contents: read + jobs: update_release_draft: + permissions: + contents: write # for release-drafter/release-drafter to create a github release + pull-requests: write # for release-drafter/release-drafter to add label to PR if: github.repository == 'python-pillow/Pillow' runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index 2e8fc9c09f0..417b1f21276 100644 --- a/.github/workflows/test-cygwin.yml +++ b/.github/workflows/test-cygwin.yml @@ -99,6 +99,8 @@ jobs: name: Cygwin Python 3.${{ matrix.python-minor-version }} success: + permissions: + contents: none needs: build runs-on: ubuntu-latest name: Cygwin Test Successful diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index 2b4dc6b5232..a789726079e 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -2,6 +2,9 @@ name: Test Docker on: [push, pull_request, workflow_dispatch] +permissions: + contents: read + jobs: build: @@ -82,6 +85,8 @@ jobs: name: ${{ matrix.docker }} success: + permissions: + contents: none needs: build runs-on: ubuntu-latest name: Docker Test Successful diff --git a/.github/workflows/test-mingw.yml b/.github/workflows/test-mingw.yml index 7b5cc8a972e..7ddb71e1f68 100644 --- a/.github/workflows/test-mingw.yml +++ b/.github/workflows/test-mingw.yml @@ -2,6 +2,9 @@ name: Test MinGW on: [push, pull_request, workflow_dispatch] +permissions: + contents: read + jobs: build: runs-on: windows-latest @@ -77,6 +80,8 @@ jobs: CODECOV_NAME: ${{ matrix.name }} success: + permissions: + contents: none needs: build runs-on: ubuntu-latest name: MinGW Test Successful diff --git a/.github/workflows/test-valgrind.yml b/.github/workflows/test-valgrind.yml index 21a2b469ee0..013e5ca4ac3 100644 --- a/.github/workflows/test-valgrind.yml +++ b/.github/workflows/test-valgrind.yml @@ -13,6 +13,9 @@ on: - "**.h" workflow_dispatch: +permissions: + contents: read + jobs: build: diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 358461b388b..b9accfdf9a5 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -2,6 +2,9 @@ name: Test Windows on: [push, pull_request, workflow_dispatch] +permissions: + contents: read + jobs: build: runs-on: windows-latest @@ -189,6 +192,8 @@ jobs: path: dist\*.whl success: + permissions: + contents: none needs: build runs-on: ubuntu-latest name: Windows Test Successful diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d41f4b57196..5614ad5f228 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,9 @@ name: Test on: [push, pull_request, workflow_dispatch] +permissions: + contents: read + jobs: build: @@ -109,6 +112,8 @@ jobs: CODECOV_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} success: + permissions: + contents: none needs: build runs-on: ubuntu-latest name: Test Successful diff --git a/.github/workflows/tidelift.yml b/.github/workflows/tidelift.yml index 9a3192f9d7d..c73f254313d 100644 --- a/.github/workflows/tidelift.yml +++ b/.github/workflows/tidelift.yml @@ -12,6 +12,9 @@ on: - ".github/workflows/tidelift.yml" workflow_dispatch: +permissions: + contents: read + jobs: build: if: github.repository_owner == 'python-pillow' From 300f6c8a59dbfd2df35715cf86be5869532371cb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 4 Jul 2022 19:33:21 +1000 Subject: [PATCH 241/326] Document where to find other codecs --- docs/handbook/writing-your-own-image-plugin.rst | 4 ++++ src/PIL/Image.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/docs/handbook/writing-your-own-image-plugin.rst b/docs/handbook/writing-your-own-image-plugin.rst index 80138742d68..323127e5b04 100644 --- a/docs/handbook/writing-your-own-image-plugin.rst +++ b/docs/handbook/writing-your-own-image-plugin.rst @@ -141,6 +141,10 @@ The fields are used as follows: uncompressed data, in a variety of pixel formats. For more information on this decoder, see the description below. + A list of C decoders can be seen under codecs section of the function array + in :file:`_imaging.c`. Python decoders are registered within the relevant + plugins. + **region** A 4-tuple specifying where to store data in the image. diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8d1b3e9568a..ce32c39ced6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -715,6 +715,11 @@ def tobytes(self, encoder_name="raw", *args): :param encoder_name: What encoder to use. The default is to use the standard "raw" encoder. + + A list of C encoders can be seen under + codecs section of the function array in + :file:`_imaging.c`. Python encoders are + registered within the relevant plugins. :param args: Extra arguments to the encoder. :returns: A :py:class:`bytes` object. """ From a0b22d22038fd9d858a8bcdf64e5f67d016e4555 Mon Sep 17 00:00:00 2001 From: Santiago Castro Date: Mon, 4 Jul 2022 09:50:47 -0700 Subject: [PATCH 242/326] Fix when `sys.executable` is empty or null --- src/PIL/ImageShow.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 9117f57e570..5ca8035b15b 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -178,14 +178,15 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.call(["open", "-a", "Preview.app", path]) - subprocess.Popen( - [ - sys.executable, - "-c", - "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", - path, - ] - ) + if sys.executable: + subprocess.Popen( + [ + sys.executable, + "-c", + "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", + path, + ] + ) return 1 From 431498de0bc5d0e4119116e654db3177cf0b7ddf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 17:57:12 +0000 Subject: [PATCH 243/326] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0) - [github.com/pre-commit/pre-commit-hooks: v4.2.0 → v4.3.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.2.0...v4.3.0) - [github.com/sphinx-contrib/sphinx-lint: v0.6 → v0.6.1](https://github.com/sphinx-contrib/sphinx-lint/compare/v0.6...v0.6.1) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1806db54cdb..e5e1f3557f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.6.0 hooks: - id: black args: ["--target-version", "py37"] @@ -37,13 +37,13 @@ repos: - id: rst-backticks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.3.0 hooks: - id: check-merge-conflict - id: check-yaml - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.6 + rev: v0.6.1 hooks: - id: sphinx-lint From 527eecae80f41965ebf6aa3847baecaa41b1158c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 5 Jul 2022 13:42:41 +1000 Subject: [PATCH 244/326] Fallback to python3 --- src/PIL/ImageShow.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 5ca8035b15b..1b2b9f05fbf 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -178,10 +178,11 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.call(["open", "-a", "Preview.app", path]) - if sys.executable: + exectable = sys.executable or shutil.which("python3") + if executable: subprocess.Popen( [ - sys.executable, + executable, "-c", "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", path, From 4f7f5470b13b4006db58f2e2396ea0f17c3de373 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 6 Jul 2022 08:30:24 +1000 Subject: [PATCH 245/326] Fixed typo --- src/PIL/ImageShow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 1b2b9f05fbf..9f9a551fb6f 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -178,7 +178,7 @@ def show_file(self, path=None, **options): else: raise TypeError("Missing required argument: 'path'") subprocess.call(["open", "-a", "Preview.app", path]) - exectable = sys.executable or shutil.which("python3") + executable = sys.executable or shutil.which("python3") if executable: subprocess.Popen( [ From fdf4084a7f8ea49bca25158930a8af80cf685b81 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 7 Jul 2022 12:43:24 +1000 Subject: [PATCH 246/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d8419f7120a..f3ba8093fd5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (2022-07-01) ------------------ +- Handle missing Python executable in ImageShow on macOS #6416 + [bryant1410, radarhere] + - Deprecate ImageFont.getsize and related functions #6381 [nulano, radarhere] From 5c3077560b51c2c8f4b394764636dc06c5726442 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 9 Jul 2022 14:38:40 +1000 Subject: [PATCH 247/326] Updated macOS tested Pillow versions [ci skip] --- docs/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index c650d10cc6f..b4cee7b6900 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -504,11 +504,11 @@ These platforms have been reported to work at the versions mentioned. | Operating system | | Tested Python | | Latest tested | | Tested | | | | versions | | Pillow version | | processors | +==================================+===========================+==================+==============+ -| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.1.1 |arm | +| macOS 12 Big Sur | 3.7, 3.8, 3.9, 3.10 | 9.2.0 |arm | +----------------------------------+---------------------------+------------------+--------------+ | macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10 | 8.4.0 |arm | | +---------------------------+------------------+--------------+ -| | 3.7, 3.8, 3.9, 3.10 | 9.1.1 |x86-64 | +| | 3.7, 3.8, 3.9, 3.10 | 9.2.0 |x86-64 | | +---------------------------+------------------+ | | | 3.6 | 8.4.0 | | +----------------------------------+---------------------------+------------------+--------------+ From 415d5b277cc590b492e0a615cc0d45f83da5866b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 06:24:56 +1000 Subject: [PATCH 248/326] Updated redirected URL --- docs/releasenotes/9.0.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index dd993d39eb4..a19da361aaa 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -45,7 +45,7 @@ Support for FreeType 2.7 has been removed; FreeType 2.8 is the minimum supported We recommend upgrading to at least `FreeType`_ 2.10.4, which fixed a severe vulnerability introduced in FreeType 2.6 (:cve:`CVE-2020-15999`). -.. _FreeType: https://www.freetype.org +.. _FreeType: https://freetype.org/ Image.show command parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 23af975a41af11da9ece5a738093438110a892a1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 06:04:04 +1000 Subject: [PATCH 249/326] Updated link for more information about SPIDER --- docs/handbook/image-file-formats.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 1826d965f7a..30452c4a6c2 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -827,11 +827,8 @@ the output format must be specified explicitly:: im.save('newimage.spi', format='SPIDER') -For more information about the SPIDER image processing package, see the -`SPIDER homepage`_ at `Wadsworth Center`_. - -.. _SPIDER homepage: https://spider.wadsworth.org/spider_doc/spider/docs/spider.html -.. _Wadsworth Center: https://www.wadsworth.org/ +For more information about the SPIDER image processing package, see +https://github.com/spider-em/SPIDER TGA ^^^ From ed98c668ee2215cf4717f57294e1894b19bd90ed Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 06:54:23 +1000 Subject: [PATCH 250/326] Fixed PSDraw rectangle --- src/PIL/PSDraw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index 743c35f0128..7d652020a6a 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -93,9 +93,9 @@ def rectangle(self, box): .. code-block:: python - %d %d M %d %d 0 Vr\n + %d %d M 0 %d %d Vr\n """ - self.fp.write(b"%d %d M %d %d 0 Vr\n" % box) + self.fp.write(b"%d %d M 0 %d %d Vr\n" % box) def text(self, xy, text): """ From d6458f21d731561f1ce03bc3145a70042b30d3bc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 12:29:03 +1000 Subject: [PATCH 251/326] Corrected CHANGES.rst [ci skip] --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index f3ba8093fd5..4b313fcb9e2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,12 +2,15 @@ Changelog (Pillow) ================== -9.2.0 (2022-07-01) +9.3.0 (unreleased) ------------------ - Handle missing Python executable in ImageShow on macOS #6416 [bryant1410, radarhere] +9.2.0 (2022-07-01) +------------------ + - Deprecate ImageFont.getsize and related functions #6381 [nulano, radarhere] From db1d945ce771d0efff89c65052aa9277a6928738 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 18:36:27 +1000 Subject: [PATCH 252/326] Changed second rectangle parameter to distance from bottom --- src/PIL/PSDraw.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index 7d652020a6a..dd643c15cc5 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -86,14 +86,8 @@ def rectangle(self, box): """ Draws a rectangle. - :param box: A 4-tuple of integers whose order and function is currently - undocumented. - - Hint: the tuple is passed into this format string: - - .. code-block:: python - - %d %d M 0 %d %d Vr\n + :param box: A tuple of four integers, specifying left, bottom, width and + height. """ self.fp.write(b"%d %d M 0 %d %d Vr\n" % box) @@ -188,9 +182,9 @@ def image(self, box, im, dpi=None): /Vl { moveto lineto stroke } bind def /Vc { newpath 0 360 arc closepath } bind def /Vr { exch dup 0 rlineto - exch dup neg 0 exch rlineto + exch dup 0 exch rlineto exch neg 0 rlineto - 0 exch rlineto + 0 exch neg rlineto 100 div setgray fill 0 setgray } bind def /Tm matrix def /Ve { Tm currentmatrix pop From 80c1ef5dcdb03bc692801930a6801b7a917838a3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 19:38:46 +1000 Subject: [PATCH 253/326] Simplified rectangle command --- src/PIL/PSDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/PSDraw.py b/src/PIL/PSDraw.py index dd643c15cc5..13b3048f67e 100644 --- a/src/PIL/PSDraw.py +++ b/src/PIL/PSDraw.py @@ -185,7 +185,7 @@ def image(self, box, im, dpi=None): exch dup 0 exch rlineto exch neg 0 rlineto 0 exch neg rlineto - 100 div setgray fill 0 setgray } bind def + setgray fill } bind def /Tm matrix def /Ve { Tm currentmatrix pop translate scale newpath 0 0 .5 0 360 arc closepath From 40a09993036618b756c4b0c050614b1438ae7d84 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 11 Jul 2022 20:02:41 +1000 Subject: [PATCH 254/326] Raise ValueError if PNG sRGB chunk is truncated --- Tests/test_file_png.py | 4 +++- src/PIL/PngImagePlugin.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 2a40ab7be9d..1af0223eb7d 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -635,7 +635,9 @@ def test_padded_idat(self): assert_image_equal_tofile(im, "Tests/images/bw_gradient.png") - @pytest.mark.parametrize("cid", (b"IHDR", b"pHYs", b"acTL", b"fcTL", b"fdAT")) + @pytest.mark.parametrize( + "cid", (b"IHDR", b"sRGB", b"pHYs", b"acTL", b"fcTL", b"fdAT") + ) def test_truncated_chunks(self, cid): fp = BytesIO() with PngImagePlugin.PngStream(fp) as png: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c0b3647884e..442c65e6f1b 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -509,6 +509,10 @@ def chunk_sRGB(self, pos, length): # 3 absolute colorimetric s = ImageFile._safe_read(self.fp, length) + if length < 1: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("Truncated sRGB chunk") self.im_info["srgb"] = s[0] return s From 208f87f9c122b0b2388883045f54198f07adb2ae Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Jul 2022 06:55:56 +1000 Subject: [PATCH 255/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4b313fcb9e2..e2ba97f21fc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Raise ValueError if PNG sRGB chunk is truncated #6431 + [radarhere] + - Handle missing Python executable in ImageShow on macOS #6416 [bryant1410, radarhere] From 6f4ff4edc49d10692922aaaf8a24f99d59c95c57 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Jul 2022 20:03:56 +1000 Subject: [PATCH 256/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e2ba97f21fc..bb975bb357b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Fixed PSDraw rectangle #6429 + [radarhere] + - Raise ValueError if PNG sRGB chunk is truncated #6431 [radarhere] From 2b9d48403a2c46d719e7174ddf530b313f487426 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 Jul 2022 07:08:25 +1000 Subject: [PATCH 257/326] Reordered SUPPORTED to match MASK_MODES --- src/PIL/BmpImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 5aacb10da8c..5e7c2be1b8b 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -172,10 +172,10 @@ def _bitmap(self, header=0, offset=0): SUPPORTED = { 32: [ (0xFF0000, 0xFF00, 0xFF, 0x0), - (0xFF0000, 0xFF00, 0xFF, 0xFF000000), + (0xFF000000, 0xFF0000, 0xFF00, 0x0), (0xFF, 0xFF00, 0xFF0000, 0xFF000000), + (0xFF0000, 0xFF00, 0xFF, 0xFF000000), (0x0, 0x0, 0x0, 0x0), - (0xFF000000, 0xFF0000, 0xFF00, 0x0), ], 24: [(0xFF0000, 0xFF00, 0xFF)], 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], From 9f6a3150924ffa3209a5ed45918315afd864a2a0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 Jul 2022 07:25:28 +1000 Subject: [PATCH 258/326] Added ABGR MASK_MODE --- Tests/images/rgb32bf-abgr.bmp | Bin 0 -> 32650 bytes Tests/test_file_bmp.py | 7 +++++++ src/PIL/BmpImagePlugin.py | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 Tests/images/rgb32bf-abgr.bmp diff --git a/Tests/images/rgb32bf-abgr.bmp b/Tests/images/rgb32bf-abgr.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2443714cadcc65a7576d0b601e10810fd0a95d1b GIT binary patch literal 32650 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index 776b499e089..d58666b4476 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -129,6 +129,13 @@ def test_rgba_bitfields(): assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp") + # This test image has been manually hexedited + # to change the bitfield compression in the header from XBGR to ABGR + with Image.open("Tests/images/rgb32bf-abgr.bmp") as im: + assert_image_equal_tofile( + im.convert("RGB"), "Tests/images/bmp/q/rgb32bf-xbgr.bmp" + ) + def test_rle8(): with Image.open("Tests/images/hopper_rle8.bmp") as im: diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 5e7c2be1b8b..7bb73fc9388 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -173,6 +173,7 @@ def _bitmap(self, header=0, offset=0): 32: [ (0xFF0000, 0xFF00, 0xFF, 0x0), (0xFF000000, 0xFF0000, 0xFF00, 0x0), + (0xFF000000, 0xFF0000, 0xFF00, 0xFF), (0xFF, 0xFF00, 0xFF0000, 0xFF000000), (0xFF0000, 0xFF00, 0xFF, 0xFF000000), (0x0, 0x0, 0x0, 0x0), @@ -183,6 +184,7 @@ def _bitmap(self, header=0, offset=0): MASK_MODES = { (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", + (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR", (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", From e1d833bce58b4c43304280a4e271d9aa5dfc9c02 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 14 Jul 2022 21:54:24 +1000 Subject: [PATCH 259/326] Install libxcb-shape0 --- .ci/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index c588af42ffa..16a056dd585 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -43,7 +43,7 @@ if [[ $(uname) != CYGWIN* ]]; then # PyQt6 doesn't support PyPy3 if [[ $GHA_PYTHON_VERSION == 3.* ]]; then - sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxkbcommon-x11-0 + sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxkbcommon-x11-0 python3 -m pip install pyqt6 fi From 3f32b79303bcf62c27ba4df50585e56891fe38ae Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 15 Jul 2022 10:50:48 +0300 Subject: [PATCH 260/326] Replace version tables with RST csv-table --- docs/installation.rst | 38 +++++++++----------------------------- docs/newer-versions.csv | 8 ++++++++ docs/older-versions.csv | 5 +++++ 3 files changed, 22 insertions(+), 29 deletions(-) create mode 100644 docs/newer-versions.csv create mode 100644 docs/older-versions.csv diff --git a/docs/installation.rst b/docs/installation.rst index b4cee7b6900..f73c1f5a586 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -15,35 +15,15 @@ Python Support Pillow supports these Python versions. -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Python |3.10 | 3.9 | 3.8 | 3.7 | 3.6 | 3.5 | 3.4 | 2.7 | -+======================+=====+=====+=====+=====+=====+=====+=====+=====+ -| Pillow >= 9.0 | Yes | Yes | Yes | Yes | | | | | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 8.3.2 - 8.4 | Yes | Yes | Yes | Yes | Yes | | | | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 8.0 - 8.3.1 | | Yes | Yes | Yes | Yes | | | | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 7.0 - 7.2 | | | Yes | Yes | Yes | Yes | | | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 6.2.1 - 6.2.2 | | | Yes | Yes | Yes | Yes | | Yes | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 6.0 - 6.2.0 | | | | Yes | Yes | Yes | | Yes | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 5.2 - 5.4 | | | | Yes | Yes | Yes | Yes | Yes | -+----------------------+-----+-----+-----+-----+-----+-----+-----+-----+ - -+------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| Python | 3.6 | 3.5 | 3.4 | 3.3 | 3.2 | 2.7 | 2.6 | 2.5 | 2.4 | -+==================+=====+=====+=====+=====+=====+=====+=====+=====+=====+ -| Pillow 5.0 - 5.1 | Yes | Yes | Yes | | | Yes | | | | -+------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 4 | Yes | Yes | Yes | Yes | | Yes | | | | -+------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow 2 - 3 | | Yes | Yes | Yes | Yes | Yes | Yes | | | -+------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ -| Pillow < 2 | | | | | | Yes | Yes | Yes | Yes | -+------------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +.. csv-table:: Newer versions + :file: newer-versions.csv + :header-rows: 1 + :widths: auto + +.. csv-table:: Older versions + :file: older-versions.csv + :header-rows: 1 + :widths: auto Basic Installation ------------------ diff --git a/docs/newer-versions.csv b/docs/newer-versions.csv new file mode 100644 index 00000000000..b9630add1e0 --- /dev/null +++ b/docs/newer-versions.csv @@ -0,0 +1,8 @@ +Python,3.10,3.9,3.8,3.7,3.6,3.5,3.4,2.7 +Pillow >= 9.0,Yes,Yes,Yes,Yes,,,, +Pillow 8.3.2 - 8.4,Yes,Yes,Yes,Yes,Yes,,, +Pillow 8.0 - 8.3.1,,Yes,Yes,Yes,Yes,,, +Pillow 7.0 - 7.2,,,Yes,Yes,Yes,Yes,, +Pillow 6.2.1 - 6.2.2,,,Yes,Yes,Yes,Yes,,Yes +Pillow 6.0 - 6.2.0,,,,Yes,Yes,Yes,,Yes +Pillow 5.2 - 5.4,,,,Yes,Yes,Yes,Yes,Yes diff --git a/docs/older-versions.csv b/docs/older-versions.csv new file mode 100644 index 00000000000..f8c70758882 --- /dev/null +++ b/docs/older-versions.csv @@ -0,0 +1,5 @@ +Python,3.6,3.5,3.4,3.3,3.2,2.7,2.6,2.5,2.4 +Pillow 5.0 - 5.1,Yes,Yes,Yes,,,Yes,,, +Pillow 4,Yes,Yes,Yes,Yes,,Yes,,, +Pillow 2 - 3,,Yes,Yes,Yes,Yes,Yes,Yes,, +Pillow < 2,,,,,,Yes,Yes,Yes,Yes \ No newline at end of file From 0f3ad23e1b1dc38a6af2fdb1c9c640b5ae106c71 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 15 Jul 2022 11:15:49 +0300 Subject: [PATCH 261/326] Add Python 3.11 for Pillow >= 9.3 --- docs/newer-versions.csv | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/newer-versions.csv b/docs/newer-versions.csv index b9630add1e0..c3f655e2fea 100644 --- a/docs/newer-versions.csv +++ b/docs/newer-versions.csv @@ -1,8 +1,9 @@ -Python,3.10,3.9,3.8,3.7,3.6,3.5,3.4,2.7 -Pillow >= 9.0,Yes,Yes,Yes,Yes,,,, -Pillow 8.3.2 - 8.4,Yes,Yes,Yes,Yes,Yes,,, -Pillow 8.0 - 8.3.1,,Yes,Yes,Yes,Yes,,, -Pillow 7.0 - 7.2,,,Yes,Yes,Yes,Yes,, -Pillow 6.2.1 - 6.2.2,,,Yes,Yes,Yes,Yes,,Yes -Pillow 6.0 - 6.2.0,,,,Yes,Yes,Yes,,Yes -Pillow 5.2 - 5.4,,,,Yes,Yes,Yes,Yes,Yes +Python,3.11,3.10,3.9,3.8,3.7,3.6,3.5,3.4,2.7 +Pillow >= 9.3,Yes,Yes,Yes,Yes,Yes,,,, +Pillow 9.0 - 9.2,,Yes,Yes,Yes,Yes,,,, +Pillow 8.3.2 - 8.4,,Yes,Yes,Yes,Yes,Yes,,, +Pillow 8.0 - 8.3.1,,,Yes,Yes,Yes,Yes,,, +Pillow 7.0 - 7.2,,,,Yes,Yes,Yes,Yes,, +Pillow 6.2.1 - 6.2.2,,,,Yes,Yes,Yes,Yes,,Yes +Pillow 6.0 - 6.2.0,,,,,Yes,Yes,Yes,,Yes +Pillow 5.2 - 5.4,,,,,Yes,Yes,Yes,Yes,Yes From 48ad0b1f381cabc2dacd932bf0272945fb815078 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 15 Jul 2022 11:25:33 +0300 Subject: [PATCH 262/326] Rebalance version tables --- docs/newer-versions.csv | 15 ++++++--------- docs/older-versions.csv | 13 ++++++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/newer-versions.csv b/docs/newer-versions.csv index c3f655e2fea..ed2369259d4 100644 --- a/docs/newer-versions.csv +++ b/docs/newer-versions.csv @@ -1,9 +1,6 @@ -Python,3.11,3.10,3.9,3.8,3.7,3.6,3.5,3.4,2.7 -Pillow >= 9.3,Yes,Yes,Yes,Yes,Yes,,,, -Pillow 9.0 - 9.2,,Yes,Yes,Yes,Yes,,,, -Pillow 8.3.2 - 8.4,,Yes,Yes,Yes,Yes,Yes,,, -Pillow 8.0 - 8.3.1,,,Yes,Yes,Yes,Yes,,, -Pillow 7.0 - 7.2,,,,Yes,Yes,Yes,Yes,, -Pillow 6.2.1 - 6.2.2,,,,Yes,Yes,Yes,Yes,,Yes -Pillow 6.0 - 6.2.0,,,,,Yes,Yes,Yes,,Yes -Pillow 5.2 - 5.4,,,,,Yes,Yes,Yes,Yes,Yes +Python,3.11,3.10,3.9,3.8,3.7,3.6,3.5 +Pillow >= 9.3,Yes,Yes,Yes,Yes,Yes,, +Pillow 9.0 - 9.2,,Yes,Yes,Yes,Yes,, +Pillow 8.3.2 - 8.4,,Yes,Yes,Yes,Yes,Yes, +Pillow 8.0 - 8.3.1,,,Yes,Yes,Yes,Yes, +Pillow 7.0 - 7.2,,,,Yes,Yes,Yes,Yes diff --git a/docs/older-versions.csv b/docs/older-versions.csv index f8c70758882..6058f0524ad 100644 --- a/docs/older-versions.csv +++ b/docs/older-versions.csv @@ -1,5 +1,8 @@ -Python,3.6,3.5,3.4,3.3,3.2,2.7,2.6,2.5,2.4 -Pillow 5.0 - 5.1,Yes,Yes,Yes,,,Yes,,, -Pillow 4,Yes,Yes,Yes,Yes,,Yes,,, -Pillow 2 - 3,,Yes,Yes,Yes,Yes,Yes,Yes,, -Pillow < 2,,,,,,Yes,Yes,Yes,Yes \ No newline at end of file +Python,3.8,3.7,3.6,3.5,3.4,3.3,3.2,2.7,2.6,2.5,2.4 +Pillow 6.2.1 - 6.2.2,Yes,Yes,Yes,Yes,,,,Yes,,, +Pillow 6.0 - 6.2.0,,Yes,Yes,Yes,,,,Yes,,, +Pillow 5.2 - 5.4,,Yes,Yes,Yes,Yes,,,Yes,,, +Pillow 5.0 - 5.1,,,Yes,Yes,Yes,,,Yes,,, +Pillow 4,,,Yes,Yes,Yes,Yes,,Yes,,, +Pillow 2 - 3,,,,Yes,Yes,Yes,Yes,Yes,Yes,, +Pillow < 2,,,,,,,,Yes,Yes,Yes,Yes \ No newline at end of file From 6801a255a3c7fd6b9e4abf1c3baa835b92fd78d5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 15 Jul 2022 13:42:36 +0300 Subject: [PATCH 263/326] Add support for Python 3.11 --- setup.cfg | 1 + setup.py | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 82873fce901..be3bc4b4f36 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: Implementation :: PyPy Topic :: Multimedia :: Graphics diff --git a/setup.py b/setup.py index 2701aa1ce12..71e853dcee9 100755 --- a/setup.py +++ b/setup.py @@ -38,7 +38,7 @@ def get_version(): ZLIB_ROOT = None FUZZING_BUILD = "LIB_FUZZING_ENGINE" in os.environ -if sys.platform == "win32" and sys.version_info >= (3, 11): +if sys.platform == "win32" and sys.version_info >= (3, 12): import atexit atexit.register( diff --git a/tox.ini b/tox.ini index 09db058845a..21b5d4b506d 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ [tox] envlist = lint - py{37,38,39,310,py3} + py{37,38,39,310,311,py3} minversion = 1.9 [testenv] From 03df65e77b57148c2a8fdfe3466567d7faa7fac8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 15 Jul 2022 14:55:08 +0300 Subject: [PATCH 264/326] Docs: Allow setting Python interpreter via command line --- docs/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index 309fc4458e0..f11d6b189e9 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,8 +2,9 @@ # # You can set these variables from the command line. +PYTHON = python3 SPHINXOPTS = -SPHINXBUILD = python3 -m sphinx.cmd.build +SPHINXBUILD = $(PYTHON) -m sphinx.cmd.build PAPER = BUILDDIR = _build @@ -42,8 +43,8 @@ clean: -rm -rf $(BUILDDIR)/* install-sphinx: - python3 -c "import sphinx" > /dev/null 2>&1 || python3 -m pip install sphinx - python3 -c "import furo" > /dev/null 2>&1 || python3 -m pip install furo + $(PYTHON) -c "import sphinx" > /dev/null 2>&1 || $(PYTHON) -m pip install sphinx + $(PYTHON) -c "import furo" > /dev/null 2>&1 || $(PYTHON) -m pip install furo html: $(MAKE) install-sphinx @@ -179,4 +180,4 @@ livehtml: html livereload $(BUILDDIR)/html -p 33233 serve: - cd $(BUILDDIR)/html; python3 -m http.server + cd $(BUILDDIR)/html; $(PYTHON) -m http.server From 55a501793401a466c369e4500eb76375a03425c2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Jul 2022 17:22:45 +1000 Subject: [PATCH 265/326] Updated libwebp to 1.2.3 --- depends/install_webp.sh | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/depends/install_webp.sh b/depends/install_webp.sh index a419a764609..ed17f2228fc 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,7 +1,7 @@ #!/bin/bash # install webp -archive=libwebp-1.2.2 +archive=libwebp-1.2.3 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 2e7c84ad0e2..bc5fb4d024b 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -157,9 +157,9 @@ def cmd_msbuild( # "bins": [r"libtiff\*.dll"], }, "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.2.tar.gz", - "filename": "libwebp-1.2.2.tar.gz", - "dir": "libwebp-1.2.2", + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.3.tar.gz", + "filename": "libwebp-1.2.3.tar.gz", + "dir": "libwebp-1.2.3", "build": [ cmd_rmdir(r"output\release-static"), # clean cmd_nmake( From 45963b1b40ca2c7e66466812ce0842d917e425b6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Jul 2022 17:25:29 +1000 Subject: [PATCH 266/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bb975bb357b..19d6fa994c1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added ABGR BMP mask mode #6436 + [radarhere] + - Fixed PSDraw rectangle #6429 [radarhere] From dfa6655bdd511cda37e568aece8b185cfc5c2d6d Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Sat, 16 Jul 2022 19:04:42 +1000 Subject: [PATCH 267/326] docs: fix simple typo, unpredicatable -> unpredictable There is a small typo in src/PIL/Image.py. Should read `unpredictable` rather than `unpredicatable`. Signed-off-by: Tim Gates --- 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 ce32c39ced6..6abb1249166 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -646,7 +646,7 @@ def __repr__(self): def _repr_pretty_(self, p, cycle): """IPython plain text display support""" - # Same as __repr__ but without unpredicatable id(self), + # Same as __repr__ but without unpredictable id(self), # to keep Jupyter notebook `text/plain` output stable. p.text( "<%s.%s image mode=%s size=%dx%d>" From 2944ff18d6ac79b881e3a3ec8d656fe08a41d1a9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Jul 2022 20:02:58 +1000 Subject: [PATCH 268/326] Support saving multiple MPO frames --- Tests/test_file_mpo.py | 41 +++++++++++++++++--- docs/handbook/image-file-formats.rst | 11 ++++++ src/PIL/JpegImagePlugin.py | 2 +- src/PIL/MpoImagePlugin.py | 57 ++++++++++++++++++++++++++-- 4 files changed, 101 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index d093f26ccd8..849857d31d6 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -5,15 +5,19 @@ from PIL import Image -from .helper import assert_image_similar, is_pypy, skip_unless_feature +from .helper import ( + assert_image_equal, + assert_image_similar, + is_pypy, + skip_unless_feature, +) test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"] pytestmark = skip_unless_feature("jpg") -def frame_roundtrip(im, **options): - # Note that for now, there is no MPO saving functionality +def roundtrip(im, **options): out = BytesIO() im.save(out, "MPO", **options) test_bytes = out.tell() @@ -237,13 +241,38 @@ def test_image_grab(): def test_save(): - # Note that only individual frames can be saved at present for test_file in test_files: with Image.open(test_file) as im: assert im.tell() == 0 - jpg0 = frame_roundtrip(im) + jpg0 = roundtrip(im) assert_image_similar(im, jpg0, 30) im.seek(1) assert im.tell() == 1 - jpg1 = frame_roundtrip(im) + jpg1 = roundtrip(im) assert_image_similar(im, jpg1, 30) + + +def test_save_all(): + for test_file in test_files: + with Image.open(test_file) as im: + im_reloaded = roundtrip(im, save_all=True) + + im.seek(0) + assert_image_similar(im, im_reloaded, 30) + + im.seek(1) + im_reloaded.seek(1) + assert_image_similar(im, im_reloaded, 30) + + im = Image.new("RGB", (1, 1)) + im2 = Image.new("RGB", (1, 1), "#f00") + im_reloaded = roundtrip(im, save_all=True, append_images=[im2]) + + assert_image_equal(im, im_reloaded) + + im_reloaded.seek(1) + assert_image_similar(im2, im_reloaded, 1) + + # Test that a single frame image will not be saved as an MPO + jpg = roundtrip(im, save_all=True) + assert "mp" not in jpg.info diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 30452c4a6c2..1728c8e0579 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -1209,6 +1209,17 @@ image when first opened. The :py:meth:`~PIL.Image.Image.seek` and :py:meth:`~PIL methods may be used to read other pictures from the file. The pictures are zero-indexed and random access is supported. +When calling :py:meth:`~PIL.Image.Image.save` to write an MPO file, by default +only the first frame of a multiframe image will be saved. If the ``save_all`` +argument is present and true, then all frames will be saved, and the following +option will also be available. + +**append_images** + A list of images to append as additional pictures. Each of the + images in the list can be single or multiframe images. + + .. versionadded:: 9.3.0 + PCD ^^^ diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 4efe6281ad6..a6ed223bc6f 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -711,7 +711,7 @@ def validate_qtables(qtables): qtables = getattr(im, "quantization", None) qtables = validate_qtables(qtables) - extra = b"" + extra = info.get("extra", b"") icc_profile = info.get("icc_profile") if icc_profile: diff --git a/src/PIL/MpoImagePlugin.py b/src/PIL/MpoImagePlugin.py index 27c30958c9c..5bfd8efc1a6 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -18,16 +18,66 @@ # See the README file for information on usage and redistribution. # -from . import Image, ImageFile, JpegImagePlugin +import itertools +import os +import struct + +from . import Image, ImageFile, ImageSequence, JpegImagePlugin, TiffImagePlugin from ._binary import i16be as i16 +from ._binary import o32le # def _accept(prefix): # return JpegImagePlugin._accept(prefix) def _save(im, fp, filename): - # Note that we can only save the current frame at present - return JpegImagePlugin._save(im, fp, filename) + JpegImagePlugin._save(im, fp, filename) + + +def _save_all(im, fp, filename): + append_images = im.encoderinfo.get("append_images", []) + if not append_images: + try: + animated = im.is_animated + except AttributeError: + animated = False + if not animated: + _save(im, fp, filename) + return + + offsets = [] + for imSequence in itertools.chain([im], append_images): + for im_frame in ImageSequence.Iterator(imSequence): + if not offsets: + # APP2 marker + im.encoderinfo["extra"] = ( + b"\xFF\xE2" + struct.pack(">H", 6 + 70) + b"MPF\0" + b" " * 70 + ) + JpegImagePlugin._save(im_frame, fp, filename) + offsets.append(fp.tell()) + else: + im_frame.save(fp, "JPEG") + offsets.append(fp.tell() - offsets[-1]) + + ifd = TiffImagePlugin.ImageFileDirectory_v2() + ifd[0xB001] = len(offsets) + + mpentries = b"" + data_offset = 0 + for i, size in enumerate(offsets): + if i == 0: + mptype = 0x030000 # Baseline MP Primary Image + else: + mptype = 0x000000 # Undefined + mpentries += struct.pack(" Date: Mon, 18 Jul 2022 16:16:06 +1000 Subject: [PATCH 269/326] Do not quote Pillow version for setuptools >= 60 --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 71e853dcee9..37477216d3f 100755 --- a/setup.py +++ b/setup.py @@ -15,7 +15,9 @@ import sys import warnings -from setuptools import Extension, setup +from setuptools import Extension +from setuptools import __version__ as setuptools_version +from setuptools import setup from setuptools.command.build_ext import build_ext @@ -850,6 +852,7 @@ def build_extensions(self): sys.platform == "win32" and sys.version_info < (3, 9) and not (PLATFORM_PYPY or PLATFORM_MINGW) + and int(setuptools_version.split('.')[0]) < 60 ): defs.append(("PILLOW_VERSION", f'"\\"{PILLOW_VERSION}\\""')) else: From aba0859db9261e94feaa592615b9d70cbd2b99ae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Jul 2022 06:17:07 +0000 Subject: [PATCH 270/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 37477216d3f..a2b2c691098 100755 --- a/setup.py +++ b/setup.py @@ -852,7 +852,7 @@ def build_extensions(self): sys.platform == "win32" and sys.version_info < (3, 9) and not (PLATFORM_PYPY or PLATFORM_MINGW) - and int(setuptools_version.split('.')[0]) < 60 + and int(setuptools_version.split(".")[0]) < 60 ): defs.append(("PILLOW_VERSION", f'"\\"{PILLOW_VERSION}\\""')) else: From 77402067fbe06dd9224422c6a39a738d6bef66db Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 18 Jul 2022 15:30:00 +0300 Subject: [PATCH 271/326] Omit ":widths: auto" Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/installation.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index f73c1f5a586..9e9dc52b3eb 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -18,12 +18,10 @@ Pillow supports these Python versions. .. csv-table:: Newer versions :file: newer-versions.csv :header-rows: 1 - :widths: auto .. csv-table:: Older versions :file: older-versions.csv :header-rows: 1 - :widths: auto Basic Installation ------------------ From 3a7e29306a1c109a2e5cb5b6dabcba9f6e0eb9fc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 19 Jul 2022 07:40:25 +1000 Subject: [PATCH 272/326] Added release notes --- docs/releasenotes/9.3.0.rst | 59 +++++++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 60 insertions(+) create mode 100644 docs/releasenotes/9.3.0.rst diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst new file mode 100644 index 00000000000..da045a50a98 --- /dev/null +++ b/docs/releasenotes/9.3.0.rst @@ -0,0 +1,59 @@ +9.3.0 +----- + +Backwards Incompatible Changes +============================== + +TODO +^^^^ + +Deprecations +============ + +TODO +^^^^ + +TODO + +API Changes +=========== + +TODO +^^^^ + +TODO + +API Additions +============= + +Saving multiple MPO frames +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Multiple MPO frames can now be saved. Using the ``save_all`` argument, all of +an image's frames will be saved to file:: + + from PIL import Image + im = Image.open("frozenpond.mpo") + im.save(out, save_all=True) + +Additional images can also be appended when saving, by combining the +``save_all`` argument with the ``append_images`` argument:: + + im.save(out, save_all=True, append_images=[im1, im2, ...]) + + +Security +======== + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 597c804f861..8c436be3bd8 100644 --- a/docs/releasenotes/index.rst +++ b/docs/releasenotes/index.rst @@ -14,6 +14,7 @@ expected to be backported to earlier versions. .. toctree:: :maxdepth: 2 + 9.3.0 9.2.0 9.1.1 9.1.0 From 13acf0a545da94a77804832a5da956d1a713862e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 19 Jul 2022 07:50:04 +1000 Subject: [PATCH 273/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 19d6fa994c1..d3c96fefd39 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Do not double quote Pillow version for setuptools >= 60 #6450 + [radarhere] + - Added ABGR BMP mask mode #6436 [radarhere] From 37e794245ea770662f54250a383e206ee0efaf15 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 19 Jul 2022 13:11:17 +1000 Subject: [PATCH 274/326] Updated libimagequant to 4.0.1 --- depends/install_imagequant.sh | 2 +- docs/installation.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 31fc2adaabe..9b3088b9450 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -archive=libimagequant-4.0.0 +archive=libimagequant-4.0.1 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/docs/installation.rst b/docs/installation.rst index 9e9dc52b3eb..f147fa6a732 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -166,7 +166,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-4.0** + * Pillow has been tested with libimagequant **2.6-4.0.1** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. From 0844fb0ed33b01656d4cf6ff0c9cd63342611476 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Jul 2022 09:05:14 +1000 Subject: [PATCH 275/326] Do not clear tile if not updating the image when seeking --- Tests/images/comment_after_only_frame.gif | Bin 0 -> 54 bytes Tests/test_file_gif.py | 5 +++++ src/PIL/GifImagePlugin.py | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 Tests/images/comment_after_only_frame.gif diff --git a/Tests/images/comment_after_only_frame.gif b/Tests/images/comment_after_only_frame.gif new file mode 100644 index 0000000000000000000000000000000000000000..8188b68473246080bb04e13f3a7b412aab4de3bf GIT binary patch literal 54 tcmZ?wbh9u|WMp7uXk Date: Fri, 22 Jul 2022 07:59:30 +1000 Subject: [PATCH 276/326] Moved code into separate function --- src/PIL/ImageFile.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 99b77a37f5f..9f08493c13f 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -499,9 +499,14 @@ def _save(im, fp, tile, bufsize=0): try: fh = fp.fileno() fp.flush() - exc = None - except (AttributeError, io.UnsupportedOperation) as e: - exc = e + _encode_tile(im, fp, tile, bufsize, fh) + except (AttributeError, io.UnsupportedOperation) as exc: + _encode_tile(im, fp, tile, bufsize, None, exc) + if hasattr(fp, "flush"): + fp.flush() + + +def _encode_tile(im, fp, tile, bufsize, fh, exc=None): for e, b, o, a in tile: if o > 0: fp.seek(o) @@ -526,8 +531,6 @@ def _save(im, fp, tile, bufsize=0): raise OSError(f"encoder error {s} when writing image file") from exc finally: encoder.cleanup() - if hasattr(fp, "flush"): - fp.flush() def _safe_read(fp, size): From ad2c6a20fe874958d8d9adecbbfeb81856155f05 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sat, 23 Jul 2022 00:30:27 +0300 Subject: [PATCH 277/326] Add support for ATI1/2(BC4/BC5) DDS files This commit adds support for loading DDS with ATI1 and ATI2 fourcc pixel format --- Tests/images/ati2.dds | Bin 0 -> 22000 bytes Tests/images/ati2.png | Bin 0 -> 28408 bytes Tests/test_file_dds.py | 14 ++++++++++++++ src/PIL/DdsImagePlugin.py | 9 ++++++++- 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Tests/images/ati2.dds create mode 100644 Tests/images/ati2.png diff --git a/Tests/images/ati2.dds b/Tests/images/ati2.dds new file mode 100644 index 0000000000000000000000000000000000000000..7199dcad7620d3b6e3e839370892f6ee901082fa GIT binary patch literal 22000 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 literal 0 HcmV?d00001 diff --git a/Tests/images/ati2.png b/Tests/images/ati2.png new file mode 100644 index 0000000000000000000000000000000000000000..ac166965944dbfc5f0f6adf43a8c4593b6564e3b GIT binary patch literal 28408 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%0AZtXOaoytljRcS#p6{ob-0~K|ppPSLo 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 f-Gc2k+VKAe$zhIzoL_Oj00000NkvXXu0mjf6s1 Date: Sat, 23 Jul 2022 01:02:06 +0300 Subject: [PATCH 278/326] Add test and test files for ATI1 pixel format and fix image mode for ATI1 --- Tests/images/ati1.dds | Bin 0 -> 11064 bytes Tests/images/ati1.png | Bin 0 -> 1134 bytes Tests/test_file_dds.py | 13 +++++++++++++ src/PIL/DdsImagePlugin.py | 2 +- 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Tests/images/ati1.dds create mode 100644 Tests/images/ati1.png diff --git a/Tests/images/ati1.dds b/Tests/images/ati1.dds new file mode 100644 index 0000000000000000000000000000000000000000..dc9445d89ab698838d671e42281af3905b23132a GIT binary patch literal 11064 zcmZ>930A0KU|?Vu;9_V1(jd&B03sL|I3N_!qykVS3lKYocp75U#3A5-O?XsnGz3ON zU^E0qLtr!n23-g+F?dc=;S^#UbPl7jgIIz7cIlj~VDjB8$0ct^dr<4P+}j&uZ-T^C zJe$;LZ2f>6|DMrvUdzde0{_~Zv>Jpp%pBg#Hu$K$e>>OXZN3uyf9k>EP%uevz#U3u z2LSywi=}h6j{HTpSFPt3Z@-fx@beci%I}4WiG=5u3NQQZyHDj+zn$QfEq@Qyf3Rm} z@N80XoMc92XAGEe%u!}J(rxKG{uT$yU%VIf>}7hc(%wg&;eT^KZsAK>=o$O&&3rq} zP{t>J>*uRitom;9ZFB97^|Ch^LHWXClFEQNoJtl8-ZV?nH_N*HKk9(Rrfl(`AE){5 zZi-Hy`hQy8!~#RN#Q$E85ASSd>9G6s=l1zV+uO`)hB-Gjf_ne=2p2f*f=XM+Ku<WPbxJ}4>oP;=e-T&Gco`ZJ20GZ=^HNMz$dw`Y#ZFO<>V~yDj^@+apz>mv1lFoV={o`F5KuHMxXhn> zb2!C0$S+JXW;us}@qR>pVnWB1w%kuC&qA2~eF*tk)xff5!g||(n=dG>WfO2H|I{LT zb9(KK;pYE2jGl9jc+L>Hk^3R~_tx`P8?Air*L=Nr^=t1OXAg<3W!rxLIBfrOvb|+e z|KFR_{|=k@XU^Z2c(^Uq!ud^D)Bm6U|Nou5fce0I9pSH&%aZ=DUe5U=Z0iReZBwrQ z-&rIU2sJw~-E+p;a%lCA%(YUo?GPhrLS?+bo?yuFiU^5^>7 zEY(xkOCR{pe|CZY$^U;00t`T8^V@RH`O3dCtPI{wDrJ*Yhg&`v^$&eR08+0`Qn<$m ztXK1mEWq`UuMpkp*E`If^HNSu75JNeK{ssc%q6;~HvGM5KX*UaT^>R-OWz=WF7ceO zKlG)_k+1!`l*X>wWNSQoon) zFW77U=hjC0kJBl!^&}xYnwLOP`Nrnj&UO+yn?x2@? z=5(MVx8+(eU*E=iVB6d<$hbgc2hCm^;K*IWA z{$o}J*`Ig>yLn!SsRc;hdzRxEpA1NA69=6TnK;j71-)@3I;&UHdScoczL5 S$VP~Paly8%)!VqZAUXlo!UFXG literal 0 HcmV?d00001 diff --git a/Tests/images/ati1.png b/Tests/images/ati1.png new file mode 100644 index 0000000000000000000000000000000000000000..da475dfd7b01c0c4ae1a8235ce03e641948e040a GIT binary patch literal 1134 zcmV-!1d;oRP)+TM;^CeW+LlqUaDGMnwc)_T&V?Au6~R zMIWRvpF3fUp*UgKStnxFYFDXSTM26hHffS3wd7Qgnl^qDHsTwfR~R<0~+|Id&?C6&Baa#`!krJPr0N zpR8CJUICxq8K3eK2;9hWjny4H0!}A^=f^LUQd$H7-$*g{gIi@xX8*Tv>PLDUt2qvNX#|3`pizS zT|N`nGB{W$peW|;)|pWt)j(gYvl5tnCX<1MB~Z3=`Ub=4y8zh@P5TZ~5X$@R@PtX2iBRZA65I3=#?`QZoStp?3Yl!MP3%DvP z?*QLjSOSC5-5bqjnE)KtqgP$pLR}qtV~J_Cgh&WOd|sURa7rXaGAXWFeBjK1 zcSSOpJYS^X0DL8WJ6_EA@K#m_;>U)fSo@QE1Or>K+D{^pNF)-8L?V$$Boc{4B9TZW z5=jA2t-g4a<77cKQ6a;nh{q_PnhksY1T+;PP=p18;aq1czix8r(n|huA64MJ567FT zmTgy}94afD`eMOClPu6^i^alW5f;CQhn&s-p}CFMhF})p$h^KE2UX5*sP9(OnvJ!!HLIo61gyPgULwnZ$ntCUwd0m>Kln}{ZAg2;cP#%Q9 zb9l>10xab(kV_Lb1K`8g5)4uUuvJijffE(wtkUWGCKy~@1)wBYrF@;$B{&r8=IMP@0pu2V1 zfgLq<=y={&GP7OpVfvbTc=bT_y$AZYi_29zw3-fz%eR-pH=Oi;k zD$gH6b^->p4>C>j)@(1th%6!iTdJvF Date: Fri, 22 Jul 2022 22:02:55 +0000 Subject: [PATCH 279/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_file_dds.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 537302a09e6..d215e901d1c 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -76,6 +76,7 @@ def test_sanity_ati1(): assert_image_equal_tofile(im, TEST_FILE_ATI1.replace(".dds", ".png")) + def test_sanity_ati2(): """Check ATI2 images can be opened""" From 4a13857aa6ac1407bbab945d4b1d781c93c2f2d4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Jul 2022 15:29:01 +1000 Subject: [PATCH 280/326] Switch to GitHub Actions artifacts for Windows wheels --- RELEASING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index aa7511c8a41..b0506748470 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -96,8 +96,8 @@ Released as needed privately to individual vendors for critical security-related ## Binary Distributions ### Windows -* [ ] Contact `@cgohlke` for Windows binaries via release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174. -* [ ] Download and extract tarball from `@cgohlke` and copy into `dist/` +* [ ] Download the artifacts from the [GitHub Actions "Test Windows" workflow](https://github.com/python-pillow/Pillow/actions/workflows/test-windows.yml) + and copy into `dist/` ### Mac and Linux * [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels): From 1050d13350d7f27f26c82298f1bd4ed0199ef768 Mon Sep 17 00:00:00 2001 From: REDxEYE Date: Sat, 23 Jul 2022 12:44:03 +0300 Subject: [PATCH 281/326] Replace test files with images with compatible license --- Tests/images/ati1.dds | Bin 11064 -> 2896 bytes Tests/images/ati1.png | Bin 1134 -> 969 bytes Tests/images/ati2.dds | Bin 22000 -> 87536 bytes Tests/images/ati2.png | Bin 28408 -> 0 bytes Tests/test_file_dds.py | 6 +++--- 5 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 Tests/images/ati2.png diff --git a/Tests/images/ati1.dds b/Tests/images/ati1.dds index dc9445d89ab698838d671e42281af3905b23132a..747e4b1b98ae610f2fc6f70fcc1e870c63b457af 100644 GIT binary patch literal 2896 zcmeH}T}V@57{_0?H9cp}%{c|7Y_*k8hMIce2e@FnID%k)G(JZu!BHfjSQon}_@DDT=l{I#|2gm8 zoiT%MiiB_^i4-6ed89ISS@yBNkjFiA}rh?1*)ub4u{wF(%a8?~ii1PdHm-UK1 zbJDV?)Y>*WuHG)4RjOzdK_(euL<(l_PwT&toqq8cw}sT5u5Em~tI_&z*De zUQ&G8v~WH#kk>p8bsVR91NCBP zXddKp3`{F#zMPe=zePow~t{;gx;SzI^n2SUo zi9RyizBmtak(i6*1B#DI(CW-!PaeqGTiufj;yR8ToB?5u5V;QN#Y~X1cdIWBv^ujF zr{DuMaXe_h{Qc@eebN8h{=vEGTYk`#g|fAfvVaV0^Fi20hy^4ou^iM|xbqHk)q3f#3AJGe0bGRPtT literal 11064 zcmZ>930A0KU|?Vu;9_V1(jd&B03sL|I3N_!qykVS3lKYocp75U#3A5-O?XsnGz3ON zU^E0qLtr!n23-g+F?dc=;S^#UbPl7jgIIz7cIlj~VDjB8$0ct^dr<4P+}j&uZ-T^C zJe$;LZ2f>6|DMrvUdzde0{_~Zv>Jpp%pBg#Hu$K$e>>OXZN3uyf9k>EP%uevz#U3u z2LSywi=}h6j{HTpSFPt3Z@-fx@beci%I}4WiG=5u3NQQZyHDj+zn$QfEq@Qyf3Rm} z@N80XoMc92XAGEe%u!}J(rxKG{uT$yU%VIf>}7hc(%wg&;eT^KZsAK>=o$O&&3rq} zP{t>J>*uRitom;9ZFB97^|Ch^LHWXClFEQNoJtl8-ZV?nH_N*HKk9(Rrfl(`AE){5 zZi-Hy`hQy8!~#RN#Q$E85ASSd>9G6s=l1zV+uO`)hB-Gjf_ne=2p2f*f=XM+Ku<WPbxJ}4>oP;=e-T&Gco`ZJ20GZ=^HNMz$dw`Y#ZFO<>V~yDj^@+apz>mv1lFoV={o`F5KuHMxXhn> zb2!C0$S+JXW;us}@qR>pVnWB1w%kuC&qA2~eF*tk)xff5!g||(n=dG>WfO2H|I{LT zb9(KK;pYE2jGl9jc+L>Hk^3R~_tx`P8?Air*L=Nr^=t1OXAg<3W!rxLIBfrOvb|+e z|KFR_{|=k@XU^Z2c(^Uq!ud^D)Bm6U|Nou5fce0I9pSH&%aZ=DUe5U=Z0iReZBwrQ z-&rIU2sJw~-E+p;a%lCA%(YUo?GPhrLS?+bo?yuFiU^5^>7 zEY(xkOCR{pe|CZY$^U;00t`T8^V@RH`O3dCtPI{wDrJ*Yhg&`v^$&eR08+0`Qn<$m ztXK1mEWq`UuMpkp*E`If^HNSu75JNeK{ssc%q6;~HvGM5KX*UaT^>R-OWz=WF7ceO zKlG)_k+1!`l*X>wWNSQoon) zFW77U=hjC0kJBl!^&}xYnwLOP`Nrnj&UO+yn?x2@? z=5(MVx8+(eU*E=iVB6d<$hbgc2hCm^;K*IWA z{$o}J*`Ig>yLn!SsRc;hdzRxEpA1NA69=6TnK;j71-)@3I;&UHdScoczL5 S$VP~Paly8%)!VqZAUXlo!UFXG diff --git a/Tests/images/ati1.png b/Tests/images/ati1.png index da475dfd7b01c0c4ae1a8235ce03e641948e040a..790d7d7dbb2deede17643a59f2515f7dbb2e2b17 100644 GIT binary patch literal 969 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�%%^RnpX`^({= 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 YGx*xW*^;UXz65-5kb*BqhyV2d0WH*dIsgCw literal 22000 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 diff --git a/Tests/images/ati2.png b/Tests/images/ati2.png deleted file mode 100644 index ac166965944dbfc5f0f6adf43a8c4593b6564e3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28408 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%0AZtXOaoytljRcS#p6{ob-0~K|ppPSLo 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 f-Gc2k+VKAe$zhIzoL_Oj00000NkvXXu0mjf6s1 Date: Mon, 25 Jul 2022 07:53:17 +1000 Subject: [PATCH 282/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d3c96fefd39..14293586cfe 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Support saving multiple MPO frames #6444 + [radarhere] + - Do not double quote Pillow version for setuptools >= 60 #6450 [radarhere] From ce7af49eed2929f2e052842a98baae6db263fa94 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 25 Jul 2022 18:09:06 +1000 Subject: [PATCH 283/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 14293586cfe..8616ff09c68 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Do not clear GIF tile when checking number of frames #6455 + [radarhere] + - Support saving multiple MPO frames #6444 [radarhere] From 6e97da02603fcd1fe2e16f6164f737200107d924 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Tue, 26 Jul 2022 00:30:32 +0300 Subject: [PATCH 284/326] fixing xmp tag orientation generated by exiftool --- Tests/images/xmp_orientation_exifool.png | Bin 0 -> 1935 bytes Tests/test_imageops.py | 7 +++++++ src/PIL/Image.py | 6 ++++++ src/PIL/ImageOps.py | 5 +++++ 4 files changed, 18 insertions(+) create mode 100644 Tests/images/xmp_orientation_exifool.png diff --git a/Tests/images/xmp_orientation_exifool.png b/Tests/images/xmp_orientation_exifool.png new file mode 100644 index 0000000000000000000000000000000000000000..fb30199c86acbc8953d1cf16ddfa9e3ec41ece19 GIT binary patch literal 1935 zcmY+F2{hZ;9>@QxtxWn1Gu2jWOXx_a$WnUdQ2t5V` z06@{v!PW(w`C!LG_JE_;L6P4`et*gwlVAAXEGzg8a;B2x}{-B@YV%Lg*YS zj29Bj48!tph+QrgT<^G12-t22CkTgd450?lu~<7E!=J)p1;a3>kcMak2nnV6V_j_R zzaT&hhX~|w*jN;b%jF`uhDcUu019JnZjM5qLY+Eg07e*ug)=!+o&hsV2P8mYy9`@; zm|rM^&0(;ZupK7VmvxncLm;5AFK7?GmO$a< z&F8iKbD6B(wdmL!K_T9S2q}c`15?RJXrp<;mb@oa+pNAZ5oVT{7IZ}6esn!{T`N9% ze`nFJoi55XkKts6B| z2Pdr~l<<_(Gx(5BY`|M;*F#uEFcj%G(&WlJ9@6LRsrRNDa{5Z^^#nw;4nBTC4q7F0am|R}D?90? z#r)z{Ho&Yi=oKapG8f_Z2$U0$pZVzW(er*>6`FTZ>B$)M?OuBIa7|5(Q<3o9#>bBz zU%ysS+!Jvq$TcAU)9!(IKDtq%_s*fzEyU{h++1*Ycs8l6s>-E=5~$Aq%XE|}*M1cG z=Oip$uDwv!_lI%7XQH$P4>8PsHaV#S-wQV~F>wl9Q8LT5vf$7E9!b^h?70 zW;7xap)6k7Y7-zBlH`e+(uC2!MH_O(g8adL8s}DZLqkJbn-cI;whTUzya9JlPa{%w zU*NOn&vDk)dWq4PsO#6eRJ$e!c=>B3W26XyGZt0(6I*)429nlIGs$hKuOAmVB4jd| zg@uKxA2I$N#rU#P^glW_XSr&Uxjw;*;hQ&aG6o3}8_2-dffKgc=T4}U4$SuSBtCiv zA5Z|)H8rDI-t+C2w1U?UhC%OPYG&489AQ~M0F}<61x#k#Lyp5=#1_d1e z*lhNA*$Z5^Y&qHf-f-|oYsg-dxH-4kuXAtf7B4c=Rpq;U^5Vk6VQ8$g?mdP_jfaMY zhL?v&zmX!`)~3*<1aiL3T|esDHPtw{$wl&}9Uwb9lRApYlm4uWEox|NoXz~SxoHXD z2d{2RL#tX_M}tkSdI{*N%7^71Zlt8-c4`v8A(l{}px{pTB?tsUb5A9dW&w z36=}C`i1y}1fbTMYAvZ=T&XK5b%Na8e&L%(N2063z> zsy^KO80ALcUA;3;(9V*=JHtxFjAL={KYcPcHa5oLS}H2o;o-y8=>r3<@EUrFTUk!= z7OwDc7o1=Y>9T};Zql*0c+VX3$b7rR&+i;z06?O!!q|kArtBgb6r`jS6cjL-Oe0cL zV`GnO+0f8%ZEa0;(!aW-^m3)UB@VahDk~_I%#4g&tPKC4bHo?ed>E^G#_^5mquUnD zgC3cyA3hLIL$H=UNqQO3EuVl`46|;e$OXKJ9FW*49Ls5{s;EF)SzQthHW`S8qX%x~ zUcs<_>z<#!)1X00HKqBs>e#2&9e)Cwqu6GJtf1H0HDqxAfPk;y4W5rEC*^*c&Y0w@ zh5dQu!SHi|kMM`>dR(dN`ktx548kjEhL#UsPhRhV_f%ziR_cE7vtJ^e;R`vN*zMM;q%9liD=p7U(yl0Y zNPLQ>sGN4tQB6EC*;zifuRm^S@3pIylfE-C-s0xwk<|RhHx#^ctNxOcCIL^^)jI}u P{!vFevh5?HPxOBPc@S?M literal 0 HcmV?d00001 diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 87fffa7b724..855b6bccd6a 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -351,6 +351,13 @@ def check(orientation_im): transposed_im = ImageOps.exif_transpose(im) assert 0x0112 not in transposed_im.getexif() + # Orientation from "XML:com.adobe.xmp" info key (from exiftool) + with Image.open("Tests/images/xmp_orientation_exiftool.png") as im: + assert im.getexif()[0x0112] == 8 + + transposed_im = ImageOps.exif_transpose(im) + assert 0x0112 not in transposed_im.getexif() + # Orientation from "Raw profile type exif" info key # This test image has been manually hexedited from exif_imagemagick.png # to have a different orientation diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 6abb1249166..816ea94db8d 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1407,6 +1407,12 @@ def getexif(self): match = re.search(r'tiff:Orientation="([0-9])"', xmp_tags) if match: self._exif[0x0112] = int(match[1]) + else: + match = re.search( + r"([0-9])", xmp_tags + ) + if match: + self._exif[0x0112] = int(match[1]) return self._exif diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index f0d4545badf..b26b1858b93 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -606,5 +606,10 @@ def exif_transpose(image): "", transposed_image.info["XML:com.adobe.xmp"], ) + transposed_image.info["XML:com.adobe.xmp"] = re.sub( + r"([0-9])", + "", + transposed_image.info["XML:com.adobe.xmp"], + ) return transposed_image return image.copy() From db20d0f8feaf2928ef68791e7e1fdffc8658cd9e Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Tue, 26 Jul 2022 00:45:23 +0300 Subject: [PATCH 285/326] fixing typo in filetest name --- ...xifool.png => xmp_tags_orientation_exiftool.png} | Bin Tests/test_imageops.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Tests/images/{xmp_orientation_exifool.png => xmp_tags_orientation_exiftool.png} (100%) diff --git a/Tests/images/xmp_orientation_exifool.png b/Tests/images/xmp_tags_orientation_exiftool.png similarity index 100% rename from Tests/images/xmp_orientation_exifool.png rename to Tests/images/xmp_tags_orientation_exiftool.png diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 855b6bccd6a..95b49596e79 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -352,7 +352,7 @@ def check(orientation_im): assert 0x0112 not in transposed_im.getexif() # Orientation from "XML:com.adobe.xmp" info key (from exiftool) - with Image.open("Tests/images/xmp_orientation_exiftool.png") as im: + with Image.open("Tests/images/xmp_tags_orientation_exiftool.png") as im: assert im.getexif()[0x0112] == 8 transposed_im = ImageOps.exif_transpose(im) From f42e2552068dd3d6a02e1b544ff07abf08e77036 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 Jul 2022 11:58:44 +1000 Subject: [PATCH 286/326] Simplified code --- src/PIL/Image.py | 10 ++-------- src/PIL/ImageOps.py | 13 +++++-------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 816ea94db8d..4eb2dead655 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1404,15 +1404,9 @@ def getexif(self): if 0x0112 not in self._exif: xmp_tags = self.info.get("XML:com.adobe.xmp") if xmp_tags: - match = re.search(r'tiff:Orientation="([0-9])"', xmp_tags) + match = re.search(r'tiff:Orientation(="|>)([0-9])', xmp_tags) if match: - self._exif[0x0112] = int(match[1]) - else: - match = re.search( - r"([0-9])", xmp_tags - ) - if match: - self._exif[0x0112] = int(match[1]) + self._exif[0x0112] = int(match[2]) return self._exif diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index b26b1858b93..48b41d87fda 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -601,15 +601,12 @@ def exif_transpose(image): "Raw profile type exif" ] = transposed_exif.tobytes().hex() elif "XML:com.adobe.xmp" in transposed_image.info: - transposed_image.info["XML:com.adobe.xmp"] = re.sub( + for pattern in ( r'tiff:Orientation="([0-9])"', - "", - transposed_image.info["XML:com.adobe.xmp"], - ) - transposed_image.info["XML:com.adobe.xmp"] = re.sub( r"([0-9])", - "", - transposed_image.info["XML:com.adobe.xmp"], - ) + ): + transposed_image.info["XML:com.adobe.xmp"] = re.sub( + pattern, "", transposed_image.info["XML:com.adobe.xmp"] + ) return transposed_image return image.copy() From 42763400740a06009d4cbcecf32a82501fbfc154 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Jul 2022 21:32:48 +1000 Subject: [PATCH 287/326] Sorted formats by n --- src/PIL/DdsImagePlugin.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/PIL/DdsImagePlugin.py b/src/PIL/DdsImagePlugin.py index 0f2cce1e50d..bba48016140 100644 --- a/src/PIL/DdsImagePlugin.py +++ b/src/PIL/DdsImagePlugin.py @@ -82,6 +82,7 @@ DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ + # DXT1 DXT1_FOURCC = 0x31545844 @@ -155,6 +156,14 @@ def _open(self): elif fourcc == b"DXT5": self.pixel_format = "DXT5" n = 3 + elif fourcc == b"ATI1": + self.pixel_format = "BC4" + n = 4 + self.mode = "L" + elif fourcc == b"ATI2": + self.pixel_format = "BC5" + n = 5 + self.mode = "RGB" elif fourcc == b"BC5S": self.pixel_format = "BC5S" n = 5 @@ -192,14 +201,6 @@ def _open(self): raise NotImplementedError( f"Unimplemented DXGI format {dxgi_format}" ) - elif fourcc == b"ATI1": - self.pixel_format = "BC4" - n = 4 - self.mode = "L" - elif fourcc == b"ATI2": - self.pixel_format = "BC5" - n = 5 - self.mode = "RGB" else: raise NotImplementedError(f"Unimplemented pixel format {repr(fourcc)}") From 7e1261c6a0001c2302d391da24a44e2b5d669b57 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Jul 2022 22:18:39 +1000 Subject: [PATCH 288/326] Simplified test code --- .../images/xmp_tags_orientation_exiftool.png | Bin 1935 -> 1258 bytes Tests/test_imageops.py | 16 +++++----------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Tests/images/xmp_tags_orientation_exiftool.png b/Tests/images/xmp_tags_orientation_exiftool.png index fb30199c86acbc8953d1cf16ddfa9e3ec41ece19..10f0f44009ae9237c1a5712ae8b0c7269b527711 100644 GIT binary patch literal 1258 zcmbtUO=uHA6rN($novXq`-gO`;GVjFO|jV~DKV)grKy@mnu=s}AL+KVR@ylDkNREki%i75Ie4RNh42p)E4-uJ%wX1=%ccJlDR z!FpG-3n5e=jfCSclDVsH6THt0&mY22n+?W-2+dt^f0s5wOr;|67(%7J2u++t=o>&2 zpAZ`75Ly~RNW6+rgFf@%hz}B5^vJM@kb4JtYS8pdBSIUN>hyjw(_Xi(;=WXA+v0r+9(!cxVtY(it%xK2VW> z$WM=1mM*evp-^B79gJpVS>EgQvYfyQLOU?p&7x{acDris0R#cAkz!yu5IE?+fSxe5WW0qtbW=ROn6y3mP*+Hj|{R<<9 zFN22+8AItwkpmqSq7$nUlV9m7e^a2<#IoWG7f<4zI;@g8x-E zxX)`}5nIw7MFW}`GO%Q62D!|E^2b(4NOdp*18!F{$oUBt)6#OL7?vy?5IEi=@|?(Z zQCugPbakz)b8yx)vvMtl)CbuW$7~h5p9Ta4^x>bn4=17usDH zT3r0`vte^Cb2518`jfjiw?`gvp35)47H2=-Ug&G7ZR_g`-q`iNd)u*RCw8(;^K;YQ zd#Q!D?rR@Ed}*yunypi9$0x50ox9&W*uV6q``sOWs(rcjMGfBC(%^no^pHPFw09sp IyMN@&FNzYMoB#j- delta 1598 zcmV-E2EqC236BpYiBL{Q4GJ0x0000DNk~Le0001h0000$2m$~A09~}`C;$KfiIH}Y z0y8j^2mx)AVgfFcxdIFjI6NP zABchm#igLdQV_MYhS1}}ceuxJ-!x4x$)%}}_nNA`++E-A``oWw@&`f)xKR{^Kk=`{ zvMm0@L!9ZP^!!k*e>iREVSv;jQ4VMt0J7gTwve#}S%Vup7iHfX4->MK1VB%+#xrehZ4r zj6pG=Z=(0CK%bq6(}37Tccsrx#9%;lx_4XC_}U+~q4kW~e}?`U5QXlXt4KDPUo)b% zA#wwR>+UUj&)E*=F0pI2QP}Wjcz9TNrWAqF_iX8GS&E`S2oXYtVc3#Uxm^|X<%Dn# zB3TKmsv2H>g@Lmynwb7+SynEW8yOjyn3zBa(M((`*0TslalU_vCxRnDi0%OjvaeEA zm3n{>N+y$pe~?zI)o!;f%c70fZto$ZK~yLRi-0h#-dAIH6l7nesw#vqm&;|d*|oK` zN~NOf`u6tr)YKFW0(uo|RcD>~qd`USH{?9q?T*@uURY1cv1LkQIl zXyQa7Q79Cu)#}R1imvM+)dv6^9v*^kU+uv>WS+k89RWP`^XP3H!zKU#LO3}&S+CdG zc9h9vYPA|6#QrC>VQXs(0B~?{;H%C($>;Npe@3I(Z1z701TVzAo4jMi1it-veSK9F zC7n+1?(P-}g-j-+Xo}O-QZZu81y1L5e^D{Fu_xJZ0<7Ts|s%o)VJUu=2 zRp*|h)9KsWTd{faBNt)-{H%o-Pf6A{u9rJ*6Izz_^72AQ2s%7qj4PE2{a|N>e=%;i z+oe*eR;vL3ip8Sen!B>FurNM8Ua!}?EySYU3zD00F>l7n?dYGm9ZiupWMaM}Mv=E9 z!yxQbM3`;#F!#Y7=OJciX9*#>T&`3q0RWDVkIUthZ1v2W@Fc`U`j z4?dPcVP~1a@R-7O7R1hI?e2wYe~mFtr_-%gE0s#oeLyyiYpdsP_yV_1<GKjeejT)4{Q*OfugXxfz@V1O$F zE2v-Ov6PsR2E@#eeM}+ln*2KczOsz{L)y?ETwdD8ej#m$jvlYAWD`jn#Ny+{t?UKT whVTS@b+O}O>4aazyu90yowR`mUz@W30osfJ$oL$>)c^nh07*qoM6N<$f|I@g`~Uy| diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 95b49596e79..bd5f44e5008 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -345,18 +345,12 @@ def check(orientation_im): check(orientation_im) # Orientation from "XML:com.adobe.xmp" info key - with Image.open("Tests/images/xmp_tags_orientation.png") as im: - assert im.getexif()[0x0112] == 3 - - transposed_im = ImageOps.exif_transpose(im) - assert 0x0112 not in transposed_im.getexif() + for suffix in ("", "_exiftool"): + with Image.open("Tests/images/xmp_tags_orientation" + suffix + ".png") as im: + assert im.getexif()[0x0112] == 3 - # Orientation from "XML:com.adobe.xmp" info key (from exiftool) - with Image.open("Tests/images/xmp_tags_orientation_exiftool.png") as im: - assert im.getexif()[0x0112] == 8 - - transposed_im = ImageOps.exif_transpose(im) - assert 0x0112 not in transposed_im.getexif() + transposed_im = ImageOps.exif_transpose(im) + assert 0x0112 not in transposed_im.getexif() # Orientation from "Raw profile type exif" info key # This test image has been manually hexedited from exif_imagemagick.png From bac83f7dd3ac738f8bc16a6647f69f373916ae2b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Jul 2022 22:27:43 +1000 Subject: [PATCH 289/326] Check that orientation is still absent after reloading Exif --- Tests/test_imageops.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index bd5f44e5008..01e40e6d4d5 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -352,6 +352,9 @@ def check(orientation_im): transposed_im = ImageOps.exif_transpose(im) assert 0x0112 not in transposed_im.getexif() + transposed_im._reload_exif() + assert 0x0112 not in transposed_im.getexif() + # Orientation from "Raw profile type exif" info key # This test image has been manually hexedited from exif_imagemagick.png # to have a different orientation From 78a6bb4c992664904896c37f20100225023a7300 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Jul 2022 22:33:27 +1000 Subject: [PATCH 290/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8616ff09c68..ed157abec29 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Added support for reading ATI1/ATI2 (BC4/BC5) DDS images #6457 + [REDxEYE, radarhere] + - Do not clear GIF tile when checking number of frames #6455 [radarhere] From cbe292212b96a27aa82501c89596efb13c263d59 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 28 Jul 2022 08:35:10 +1000 Subject: [PATCH 291/326] Added release notes for #6457 --- docs/releasenotes/9.3.0.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index da045a50a98..c64423b0152 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -53,7 +53,7 @@ TODO Other Changes ============= -TODO -^^^^ +Added DDS ATI1 and ATI2 reading +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -TODO +Support has been added to read the ATI1 and ATI2 formats of DDS images. From f2ce07cf228024d7d35d31c74fa385833262c9d6 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 30 Jul 2022 13:29:10 +1000 Subject: [PATCH 292/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ed157abec29..b821e7732f6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Parse orientation from XMP tag contents #6463 + [bigcat88, radarhere] + - Added support for reading ATI1/ATI2 (BC4/BC5) DDS images #6457 [REDxEYE, radarhere] From 5cc9ab5b1d78dd154d7a60883acb3d7fa5fd09c3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Aug 2022 08:55:31 +1000 Subject: [PATCH 293/326] Updated harfbuzz to 5.1.0 --- winbuild/build_prepare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index bc5fb4d024b..b1e6e4b8e7a 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -281,9 +281,9 @@ def cmd_msbuild( "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/4.4.1.zip", - "filename": "harfbuzz-4.4.1.zip", - "dir": "harfbuzz-4.4.1", + "url": "https://github.com/harfbuzz/harfbuzz/archive/5.1.0.zip", + "filename": "harfbuzz-5.1.0.zip", + "dir": "harfbuzz-5.1.0", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From f5b27f90f7efcf01a68f7e3d84531d03e9ebfc5e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Aug 2022 20:38:47 +1000 Subject: [PATCH 294/326] Save 1 mode PDF using CCITTFaxDecode filter --- Tests/test_file_pdf.py | 2 +- src/PIL/PdfImagePlugin.py | 44 +++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index c71d4f5f22b..310619fb255 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -43,7 +43,7 @@ def test_monochrome(tmp_path): # Act / Assert outfile = helper_save_as_pdf(tmp_path, mode) - assert os.path.getsize(outfile) < 15000 + assert os.path.getsize(outfile) < 5000 def test_greyscale(tmp_path): diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 2109a6f52cb..d1b34be487e 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -21,10 +21,11 @@ ## import io +import math import os import time -from . import Image, ImageFile, ImageSequence, PdfParser, __version__ +from . import Image, ImageFile, ImageSequence, PdfParser, TiffImagePlugin, __version__ # # -------------------------------------------------------------------- @@ -123,8 +124,26 @@ def _save(im, fp, filename, save_all=False): params = None decode = None + # + # Get image characteristics + + width, height = im.size + if im.mode == "1": - filter = "DCTDecode" + filter = "CCITTFaxDecode" + bits = 1 + params = PdfParser.PdfArray( + [ + PdfParser.PdfDict( + { + "K": -1, + "BlackIs1": True, + "Columns": width, + "Rows": height, + } + ) + ] + ) colorspace = PdfParser.PdfName("DeviceGray") procset = "ImageB" # grayscale elif im.mode == "L": @@ -161,6 +180,11 @@ def _save(im, fp, filename, save_all=False): if filter == "ASCIIHexDecode": ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)]) + elif filter == "CCITTFaxDecode": + original_strip_size = TiffImagePlugin.STRIP_SIZE + TiffImagePlugin.STRIP_SIZE = math.ceil(im.width / 8) * im.height + im.save(op, "TIFF", compression="group4") + TiffImagePlugin.STRIP_SIZE = original_strip_size elif filter == "DCTDecode": Image.SAVE["JPEG"](im, op, filename) elif filter == "FlateDecode": @@ -170,22 +194,24 @@ def _save(im, fp, filename, save_all=False): else: raise ValueError(f"unsupported PDF filter ({filter})") - # - # Get image characteristics - - width, height = im.size + stream = op.getvalue() + if filter == "CCITTFaxDecode": + stream = stream[8:] + filter = PdfParser.PdfArray([PdfParser.PdfName(filter)]) + else: + filter = PdfParser.PdfName(filter) existing_pdf.write_obj( image_refs[page_number], - stream=op.getvalue(), + stream=stream, Type=PdfParser.PdfName("XObject"), Subtype=PdfParser.PdfName("Image"), Width=width, # * 72.0 / resolution, Height=height, # * 72.0 / resolution, - Filter=PdfParser.PdfName(filter), + Filter=filter, BitsPerComponent=bits, Decode=decode, - DecodeParams=params, + DecodeParms=params, ColorSpace=colorspace, ) From 2b14d83549b2100c5d08dd8cd9231dd53dde377b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 1 Aug 2022 21:41:17 +1000 Subject: [PATCH 295/326] Added strip_size as TIFF encoder argument --- Tests/test_file_libtiff.py | 12 ++++++++---- src/PIL/PdfImagePlugin.py | 13 ++++++++----- src/PIL/TiffImagePlugin.py | 3 ++- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index a43548ae0f3..3084425a406 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -1011,14 +1011,18 @@ def test_save_multistrip(self, compression, tmp_path): # Assert that there are multiple strips assert len(im.tag_v2[STRIPOFFSETS]) > 1 - def test_save_single_strip(self, tmp_path): + @pytest.mark.parametrize("argument", (True, False)) + def test_save_single_strip(self, argument, tmp_path): im = hopper("RGB").resize((256, 256)) out = str(tmp_path / "temp.tif") - TiffImagePlugin.STRIP_SIZE = 2**18 + if not argument: + TiffImagePlugin.STRIP_SIZE = 2**18 try: - - im.save(out, compression="tiff_adobe_deflate") + arguments = {"compression": "tiff_adobe_deflate"} + if argument: + arguments["strip_size"] = 2**18 + im.save(out, **arguments) with Image.open(out) as im: assert len(im.tag_v2[STRIPOFFSETS]) == 1 diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index d1b34be487e..181a05b8d26 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -25,7 +25,7 @@ import os import time -from . import Image, ImageFile, ImageSequence, PdfParser, TiffImagePlugin, __version__ +from . import Image, ImageFile, ImageSequence, PdfParser, __version__ # # -------------------------------------------------------------------- @@ -181,10 +181,13 @@ def _save(im, fp, filename, save_all=False): if filter == "ASCIIHexDecode": ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)]) elif filter == "CCITTFaxDecode": - original_strip_size = TiffImagePlugin.STRIP_SIZE - TiffImagePlugin.STRIP_SIZE = math.ceil(im.width / 8) * im.height - im.save(op, "TIFF", compression="group4") - TiffImagePlugin.STRIP_SIZE = original_strip_size + im.save( + op, + "TIFF", + compression="group4", + # use a single strip + strip_size=math.ceil(im.width / 8) * im.height, + ) elif filter == "DCTDecode": Image.SAVE["JPEG"](im, op, filename) elif filter == "FlateDecode": diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 0dd49340d4b..da33cc5a501 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1684,7 +1684,8 @@ def _save(im, fp, filename): stride = len(bits) * ((im.size[0] * bits[0] + 7) // 8) # aim for given strip size (64 KB by default) when using libtiff writer if libtiff: - rows_per_strip = 1 if stride == 0 else min(STRIP_SIZE // stride, im.size[1]) + im_strip_size = encoderinfo.get("strip_size", STRIP_SIZE) + rows_per_strip = 1 if stride == 0 else min(im_strip_size // stride, im.size[1]) # JPEG encoder expects multiple of 8 rows if compression == "jpeg": rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, im.size[1]) From 82974a404c4df2f4fac63c1a6c3a4607217a847b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 18:14:07 +0000 Subject: [PATCH 296/326] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/Lucas-C/pre-commit-hooks: v1.2.0 → v1.3.0](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.2.0...v1.3.0) - [github.com/PyCQA/flake8: 4.0.1 → 5.0.2](https://github.com/PyCQA/flake8/compare/4.0.1...5.0.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5e1f3557f2..1bb71bd72ad 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,13 +19,13 @@ repos: - id: yesqa - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.2.0 + rev: v1.3.0 hooks: - id: remove-tabs exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$) - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 + rev: 5.0.2 hooks: - id: flake8 additional_dependencies: [flake8-2020, flake8-implicit-str-concat] From 8464ed423b20741a6f71385ba03d56cea91c455f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Aug 2022 07:46:41 +1000 Subject: [PATCH 297/326] Updated Valgrind job to Jammy --- .github/workflows/test-valgrind.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-valgrind.yml b/.github/workflows/test-valgrind.yml index 013e5ca4ac3..dda1b357785 100644 --- a/.github/workflows/test-valgrind.yml +++ b/.github/workflows/test-valgrind.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false matrix: docker: [ - ubuntu-20.04-focal-amd64-valgrind, + ubuntu-22.04-jammy-amd64-valgrind, ] dockerTag: [main] From 1112ad67a35eb25d0363d68227fbbb3eea9f2b36 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Aug 2022 14:18:31 +1000 Subject: [PATCH 298/326] Document that orientation data is removed by exif_transpose() --- src/PIL/ImageOps.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 48b41d87fda..44214fead5e 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -572,8 +572,11 @@ def solarize(image, threshold=128): def exif_transpose(image): """ - If an image has an EXIF Orientation tag, return a new image that is - transposed accordingly. Otherwise, return a copy of the image. + If an image has an EXIF Orientation tag return a new image that is + transposed accordingly. The new image will have the orientation data + removed. + + Otherwise, return a copy of the image. :param image: The image to transpose. :return: An image. From 1197e1998214ca54e41772d2f804b02e528a7bab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Aug 2022 14:19:17 +1000 Subject: [PATCH 299/326] Document that exif_transpose() does not change orientations of 1 --- src/PIL/ImageOps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index 44214fead5e..0c3f900caac 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -572,9 +572,9 @@ def solarize(image, threshold=128): def exif_transpose(image): """ - If an image has an EXIF Orientation tag return a new image that is - transposed accordingly. The new image will have the orientation data - removed. + If an image has an EXIF Orientation tag, other than 1, return a new image + that is transposed accordingly. The new image will have the orientation + data removed. Otherwise, return a copy of the image. From 101f1158534f77594d6383125b4a16652d43ae91 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 4 Aug 2022 20:03:24 +1000 Subject: [PATCH 300/326] Increased tolerance to allow for libtiff with libjpeg-turbo --- Tests/test_file_libtiff.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index a43548ae0f3..01f29fbd150 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -856,7 +856,7 @@ def test_strip_cmyk_16l_jpeg(self): def test_strip_ycbcr_jpeg_2x2_sampling(self): infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif" with Image.open(infile) as im: - assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) + assert_image_similar_tofile(im, "Tests/images/flower.jpg", 1.2) @mark_if_feature_version( pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing" @@ -864,7 +864,7 @@ def test_strip_ycbcr_jpeg_2x2_sampling(self): def test_strip_ycbcr_jpeg_1x1_sampling(self): infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif" with Image.open(infile) as im: - assert_image_equal_tofile(im, "Tests/images/flower2.jpg") + assert_image_similar_tofile(im, "Tests/images/flower2.jpg", 0.01) def test_tiled_cmyk_jpeg(self): infile = "Tests/images/tiff_tiled_cmyk_jpeg.tif" @@ -877,7 +877,7 @@ def test_tiled_cmyk_jpeg(self): def test_tiled_ycbcr_jpeg_1x1_sampling(self): infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif" with Image.open(infile) as im: - assert_image_equal_tofile(im, "Tests/images/flower2.jpg") + assert_image_similar_tofile(im, "Tests/images/flower2.jpg", 0.01) @mark_if_feature_version( pytest.mark.valgrind_known_error, "libjpeg_turbo", "2.0", reason="Known Failing" @@ -885,7 +885,7 @@ def test_tiled_ycbcr_jpeg_1x1_sampling(self): def test_tiled_ycbcr_jpeg_2x2_sampling(self): infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif" with Image.open(infile) as im: - assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5) + assert_image_similar_tofile(im, "Tests/images/flower.jpg", 1.5) def test_strip_planar_rgb(self): # gdal_translate -co TILED=no -co INTERLEAVE=BAND -co COMPRESS=LZW \ From e77a7b6b4f0b496a70cabd8360f0988d32bea063 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 5 Aug 2022 23:29:58 +1000 Subject: [PATCH 301/326] Added support for RGBA PSD images --- Tests/images/rgba.psd | Bin 0 -> 2448 bytes Tests/test_file_psd.py | 7 ++++++- src/PIL/PsdImagePlugin.py | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Tests/images/rgba.psd diff --git a/Tests/images/rgba.psd b/Tests/images/rgba.psd new file mode 100644 index 0000000000000000000000000000000000000000..45fb7c3cca0cbae6a57dc605931f9abcbba65013 GIT binary patch literal 2448 zcmcC;3J7LkWPkt`Ae92f91P4*F&PUdPhaM@V4eVwWCTM5{RSxZ) z8MZ0E)<#kC-9HyiiMjyLSY%W1C^>5gw1|Uw!%uTwF$)WhOkNXUK*X#b5%f{(auzz$&qhRY}%4S~TI0_+UbffV{|?*E2SL~IB!G|U}EqaiTVLf|38 P-Tx068b%~V>kJD3HVMyL literal 0 HcmV?d00001 diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index b4b5b7a0c65..4f934375c7c 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -4,7 +4,7 @@ from PIL import Image, PsdImagePlugin -from .helper import assert_image_similar, hopper, is_pypy +from .helper import assert_image_equal_tofile, assert_image_similar, hopper, is_pypy test_file = "Tests/images/hopper.psd" @@ -107,6 +107,11 @@ def test_open_after_exclusive_load(): im.load() +def test_rgba(): + with Image.open("Tests/images/rgba.psd") as im: + assert_image_equal_tofile(im, "Tests/images/imagedraw_square.png") + + def test_icc_profile(): with Image.open(test_file) as im: assert "icc_profile" in im.info diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 04c2e4fe379..bd10e3b95dd 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -75,6 +75,9 @@ def _open(self): if channels > psd_channels: raise OSError("not enough channels") + if mode == "RGB" and psd_channels == 4: + mode = "RGBA" + channels = 4 self.mode = mode self._size = i32(s, 18), i32(s, 14) From 61ec41511da16f8083e8ebab1c60c5b60625a336 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Aug 2022 15:40:10 +1000 Subject: [PATCH 302/326] Updated libwebp to 1.2.4 --- depends/install_webp.sh | 2 +- winbuild/build_prepare.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/depends/install_webp.sh b/depends/install_webp.sh index ed17f2228fc..05867b7d448 100755 --- a/depends/install_webp.sh +++ b/depends/install_webp.sh @@ -1,7 +1,7 @@ #!/bin/bash # install webp -archive=libwebp-1.2.3 +archive=libwebp-1.2.4 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index b1e6e4b8e7a..d46c1a40911 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -157,9 +157,9 @@ def cmd_msbuild( # "bins": [r"libtiff\*.dll"], }, "libwebp": { - "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.3.tar.gz", - "filename": "libwebp-1.2.3.tar.gz", - "dir": "libwebp-1.2.3", + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.4.tar.gz", + "filename": "libwebp-1.2.4.tar.gz", + "dir": "libwebp-1.2.4", "build": [ cmd_rmdir(r"output\release-static"), # clean cmd_nmake( From 04d976131673b94c6065d5cf92b0eab53c4469f3 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Aug 2022 17:29:44 +1000 Subject: [PATCH 303/326] Changed "font" to class variable --- Tests/test_imagedraw.py | 17 +++++++++++++++++ src/PIL/ImageDraw.py | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 23bc756bb14..961b4d08130 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1314,6 +1314,23 @@ def test_stroke_multiline(): assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_multiline.png", 3.3) +def test_setting_default_font(): + # Arrange + im = Image.new("RGB", (100, 250)) + draw = ImageDraw.Draw(im) + font = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 120) + + # Act + ImageDraw.ImageDraw.font = font + + # Assert + try: + assert draw.getfont() == font + finally: + ImageDraw.ImageDraw.font = None + assert isinstance(draw.getfont(), ImageFont.ImageFont) + + def test_same_color_outline(): # Prepare shape x0, y0 = 5, 5 diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 8970471d3b2..712ec6e0912 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -46,6 +46,8 @@ class ImageDraw: + font = None + def __init__(self, im, mode=None): """ Create a drawing instance. @@ -86,7 +88,6 @@ def __init__(self, im, mode=None): else: self.fontmode = "L" # aliasing is okay for other modes self.fill = 0 - self.font = None def getfont(self): """ From 42a5a743c18d87d9c54ed5ff11303caf9fcd0b4b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Aug 2022 22:48:10 +1000 Subject: [PATCH 304/326] Note to Windows users that FreeType will keep the font file open --- src/PIL/ImageFont.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index a3b711c6077..efd702b8685 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -906,9 +906,10 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): This function loads a font object from the given file or file-like object, and creates a font object for a font of the given size. - Pillow uses FreeType to open font files. If you are opening many fonts - simultaneously on Windows, be aware that Windows limits the number of files - that can be open in C at once to 512. If you approach that limit, an + Pillow uses FreeType to open font files. On Windows, be aware that FreeType + will keep the file open as long as the FreeTypeFont object exists. Windows + limits the number of files that can be open in C at once to 512, so if many + fonts are opened simultaneously and that limit is approached, an ``OSError`` may be thrown, reporting that FreeType "cannot open resource". This function requires the _imagingft service. From c24b6ef4f095ba2b9e3f35d8f470d931b1310a11 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 6 Aug 2022 23:01:36 +1000 Subject: [PATCH 305/326] Document workaround --- src/PIL/ImageFont.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index efd702b8685..9386d008602 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -911,6 +911,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): limits the number of files that can be open in C at once to 512, so if many fonts are opened simultaneously and that limit is approached, an ``OSError`` may be thrown, reporting that FreeType "cannot open resource". + A workaround would be to copy the file(s) into memory, and open that instead. This function requires the _imagingft service. From 5d71ba3ca140914ff05ad8246b6d0a7053216556 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 8 Aug 2022 09:13:06 +1000 Subject: [PATCH 306/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b821e7732f6..5f99d9d254e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Save 1 mode PDF using CCITTFaxDecode filter #6470 + [radarhere] + +- Added support for RGBA PSD images #6481 + [radarhere] + - Parse orientation from XMP tag contents #6463 [bigcat88, radarhere] From 8135bd5cfbfa1e9eacd7c24adbbfac14bb92c9e0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 11 Aug 2022 10:35:44 +1000 Subject: [PATCH 307/326] Added documentation --- docs/reference/ImageDraw.rst | 7 ++++++- docs/releasenotes/9.3.0.rst | 10 ++++++++++ src/PIL/ImageDraw.py | 5 +++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index c2d72c804c1..1ef9079fba0 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -64,7 +64,7 @@ Fonts PIL can use bitmap fonts or OpenType/TrueType fonts. -Bitmap fonts are stored in PIL’s own format, where each font typically consists +Bitmap fonts are stored in PIL's own format, where each font typically consists of two files, one named .pil and the other usually named .pbm. The former contains font metrics, the latter raster data. @@ -146,6 +146,11 @@ Methods Get the current default font. + To set the default font for all future ImageDraw instances:: + + from PIL import ImageDraw, ImageFont + ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + :returns: An image font. .. py:method:: ImageDraw.arc(xy, start, end, fill=None, width=0) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index c64423b0152..a8db4edd655 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -26,6 +26,16 @@ TODO API Additions ============= +Allow default ImageDraw font to be set +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Rather than specifying a font when calling text-related ImageDraw methods, or +setting a font on each ImageDraw instance, the default font can now be set for +all future ImageDraw operations. + + from PIL import ImageDraw, ImageFont + ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + Saving multiple MPO frames ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 712ec6e0912..e84dafb12e9 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -93,6 +93,11 @@ def getfont(self): """ Get the current default font. + To set the default font for all future ImageDraw instances:: + + from PIL import ImageDraw, ImageFont + ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") + :returns: An image font.""" if not self.font: # FIXME: should add a font repository From 84bdb635c2d144ec416382210d9825e5cdda065d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Aug 2022 16:36:46 +1000 Subject: [PATCH 308/326] Updated libjpeg-turbo to 2.1.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 d46c1a40911..a381d636dd8 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -108,9 +108,9 @@ def cmd_msbuild( deps = { "libjpeg": { "url": SF_PROJECTS - + "/libjpeg-turbo/files/2.1.3/libjpeg-turbo-2.1.3.tar.gz/download", - "filename": "libjpeg-turbo-2.1.3.tar.gz", - "dir": "libjpeg-turbo-2.1.3", + + "/libjpeg-turbo/files/2.1.4/libjpeg-turbo-2.1.4.tar.gz/download", + "filename": "libjpeg-turbo-2.1.4.tar.gz", + "dir": "libjpeg-turbo-2.1.4", "build": [ cmd_cmake( [ From 55d94558fbaa809c0cc03c072bf7119fb2b27e78 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 13 Aug 2022 23:14:32 +1000 Subject: [PATCH 309/326] Do not install test-image-results on GitHub Actions --- .ci/install.sh | 1 - .github/workflows/macos-install.sh | 1 - 2 files changed, 2 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index 16a056dd585..7ead209bec2 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -35,7 +35,6 @@ python3 -m pip install -U pytest python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma -python3 -m pip install test-image-results if [[ $(uname) != CYGWIN* ]]; then # TODO Remove condition when NumPy supports 3.11 diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index 06b82964559..bb0bcd6803e 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -12,7 +12,6 @@ python3 -m pip install -U pytest python3 -m pip install -U pytest-cov python3 -m pip install -U pytest-timeout python3 -m pip install pyroma -python3 -m pip install test-image-results echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg # TODO Remove condition when NumPy supports 3.11 From 520fa19dab4b60d732d273aab8bff195ce5875cf Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 15 Aug 2022 09:15:35 +1000 Subject: [PATCH 310/326] Fixed formatting Co-authored-by: Hugo van Kemenade --- docs/releasenotes/9.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releasenotes/9.3.0.rst b/docs/releasenotes/9.3.0.rst index a8db4edd655..7109a09f2b4 100644 --- a/docs/releasenotes/9.3.0.rst +++ b/docs/releasenotes/9.3.0.rst @@ -31,7 +31,7 @@ Allow default ImageDraw font to be set Rather than specifying a font when calling text-related ImageDraw methods, or setting a font on each ImageDraw instance, the default font can now be set for -all future ImageDraw operations. +all future ImageDraw operations:: from PIL import ImageDraw, ImageFont ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") From b84816c02f84bb42f440387366e391fa2ed79020 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 16 Aug 2022 22:45:55 +1000 Subject: [PATCH 311/326] Added pa2p --- src/libImaging/Convert.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index 5dc17db60d0..f0d42f7ff47 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1026,6 +1026,14 @@ pa2l(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { } } +static void +pa2p(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { + int x; + for (x = 0; x < xsize; x++, in += 4) { + *out++ = in[0]; + } +} + static void p2pa(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { int x; @@ -1209,6 +1217,8 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) { convert = alpha ? pa2l : p2l; } else if (strcmp(mode, "LA") == 0) { convert = alpha ? pa2la : p2la; + } else if (strcmp(mode, "P") == 0) { + convert = pa2p; } else if (strcmp(mode, "PA") == 0) { convert = p2pa; } else if (strcmp(mode, "I") == 0) { @@ -1233,6 +1243,10 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) { if (!imOut) { return NULL; } + if (strcmp(mode, "P") == 0) { + ImagingPaletteDelete(imOut->palette); + imOut->palette = ImagingPaletteDuplicate(imIn->palette); + } ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) { From 6b35dc2a8ab238145460af37096c9b53a301a235 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 17 Aug 2022 19:17:41 +1000 Subject: [PATCH 312/326] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5f99d9d254e..fb634eabad1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.3.0 (unreleased) ------------------ +- Allow default ImageDraw font to be set #6484 + [radarhere, hugovk] + - Save 1 mode PDF using CCITTFaxDecode filter #6470 [radarhere] From f9d33b40ad0d9a3cea4be3aa2fa65b3beb477e2a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 19 Aug 2022 12:09:47 +1000 Subject: [PATCH 313/326] Ubuntu dependencies also apply to Jammy [ci skip] --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index f147fa6a732..42cd7df9d31 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -367,7 +367,7 @@ In Alpine, the command is:: .. Note:: ``redhat-rpm-config`` is required on Fedora 23, but not earlier versions. -Prerequisites for **Ubuntu 16.04 LTS - 20.04 LTS** are installed with:: +Prerequisites for **Ubuntu 16.04 LTS - 22.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 8a1837c80d8bfb616ef5d37be11522da701d5104 Mon Sep 17 00:00:00 2001 From: Christoph Gohlke Date: Sat, 20 Aug 2022 19:39:04 -0700 Subject: [PATCH 314/326] DOC: fix image-file-formats.rst --- docs/handbook/image-file-formats.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 1728c8e0579..7db7b117a77 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -968,7 +968,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum methods are: :data:`None`, ``"group3"``, ``"group4"``, ``"jpeg"``, ``"lzma"``, ``"packbits"``, ``"tiff_adobe_deflate"``, ``"tiff_ccitt"``, ``"tiff_lzw"``, ``"tiff_raw_16"``, ``"tiff_sgilog"``, ``"tiff_sgilog24"``, ``"tiff_thunderscan"``, - ``"webp"`, ``"zstd"`` + ``"webp"``, ``"zstd"`` **quality** The image quality for JPEG compression, on a scale from 0 (worst) to 100 From fd47eed73a7aa178848f280f09435b55bbaefd69 Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 22 Aug 2022 09:23:42 -0500 Subject: [PATCH 315/326] parametrize Tests/test_image_paste.py --- Tests/test_image_paste.py | 486 +++++++++++++++++++++----------------- 1 file changed, 266 insertions(+), 220 deletions(-) diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index 4ea1d73ce16..bb01ff11067 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -1,3 +1,5 @@ +import pytest + from PIL import Image from .helper import CachedProperty, assert_image_equal @@ -101,226 +103,270 @@ def gradient_RGBa(self): ], ) - def test_image_solid(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "red") - im2 = getattr(self, "gradient_" + mode) - - im.paste(im2, (12, 23)) - - im = im.crop((12, 23, im2.width + 12, im2.height + 23)) - assert_image_equal(im, im2) - - def test_image_mask_1(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "white") - im2 = getattr(self, "gradient_" + mode) - - self.assert_9points_paste( - im, - im2, - self.mask_1, - [ - (255, 255, 255, 255), - (255, 255, 255, 255), - (127, 254, 127, 0), - (255, 255, 255, 255), - (255, 255, 255, 255), - (191, 190, 63, 64), - (127, 0, 127, 254), - (191, 64, 63, 190), - (255, 255, 255, 255), - ], - ) - - def test_image_mask_L(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "white") - im2 = getattr(self, "gradient_" + mode) - - self.assert_9points_paste( - im, - im2, - self.mask_L, - [ - (128, 191, 255, 191), - (208, 239, 239, 208), - (255, 255, 255, 255), - (112, 111, 206, 207), - (192, 191, 191, 191), - (239, 239, 207, 207), - (128, 1, 128, 254), - (207, 113, 112, 207), - (255, 191, 128, 191), - ], - ) - - def test_image_mask_LA(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "white") - im2 = getattr(self, "gradient_" + mode) - - self.assert_9points_paste( - im, - im2, - self.gradient_LA, - [ - (128, 191, 255, 191), - (112, 207, 206, 111), - (128, 254, 128, 1), - (208, 208, 239, 239), - (192, 191, 191, 191), - (207, 207, 112, 113), - (255, 255, 255, 255), - (239, 207, 207, 239), - (255, 191, 128, 191), - ], - ) - - def test_image_mask_RGBA(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "white") - im2 = getattr(self, "gradient_" + mode) - - self.assert_9points_paste( - im, - im2, - self.gradient_RGBA, - [ - (128, 191, 255, 191), - (208, 239, 239, 208), - (255, 255, 255, 255), - (112, 111, 206, 207), - (192, 191, 191, 191), - (239, 239, 207, 207), - (128, 1, 128, 254), - (207, 113, 112, 207), - (255, 191, 128, 191), - ], - ) - - def test_image_mask_RGBa(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "white") - im2 = getattr(self, "gradient_" + mode) - - self.assert_9points_paste( - im, - im2, - self.gradient_RGBa, - [ - (128, 255, 126, 255), - (0, 127, 126, 255), - (126, 253, 126, 255), - (128, 127, 254, 255), - (0, 255, 254, 255), - (126, 125, 254, 255), - (128, 1, 128, 255), - (0, 129, 128, 255), - (126, 255, 128, 255), - ], - ) - - def test_color_solid(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), "black") - - rect = (12, 23, 128 + 12, 128 + 23) - im.paste("white", rect) - - hist = im.crop(rect).histogram() - while hist: - head, hist = hist[:256], hist[256:] - assert head[255] == 128 * 128 - assert sum(head[:255]) == 0 - - def test_color_mask_1(self): - for mode in ("RGBA", "RGB", "L"): - im = Image.new(mode, (200, 200), (50, 60, 70, 80)[: len(mode)]) - color = (10, 20, 30, 40)[: len(mode)] - - self.assert_9points_paste( - im, - color, - self.mask_1, - [ - (50, 60, 70, 80), - (50, 60, 70, 80), - (10, 20, 30, 40), - (50, 60, 70, 80), - (50, 60, 70, 80), - (10, 20, 30, 40), - (10, 20, 30, 40), - (10, 20, 30, 40), - (50, 60, 70, 80), - ], - ) - - def test_color_mask_L(self): - for mode in ("RGBA", "RGB", "L"): - im = getattr(self, "gradient_" + mode).copy() - color = "white" - - self.assert_9points_paste( - im, - color, - self.mask_L, - [ - (127, 191, 254, 191), - (111, 207, 206, 110), - (127, 254, 127, 0), - (207, 207, 239, 239), - (191, 191, 190, 191), - (207, 206, 111, 112), - (254, 254, 254, 255), - (239, 206, 206, 238), - (254, 191, 127, 191), - ], - ) - - def test_color_mask_RGBA(self): - for mode in ("RGBA", "RGB", "L"): - im = getattr(self, "gradient_" + mode).copy() - color = "white" - - self.assert_9points_paste( - im, - color, - self.gradient_RGBA, - [ - (127, 191, 254, 191), - (111, 207, 206, 110), - (127, 254, 127, 0), - (207, 207, 239, 239), - (191, 191, 190, 191), - (207, 206, 111, 112), - (254, 254, 254, 255), - (239, 206, 206, 238), - (254, 191, 127, 191), - ], - ) - - def test_color_mask_RGBa(self): - for mode in ("RGBA", "RGB", "L"): - im = getattr(self, "gradient_" + mode).copy() - color = "white" - - self.assert_9points_paste( - im, - color, - self.gradient_RGBa, - [ - (255, 63, 126, 63), - (47, 143, 142, 46), - (126, 253, 126, 255), - (15, 15, 47, 47), - (63, 63, 62, 63), - (142, 141, 46, 47), - (255, 255, 255, 0), - (48, 15, 15, 47), - (126, 63, 255, 63), - ], - ) + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_image_solid(self, mode): + im = Image.new(mode, (200, 200), "red") + im2 = getattr(self, "gradient_" + mode) + + im.paste(im2, (12, 23)) + + im = im.crop((12, 23, im2.width + 12, im2.height + 23)) + assert_image_equal(im, im2) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_image_mask_1(self, mode): + im = Image.new(mode, (200, 200), "white") + im2 = getattr(self, "gradient_" + mode) + + self.assert_9points_paste( + im, + im2, + self.mask_1, + [ + (255, 255, 255, 255), + (255, 255, 255, 255), + (127, 254, 127, 0), + (255, 255, 255, 255), + (255, 255, 255, 255), + (191, 190, 63, 64), + (127, 0, 127, 254), + (191, 64, 63, 190), + (255, 255, 255, 255), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_image_mask_L(self, mode): + im = Image.new(mode, (200, 200), "white") + im2 = getattr(self, "gradient_" + mode) + + self.assert_9points_paste( + im, + im2, + self.mask_L, + [ + (128, 191, 255, 191), + (208, 239, 239, 208), + (255, 255, 255, 255), + (112, 111, 206, 207), + (192, 191, 191, 191), + (239, 239, 207, 207), + (128, 1, 128, 254), + (207, 113, 112, 207), + (255, 191, 128, 191), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_image_mask_LA(self, mode): + im = Image.new(mode, (200, 200), "white") + im2 = getattr(self, "gradient_" + mode) + + self.assert_9points_paste( + im, + im2, + self.gradient_LA, + [ + (128, 191, 255, 191), + (112, 207, 206, 111), + (128, 254, 128, 1), + (208, 208, 239, 239), + (192, 191, 191, 191), + (207, 207, 112, 113), + (255, 255, 255, 255), + (239, 207, 207, 239), + (255, 191, 128, 191), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_image_mask_RGBA(self, mode): + im = Image.new(mode, (200, 200), "white") + im2 = getattr(self, "gradient_" + mode) + + self.assert_9points_paste( + im, + im2, + self.gradient_RGBA, + [ + (128, 191, 255, 191), + (208, 239, 239, 208), + (255, 255, 255, 255), + (112, 111, 206, 207), + (192, 191, 191, 191), + (239, 239, 207, 207), + (128, 1, 128, 254), + (207, 113, 112, 207), + (255, 191, 128, 191), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_image_mask_RGBa(self, mode): + im = Image.new(mode, (200, 200), "white") + im2 = getattr(self, "gradient_" + mode) + + self.assert_9points_paste( + im, + im2, + self.gradient_RGBa, + [ + (128, 255, 126, 255), + (0, 127, 126, 255), + (126, 253, 126, 255), + (128, 127, 254, 255), + (0, 255, 254, 255), + (126, 125, 254, 255), + (128, 1, 128, 255), + (0, 129, 128, 255), + (126, 255, 128, 255), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_color_solid(self, mode): + im = Image.new(mode, (200, 200), "black") + + rect = (12, 23, 128 + 12, 128 + 23) + im.paste("white", rect) + + hist = im.crop(rect).histogram() + while hist: + head, hist = hist[:256], hist[256:] + assert head[255] == 128 * 128 + assert sum(head[:255]) == 0 + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_color_mask_1(self, mode): + im = Image.new(mode, (200, 200), (50, 60, 70, 80)[: len(mode)]) + color = (10, 20, 30, 40)[: len(mode)] + + self.assert_9points_paste( + im, + color, + self.mask_1, + [ + (50, 60, 70, 80), + (50, 60, 70, 80), + (10, 20, 30, 40), + (50, 60, 70, 80), + (50, 60, 70, 80), + (10, 20, 30, 40), + (10, 20, 30, 40), + (10, 20, 30, 40), + (50, 60, 70, 80), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_color_mask_L(self, mode): + im = getattr(self, "gradient_" + mode).copy() + color = "white" + + self.assert_9points_paste( + im, + color, + self.mask_L, + [ + (127, 191, 254, 191), + (111, 207, 206, 110), + (127, 254, 127, 0), + (207, 207, 239, 239), + (191, 191, 190, 191), + (207, 206, 111, 112), + (254, 254, 254, 255), + (239, 206, 206, 238), + (254, 191, 127, 191), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_color_mask_RGBA(self, mode): + im = getattr(self, "gradient_" + mode).copy() + color = "white" + + self.assert_9points_paste( + im, + color, + self.gradient_RGBA, + [ + (127, 191, 254, 191), + (111, 207, 206, 110), + (127, 254, 127, 0), + (207, 207, 239, 239), + (191, 191, 190, 191), + (207, 206, 111, 112), + (254, 254, 254, 255), + (239, 206, 206, 238), + (254, 191, 127, 191), + ], + ) + + @pytest.mark.parametrize("mode", [ + "RGBA", + "RGB", + "L", + ]) + def test_color_mask_RGBa(self, mode): + im = getattr(self, "gradient_" + mode).copy() + color = "white" + + self.assert_9points_paste( + im, + color, + self.gradient_RGBa, + [ + (255, 63, 126, 63), + (47, 143, 142, 46), + (126, 253, 126, 255), + (15, 15, 47, 47), + (63, 63, 62, 63), + (142, 141, 46, 47), + (255, 255, 255, 0), + (48, 15, 15, 47), + (126, 63, 255, 63), + ], + ) def test_different_sizes(self): im = Image.new("RGB", (100, 100)) From 1421f94b6de11800a5b6ecc4ef43e6eaeb039dc8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:25:29 +0000 Subject: [PATCH 316/326] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Tests/test_image_paste.py | 143 +++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 55 deletions(-) diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index bb01ff11067..0b40ba671fb 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -103,11 +103,14 @@ def gradient_RGBa(self): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_image_solid(self, mode): im = Image.new(mode, (200, 200), "red") im2 = getattr(self, "gradient_" + mode) @@ -117,11 +120,14 @@ def test_image_solid(self, mode): im = im.crop((12, 23, im2.width + 12, im2.height + 23)) assert_image_equal(im, im2) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_image_mask_1(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -143,11 +149,14 @@ def test_image_mask_1(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_image_mask_L(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -169,11 +178,14 @@ def test_image_mask_L(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_image_mask_LA(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -195,11 +207,14 @@ def test_image_mask_LA(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_image_mask_RGBA(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -221,11 +236,14 @@ def test_image_mask_RGBA(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_image_mask_RGBa(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -247,11 +265,14 @@ def test_image_mask_RGBa(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_color_solid(self, mode): im = Image.new(mode, (200, 200), "black") @@ -264,11 +285,14 @@ def test_color_solid(self, mode): assert head[255] == 128 * 128 assert sum(head[:255]) == 0 - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_color_mask_1(self, mode): im = Image.new(mode, (200, 200), (50, 60, 70, 80)[: len(mode)]) color = (10, 20, 30, 40)[: len(mode)] @@ -290,11 +314,14 @@ def test_color_mask_1(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_color_mask_L(self, mode): im = getattr(self, "gradient_" + mode).copy() color = "white" @@ -316,11 +343,14 @@ def test_color_mask_L(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_color_mask_RGBA(self, mode): im = getattr(self, "gradient_" + mode).copy() color = "white" @@ -342,11 +372,14 @@ def test_color_mask_RGBA(self, mode): ], ) - @pytest.mark.parametrize("mode", [ - "RGBA", - "RGB", - "L", - ]) + @pytest.mark.parametrize( + "mode", + [ + "RGBA", + "RGB", + "L", + ], + ) def test_color_mask_RGBa(self, mode): im = getattr(self, "gradient_" + mode).copy() color = "white" From b236c61c04c0f6a6cc1ac24f5a56e327e890ad9c Mon Sep 17 00:00:00 2001 From: Yay295 Date: Mon, 22 Aug 2022 13:29:26 -0500 Subject: [PATCH 317/326] make @pytest.mark.parametrize annotations one line --- Tests/test_image_paste.py | 99 +++++---------------------------------- 1 file changed, 11 insertions(+), 88 deletions(-) diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index 0b40ba671fb..1ab02017de1 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -103,14 +103,7 @@ def gradient_RGBa(self): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_image_solid(self, mode): im = Image.new(mode, (200, 200), "red") im2 = getattr(self, "gradient_" + mode) @@ -120,14 +113,7 @@ def test_image_solid(self, mode): im = im.crop((12, 23, im2.width + 12, im2.height + 23)) assert_image_equal(im, im2) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_image_mask_1(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -149,14 +135,7 @@ def test_image_mask_1(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_image_mask_L(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -178,14 +157,7 @@ def test_image_mask_L(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_image_mask_LA(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -207,14 +179,7 @@ def test_image_mask_LA(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_image_mask_RGBA(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -236,14 +201,7 @@ def test_image_mask_RGBA(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_image_mask_RGBa(self, mode): im = Image.new(mode, (200, 200), "white") im2 = getattr(self, "gradient_" + mode) @@ -265,14 +223,7 @@ def test_image_mask_RGBa(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_color_solid(self, mode): im = Image.new(mode, (200, 200), "black") @@ -285,14 +236,7 @@ def test_color_solid(self, mode): assert head[255] == 128 * 128 assert sum(head[:255]) == 0 - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_color_mask_1(self, mode): im = Image.new(mode, (200, 200), (50, 60, 70, 80)[: len(mode)]) color = (10, 20, 30, 40)[: len(mode)] @@ -314,14 +258,7 @@ def test_color_mask_1(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_color_mask_L(self, mode): im = getattr(self, "gradient_" + mode).copy() color = "white" @@ -343,14 +280,7 @@ def test_color_mask_L(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_color_mask_RGBA(self, mode): im = getattr(self, "gradient_" + mode).copy() color = "white" @@ -372,14 +302,7 @@ def test_color_mask_RGBA(self, mode): ], ) - @pytest.mark.parametrize( - "mode", - [ - "RGBA", - "RGB", - "L", - ], - ) + @pytest.mark.parametrize("mode", ["RGBA", "RGB", "L"]) def test_color_mask_RGBa(self, mode): im = getattr(self, "gradient_" + mode).copy() color = "white" From b6b42b8e569ad42686f5522c7e4228fbf68101fd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Aug 2022 07:41:12 +1000 Subject: [PATCH 318/326] Updated libimagequant to 4.0.2 --- depends/install_imagequant.sh | 2 +- docs/installation.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 9b3088b9450..76f4cb95f3f 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -archive=libimagequant-4.0.1 +archive=libimagequant-4.0.2 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/docs/installation.rst b/docs/installation.rst index 42cd7df9d31..a8cd5e4415a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -166,7 +166,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-4.0.1** + * Pillow has been tested with libimagequant **2.6-4.0.2** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. From a3e61c1f89ea726d011683486ce81d6c448a2374 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Aug 2022 09:16:40 +1000 Subject: [PATCH 319/326] Temporarily skip valgrind failure --- Tests/test_file_pdf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 310619fb255..df0b7abe642 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -37,6 +37,7 @@ def helper_save_as_pdf(tmp_path, mode, **kwargs): return outfile +@pytest.mark.valgrind_known_error(reason="Temporary skip") def test_monochrome(tmp_path): # Arrange mode = "1" From 0ed03d4a58d5f31d570fc9fc391298ce032ad7ce Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 23 Aug 2022 21:41:32 +1000 Subject: [PATCH 320/326] Parametrize tests --- Tests/test_file_apng.py | 18 +- Tests/test_file_container.py | 132 ++++++++------- Tests/test_file_im.py | 15 +- Tests/test_file_libtiff.py | 82 ++++----- Tests/test_file_mpo.py | 195 +++++++++++---------- Tests/test_file_tga.py | 77 ++++----- Tests/test_file_wmf.py | 10 +- Tests/test_image.py | 85 +++++----- Tests/test_image_access.py | 35 ++-- Tests/test_image_convert.py | 57 +++---- Tests/test_image_copy.py | 53 +++--- Tests/test_image_crop.py | 19 +-- Tests/test_image_resample.py | 288 +++++++++++++++---------------- Tests/test_image_resize.py | 27 +-- Tests/test_image_rotate.py | 30 ++-- Tests/test_image_transpose.py | 289 +++++++++++++++----------------- Tests/test_imagedraw.py | 20 +-- Tests/test_qt_image_toqimage.py | 58 +++---- 18 files changed, 730 insertions(+), 760 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index ad61a07ccc5..d624bbb849c 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -325,8 +325,9 @@ def open(): pytest.warns(UserWarning, open) -def test_apng_sequence_errors(): - test_files = [ +@pytest.mark.parametrize( + "f", + ( "sequence_start.png", "sequence_gap.png", "sequence_repeat.png", @@ -334,12 +335,13 @@ def test_apng_sequence_errors(): "sequence_reorder.png", "sequence_reorder_chunk.png", "sequence_fdat_fctl.png", - ] - for f in test_files: - with pytest.raises(SyntaxError): - with Image.open(f"Tests/images/apng/{f}") as im: - im.seek(im.n_frames - 1) - im.load() + ), +) +def test_apng_sequence_errors(f): + with pytest.raises(SyntaxError): + with Image.open(f"Tests/images/apng/{f}") as im: + im.seek(im.n_frames - 1) + im.load() def test_apng_save(tmp_path): diff --git a/Tests/test_file_container.py b/Tests/test_file_container.py index b752e217faa..65cf6a75ea3 100644 --- a/Tests/test_file_container.py +++ b/Tests/test_file_container.py @@ -1,3 +1,5 @@ +import pytest + from PIL import ContainerIO, Image from .helper import hopper @@ -59,89 +61,89 @@ def test_seek_mode_2(): assert container.tell() == 100 -def test_read_n0(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_read_n0(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 22, 100) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 22, 100) - # Act - container.seek(81) - data = container.read() + # Act + container.seek(81) + data = container.read() - # Assert - if bytesmode: - data = data.decode() - assert data == "7\nThis is line 8\n" + # Assert + if bytesmode: + data = data.decode() + assert data == "7\nThis is line 8\n" -def test_read_n(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_read_n(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 22, 100) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 22, 100) - # Act - container.seek(81) - data = container.read(3) + # Act + container.seek(81) + data = container.read(3) - # Assert - if bytesmode: - data = data.decode() - assert data == "7\nT" + # Assert + if bytesmode: + data = data.decode() + assert data == "7\nT" -def test_read_eof(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_read_eof(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 22, 100) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 22, 100) - # Act - container.seek(100) - data = container.read() + # Act + container.seek(100) + data = container.read() - # Assert - if bytesmode: - data = data.decode() - assert data == "" + # Assert + if bytesmode: + data = data.decode() + assert data == "" -def test_readline(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_readline(bytesmode): # Arrange - for bytesmode in (True, False): - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 0, 120) + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 0, 120) - # Act - data = container.readline() + # Act + data = container.readline() - # Assert - if bytesmode: - data = data.decode() - assert data == "This is line 1\n" + # Assert + if bytesmode: + data = data.decode() + assert data == "This is line 1\n" -def test_readlines(): +@pytest.mark.parametrize("bytesmode", (True, False)) +def test_readlines(bytesmode): # Arrange - for bytesmode in (True, False): - expected = [ - "This is line 1\n", - "This is line 2\n", - "This is line 3\n", - "This is line 4\n", - "This is line 5\n", - "This is line 6\n", - "This is line 7\n", - "This is line 8\n", - ] - with open(TEST_FILE, "rb" if bytesmode else "r") as fh: - container = ContainerIO.ContainerIO(fh, 0, 120) - - # Act - data = container.readlines() - - # Assert - if bytesmode: - data = [line.decode() for line in data] - assert data == expected + expected = [ + "This is line 1\n", + "This is line 2\n", + "This is line 3\n", + "This is line 4\n", + "This is line 5\n", + "This is line 6\n", + "This is line 7\n", + "This is line 8\n", + ] + with open(TEST_FILE, "rb" if bytesmode else "r") as fh: + container = ContainerIO.ContainerIO(fh, 0, 120) + + # Act + data = container.readlines() + + # Assert + if bytesmode: + data = [line.decode() for line in data] + assert data == expected diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 675210c30d2..e458a197ca4 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -78,15 +78,12 @@ def test_eoferror(): im.seek(n_frames - 1) -def test_roundtrip(tmp_path): - def roundtrip(mode): - out = str(tmp_path / "temp.im") - im = hopper(mode) - im.save(out) - assert_image_equal_tofile(im, out) - - for mode in ["RGB", "P", "PA"]: - roundtrip(mode) +@pytest.mark.parametrize("mode", ("RGB", "P", "PA")) +def test_roundtrip(mode, tmp_path): + out = str(tmp_path / "temp.im") + im = hopper(mode) + im.save(out) + assert_image_equal_tofile(im, out) def test_save_unsupported_mode(tmp_path): diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index f9d8e282647..86a0fda04e5 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -135,50 +135,50 @@ def test_adobe_deflate_tiff(self): assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") - def test_write_metadata(self, tmp_path): + @pytest.mark.parametrize("legacy_api", (False, True)) + def test_write_metadata(self, legacy_api, tmp_path): """Test metadata writing through libtiff""" - for legacy_api in [False, True]: - f = str(tmp_path / "temp.tiff") - with Image.open("Tests/images/hopper_g4.tif") as img: - img.save(f, tiffinfo=img.tag) - - if legacy_api: - original = img.tag.named() - else: - original = img.tag_v2.named() - - # PhotometricInterpretation is set from SAVE_INFO, - # not the original image. - ignored = [ - "StripByteCounts", - "RowsPerStrip", - "PageNumber", - "PhotometricInterpretation", - ] - - with Image.open(f) as loaded: - if legacy_api: - reloaded = loaded.tag.named() - else: - reloaded = loaded.tag_v2.named() - - for tag, value in itertools.chain(reloaded.items(), original.items()): - if tag not in ignored: - val = original[tag] - if tag.endswith("Resolution"): - if legacy_api: - assert val[0][0] / val[0][1] == ( - 4294967295 / 113653537 - ), f"{tag} didn't roundtrip" - else: - assert val == 37.79000115940079, f"{tag} didn't roundtrip" + f = str(tmp_path / "temp.tiff") + with Image.open("Tests/images/hopper_g4.tif") as img: + img.save(f, tiffinfo=img.tag) + + if legacy_api: + original = img.tag.named() + else: + original = img.tag_v2.named() + + # PhotometricInterpretation is set from SAVE_INFO, + # not the original image. + ignored = [ + "StripByteCounts", + "RowsPerStrip", + "PageNumber", + "PhotometricInterpretation", + ] + + with Image.open(f) as loaded: + if legacy_api: + reloaded = loaded.tag.named() + else: + reloaded = loaded.tag_v2.named() + + for tag, value in itertools.chain(reloaded.items(), original.items()): + if tag not in ignored: + val = original[tag] + if tag.endswith("Resolution"): + if legacy_api: + assert val[0][0] / val[0][1] == ( + 4294967295 / 113653537 + ), f"{tag} didn't roundtrip" else: - assert val == value, f"{tag} didn't roundtrip" + assert val == 37.79000115940079, f"{tag} didn't roundtrip" + else: + assert val == value, f"{tag} didn't roundtrip" - # https://github.com/python-pillow/Pillow/issues/1561 - requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"] - for field in requested_fields: - assert field in reloaded, f"{field} not in metadata" + # https://github.com/python-pillow/Pillow/issues/1561 + requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"] + for field in requested_fields: + assert field in reloaded, f"{field} not in metadata" @pytest.mark.valgrind_known_error(reason="Known invalid metadata") def test_additional_metadata(self, tmp_path): diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 849857d31d6..d94bdaa96c9 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -27,13 +27,13 @@ def roundtrip(im, **options): return im -def test_sanity(): - for test_file in test_files: - with Image.open(test_file) as im: - im.load() - assert im.mode == "RGB" - assert im.size == (640, 480) - assert im.format == "MPO" +@pytest.mark.parametrize("test_file", test_files) +def test_sanity(test_file): + with Image.open(test_file) as im: + im.load() + assert im.mode == "RGB" + assert im.size == (640, 480) + assert im.format == "MPO" @pytest.mark.skipif(is_pypy(), reason="Requires CPython") @@ -66,26 +66,25 @@ def test_context_manager(): im.load() -def test_app(): - for test_file in test_files: - # Test APP/COM reader (@PIL135) - with Image.open(test_file) as im: - assert im.applist[0][0] == "APP1" - assert im.applist[1][0] == "APP2" - assert ( - im.applist[1][1][:16] - == b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00" - ) - assert len(im.applist) == 2 +@pytest.mark.parametrize("test_file", test_files) +def test_app(test_file): + # Test APP/COM reader (@PIL135) + with Image.open(test_file) as im: + assert im.applist[0][0] == "APP1" + assert im.applist[1][0] == "APP2" + assert ( + im.applist[1][1][:16] == b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00" + ) + assert len(im.applist) == 2 -def test_exif(): - for test_file in test_files: - with Image.open(test_file) as im: - info = im._getexif() - assert info[272] == "Nintendo 3DS" - assert info[296] == 2 - assert info[34665] == 188 +@pytest.mark.parametrize("test_file", test_files) +def test_exif(test_file): + with Image.open(test_file) as im: + info = im._getexif() + assert info[272] == "Nintendo 3DS" + assert info[296] == 2 + assert info[34665] == 188 def test_frame_size(): @@ -137,12 +136,12 @@ def test_reload_exif_after_seek(): assert 296 in exif -def test_mp(): - for test_file in test_files: - with Image.open(test_file) as im: - mpinfo = im._getmp() - assert mpinfo[45056] == b"0100" - assert mpinfo[45057] == 2 +@pytest.mark.parametrize("test_file", test_files) +def test_mp(test_file): + with Image.open(test_file) as im: + mpinfo = im._getmp() + assert mpinfo[45056] == b"0100" + assert mpinfo[45057] == 2 def test_mp_offset(): @@ -162,48 +161,48 @@ def test_mp_no_data(): im.seek(1) -def test_mp_attribute(): - for test_file in test_files: - with Image.open(test_file) as im: - mpinfo = im._getmp() - frame_number = 0 - for mpentry in mpinfo[0xB002]: - mpattr = mpentry["Attribute"] - if frame_number: - assert not mpattr["RepresentativeImageFlag"] - else: - assert mpattr["RepresentativeImageFlag"] - assert not mpattr["DependentParentImageFlag"] - assert not mpattr["DependentChildImageFlag"] - assert mpattr["ImageDataFormat"] == "JPEG" - assert mpattr["MPType"] == "Multi-Frame Image: (Disparity)" - assert mpattr["Reserved"] == 0 - frame_number += 1 - - -def test_seek(): - for test_file in test_files: - with Image.open(test_file) as im: - assert im.tell() == 0 - # prior to first image raises an error, both blatant and borderline - with pytest.raises(EOFError): - im.seek(-1) - with pytest.raises(EOFError): - im.seek(-523) - # after the final image raises an error, - # both blatant and borderline - with pytest.raises(EOFError): - im.seek(2) - with pytest.raises(EOFError): - im.seek(523) - # bad calls shouldn't change the frame - assert im.tell() == 0 - # this one will work - im.seek(1) - assert im.tell() == 1 - # and this one, too - im.seek(0) - assert im.tell() == 0 +@pytest.mark.parametrize("test_file", test_files) +def test_mp_attribute(test_file): + with Image.open(test_file) as im: + mpinfo = im._getmp() + frame_number = 0 + for mpentry in mpinfo[0xB002]: + mpattr = mpentry["Attribute"] + if frame_number: + assert not mpattr["RepresentativeImageFlag"] + else: + assert mpattr["RepresentativeImageFlag"] + assert not mpattr["DependentParentImageFlag"] + assert not mpattr["DependentChildImageFlag"] + assert mpattr["ImageDataFormat"] == "JPEG" + assert mpattr["MPType"] == "Multi-Frame Image: (Disparity)" + assert mpattr["Reserved"] == 0 + frame_number += 1 + + +@pytest.mark.parametrize("test_file", test_files) +def test_seek(test_file): + with Image.open(test_file) as im: + assert im.tell() == 0 + # prior to first image raises an error, both blatant and borderline + with pytest.raises(EOFError): + im.seek(-1) + with pytest.raises(EOFError): + im.seek(-523) + # after the final image raises an error, + # both blatant and borderline + with pytest.raises(EOFError): + im.seek(2) + with pytest.raises(EOFError): + im.seek(523) + # bad calls shouldn't change the frame + assert im.tell() == 0 + # this one will work + im.seek(1) + assert im.tell() == 1 + # and this one, too + im.seek(0) + assert im.tell() == 0 def test_n_frames(): @@ -225,31 +224,31 @@ def test_eoferror(): im.seek(n_frames - 1) -def test_image_grab(): - for test_file in test_files: - with Image.open(test_file) as im: - assert im.tell() == 0 - im0 = im.tobytes() - im.seek(1) - assert im.tell() == 1 - im1 = im.tobytes() - im.seek(0) - assert im.tell() == 0 - im02 = im.tobytes() - assert im0 == im02 - assert im0 != im1 - - -def test_save(): - for test_file in test_files: - with Image.open(test_file) as im: - assert im.tell() == 0 - jpg0 = roundtrip(im) - assert_image_similar(im, jpg0, 30) - im.seek(1) - assert im.tell() == 1 - jpg1 = roundtrip(im) - assert_image_similar(im, jpg1, 30) +@pytest.mark.parametrize("test_file", test_files) +def test_image_grab(test_file): + with Image.open(test_file) as im: + assert im.tell() == 0 + im0 = im.tobytes() + im.seek(1) + assert im.tell() == 1 + im1 = im.tobytes() + im.seek(0) + assert im.tell() == 0 + im02 = im.tobytes() + assert im0 == im02 + assert im0 != im1 + + +@pytest.mark.parametrize("test_file", test_files) +def test_save(test_file): + with Image.open(test_file) as im: + assert im.tell() == 0 + jpg0 = roundtrip(im) + assert_image_similar(im, jpg0, 30) + im.seek(1) + assert im.tell() == 1 + jpg1 = roundtrip(im) + assert_image_similar(im, jpg1, 30) def test_save_all(): diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index 0c8c9f30485..cbbb7df1d12 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -18,51 +18,48 @@ _ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1} -def test_sanity(tmp_path): - for mode in _MODES: - - def roundtrip(original_im): - out = str(tmp_path / "temp.tga") +@pytest.mark.parametrize("mode", _MODES) +def test_sanity(mode, tmp_path): + def roundtrip(original_im): + out = str(tmp_path / "temp.tga") - original_im.save(out, rle=rle) - with Image.open(out) as saved_im: - if rle: + original_im.save(out, rle=rle) + with Image.open(out) as saved_im: + if rle: + assert saved_im.info["compression"] == original_im.info["compression"] + assert saved_im.info["orientation"] == original_im.info["orientation"] + if mode == "P": + assert saved_im.getpalette() == original_im.getpalette() + + assert_image_equal(saved_im, original_im) + + png_paths = glob(os.path.join(_TGA_DIR_COMMON, f"*x*_{mode.lower()}.png")) + + for png_path in png_paths: + with Image.open(png_path) as reference_im: + assert reference_im.mode == mode + + path_no_ext = os.path.splitext(png_path)[0] + for origin, rle in product(_ORIGINS, (True, False)): + tga_path = "{}_{}_{}.tga".format( + path_no_ext, origin, "rle" if rle else "raw" + ) + + with Image.open(tga_path) as original_im: + assert original_im.format == "TGA" + assert original_im.get_format_mimetype() == "image/x-tga" + if rle: + assert original_im.info["compression"] == "tga_rle" assert ( - saved_im.info["compression"] == original_im.info["compression"] + original_im.info["orientation"] + == _ORIGIN_TO_ORIENTATION[origin] ) - assert saved_im.info["orientation"] == original_im.info["orientation"] - if mode == "P": - assert saved_im.getpalette() == original_im.getpalette() - - assert_image_equal(saved_im, original_im) + if mode == "P": + assert original_im.getpalette() == reference_im.getpalette() - png_paths = glob(os.path.join(_TGA_DIR_COMMON, f"*x*_{mode.lower()}.png")) - - for png_path in png_paths: - with Image.open(png_path) as reference_im: - assert reference_im.mode == mode - - path_no_ext = os.path.splitext(png_path)[0] - for origin, rle in product(_ORIGINS, (True, False)): - tga_path = "{}_{}_{}.tga".format( - path_no_ext, origin, "rle" if rle else "raw" - ) + assert_image_equal(original_im, reference_im) - with Image.open(tga_path) as original_im: - assert original_im.format == "TGA" - assert original_im.get_format_mimetype() == "image/x-tga" - if rle: - assert original_im.info["compression"] == "tga_rle" - assert ( - original_im.info["orientation"] - == _ORIGIN_TO_ORIENTATION[origin] - ) - if mode == "P": - assert original_im.getpalette() == reference_im.getpalette() - - assert_image_equal(original_im, reference_im) - - roundtrip(original_im) + roundtrip(original_im) def test_palette_depth_16(tmp_path): diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index d6769a24b0b..439cb15bca9 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -66,10 +66,10 @@ def test_load_set_dpi(): assert_image_similar_tofile(im, "Tests/images/drawing_wmf_ref_144.png", 2.1) -def test_save(tmp_path): +@pytest.mark.parametrize("ext", (".wmf", ".emf")) +def test_save(ext, tmp_path): im = hopper() - for ext in [".wmf", ".emf"]: - tmpfile = str(tmp_path / ("temp" + ext)) - with pytest.raises(OSError): - im.save(tmpfile) + tmpfile = str(tmp_path / ("temp" + ext)) + with pytest.raises(OSError): + im.save(tmpfile) diff --git a/Tests/test_image.py b/Tests/test_image.py index 6dc89918f05..7cebed127d9 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -22,8 +22,9 @@ class TestImage: - def test_image_modes_success(self): - for mode in [ + @pytest.mark.parametrize( + "mode", + ( "1", "P", "PA", @@ -44,22 +45,18 @@ def test_image_modes_success(self): "YCbCr", "LAB", "HSV", - ]: - Image.new(mode, (1, 1)) + ), + ) + def test_image_modes_success(self, mode): + Image.new(mode, (1, 1)) - def test_image_modes_fail(self): - for mode in [ - "", - "bad", - "very very long", - "BGR;15", - "BGR;16", - "BGR;24", - "BGR;32", - ]: - with pytest.raises(ValueError) as e: - Image.new(mode, (1, 1)) - assert str(e.value) == "unrecognized image mode" + @pytest.mark.parametrize( + "mode", ("", "bad", "very very long", "BGR;15", "BGR;16", "BGR;24", "BGR;32") + ) + def test_image_modes_fail(self, mode): + with pytest.raises(ValueError) as e: + Image.new(mode, (1, 1)) + assert str(e.value) == "unrecognized image mode" def test_exception_inheritance(self): assert issubclass(UnidentifiedImageError, OSError) @@ -539,23 +536,22 @@ def test_linear_gradient_wrong_mode(self): with pytest.raises(ValueError): Image.linear_gradient(wrong_mode) - def test_linear_gradient(self): - + @pytest.mark.parametrize("mode", ("L", "P", "I", "F")) + def test_linear_gradient(self, mode): # Arrange target_file = "Tests/images/linear_gradient.png" - for mode in ["L", "P", "I", "F"]: - # Act - im = Image.linear_gradient(mode) + # Act + im = Image.linear_gradient(mode) - # Assert - assert im.size == (256, 256) - assert im.mode == mode - assert im.getpixel((0, 0)) == 0 - assert im.getpixel((255, 255)) == 255 - with Image.open(target_file) as target: - target = target.convert(mode) - assert_image_equal(im, target) + # Assert + assert im.size == (256, 256) + assert im.mode == mode + assert im.getpixel((0, 0)) == 0 + assert im.getpixel((255, 255)) == 255 + with Image.open(target_file) as target: + target = target.convert(mode) + assert_image_equal(im, target) def test_radial_gradient_wrong_mode(self): # Arrange @@ -565,23 +561,22 @@ def test_radial_gradient_wrong_mode(self): with pytest.raises(ValueError): Image.radial_gradient(wrong_mode) - def test_radial_gradient(self): - + @pytest.mark.parametrize("mode", ("L", "P", "I", "F")) + def test_radial_gradient(self, mode): # Arrange target_file = "Tests/images/radial_gradient.png" - for mode in ["L", "P", "I", "F"]: - - # Act - im = Image.radial_gradient(mode) - - # Assert - assert im.size == (256, 256) - assert im.mode == mode - assert im.getpixel((0, 0)) == 255 - assert im.getpixel((128, 128)) == 0 - with Image.open(target_file) as target: - target = target.convert(mode) - assert_image_equal(im, target) + + # Act + im = Image.radial_gradient(mode) + + # Assert + assert im.size == (256, 256) + assert im.mode == mode + assert im.getpixel((0, 0)) == 255 + assert im.getpixel((128, 128)) == 0 + with Image.open(target_file) as target: + target = target.convert(mode) + assert_image_equal(im, target) def test_register_extensions(self): test_format = "a" diff --git a/Tests/test_image_access.py b/Tests/test_image_access.py index 617274a576d..bb75eb0b5a9 100644 --- a/Tests/test_image_access.py +++ b/Tests/test_image_access.py @@ -184,8 +184,9 @@ def check(self, mode, c=None): with pytest.raises(error): im.getpixel((-1, -1)) - def test_basic(self): - for mode in ( + @pytest.mark.parametrize( + "mode", + ( "1", "L", "LA", @@ -200,23 +201,25 @@ def test_basic(self): "RGBX", "CMYK", "YCbCr", - ): - self.check(mode) + ), + ) + def test_basic(self, mode): + self.check(mode) - def test_signedness(self): + @pytest.mark.parametrize("mode", ("I;16", "I;16B")) + def test_signedness(self, mode): # see https://github.com/python-pillow/Pillow/issues/452 # pixelaccess is using signed int* instead of uint* - for mode in ("I;16", "I;16B"): - self.check(mode, 2**15 - 1) - self.check(mode, 2**15) - self.check(mode, 2**15 + 1) - self.check(mode, 2**16 - 1) - - def test_p_putpixel_rgb_rgba(self): - for color in [(255, 0, 0), (255, 0, 0, 255)]: - im = Image.new("P", (1, 1), 0) - im.putpixel((0, 0), color) - assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0) + self.check(mode, 2**15 - 1) + self.check(mode, 2**15) + self.check(mode, 2**15 + 1) + self.check(mode, 2**16 - 1) + + @pytest.mark.parametrize("color", ((255, 0, 0), (255, 0, 0, 255))) + def test_p_putpixel_rgb_rgba(self, color): + im = Image.new("P", (1, 1), 0) + im.putpixel((0, 0), color) + assert im.convert("RGB").getpixel((0, 0)) == (255, 0, 0) @pytest.mark.skipif(cffi is None, reason="No CFFI") diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index e5639e10533..8f4b8b43c64 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -268,36 +268,33 @@ def test_matrix_wrong_mode(): im.convert(mode="L", matrix=matrix) -def test_matrix_xyz(): - def matrix_convert(mode): - # Arrange - im = hopper("RGB") - im.info["transparency"] = (255, 0, 0) - # fmt: off - matrix = ( - 0.412453, 0.357580, 0.180423, 0, - 0.212671, 0.715160, 0.072169, 0, - 0.019334, 0.119193, 0.950227, 0) - # fmt: on - assert im.mode == "RGB" - - # Act - # Convert an RGB image to the CIE XYZ colour space - converted_im = im.convert(mode=mode, matrix=matrix) - - # Assert - assert converted_im.mode == mode - assert converted_im.size == im.size - with Image.open("Tests/images/hopper-XYZ.png") as target: - if converted_im.mode == "RGB": - assert_image_similar(converted_im, target, 3) - assert converted_im.info["transparency"] == (105, 54, 4) - else: - assert_image_similar(converted_im, target.getchannel(0), 1) - assert converted_im.info["transparency"] == 105 - - matrix_convert("RGB") - matrix_convert("L") +@pytest.mark.parametrize("mode", ("RGB", "L")) +def test_matrix_xyz(mode): + # Arrange + im = hopper("RGB") + im.info["transparency"] = (255, 0, 0) + # fmt: off + matrix = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0) + # fmt: on + assert im.mode == "RGB" + + # Act + # Convert an RGB image to the CIE XYZ colour space + converted_im = im.convert(mode=mode, matrix=matrix) + + # Assert + assert converted_im.mode == mode + assert converted_im.size == im.size + with Image.open("Tests/images/hopper-XYZ.png") as target: + if converted_im.mode == "RGB": + assert_image_similar(converted_im, target, 3) + assert converted_im.info["transparency"] == (105, 54, 4) + else: + assert_image_similar(converted_im, target.getchannel(0), 1) + assert converted_im.info["transparency"] == 105 def test_matrix_identity(): diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py index 21e438654b1..591832147d7 100644 --- a/Tests/test_image_copy.py +++ b/Tests/test_image_copy.py @@ -1,37 +1,40 @@ import copy +import pytest + from PIL import Image from .helper import hopper -def test_copy(): +@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F")) +def test_copy(mode): cropped_coordinates = (10, 10, 20, 20) cropped_size = (10, 10) - for mode in "1", "P", "L", "RGB", "I", "F": - # Internal copy method - im = hopper(mode) - out = im.copy() - assert out.mode == im.mode - assert out.size == im.size - - # Python's copy method - im = hopper(mode) - out = copy.copy(im) - assert out.mode == im.mode - assert out.size == im.size - - # Internal copy method on a cropped image - im = hopper(mode) - out = im.crop(cropped_coordinates).copy() - assert out.mode == im.mode - assert out.size == cropped_size - - # Python's copy method on a cropped image - im = hopper(mode) - out = copy.copy(im.crop(cropped_coordinates)) - assert out.mode == im.mode - assert out.size == cropped_size + + # Internal copy method + im = hopper(mode) + out = im.copy() + assert out.mode == im.mode + assert out.size == im.size + + # Python's copy method + im = hopper(mode) + out = copy.copy(im) + assert out.mode == im.mode + assert out.size == im.size + + # Internal copy method on a cropped image + im = hopper(mode) + out = im.crop(cropped_coordinates).copy() + assert out.mode == im.mode + assert out.size == cropped_size + + # Python's copy method on a cropped image + im = hopper(mode) + out = copy.copy(im.crop(cropped_coordinates)) + assert out.mode == im.mode + assert out.size == cropped_size def test_copy_zero(): diff --git a/Tests/test_image_crop.py b/Tests/test_image_crop.py index 6574e6efd1a..4aa41de2792 100644 --- a/Tests/test_image_crop.py +++ b/Tests/test_image_crop.py @@ -5,17 +5,14 @@ from .helper import assert_image_equal, hopper -def test_crop(): - def crop(mode): - im = hopper(mode) - assert_image_equal(im.crop(), im) - - cropped = im.crop((50, 50, 100, 100)) - assert cropped.mode == mode - assert cropped.size == (50, 50) - - for mode in "1", "P", "L", "RGB", "I", "F": - crop(mode) +@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F")) +def test_crop(mode): + im = hopper(mode) + assert_image_equal(im.crop(), im) + + cropped = im.crop((50, 50, 100, 100)) + assert cropped.mode == mode + assert cropped.size == (50, 50) def test_wide_crop(): diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 6d050efccba..883bb9b195f 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -100,40 +100,41 @@ def serialize_image(self, image): for y in range(image.size[1]) ) - def test_reduce_box(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.Resampling.BOX) - # fmt: off - data = ("e1 e1" - "e1 e1") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_reduce_bilinear(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.Resampling.BILINEAR) - # fmt: off - data = ("e1 c9" - "c9 b7") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_reduce_hamming(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (8, 8), 0xE1) - case = case.resize((4, 4), Image.Resampling.HAMMING) - # fmt: off - data = ("e1 da" - "da d3") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_reduce_bicubic(self): + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_box(self, mode): + case = self.make_case(mode, (8, 8), 0xE1) + case = case.resize((4, 4), Image.Resampling.BOX) + # fmt: off + data = ("e1 e1" + "e1 e1") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_bilinear(self, mode): + case = self.make_case(mode, (8, 8), 0xE1) + case = case.resize((4, 4), Image.Resampling.BILINEAR) + # fmt: off + data = ("e1 c9" + "c9 b7") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_hamming(self, mode): + case = self.make_case(mode, (8, 8), 0xE1) + case = case.resize((4, 4), Image.Resampling.HAMMING) + # fmt: off + data = ("e1 da" + "da d3") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_bicubic(self, mode): for mode in ["RGBX", "RGB", "La", "L"]: case = self.make_case(mode, (12, 12), 0xE1) case = case.resize((6, 6), Image.Resampling.BICUBIC) @@ -145,79 +146,79 @@ def test_reduce_bicubic(self): for channel in case.split(): self.check_case(channel, self.make_sample(data, (6, 6))) - def test_reduce_lanczos(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (16, 16), 0xE1) - case = case.resize((8, 8), Image.Resampling.LANCZOS) - # fmt: off - data = ("e1 e0 e4 d7" - "e0 df e3 d6" - "e4 e3 e7 da" - "d7 d6 d9 ce") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (8, 8))) - - def test_enlarge_box(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.Resampling.BOX) - # fmt: off - data = ("e1 e1" - "e1 e1") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_enlarge_bilinear(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.Resampling.BILINEAR) - # fmt: off - data = ("e1 b0" - "b0 98") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_enlarge_hamming(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (2, 2), 0xE1) - case = case.resize((4, 4), Image.Resampling.HAMMING) - # fmt: off - data = ("e1 d2" - "d2 c5") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (4, 4))) - - def test_enlarge_bicubic(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (4, 4), 0xE1) - case = case.resize((8, 8), Image.Resampling.BICUBIC) - # fmt: off - data = ("e1 e5 ee b9" - "e5 e9 f3 bc" - "ee f3 fd c1" - "b9 bc c1 a2") - # fmt: on - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (8, 8))) - - def test_enlarge_lanczos(self): - for mode in ["RGBX", "RGB", "La", "L"]: - case = self.make_case(mode, (6, 6), 0xE1) - case = case.resize((12, 12), Image.Resampling.LANCZOS) - data = ( - "e1 e0 db ed f5 b8" - "e0 df da ec f3 b7" - "db db d6 e7 ee b5" - "ed ec e6 fb ff bf" - "f5 f4 ee ff ff c4" - "b8 b7 b4 bf c4 a0" - ) - for channel in case.split(): - self.check_case(channel, self.make_sample(data, (12, 12))) + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_reduce_lanczos(self, mode): + case = self.make_case(mode, (16, 16), 0xE1) + case = case.resize((8, 8), Image.Resampling.LANCZOS) + # fmt: off + data = ("e1 e0 e4 d7" + "e0 df e3 d6" + "e4 e3 e7 da" + "d7 d6 d9 ce") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_box(self, mode): + case = self.make_case(mode, (2, 2), 0xE1) + case = case.resize((4, 4), Image.Resampling.BOX) + # fmt: off + data = ("e1 e1" + "e1 e1") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_bilinear(self, mode): + case = self.make_case(mode, (2, 2), 0xE1) + case = case.resize((4, 4), Image.Resampling.BILINEAR) + # fmt: off + data = ("e1 b0" + "b0 98") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_hamming(self, mode): + case = self.make_case(mode, (2, 2), 0xE1) + case = case.resize((4, 4), Image.Resampling.HAMMING) + # fmt: off + data = ("e1 d2" + "d2 c5") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (4, 4))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_bicubic(self, mode): + case = self.make_case(mode, (4, 4), 0xE1) + case = case.resize((8, 8), Image.Resampling.BICUBIC) + # fmt: off + data = ("e1 e5 ee b9" + "e5 e9 f3 bc" + "ee f3 fd c1" + "b9 bc c1 a2") + # fmt: on + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (8, 8))) + + @pytest.mark.parametrize("mode", ("RGBX", "RGB", "La", "L")) + def test_enlarge_lanczos(self, mode): + case = self.make_case(mode, (6, 6), 0xE1) + case = case.resize((12, 12), Image.Resampling.LANCZOS) + data = ( + "e1 e0 db ed f5 b8" + "e0 df da ec f3 b7" + "db db d6 e7 ee b5" + "ed ec e6 fb ff bf" + "f5 f4 ee ff ff c4" + "b8 b7 b4 bf c4 a0" + ) + for channel in case.split(): + self.check_case(channel, self.make_sample(data, (12, 12))) def test_box_filter_correct_range(self): im = Image.new("RGB", (8, 8), "#1688ff").resize( @@ -419,40 +420,43 @@ def test_nonzero_coefficients(self): class TestCoreResampleBox: - def test_wrong_arguments(self): - im = hopper() - for resample in ( + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ): - im.resize((32, 32), resample, (0, 0, im.width, im.height)) - im.resize((32, 32), resample, (20, 20, im.width, im.height)) - im.resize((32, 32), resample, (20, 20, 20, 100)) - im.resize((32, 32), resample, (20, 20, 100, 20)) - - with pytest.raises(TypeError, match="must be sequence of length 4"): - im.resize((32, 32), resample, (im.width, im.height)) - - with pytest.raises(ValueError, match="can't be negative"): - im.resize((32, 32), resample, (-20, 20, 100, 100)) - with pytest.raises(ValueError, match="can't be negative"): - im.resize((32, 32), resample, (20, -20, 100, 100)) - - with pytest.raises(ValueError, match="can't be empty"): - im.resize((32, 32), resample, (20.1, 20, 20, 100)) - with pytest.raises(ValueError, match="can't be empty"): - im.resize((32, 32), resample, (20, 20.1, 100, 20)) - with pytest.raises(ValueError, match="can't be empty"): - im.resize((32, 32), resample, (20.1, 20.1, 20, 20)) - - with pytest.raises(ValueError, match="can't exceed"): - im.resize((32, 32), resample, (0, 0, im.width + 1, im.height)) - with pytest.raises(ValueError, match="can't exceed"): - im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) + ), + ) + def test_wrong_arguments(self, resample): + im = hopper() + im.resize((32, 32), resample, (0, 0, im.width, im.height)) + im.resize((32, 32), resample, (20, 20, im.width, im.height)) + im.resize((32, 32), resample, (20, 20, 20, 100)) + im.resize((32, 32), resample, (20, 20, 100, 20)) + + with pytest.raises(TypeError, match="must be sequence of length 4"): + im.resize((32, 32), resample, (im.width, im.height)) + + with pytest.raises(ValueError, match="can't be negative"): + im.resize((32, 32), resample, (-20, 20, 100, 100)) + with pytest.raises(ValueError, match="can't be negative"): + im.resize((32, 32), resample, (20, -20, 100, 100)) + + with pytest.raises(ValueError, match="can't be empty"): + im.resize((32, 32), resample, (20.1, 20, 20, 100)) + with pytest.raises(ValueError, match="can't be empty"): + im.resize((32, 32), resample, (20, 20.1, 100, 20)) + with pytest.raises(ValueError, match="can't be empty"): + im.resize((32, 32), resample, (20.1, 20.1, 20, 20)) + + with pytest.raises(ValueError, match="can't exceed"): + im.resize((32, 32), resample, (0, 0, im.width + 1, im.height)) + with pytest.raises(ValueError, match="can't exceed"): + im.resize((32, 32), resample, (0, 0, im.width, im.height + 1)) def resize_tiled(self, im, dst_size, xtiles, ytiles): def split_range(size, tiles): @@ -509,14 +513,14 @@ def test_subsample(self): with pytest.raises(AssertionError, match=r"difference 29\."): assert_image_similar(reference, without_box, 5) - def test_formats(self): + @pytest.mark.parametrize("mode", ("RGB", "L", "RGBA", "LA", "I", "")) + def test_formats(self, mode): for resample in [Image.Resampling.NEAREST, Image.Resampling.BILINEAR]: - for mode in ["RGB", "L", "RGBA", "LA", "I", ""]: - im = hopper(mode) - box = (20, 20, im.size[0] - 20, im.size[1] - 20) - with_box = im.resize((32, 32), resample, box) - cropped = im.crop(box).resize((32, 32), resample) - assert_image_similar(cropped, with_box, 0.4) + im = hopper(mode) + box = (20, 20, im.size[0] - 20, im.size[1] - 20) + with_box = im.resize((32, 32), resample, box) + cropped = im.crop(box).resize((32, 32), resample) + assert_image_similar(cropped, with_box, 0.4) def test_passthrough(self): # When no resize is required diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 8347fabb9e5..ae12202e4ed 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -22,24 +22,15 @@ def resize(self, im, size, f): im.load() return im._new(im.im.resize(size, f)) - def test_nearest_mode(self): - for mode in [ - "1", - "P", - "L", - "I", - "F", - "RGB", - "RGBA", - "CMYK", - "YCbCr", - "I;16", - ]: # exotic mode - im = hopper(mode) - r = self.resize(im, (15, 12), Image.Resampling.NEAREST) - assert r.mode == mode - assert r.size == (15, 12) - assert r.im.bands == im.im.bands + @pytest.mark.parametrize( + "mode", ("1", "P", "L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr", "I;16") + ) + def test_nearest_mode(self, mode): + im = hopper(mode) + r = self.resize(im, (15, 12), Image.Resampling.NEAREST) + assert r.mode == mode + assert r.size == (15, 12) + assert r.im.bands == im.im.bands def test_convolution_modes(self): with pytest.raises(ValueError): diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index f96864c53df..a19f19831fd 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -1,3 +1,5 @@ +import pytest + from PIL import Image from .helper import ( @@ -22,25 +24,25 @@ def rotate(im, mode, angle, center=None, translate=None): assert out.size != im.size -def test_mode(): - for mode in ("1", "P", "L", "RGB", "I", "F"): - im = hopper(mode) - rotate(im, mode, 45) +@pytest.mark.parametrize("mode", ("1", "P", "L", "RGB", "I", "F")) +def test_mode(mode): + im = hopper(mode) + rotate(im, mode, 45) -def test_angle(): - for angle in (0, 90, 180, 270): - with Image.open("Tests/images/test-card.png") as im: - rotate(im, im.mode, angle) +@pytest.mark.parametrize("angle", (0, 90, 180, 270)) +def test_angle(angle): + with Image.open("Tests/images/test-card.png") as im: + rotate(im, im.mode, angle) - im = hopper() - assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1)) + im = hopper() + assert_image_equal(im.rotate(angle), im.rotate(angle, expand=1)) -def test_zero(): - for angle in (0, 45, 90, 180, 270): - im = Image.new("RGB", (0, 0)) - rotate(im, im.mode, angle) +@pytest.mark.parametrize("angle", (0, 45, 90, 180, 270)) +def test_zero(angle): + im = Image.new("RGB", (0, 0)) + rotate(im, im.mode, angle) def test_resample(): diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 6408e156491..877f439ca26 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,3 +1,5 @@ +import pytest + from PIL.Image import Transpose from . import helper @@ -9,157 +11,136 @@ } -def test_flip_left_right(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.FLIP_LEFT_RIGHT) - assert out.mode == mode - assert out.size == im.size - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((x - 2, 1)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) - assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, y - 2)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, y - 2)) - - for mode in HOPPER: - transpose(mode) - - -def test_flip_top_bottom(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.FLIP_TOP_BOTTOM) - assert out.mode == mode - assert out.size == im.size - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((1, y - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((x - 2, y - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((x - 2, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_rotate_90(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.ROTATE_90) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((1, x - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) - assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, x - 2)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_rotate_180(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.ROTATE_180) - assert out.mode == mode - assert out.size == im.size - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((x - 2, y - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, y - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_rotate_270(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.ROTATE_270) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((y - 2, 1)) - assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, x - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, x - 2)) - - for mode in HOPPER: - transpose(mode) - - -def test_transpose(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.TRANSPOSE) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((1, 1)) - assert im.getpixel((x - 2, 1)) == out.getpixel((1, x - 2)) - assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, 1)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, x - 2)) - - for mode in HOPPER: - transpose(mode) - - -def test_tranverse(): - def transpose(mode): - im = HOPPER[mode] - out = im.transpose(Transpose.TRANSVERSE) - assert out.mode == mode - assert out.size == im.size[::-1] - - x, y = im.size - assert im.getpixel((1, 1)) == out.getpixel((y - 2, x - 2)) - assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, 1)) - assert im.getpixel((1, y - 2)) == out.getpixel((1, x - 2)) - assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) - - for mode in HOPPER: - transpose(mode) - - -def test_roundtrip(): - for mode in HOPPER: - im = HOPPER[mode] - - def transpose(first, second): - return im.transpose(first).transpose(second) - - assert_image_equal( - im, transpose(Transpose.FLIP_LEFT_RIGHT, Transpose.FLIP_LEFT_RIGHT) - ) - assert_image_equal( - im, transpose(Transpose.FLIP_TOP_BOTTOM, Transpose.FLIP_TOP_BOTTOM) - ) - assert_image_equal(im, transpose(Transpose.ROTATE_90, Transpose.ROTATE_270)) - assert_image_equal(im, transpose(Transpose.ROTATE_180, Transpose.ROTATE_180)) - assert_image_equal( - im.transpose(Transpose.TRANSPOSE), - transpose(Transpose.ROTATE_90, Transpose.FLIP_TOP_BOTTOM), - ) - assert_image_equal( - im.transpose(Transpose.TRANSPOSE), - transpose(Transpose.ROTATE_270, Transpose.FLIP_LEFT_RIGHT), - ) - assert_image_equal( - im.transpose(Transpose.TRANSVERSE), - transpose(Transpose.ROTATE_90, Transpose.FLIP_LEFT_RIGHT), - ) - assert_image_equal( - im.transpose(Transpose.TRANSVERSE), - transpose(Transpose.ROTATE_270, Transpose.FLIP_TOP_BOTTOM), - ) - assert_image_equal( - im.transpose(Transpose.TRANSVERSE), - transpose(Transpose.ROTATE_180, Transpose.TRANSPOSE), - ) +@pytest.mark.parametrize("mode", HOPPER) +def test_flip_left_right(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.FLIP_LEFT_RIGHT) + assert out.mode == mode + assert out.size == im.size + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((x - 2, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, y - 2)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_flip_top_bottom(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.FLIP_TOP_BOTTOM) + assert out.mode == mode + assert out.size == im.size + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, y - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((x - 2, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_rotate_90(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.ROTATE_90) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, x - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_rotate_180(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.ROTATE_180) + assert out.mode == mode + assert out.size == im.size + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((x - 2, y - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, y - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((x - 2, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_rotate_270(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.ROTATE_270) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((y - 2, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, x - 2)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_transpose(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.TRANSPOSE) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((1, 1)) + assert im.getpixel((x - 2, 1)) == out.getpixel((1, x - 2)) + assert im.getpixel((1, y - 2)) == out.getpixel((y - 2, 1)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((y - 2, x - 2)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_tranverse(mode): + im = HOPPER[mode] + out = im.transpose(Transpose.TRANSVERSE) + assert out.mode == mode + assert out.size == im.size[::-1] + + x, y = im.size + assert im.getpixel((1, 1)) == out.getpixel((y - 2, x - 2)) + assert im.getpixel((x - 2, 1)) == out.getpixel((y - 2, 1)) + assert im.getpixel((1, y - 2)) == out.getpixel((1, x - 2)) + assert im.getpixel((x - 2, y - 2)) == out.getpixel((1, 1)) + + +@pytest.mark.parametrize("mode", HOPPER) +def test_roundtrip(mode): + im = HOPPER[mode] + + def transpose(first, second): + return im.transpose(first).transpose(second) + + assert_image_equal( + im, transpose(Transpose.FLIP_LEFT_RIGHT, Transpose.FLIP_LEFT_RIGHT) + ) + assert_image_equal( + im, transpose(Transpose.FLIP_TOP_BOTTOM, Transpose.FLIP_TOP_BOTTOM) + ) + assert_image_equal(im, transpose(Transpose.ROTATE_90, Transpose.ROTATE_270)) + assert_image_equal(im, transpose(Transpose.ROTATE_180, Transpose.ROTATE_180)) + assert_image_equal( + im.transpose(Transpose.TRANSPOSE), + transpose(Transpose.ROTATE_90, Transpose.FLIP_TOP_BOTTOM), + ) + assert_image_equal( + im.transpose(Transpose.TRANSPOSE), + transpose(Transpose.ROTATE_270, Transpose.FLIP_LEFT_RIGHT), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_90, Transpose.FLIP_LEFT_RIGHT), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_270, Transpose.FLIP_TOP_BOTTOM), + ) + assert_image_equal( + im.transpose(Transpose.TRANSVERSE), + transpose(Transpose.ROTATE_180, Transpose.TRANSPOSE), + ) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 961b4d08130..d1dd1e47c1c 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -625,20 +625,20 @@ def test_polygon2(): helper_polygon(POINTS2) -def test_polygon_kite(): +@pytest.mark.parametrize("mode", ("RGB", "L")) +def test_polygon_kite(mode): # Test drawing lines of different gradients (dx>dy, dy>dx) and # vertical (dx==0) and horizontal (dy==0) lines - for mode in ["RGB", "L"]: - # Arrange - im = Image.new(mode, (W, H)) - draw = ImageDraw.Draw(im) - expected = f"Tests/images/imagedraw_polygon_kite_{mode}.png" + # Arrange + im = Image.new(mode, (W, H)) + draw = ImageDraw.Draw(im) + expected = f"Tests/images/imagedraw_polygon_kite_{mode}.png" - # Act - draw.polygon(KITE_POINTS, fill="blue", outline="yellow") + # Act + draw.polygon(KITE_POINTS, fill="blue", outline="yellow") - # Assert - assert_image_equal_tofile(im, expected) + # Assert + assert_image_equal_tofile(im, expected) def test_polygon_1px_high(): diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index 60bfaeb9b75..af0b0c2935f 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -16,32 +16,32 @@ from PIL.ImageQt import QImage -def test_sanity(tmp_path): - for mode in ("RGB", "RGBA", "L", "P", "1"): - src = hopper(mode) - data = ImageQt.toqimage(src) - - assert isinstance(data, QImage) - assert not data.isNull() - - # reload directly from the qimage - rt = ImageQt.fromqimage(data) - if mode in ("L", "P", "1"): - assert_image_equal(rt, src.convert("RGB")) - else: - assert_image_equal(rt, src) - - if mode == "1": - # BW appears to not save correctly on QT4 and QT5 - # kicks out errors on console: - # libpng warning: Invalid color type/bit depth combination - # in IHDR - # libpng error: Invalid IHDR data - continue - - # Test saving the file - tempfile = str(tmp_path / f"temp_{mode}.png") - data.save(tempfile) - - # Check that it actually worked. - assert_image_equal_tofile(src, tempfile) +@pytest.mark.parametrize("mode", ("RGB", "RGBA", "L", "P", "1")) +def test_sanity(mode, tmp_path): + src = hopper(mode) + data = ImageQt.toqimage(src) + + assert isinstance(data, QImage) + assert not data.isNull() + + # reload directly from the qimage + rt = ImageQt.fromqimage(data) + if mode in ("L", "P", "1"): + assert_image_equal(rt, src.convert("RGB")) + else: + assert_image_equal(rt, src) + + if mode == "1": + # BW appears to not save correctly on QT4 and QT5 + # kicks out errors on console: + # libpng warning: Invalid color type/bit depth combination + # in IHDR + # libpng error: Invalid IHDR data + return + + # Test saving the file + tempfile = str(tmp_path / f"temp_{mode}.png") + data.save(tempfile) + + # Check that it actually worked. + assert_image_equal_tofile(src, tempfile) From 1c391fe31f902b604a7bc4ebd9b4315fa5ef8e1f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Aug 2022 08:11:02 +1000 Subject: [PATCH 321/326] Renamed argument --- Tests/test_file_apng.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/test_file_apng.py b/Tests/test_file_apng.py index d624bbb849c..0ff05f608c2 100644 --- a/Tests/test_file_apng.py +++ b/Tests/test_file_apng.py @@ -326,7 +326,7 @@ def open(): @pytest.mark.parametrize( - "f", + "test_file", ( "sequence_start.png", "sequence_gap.png", @@ -337,9 +337,9 @@ def open(): "sequence_fdat_fctl.png", ), ) -def test_apng_sequence_errors(f): +def test_apng_sequence_errors(test_file): with pytest.raises(SyntaxError): - with Image.open(f"Tests/images/apng/{f}") as im: + with Image.open(f"Tests/images/apng/{test_file}") as im: im.seek(im.n_frames - 1) im.load() From 8f25ea46ebd471c48eb424c8754ea1747a54776a Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Wed, 24 Aug 2022 08:12:14 +1000 Subject: [PATCH 322/326] Qt4 is no longer supported Co-authored-by: Hugo van Kemenade --- Tests/test_qt_image_toqimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index af0b0c2935f..c1983031a14 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -32,7 +32,7 @@ def test_sanity(mode, tmp_path): assert_image_equal(rt, src) if mode == "1": - # BW appears to not save correctly on QT4 and QT5 + # BW appears to not save correctly on QT5 # kicks out errors on console: # libpng warning: Invalid color type/bit depth combination # in IHDR From 3353ea80e1c873acdb11636cf3d387b8e59580c4 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Aug 2022 10:37:40 +1000 Subject: [PATCH 323/326] Further parametrizations --- Tests/test_image_resample.py | 16 ++- Tests/test_image_resize.py | 241 ++++++++++++++++++----------------- 2 files changed, 135 insertions(+), 122 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 883bb9b195f..5ce98a23568 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -514,13 +514,15 @@ def test_subsample(self): assert_image_similar(reference, without_box, 5) @pytest.mark.parametrize("mode", ("RGB", "L", "RGBA", "LA", "I", "")) - def test_formats(self, mode): - for resample in [Image.Resampling.NEAREST, Image.Resampling.BILINEAR]: - im = hopper(mode) - box = (20, 20, im.size[0] - 20, im.size[1] - 20) - with_box = im.resize((32, 32), resample, box) - cropped = im.crop(box).resize((32, 32), resample) - assert_image_similar(cropped, with_box, 0.4) + @pytest.mark.parametrize( + "resample", (Image.Resampling.NEAREST, Image.Resampling.BILINEAR) + ) + def test_formats(self, mode, resample): + im = hopper(mode) + box = (20, 20, im.size[0] - 20, im.size[1] - 20) + with_box = im.resize((32, 32), resample, box) + cropped = im.crop(box).resize((32, 32), resample) + assert_image_similar(cropped, with_box, 0.4) def test_passthrough(self): # When no resize is required diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index ae12202e4ed..83c54cf6211 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -46,33 +46,58 @@ def test_convolution_modes(self): assert r.size == (15, 12) assert r.im.bands == im.im.bands - def test_reduce_filters(self): - for f in [ + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ]: - r = self.resize(hopper("RGB"), (15, 12), f) - assert r.mode == "RGB" - assert r.size == (15, 12) + ), + ) + def test_reduce_filters(self, resample): + r = self.resize(hopper("RGB"), (15, 12), resample) + assert r.mode == "RGB" + assert r.size == (15, 12) - def test_enlarge_filters(self): - for f in [ + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ]: - r = self.resize(hopper("RGB"), (212, 195), f) - assert r.mode == "RGB" - assert r.size == (212, 195) + ), + ) + def test_enlarge_filters(self, resample): + r = self.resize(hopper("RGB"), (212, 195), resample) + assert r.mode == "RGB" + assert r.size == (212, 195) - def test_endianness(self): + @pytest.mark.parametrize( + "resample", + ( + Image.Resampling.NEAREST, + Image.Resampling.BOX, + Image.Resampling.BILINEAR, + Image.Resampling.HAMMING, + Image.Resampling.BICUBIC, + Image.Resampling.LANCZOS, + ), + ) + @pytest.mark.parametrize( + "mode, channels_set", + ( + ("RGB", ("blank", "filled", "dirty")), + ("RGBA", ("blank", "blank", "filled", "dirty")), + ("LA", ("filled", "dirty")), + ), + ) + def test_endianness(self, resample, mode, channels_set): # Make an image with one colored pixel, in one channel. # When resized, that channel should be the same as a GS image. # Other channels should be unaffected. @@ -86,47 +111,37 @@ def test_endianness(self): } samples["dirty"].putpixel((1, 1), 128) - for f in [ - Image.Resampling.NEAREST, - Image.Resampling.BOX, - Image.Resampling.BILINEAR, - Image.Resampling.HAMMING, - Image.Resampling.BICUBIC, - Image.Resampling.LANCZOS, - ]: - # samples resized with current filter - references = { - name: self.resize(ch, (4, 4), f) for name, ch in samples.items() - } - - for mode, channels_set in [ - ("RGB", ("blank", "filled", "dirty")), - ("RGBA", ("blank", "blank", "filled", "dirty")), - ("LA", ("filled", "dirty")), - ]: - for channels in set(permutations(channels_set)): - # compile image from different channels permutations - im = Image.merge(mode, [samples[ch] for ch in channels]) - resized = self.resize(im, (4, 4), f) - - for i, ch in enumerate(resized.split()): - # check what resized channel in image is the same - # as separately resized channel - assert_image_equal(ch, references[channels[i]]) - - def test_enlarge_zero(self): - for f in [ + # samples resized with current filter + references = { + name: self.resize(ch, (4, 4), resample) for name, ch in samples.items() + } + + for channels in set(permutations(channels_set)): + # compile image from different channels permutations + im = Image.merge(mode, [samples[ch] for ch in channels]) + resized = self.resize(im, (4, 4), resample) + + for i, ch in enumerate(resized.split()): + # check what resized channel in image is the same + # as separately resized channel + assert_image_equal(ch, references[channels[i]]) + + @pytest.mark.parametrize( + "resample", + ( Image.Resampling.NEAREST, Image.Resampling.BOX, Image.Resampling.BILINEAR, Image.Resampling.HAMMING, Image.Resampling.BICUBIC, Image.Resampling.LANCZOS, - ]: - r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), f) - assert r.mode == "RGB" - assert r.size == (212, 195) - assert r.getdata()[0] == (0, 0, 0) + ), + ) + def test_enlarge_zero(self, resample): + r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), resample) + assert r.mode == "RGB" + assert r.size == (212, 195) + assert r.getdata()[0] == (0, 0, 0) def test_unknown_filter(self): with pytest.raises(ValueError): @@ -170,74 +185,71 @@ def test_reducing_gap_values(self, gradients_image): (52, 34), Image.Resampling.BICUBIC, reducing_gap=0.99 ) - def test_reducing_gap_1(self, gradients_image): - for box, epsilon in [ - (None, 4), - ((1.1, 2.2, 510.8, 510.9), 4), - ((3, 10, 410, 256), 10), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=1.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + ((None, 4), ((1.1, 2.2, 510.8, 510.9), 4), ((3, 10, 410, 256), 10)), + ) + def test_reducing_gap_1(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=1.0 + ) - with pytest.raises(AssertionError): - assert_image_equal(ref, im) + with pytest.raises(AssertionError): + assert_image_equal(ref, im) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) - def test_reducing_gap_2(self, gradients_image): - for box, epsilon in [ - (None, 1.5), - ((1.1, 2.2, 510.8, 510.9), 1.5), - ((3, 10, 410, 256), 1), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=2.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + ((None, 1.5), ((1.1, 2.2, 510.8, 510.9), 1.5), ((3, 10, 410, 256), 1)), + ) + def test_reducing_gap_2(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=2.0 + ) - with pytest.raises(AssertionError): - assert_image_equal(ref, im) + with pytest.raises(AssertionError): + assert_image_equal(ref, im) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) - def test_reducing_gap_3(self, gradients_image): - for box, epsilon in [ - (None, 1), - ((1.1, 2.2, 510.8, 510.9), 1), - ((3, 10, 410, 256), 0.5), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=3.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + ((None, 1), ((1.1, 2.2, 510.8, 510.9), 1), ((3, 10, 410, 256), 0.5)), + ) + def test_reducing_gap_3(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=3.0 + ) - with pytest.raises(AssertionError): - assert_image_equal(ref, im) + with pytest.raises(AssertionError): + assert_image_equal(ref, im) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) - def test_reducing_gap_8(self, gradients_image): - for box in [None, (1.1, 2.2, 510.8, 510.9), (3, 10, 410, 256)]: - ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=8.0 - ) + @pytest.mark.parametrize("box", (None, (1.1, 2.2, 510.8, 510.9), (3, 10, 410, 256))) + def test_reducing_gap_8(self, gradients_image, box): + ref = gradients_image.resize((52, 34), Image.Resampling.BICUBIC, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BICUBIC, box=box, reducing_gap=8.0 + ) - assert_image_equal(ref, im) + assert_image_equal(ref, im) - def test_box_filter(self, gradients_image): - for box, epsilon in [ - ((0, 0, 512, 512), 5.5), - ((0.9, 1.7, 128, 128), 9.5), - ]: - ref = gradients_image.resize((52, 34), Image.Resampling.BOX, box=box) - im = gradients_image.resize( - (52, 34), Image.Resampling.BOX, box=box, reducing_gap=1.0 - ) + @pytest.mark.parametrize( + "box, epsilon", + (((0, 0, 512, 512), 5.5), ((0.9, 1.7, 128, 128), 9.5)), + ) + def test_box_filter(self, gradients_image, box, epsilon): + ref = gradients_image.resize((52, 34), Image.Resampling.BOX, box=box) + im = gradients_image.resize( + (52, 34), Image.Resampling.BOX, box=box, reducing_gap=1.0 + ) - assert_image_similar(ref, im, epsilon) + assert_image_similar(ref, im, epsilon) class TestImageResize: @@ -264,15 +276,14 @@ def test_load_first(self): im = im.resize((64, 64)) assert im.size == (64, 64) - def test_default_filter(self): - for mode in "L", "RGB", "I", "F": - im = hopper(mode) - assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20)) - - for mode in "1", "P": - im = hopper(mode) - assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) + @pytest.mark.parametrize("mode", ("L", "RGB", "I", "F")) + def test_default_filter_bicubic(self, mode): + im = hopper(mode) + assert im.resize((20, 20), Image.Resampling.BICUBIC) == im.resize((20, 20)) - for mode in "I;16", "I;16L", "I;16B", "BGR;15", "BGR;16": - im = hopper(mode) - assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) + @pytest.mark.parametrize( + "mode", ("1", "P", "I;16", "I;16L", "I;16B", "BGR;15", "BGR;16") + ) + def test_default_filter_nearest(self, mode): + im = hopper(mode) + assert im.resize((20, 20), Image.Resampling.NEAREST) == im.resize((20, 20)) From 56ba3ff68c678d0bf5f483b08f8c7428009ac226 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 24 Aug 2022 15:39:43 +1000 Subject: [PATCH 324/326] Build lcms2 VC2022 --- winbuild/build_prepare.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index a381d636dd8..94e5dd87114 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -226,21 +226,21 @@ def cmd_msbuild( "filename": "lcms2-2.13.1.tar.gz", "dir": "lcms2-2.13.1", "patch": { - r"Projects\VC2019\lcms2_static\lcms2_static.vcxproj": { + r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": { # default is /MD for x86 and /MT for x64, we need /MD always "MultiThreaded": "MultiThreadedDLL", # noqa: E501 # retarget to default toolset (selected by vcvarsall.bat) - "v142": "$(DefaultPlatformToolset)", # noqa: E501 + "v143": "$(DefaultPlatformToolset)", # noqa: E501 # retarget to latest (selected by vcvarsall.bat) "10.0": "$(WindowsSDKVersion)", # noqa: E501 } }, "build": [ cmd_rmdir("Lib"), - cmd_rmdir(r"Projects\VC2019\Release"), - cmd_msbuild(r"Projects\VC2019\lcms2.sln", "Release", "Clean"), + cmd_rmdir(r"Projects\VC2022\Release"), + cmd_msbuild(r"Projects\VC2022\lcms2.sln", "Release", "Clean"), cmd_msbuild( - r"Projects\VC2019\lcms2.sln", "Release", "lcms2_static:Rebuild" + r"Projects\VC2022\lcms2.sln", "Release", "lcms2_static:Rebuild" ), cmd_xcopy("include", "{inc_dir}"), ], From 5a38c7f95357e29b05edadcfd86a78eec1cc6ed9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 25 Aug 2022 13:05:21 +1000 Subject: [PATCH 325/326] Updated libimagequant to 4.0.4 --- depends/install_imagequant.sh | 2 +- docs/installation.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/depends/install_imagequant.sh b/depends/install_imagequant.sh index 76f4cb95f3f..64dd024bd7f 100755 --- a/depends/install_imagequant.sh +++ b/depends/install_imagequant.sh @@ -1,7 +1,7 @@ #!/bin/bash # install libimagequant -archive=libimagequant-4.0.2 +archive=libimagequant-4.0.4 ./download-and-extract.sh $archive https://raw.githubusercontent.com/python-pillow/pillow-depends/main/$archive.tar.gz diff --git a/docs/installation.rst b/docs/installation.rst index a8cd5e4415a..bb547c1adaa 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -166,7 +166,7 @@ Many of Pillow's features require external libraries: * **libimagequant** provides improved color quantization - * Pillow has been tested with libimagequant **2.6-4.0.2** + * Pillow has been tested with libimagequant **2.6-4.0.4** * Libimagequant is licensed GPLv3, which is more restrictive than the Pillow license, therefore we will not be distributing binaries with libimagequant support enabled. From ac83011fbf91341ec50761784a8d4e4c5baae9f2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 26 Aug 2022 18:09:18 +1000 Subject: [PATCH 326/326] NumPy now supports Python 3.11 --- .ci/install.sh | 3 +-- .github/workflows/macos-install.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index 7ead209bec2..518b66acc23 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -37,8 +37,7 @@ python3 -m pip install -U pytest-timeout python3 -m pip install pyroma if [[ $(uname) != CYGWIN* ]]; then - # TODO Remove condition when NumPy supports 3.11 - if ! [ "$GHA_PYTHON_VERSION" == "3.11-dev" ]; then python3 -m pip install numpy ; fi + python3 -m pip install numpy # PyQt6 doesn't support PyPy3 if [[ $GHA_PYTHON_VERSION == 3.* ]]; then diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index bb0bcd6803e..65f2b81d543 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -14,8 +14,7 @@ python3 -m pip install -U pytest-timeout python3 -m pip install pyroma echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg -# TODO Remove condition when NumPy supports 3.11 -if ! [ "$GHA_PYTHON_VERSION" == "3.11-dev" ]; then python3 -m pip install numpy ; fi +python3 -m pip install numpy # extra test images pushd depends && ./install_extra_test_images.sh && popd