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/294] 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 000000000..4275e65a6 --- /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/294] 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 4275e65a6..c22fe2bec 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/294] 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 c22fe2bec..cb0c84ef5 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/294] 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 cb0c84ef5..904658f80 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/294] 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 904658f80..c1404b2c6 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/294] 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 c1404b2c6..ff496443b 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/294] 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 ff496443b..947110514 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/294] 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 947110514..d7c74e7bc 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 1fb6897d2..3e5123c7a 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/294] 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 d7c74e7bc..24bd6a2be 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/294] 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 24bd6a2be..8bb3e4cdd 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/294] 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 8bb3e4cdd..6215b72f8 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/294] 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 8ff7c5f64..b0f019d66 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 6215b72f8..bbe5eccee 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/294] 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 bbe5eccee..d3ab0a361 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/294] 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 d3ab0a361..b6a305b71 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 def7adf3f..936474fe8 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/294] 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 3e5123c7a..d77e2742f 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/294] 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 b6a305b71..f43460293 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/294] 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 f43460293..10b83fe88 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/294] 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 10b83fe88..162522c0a 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/294] 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 162522c0a..ca18309be 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/294] 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 53832c573..23a6fcd4d 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 ca18309be..022ef4808 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/294] 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 022ef4808..77d55682f 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/294] 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 8b289bda6..8dfa07f4e 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 77d55682f..b78e1b231 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/294] 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 b78e1b231..f4fbfd1f8 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/294] Install Pillow directly --- .ci/build_cygwin.sh | 8 +++++ .ci/install.sh | 59 ++++++++++++++++++------------- .github/workflows/test-cygwin.yml | 19 +++------- 3 files changed, 47 insertions(+), 39 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 000000000..d621a5336 --- /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 efc57a641..ec427ff27 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 +if [[ $(uname) != CYGWIN* ]]; then + PYTHONOPTIMIZE=0 python3 -m pip install cffi + 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 + + # 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 - -# webp -pushd depends && ./install_webp.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 diff --git a/.github/workflows/test-cygwin.yml b/.github/workflows/test-cygwin.yml index f4fbfd1f8..6e2dc5b59 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/294] 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 6e2dc5b59..2dcdecd30 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/294] 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 2dcdecd30..e169a3141 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/294] 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 e169a3141..3b1332542 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/294] 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 f8829fc21..d76c26090 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -269,7 +269,7 @@ class DisplayViewer(UnixViewer): 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/294] 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 b0f019d66..8ff7c5f64 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/294] 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 9d32927d4..c94b27a47 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -23,21 +23,25 @@ from . import Image, ImageFile 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmImageFile(ImageFile.ImageFile): 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/294] 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 c94b27a47..6bebec6de 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -142,6 +142,108 @@ class PpmImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- +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/294] 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 6bebec6de..86616b89f 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -217,7 +217,68 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 | Bin 0 -> 167160 bytes Tests/test_file_ppm.py | 152 ++++++++++++++++++++++++++-- 11 files changed, 168 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 000000000..a48ab5544 --- /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 000000000..9f5c6e501 --- /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 000000000..37d152c73 --- /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 0000000000000000000000000000000000000000..c39a92a0bd6cf924c42777c04967a29d323fed3f GIT binary patch literal 167160 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 GLifMZZ->GF literal 0 HcmV?d00001 diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 0e4f1ba68..40ff08c99 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -3,9 +3,14 @@ from io import BytesIO 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/294] 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 86616b89f..c26d86362 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/294] 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 40ff08c99..d34aa738b 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/294] 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 c26d86362..412376982 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -24,9 +24,9 @@ b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" 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/294] 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 d34aa738b..1832e3e46 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -5,12 +5,7 @@ import pytest 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/294] [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 1832e3e46..ddeded6fb 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 9a5cac7d0..cae078d02 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -145,7 +145,7 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 ddeded6fb..8fe68d33e 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 cae078d02..497b98395 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -23,20 +23,19 @@ from . import Image, ImageFile 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 497b98395..0a71b6fb6 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -196,16 +196,13 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 0a71b6fb6..0f3cbcc4b 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -250,12 +250,7 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 0f3cbcc4b..c73a814eb 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -174,11 +174,10 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 c73a814eb..0e19cab07 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -174,13 +174,14 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 0e19cab07..d238cd26c 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 d238cd26c..73f796b94 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -100,6 +100,7 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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/294] 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 dffd1006f..9caea84a8 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 b798bb969..fad6cdf1b 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 4cb8e4b3fae524924b3cd27b27be1818f644ae61 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 5 Apr 2022 16:33:20 +0300 Subject: [PATCH 047/294] Add deprecations helper --- Tests/test_deprecate.py | 70 +++++++++++++++++++++++++++++ docs/reference/internal_modules.rst | 8 ++++ src/PIL/BlpImagePlugin.py | 17 +------ src/PIL/FitsStubImagePlugin.py | 13 +++--- src/PIL/FtexImagePlugin.py | 17 +------ src/PIL/Image.py | 48 +++----------------- src/PIL/ImageCms.py | 18 ++------ src/PIL/ImageFont.py | 18 +------- src/PIL/ImagePalette.py | 8 +--- src/PIL/ImageShow.py | 45 ++++--------------- src/PIL/ImageTk.py | 8 +--- src/PIL/JpegImagePlugin.py | 7 +-- src/PIL/PngImagePlugin.py | 16 +------ src/PIL/_deprecate.py | 62 +++++++++++++++++++++++++ src/PIL/_tkinter_finder.py | 10 ++--- 15 files changed, 182 insertions(+), 183 deletions(-) create mode 100644 Tests/test_deprecate.py create mode 100644 src/PIL/_deprecate.py diff --git a/Tests/test_deprecate.py b/Tests/test_deprecate.py new file mode 100644 index 000000000..df5246d50 --- /dev/null +++ b/Tests/test_deprecate.py @@ -0,0 +1,70 @@ +import pytest + +from PIL import _deprecate + + +@pytest.mark.parametrize( + "version, expected", + [ + ( + 10, + r"Old thing is deprecated and will be removed in Pillow 10 " + r"\(2023-07-01\)\. Use new thing instead\.", + ), + ( + None, + r"Old thing is deprecated and will be removed in a future version. " + r"Use new thing instead\.", + ), + ], +) +def test_version(version, expected): + with pytest.warns(DeprecationWarning, match=expected): + _deprecate.deprecate("Old thing", version, "new thing") + + +def test_unknown_version(): + expected = r"Unknown removal version, update PIL\._deprecate\?" + with pytest.raises(ValueError, match=expected): + _deprecate.deprecate("Old thing", 12345, "new thing") + + +def test_plural(): + expected = ( + r"Old things are deprecated and will be removed in Pillow 10 \(2023-07-01\)\. " + r"Use new thing instead\." + ) + with pytest.warns(DeprecationWarning, match=expected): + _deprecate.deprecate("Old things", 10, "new thing", plural=True) + + +def test_replacement_and_action(): + expected = "Use only one of 'replacement' and 'action'" + with pytest.raises(ValueError, match=expected): + _deprecate.deprecate( + "Old thing", 10, replacement="new thing", action="Upgrade to new thing" + ) + + +@pytest.mark.parametrize( + "action", + [ + "Upgrade to new thing", + "Upgrade to new thing.", + ], +) +def test_action(action): + expected = ( + r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)\. " + r"Upgrade to new thing\." + ) + with pytest.warns(DeprecationWarning, match=expected): + _deprecate.deprecate("Old thing", 10, action=action) + + +def test_no_replacement_or_action(): + expected = ( + r"Old thing is deprecated and will be removed in Pillow 10 \(2023-07-01\)" + ) + with pytest.warns(DeprecationWarning, match=expected): + _deprecate.deprecate("Old thing", 10) diff --git a/docs/reference/internal_modules.rst b/docs/reference/internal_modules.rst index 1105ff76e..363a67d9b 100644 --- a/docs/reference/internal_modules.rst +++ b/docs/reference/internal_modules.rst @@ -9,6 +9,14 @@ Internal Modules :undoc-members: :show-inheritance: +:mod:`~PIL._deprecate` Module +----------------------------- + +.. automodule:: PIL._deprecate + :members: + :undoc-members: + :show-inheritance: + :mod:`~PIL._tkinter_finder` Module ---------------------------------- diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index ecd3da5df..3fd61c50f 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -31,11 +31,11 @@ BLP files come in many different flavours: import os import struct -import warnings from enum import IntEnum from io import BytesIO from . import Image, ImageFile +from ._deprecate import deprecate class Format(IntEnum): @@ -55,7 +55,6 @@ class AlphaEncoding(IntEnum): def __getattr__(name): - deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " for enum, prefix in { Format: "BLP_FORMAT_", Encoding: "BLP_ENCODING_", @@ -64,19 +63,7 @@ def __getattr__(name): if name.startswith(prefix): name = name[len(prefix) :] if name in enum.__members__: - warnings.warn( - prefix - + name - + " is " - + deprecated - + "Use " - + enum.__name__ - + "." - + name - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/src/PIL/FitsStubImagePlugin.py b/src/PIL/FitsStubImagePlugin.py index 9eed02999..440240a99 100644 --- a/src/PIL/FitsStubImagePlugin.py +++ b/src/PIL/FitsStubImagePlugin.py @@ -9,9 +9,8 @@ # See the README file for information on usage and redistribution. # -import warnings - from . import FitsImagePlugin, Image, ImageFile +from ._deprecate import deprecate _handler = None @@ -25,11 +24,11 @@ def register_handler(handler): global _handler _handler = handler - warnings.warn( - "FitsStubImagePlugin is deprecated and will be removed in Pillow " - "10 (2023-07-01). FITS images can now be read without a handler through " - "FitsImagePlugin instead.", - DeprecationWarning, + deprecate( + "FitsStubImagePlugin", + 10, + action="FITS images can now be read without " + "a handler through FitsImagePlugin instead", ) # Override FitsImagePlugin with this handler diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index 55d28e1ff..d58a21ac3 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -52,11 +52,11 @@ Note: All data is stored in little-Endian (Intel) byte order. """ import struct -import warnings from enum import IntEnum from io import BytesIO from . import Image, ImageFile +from ._deprecate import deprecate MAGIC = b"FTEX" @@ -67,24 +67,11 @@ class Format(IntEnum): def __getattr__(name): - deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " for enum, prefix in {Format: "FORMAT_"}.items(): if name.startswith(prefix): name = name[len(prefix) :] if name in enum.__members__: - warnings.warn( - prefix - + name - + " is " - + deprecated - + "Use " - + enum.__name__ - + "." - + name - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 813ac52aa..0c674fff2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -50,28 +50,17 @@ except ImportError: # Use __version__ instead. from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins from ._binary import i32le, o32be, o32le +from ._deprecate import deprecate from ._util import deferred_error, isPath def __getattr__(name): - deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " categories = {"NORMAL": 0, "SEQUENCE": 1, "CONTAINER": 2} if name in categories: - warnings.warn( - "Image categories are " + deprecated + "Use is_animated instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate("Image categories", 10, "is_animated", plural=True) return categories[name] elif name in ("NEAREST", "NONE"): - warnings.warn( - name - + " is " - + deprecated - + "Use Resampling.NEAREST or Dither.NONE instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(name, 10, "Resampling.NEAREST or Dither.NONE") return 0 old_resampling = { "LINEAR": "BILINEAR", @@ -79,31 +68,11 @@ def __getattr__(name): "ANTIALIAS": "LANCZOS", } if name in old_resampling: - warnings.warn( - name - + " is " - + deprecated - + "Use Resampling." - + old_resampling[name] - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(name, 10, old_resampling[name]) return Resampling[old_resampling[name]] for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): if name in enum.__members__: - warnings.warn( - name - + " is " - + deprecated - + "Use " - + enum.__name__ - + "." - + name - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(name, 10, f"{enum.__name__}.{name}") return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") @@ -538,12 +507,7 @@ class Image: def __getattr__(self, name): if name == "category": - warnings.warn( - "Image categories are deprecated and will be removed in Pillow 10 " - "(2023-07-01). Use is_animated instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate("Image categories", 10, "is_animated", plural=True) return self._category raise AttributeError(name) diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index ea328e149..9e185dde7 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -16,11 +16,12 @@ # below for the original description. import sys -import warnings from enum import IntEnum from PIL import Image +from ._deprecate import deprecate + try: from PIL import _imagingcms except ImportError as ex: @@ -117,24 +118,11 @@ class Direction(IntEnum): def __getattr__(name): - deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " for enum, prefix in {Intent: "INTENT_", Direction: "DIRECTION_"}.items(): if name.startswith(prefix): name = name[len(prefix) :] if name in enum.__members__: - warnings.warn( - prefix - + name - + " is " - + deprecated - + "Use " - + enum.__name__ - + "." - + name - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index f21b6de71..f420e197e 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -33,6 +33,7 @@ from enum import IntEnum from io import BytesIO from . import Image +from ._deprecate import deprecate from ._util import isDirectory, isPath @@ -42,24 +43,11 @@ class Layout(IntEnum): def __getattr__(name): - deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " for enum, prefix in {Layout: "LAYOUT_"}.items(): if name.startswith(prefix): name = name[len(prefix) :] if name in enum.__members__: - warnings.warn( - prefix - + name - + " is " - + deprecated - + "Use " - + enum.__name__ - + "." - + name - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") @@ -196,8 +184,6 @@ class FreeTypeFont: if core.HAVE_RAQM: layout_engine = Layout.RAQM elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: - import warnings - warnings.warn( "Raqm layout was requested, but Raqm is not available. " "Falling back to basic layout." diff --git a/src/PIL/ImagePalette.py b/src/PIL/ImagePalette.py index cfc72c254..853147ac2 100644 --- a/src/PIL/ImagePalette.py +++ b/src/PIL/ImagePalette.py @@ -17,9 +17,9 @@ # import array -import warnings from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile +from ._deprecate import deprecate class ImagePalette: @@ -40,11 +40,7 @@ class ImagePalette: self.palette = palette or bytearray() self.dirty = None if size != 0: - warnings.warn( - "The size parameter is deprecated and will be removed in Pillow 10 " - "(2023-07-01).", - DeprecationWarning, - ) + deprecate("The size parameter", 10, None) if size != len(self.palette): raise ValueError("wrong palette size") diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 395bb2258..66f86211f 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -15,11 +15,12 @@ import os import shutil import subprocess import sys -import warnings from shlex import quote from PIL import Image +from ._deprecate import deprecate + _viewers = [] @@ -120,11 +121,7 @@ class Viewer: """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") @@ -176,11 +173,7 @@ class MacViewer(Viewer): """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") @@ -228,11 +221,7 @@ class XDGViewer(UnixViewer): """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") @@ -261,11 +250,7 @@ class DisplayViewer(UnixViewer): """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") @@ -296,11 +281,7 @@ class GmDisplayViewer(UnixViewer): """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") @@ -325,11 +306,7 @@ class EogViewer(UnixViewer): """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") @@ -360,11 +337,7 @@ class XVViewer(UnixViewer): """ if path is None: if "file" in options: - warnings.warn( - "The 'file' argument is deprecated and will be removed in Pillow " - "10 (2023-07-01). Use 'path' instead.", - DeprecationWarning, - ) + deprecate("The 'file' argument", 10, "'path'") path = options.pop("file") else: raise TypeError("Missing required argument: 'path'") diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index c9a9135b0..98faf50ff 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -26,10 +26,10 @@ # import tkinter -import warnings from io import BytesIO from . import Image +from ._deprecate import deprecate # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -187,11 +187,7 @@ class PhotoImage: """ if box is not None: - warnings.warn( - "The box parameter is deprecated and will be removed in Pillow 10 " - "(2023-07-01).", - DeprecationWarning, - ) + deprecate("The box parameter", 10, None) # convert to blittable im.load() diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 93741ec6e..b6ab7b0ee 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -45,6 +45,7 @@ from . import Image, ImageFile, TiffImagePlugin from ._binary import i16be as i16 from ._binary import i32be as i32 from ._binary import o8 +from ._deprecate import deprecate from .JpegPresets import presets # @@ -603,11 +604,7 @@ samplings = { def convert_dict_qtables(qtables): - warnings.warn( - "convert_dict_qtables is deprecated and will be removed in Pillow 10" - "(2023-07-01). Conversion is no longer needed.", - DeprecationWarning, - ) + deprecate("convert_dict_qtables", 10, action="Conversion is no longer needed") return qtables diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 53525e22e..c939b86e7 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -45,6 +45,7 @@ from ._binary import i32be as i32 from ._binary import o8 from ._binary import o16be as o16 from ._binary import o32be as o32 +from ._deprecate import deprecate logger = logging.getLogger(__name__) @@ -131,24 +132,11 @@ class Blend(IntEnum): def __getattr__(name): - deprecated = "deprecated and will be removed in Pillow 10 (2023-07-01). " for enum, prefix in {Disposal: "APNG_DISPOSE_", Blend: "APNG_BLEND_"}.items(): if name.startswith(prefix): name = name[len(prefix) :] if name in enum.__members__: - warnings.warn( - prefix - + name - + " is " - + deprecated - + "Use " - + enum.__name__ - + "." - + name - + " instead.", - DeprecationWarning, - stacklevel=2, - ) + deprecate(f"{prefix}{name}", 10, f"{enum.__name__}.{name}") return enum[name] raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/src/PIL/_deprecate.py b/src/PIL/_deprecate.py new file mode 100644 index 000000000..abc562570 --- /dev/null +++ b/src/PIL/_deprecate.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import warnings + + +def deprecate( + deprecated: str, + when: int | None, + replacement: str | None = None, + *, + action: str | None = None, + plural: bool = False, +) -> None: + """ + Deprecations helper. + + :param deprecated: Name of thing to be deprecated. + :param when: Pillow major version to be removed in. + :param replacement: Name of replacement. + :param action: Instead of "replacement", give a custom call to action + e.g. "Upgrade to new thing". + :param plural: if the deprecated thing is plural, needing "are" instead of "is". + + Usually of the form: + + "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). + Use [new thing] instead." + + You can leave out the replacement sentence: + + "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd)" + + Or with another call to action: + + "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). + [Upgrade to new thing]." + """ + + is_ = "are" if plural else "is" + + if when == 10: + removed = "Pillow 10 (2023-07-01)" + elif when is None: + removed = "a future version" + else: + raise ValueError(f"Unknown removal version, update {__name__}?") + + if replacement and action: + raise ValueError("Use only one of 'replacement' and 'action'") + + if replacement: + action = f". Use {replacement} instead." + elif action: + action = f". {action.rstrip('.')}." + else: + action = "" + + warnings.warn( + f"{deprecated} {is_} deprecated and will be removed in {removed}{action}", + DeprecationWarning, + stacklevel=3, + ) diff --git a/src/PIL/_tkinter_finder.py b/src/PIL/_tkinter_finder.py index 5253f0759..5cd7e9b1f 100644 --- a/src/PIL/_tkinter_finder.py +++ b/src/PIL/_tkinter_finder.py @@ -2,9 +2,10 @@ """ import sys import tkinter -import warnings from tkinter import _tkinter as tk +from ._deprecate import deprecate + try: if hasattr(sys, "pypy_find_executable"): TKINTER_LIB = tk.tklib_cffi.__file__ @@ -17,9 +18,6 @@ except AttributeError: tk_version = str(tkinter.TkVersion) if tk_version == "8.4": - warnings.warn( - "Support for Tk/Tcl 8.4 is deprecated and will be removed" - " in Pillow 10 (2023-07-01). Please upgrade to Tk/Tcl 8.5 " - "or newer.", - DeprecationWarning, + deprecate( + "Support for Tk/Tcl 8.4", 10, action="Please upgrade to Tk/Tcl 8.5 or newer" ) From aae38c55b2cfe5e3037d359470b6230adef78480 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 6 Apr 2022 13:37:17 +0300 Subject: [PATCH 048/294] Update raw strings Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_deprecate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_deprecate.py b/Tests/test_deprecate.py index df5246d50..906b337e6 100644 --- a/Tests/test_deprecate.py +++ b/Tests/test_deprecate.py @@ -8,12 +8,12 @@ from PIL import _deprecate [ ( 10, - r"Old thing is deprecated and will be removed in Pillow 10 " + "Old thing is deprecated and will be removed in Pillow 10 " r"\(2023-07-01\)\. Use new thing instead\.", ), ( None, - r"Old thing is deprecated and will be removed in a future version. " + r"Old thing is deprecated and will be removed in a future version\. " r"Use new thing instead\.", ), ], From 42f67d184a609a6e36c6c577777144ea51e91321 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 7 Apr 2022 08:58:57 +1000 Subject: [PATCH 049/294] Round lut values where necessary --- Tests/test_image_point.py | 1 + src/PIL/Image.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Tests/test_image_point.py b/Tests/test_image_point.py index 366f45854..428ad116b 100644 --- a/Tests/test_image_point.py +++ b/Tests/test_image_point.py @@ -10,6 +10,7 @@ def test_sanity(): im.point(list(range(256))) im.point(list(range(256)) * 3) im.point(lambda x: x) + im.point(lambda x: x * 1.2) im = im.convert("I") with pytest.raises(ValueError): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2e8279583..74ddca911 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1720,6 +1720,8 @@ class Image: # FIXME: _imaging returns a confusing error message for this case raise ValueError("point operation not supported for this mode") + if mode != "F": + lut = [round(i) for i in lut] return self._new(self.im.point(lut, mode)) def putalpha(self, alpha): From 0b5cdba4311aa26ee7e578d04a19fc1d8fd07743 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 8 Apr 2022 19:11:27 +1000 Subject: [PATCH 050/294] Load before getting size in resize() --- Tests/test_image_resize.py | 7 +++++++ src/PIL/Image.py | 1 + 2 files changed, 8 insertions(+) diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 04b7c8c97..6961afa60 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -264,6 +264,13 @@ class TestImageResize: with pytest.raises(ValueError): im.resize((10, 10), "unknown") + def test_load_first(self): + # load() may change the size of the image + # Test that resize() is calling it before getting the size + with Image.open("Tests/images/g4_orientation_5.tif") as im: + 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) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 2e8279583..aa7e3c2e6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2024,6 +2024,7 @@ class Image: size = tuple(size) + self.load() if box is None: box = (0, 0) + self.size else: From e824558076f4b305fa0e4ca0f802884df77e2e57 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 8 Apr 2022 21:22:31 +1000 Subject: [PATCH 051/294] If removal version has arrived, raise an error --- Tests/test_deprecate.py | 21 +++++++++++++++++++++ src/PIL/_deprecate.py | 10 +++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Tests/test_deprecate.py b/Tests/test_deprecate.py index 906b337e6..30ed4a808 100644 --- a/Tests/test_deprecate.py +++ b/Tests/test_deprecate.py @@ -29,6 +29,27 @@ def test_unknown_version(): _deprecate.deprecate("Old thing", 12345, "new thing") +@pytest.mark.parametrize( + "deprecated, plural, expected", + [ + ( + "Old thing", + False, + r"Old thing is deprecated and should be removed\.", + ), + ( + "Old things", + True, + r"Old things are deprecated and should be removed\.", + ), + ], +) +def test_old_version(deprecated, plural, expected): + expected = r"" + with pytest.raises(RuntimeError, match=expected): + _deprecate.deprecate(deprecated, 1, plural=plural) + + def test_plural(): expected = ( r"Old things are deprecated and will be removed in Pillow 10 \(2023-07-01\)\. " diff --git a/src/PIL/_deprecate.py b/src/PIL/_deprecate.py index abc562570..6a148f3d8 100644 --- a/src/PIL/_deprecate.py +++ b/src/PIL/_deprecate.py @@ -2,6 +2,8 @@ from __future__ import annotations import warnings +from . import __version__ + def deprecate( deprecated: str, @@ -38,10 +40,12 @@ def deprecate( is_ = "are" if plural else "is" - if when == 10: - removed = "Pillow 10 (2023-07-01)" - elif when is None: + if when is None: removed = "a future version" + elif when <= int(__version__.split(".")[0]): + raise RuntimeError(f"{deprecated} {is_} deprecated and should be removed.") + elif when == 10: + removed = "Pillow 10 (2023-07-01)" else: raise ValueError(f"Unknown removal version, update {__name__}?") From 98e8e6df3320d99e2de588307bddaab59e103401 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 9 Apr 2022 07:57:15 +1000 Subject: [PATCH 052/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 06297a5ca..dc69e4587 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Round lut values where necessary #6188 + [radarhere] + +- Load before getting size in resize() #6190 + [radarhere] + - Load image before performing size calculations in thumbnail() #6186 [radarhere] From d32d96fab1ff7463baee28990cafb426b15568ca Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 17:39:56 +0300 Subject: [PATCH 053/294] Remove redundant property already defined in another section --- .editorconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 798ab753c..d74549fe2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,7 +16,6 @@ trim_trailing_whitespace = true [*.yml] # Two-space indentation indent_size = 2 -indent_style = space # Tab indentation (no size specified) [Makefile] From 935bdfa516cd7ce19ad240bb96a55de4f18f46c9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 17:47:32 +0300 Subject: [PATCH 054/294] Use triple-quoted docstrings --- src/PIL/ImageFont.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index f21b6de71..f73d4a30e 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -93,7 +93,7 @@ except ImportError: class ImageFont: - "PIL font wrapper" + """PIL font wrapper""" def _load_pilfont(self, filename): @@ -181,7 +181,7 @@ class ImageFont: class FreeTypeFont: - "FreeType font wrapper (requires _imagingft service)" + """FreeType font wrapper (requires _imagingft service)""" def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None): # FIXME: use service provider instead @@ -775,7 +775,7 @@ class FreeTypeFont: class TransposedFont: - "Wrapper for writing rotated or mirrored text" + """Wrapper for writing rotated or mirrored text""" def __init__(self, font, orientation=None): """ From 74ccda3affb596cc7c415f566635eb772b1713c7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 17:50:17 +0300 Subject: [PATCH 055/294] Simplify chained comparison --- src/PIL/WebPImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 7dd3f5272..2101271c4 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -222,7 +222,7 @@ def _save_all(im, fp, filename): if ( not isinstance(background, (list, tuple)) or len(background) != 4 - or not all(v >= 0 and v < 256 for v in background) + or not all(0 <= v < 256 for v in background) ): raise OSError( "Background color is not an RGBA tuple clamped to (0-255): %s" From 13994d4b3634fb3514c47fe63fe2ca1dad985b48 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 17:56:42 +0300 Subject: [PATCH 056/294] More f-strings --- src/PIL/WebPImagePlugin.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PIL/WebPImagePlugin.py b/src/PIL/WebPImagePlugin.py index 2101271c4..c1f4b730f 100644 --- a/src/PIL/WebPImagePlugin.py +++ b/src/PIL/WebPImagePlugin.py @@ -225,8 +225,7 @@ def _save_all(im, fp, filename): or not all(0 <= v < 256 for v in background) ): raise OSError( - "Background color is not an RGBA tuple clamped to (0-255): %s" - % str(background) + f"Background color is not an RGBA tuple clamped to (0-255): {background}" ) # Convert to packed uint From c9acb9b387150f30784dfbbcf2fee2c95c4e374b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 19:27:57 +0300 Subject: [PATCH 057/294] Update docstring Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/_deprecate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/_deprecate.py b/src/PIL/_deprecate.py index 6a148f3d8..30a8a8971 100644 --- a/src/PIL/_deprecate.py +++ b/src/PIL/_deprecate.py @@ -26,7 +26,7 @@ def deprecate( Usually of the form: "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). - Use [new thing] instead." + Use [replacement] instead." You can leave out the replacement sentence: @@ -35,7 +35,7 @@ def deprecate( Or with another call to action: "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). - [Upgrade to new thing]." + [action]." """ is_ = "are" if plural else "is" From a9707e0a6f130fc87317cf8d43fbc020db0524ba Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 18:17:20 +0300 Subject: [PATCH 058/294] Remove redundant regex escapes --- src/PIL/PdfParser.py | 10 +++++----- src/PIL/XbmImagePlugin.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PIL/PdfParser.py b/src/PIL/PdfParser.py index 9aa0fd6fa..fd5cc5a61 100644 --- a/src/PIL/PdfParser.py +++ b/src/PIL/PdfParser.py @@ -590,7 +590,7 @@ class PdfParser: whitespace_mandatory + rb"trailer" + whitespace_optional - + rb"\<\<(.*\>\>)" + + rb"<<(.*>>)" + newline + rb"startxref" + newline @@ -605,7 +605,7 @@ class PdfParser: whitespace_optional + rb"trailer" + whitespace_optional - + rb"\<\<(.*?\>\>)" + + rb"<<(.*?>>)" + newline + rb"startxref" + newline @@ -659,8 +659,8 @@ class PdfParser: + delimiter_or_ws + rb")" ) - re_dict_start = re.compile(whitespace_optional + rb"\<\<") - re_dict_end = re.compile(whitespace_optional + rb"\>\>" + whitespace_optional) + re_dict_start = re.compile(whitespace_optional + rb"<<") + re_dict_end = re.compile(whitespace_optional + rb">>" + whitespace_optional) @classmethod def interpret_trailer(cls, trailer_data): @@ -719,7 +719,7 @@ class PdfParser: re_array_start = re.compile(whitespace_optional + rb"\[") re_array_end = re.compile(whitespace_optional + rb"]") re_string_hex = re.compile( - whitespace_optional + rb"\<(" + whitespace_or_hex + rb"*)\>" + whitespace_optional + rb"<(" + whitespace_or_hex + rb"*)>" ) re_string_lit = re.compile(whitespace_optional + rb"\(") re_indirect_reference = re.compile( diff --git a/src/PIL/XbmImagePlugin.py b/src/PIL/XbmImagePlugin.py index 15379ce80..59acabeba 100644 --- a/src/PIL/XbmImagePlugin.py +++ b/src/PIL/XbmImagePlugin.py @@ -31,7 +31,7 @@ xbm_head = re.compile( b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" b")?" - b"[\\000-\\377]*_bits\\[\\]" + rb"[\000-\377]*_bits\[]" ) From 6a648c9ce7e06c04051852d48cc68c527bf4f0f7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 19:17:51 +0300 Subject: [PATCH 059/294] Add comma to make a tuple --- src/PIL/FpxImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/FpxImagePlugin.py b/src/PIL/FpxImagePlugin.py index 5e385469f..f955b2347 100644 --- a/src/PIL/FpxImagePlugin.py +++ b/src/PIL/FpxImagePlugin.py @@ -22,7 +22,7 @@ from ._binary import i32le as i32 # we map from colour field tuples to (mode, rawmode) descriptors MODES = { # opacity - (0x00007FFE): ("A", "L"), + (0x00007FFE,): ("A", "L"), # monochrome (0x00010000,): ("L", "L"), (0x00018000, 0x00017FFE): ("RGBA", "LA"), @@ -162,7 +162,7 @@ class FpxImageFile(ImageFile.ImageFile): "raw", (x, y, x + xtile, y + ytile), i32(s, i) + 28, - (self.rawmode), + (self.rawmode,), ) ) From ee85e387bab535e2339b9d3cd1ab87c61d23af15 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 19:25:40 +0300 Subject: [PATCH 060/294] Remove redundant parentheses --- Tests/test_image_resample.py | 2 +- selftest.py | 2 +- setup.py | 2 +- src/PIL/BlpImagePlugin.py | 2 +- src/PIL/EpsImagePlugin.py | 2 +- src/PIL/FtexImagePlugin.py | 2 +- src/PIL/ImageCms.py | 4 ++-- src/PIL/ImageColor.py | 10 +++++----- src/PIL/ImageFile.py | 4 ++-- src/PIL/Jpeg2KImagePlugin.py | 4 ++-- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/PyAccess.py | 6 +++--- src/PIL/TiffImagePlugin.py | 4 ++-- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 125422337..6d050efcc 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -458,7 +458,7 @@ class TestCoreResampleBox: def split_range(size, tiles): scale = size / tiles for i in range(tiles): - yield (int(round(scale * i)), int(round(scale * (i + 1)))) + yield int(round(scale * i)), int(round(scale * (i + 1))) tiled = Image.new(im.mode, dst_size) scale = (im.size[0] / tiled.size[0], im.size[1] / tiled.size[1]) diff --git a/selftest.py b/selftest.py index 5010c1213..6eeadd1db 100755 --- a/selftest.py +++ b/selftest.py @@ -20,7 +20,7 @@ def testimage(): >>> from PIL import Image, ImageDraw, ImageFilter, ImageMath >>> im = Image.new("1", (128, 128)) # monochrome - >>> def _info(im): return (im.format, im.mode, im.size) + >>> def _info(im): return im.format, im.mode, im.size >>> _info(im) (None, '1', (128, 128)) >>> _info(Image.new("L", (128, 128))) # grayscale (luminance) diff --git a/setup.py b/setup.py index 3468b260d..9a05e5105 100755 --- a/setup.py +++ b/setup.py @@ -269,7 +269,7 @@ def _pkg_config(name): .strip() .replace("-I", "") ) - return (libs, cflags) + return libs, cflags except Exception: pass diff --git a/src/PIL/BlpImagePlugin.py b/src/PIL/BlpImagePlugin.py index ecd3da5df..e4e029596 100644 --- a/src/PIL/BlpImagePlugin.py +++ b/src/PIL/BlpImagePlugin.py @@ -82,7 +82,7 @@ def __getattr__(name): def unpack_565(i): - return (((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3) + return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 def decode_dxt1(data, alpha=False): diff --git a/src/PIL/EpsImagePlugin.py b/src/PIL/EpsImagePlugin.py index b67363beb..3b782d6b3 100644 --- a/src/PIL/EpsImagePlugin.py +++ b/src/PIL/EpsImagePlugin.py @@ -325,7 +325,7 @@ class EpsImageFile(ImageFile.ImageFile): else: raise SyntaxError("not an EPS file") - return (length, offset) + return length, offset def load(self, scale=1, transparency=False): # Load EPS via Ghostscript diff --git a/src/PIL/FtexImagePlugin.py b/src/PIL/FtexImagePlugin.py index 55d28e1ff..1d74390d8 100644 --- a/src/PIL/FtexImagePlugin.py +++ b/src/PIL/FtexImagePlugin.py @@ -114,7 +114,7 @@ class FtexImageFile(ImageFile.ImageFile): if format == Format.DXT1: self.mode = "RGBA" - self.tile = [("bcn", (0, 0) + self.size, 0, (1))] + self.tile = [("bcn", (0, 0) + self.size, 0, 1)] elif format == Format.UNCOMPRESSED: self.tile = [("raw", (0, 0) + self.size, 0, ("RGB", 0, 1))] else: diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index ea328e149..5a2dbae50 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -162,7 +162,7 @@ FLAGS = { "SOFTPROOFING": 16384, # Do softproofing "PRESERVEBLACK": 32768, # Black preservation "NODEFAULTRESOURCEDEF": 16777216, # CRD special - "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16, # Gridpoints + "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints } _MAX_FLAG = 0 @@ -1026,4 +1026,4 @@ def versions(): (pyCMS) Fetches versions. """ - return (VERSION, core.littlecms_version, sys.version.split()[0], Image.__version__) + return VERSION, core.littlecms_version, sys.version.split()[0], Image.__version__ diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py index 25f92f2c7..13510e4d4 100644 --- a/src/PIL/ImageColor.py +++ b/src/PIL/ImageColor.py @@ -45,7 +45,7 @@ def getrgb(color): # check for known string formats if re.match("#[a-f0-9]{3}$", color): - return (int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16)) + return int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) if re.match("#[a-f0-9]{4}$", color): return ( @@ -56,7 +56,7 @@ def getrgb(color): ) if re.match("#[a-f0-9]{6}$", color): - return (int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)) + return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) if re.match("#[a-f0-9]{8}$", color): return ( @@ -68,7 +68,7 @@ def getrgb(color): m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) if m: - return (int(m.group(1)), int(m.group(2)), int(m.group(3))) + return int(m.group(1)), int(m.group(2)), int(m.group(3)) m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: @@ -114,7 +114,7 @@ def getrgb(color): m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) if m: - return (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4))) + return int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) raise ValueError(f"unknown color specifier: {repr(color)}") @@ -140,7 +140,7 @@ def getcolor(color, mode): # scaled to 24 bits to match the convert's implementation. color = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 if mode[-1] == "A": - return (color, alpha) + return color, alpha else: if mode[-1] == "A": return color + (alpha,) diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 34f344f1d..3f8c2abe7 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -571,7 +571,7 @@ class PyCodecState: self.yoff = 0 def extents(self): - return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize) + return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize class PyCodec: @@ -681,7 +681,7 @@ class PyDecoder(PyCodec): if not rawmode: rawmode = self.mode - d = Image._getdecoder(self.mode, "raw", (rawmode)) + d = Image._getdecoder(self.mode, "raw", rawmode) d.setimage(self.im, self.state.extents()) s = d.decode(data) diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index fb5d70cee..c67d8d6bf 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -124,7 +124,7 @@ def _parse_codestream(fp): else: mode = None - return (size, mode) + return size, mode def _res_to_dpi(num, denom, exp): @@ -191,7 +191,7 @@ def _parse_jp2_header(fp): if size is None or mode is None: raise SyntaxError("Malformed JP2 header") - return (size, mode, mimetype, dpi) + return size, mode, mimetype, dpi ## diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index 93741ec6e..aae2a4591 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -444,7 +444,7 @@ class JpegImageFile(ImageFile.ImageFile): self.decoderconfig = (scale, 0) box = (0, 0, original_size[0] / scale, original_size[1] / scale) - return (self.mode, box) + return self.mode, box def load_djpeg(self): diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index eeaa0ccc4..5280e2a49 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -135,7 +135,7 @@ class _PyAccess32_2(PyAccess): def get_pixel(self, x, y): pixel = self.pixels[y][x] - return (pixel.r, pixel.a) + return pixel.r, pixel.a def set_pixel(self, x, y, color): pixel = self.pixels[y][x] @@ -152,7 +152,7 @@ class _PyAccess32_3(PyAccess): def get_pixel(self, x, y): pixel = self.pixels[y][x] - return (pixel.r, pixel.g, pixel.b) + return pixel.r, pixel.g, pixel.b def set_pixel(self, x, y, color): pixel = self.pixels[y][x] @@ -171,7 +171,7 @@ class _PyAccess32_4(PyAccess): def get_pixel(self, x, y): pixel = self.pixels[y][x] - return (pixel.r, pixel.g, pixel.b, pixel.a) + return pixel.r, pixel.g, pixel.b, pixel.a def set_pixel(self, x, y, color): pixel = self.pixels[y][x] diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e96f58440..b2d15d807 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -353,10 +353,10 @@ class IFDRational(Rational): """ if self.denominator == 0: - return (self.numerator, self.denominator) + return self.numerator, self.denominator f = self._val.limit_denominator(max_denominator) - return (f.numerator, f.denominator) + return f.numerator, f.denominator def __repr__(self): return str(float(self._val)) From b863da6debaaf82f103ec0c8aaeb0a69d6390f7a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 19:50:49 +0300 Subject: [PATCH 061/294] Don't redeclare (unused) loop variable --- src/PIL/PsdImagePlugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index 283219579..dd755ed15 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -178,7 +178,7 @@ def _layerinfo(fp, ct_bytes): if ct_bytes < (abs(ct) * 20): raise SyntaxError("Layer block too short for number of layers requested") - for i in range(abs(ct)): + for _ in range(abs(ct)): # bounding box y0 = i32(read(4)) @@ -193,7 +193,7 @@ def _layerinfo(fp, ct_bytes): if len(types) > 4: continue - for i in types: + for _ in types: type = i16(read(2)) if type == 65535: From 9d87b26a670ef5e207f1e339bf5a4f65fb26688c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 19:52:38 +0300 Subject: [PATCH 062/294] Don't redeclare loop variable --- Tests/test_image_convert.py | 14 +++++++------- src/PIL/XpmImagePlugin.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/test_image_convert.py b/Tests/test_image_convert.py index 727c282d7..96587e4e2 100644 --- a/Tests/test_image_convert.py +++ b/Tests/test_image_convert.py @@ -27,15 +27,15 @@ def test_sanity(): "HSV", ) - for mode in modes: - im = hopper(mode) - for mode in modes: - convert(im, mode) + for input_mode in modes: + im = hopper(input_mode) + for output_mode in modes: + convert(im, output_mode) # Check 0 - im = Image.new(mode, (0, 0)) - for mode in modes: - convert(im, mode) + im = Image.new(input_mode, (0, 0)) + for output_mode in modes: + convert(im, output_mode) def test_default(): diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index ebd65ba58..19c50cafc 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -64,7 +64,7 @@ class XpmImageFile(ImageFile.ImageFile): palette = [b"\0\0\0"] * 256 - for i in range(pal): + for _ in range(pal): s = self.fp.readline() if s[-2:] == b"\r\n": From 830da5c41efa1470a5ad4936164dca2c0f8c64d8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 19:59:42 +0300 Subject: [PATCH 063/294] Rewrite dict creation as dict literal for better performance --- src/PIL/BmpImagePlugin.py | 4 +--- src/PIL/Image.py | 7 +++---- src/PIL/TiffImagePlugin.py | 3 +-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index 7e7e742cd..4dc2b93c3 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -76,10 +76,8 @@ class BmpImageFile(ImageFile.ImageFile): read, seek = self.fp.read, self.fp.seek if header: seek(header) - file_info = {} # read bmp header size @offset 14 (this is part of the header size) - file_info["header_size"] = i32(read(4)) - file_info["direction"] = -1 + file_info = {"header_size": i32(read(4)), "direction": -1} # -------------------- If requested, read header at a specific position # read the rest of the bmp header, without its size diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ab391b9b4..ca7f8308e 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2992,12 +2992,11 @@ _fromarray_typemap = { ((1, 1, 2), "|u1"): ("LA", "LA"), ((1, 1, 3), "|u1"): ("RGB", "RGB"), ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), + # shortcuts: + ((1, 1), _ENDIAN + "i4"): ("I", "I"), + ((1, 1), _ENDIAN + "f4"): ("F", "F"), } -# shortcuts -_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") -_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") - def _decompression_bomb_check(size): if MAX_IMAGE_PIXELS is None: diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index b2d15d807..6b01a552e 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1748,9 +1748,8 @@ def _save(im, fp, filename): SUBIFD, ] - atts = {} # bits per sample is a single short in the tiff directory, not a list. - atts[BITSPERSAMPLE] = bits[0] + atts = {BITSPERSAMPLE: bits[0]} # Merge the ones that we have with (optional) more bits from # the original file, e.g x,y resolution so that we can # save(load('')) == original file. From 73cf0cb3d96bb4e30bd1748529d1e459581c4873 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 20:02:34 +0300 Subject: [PATCH 064/294] Use cls for classmethods --- Tests/test_decompression_bomb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index 1778491ab..95d709a27 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -70,12 +70,12 @@ class TestDecompressionBomb: class TestDecompressionCrop: @classmethod - def setup_class(self): + def setup_class(cls): width, height = 128, 128 Image.MAX_IMAGE_PIXELS = height * width * 4 - 1 @classmethod - def teardown_class(self): + def teardown_class(cls): Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT def testEnlargeCrop(self): From 855c1a12dac7c5b2492f134661f62f18af2080f3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 20:08:22 +0300 Subject: [PATCH 065/294] Use self for first method parameter --- Tests/test_image.py | 2 +- src/PIL/TiffImagePlugin.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/test_image.py b/Tests/test_image.py index 07cf6eb92..d42fb9f1d 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -170,7 +170,7 @@ class TestImage: temp_file = str(tmp_path / "temp.jpg") class FP: - def write(a, b): + def write(self, b): pass fp = FP() diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 6b01a552e..b83132167 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -338,12 +338,12 @@ class IFDRational(Rational): self._val = Fraction(value, denominator) @property - def numerator(a): - return a._numerator + def numerator(self): + return self._numerator @property - def denominator(a): - return a._denominator + def denominator(self): + return self._denominator def limit_rational(self, max_denominator): """ From 1997c814abcbc071fb9f289fda021e8d08cad4a7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 20:22:47 +0300 Subject: [PATCH 066/294] Move useful comment into docstring --- src/PIL/GifImagePlugin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b798bb969..94dbdfb06 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -990,8 +990,6 @@ def getheader(im, palette=None, info=None): return header, used_palette_colors -# To specify duration, add the time in milliseconds to getdata(), -# e.g. getdata(im_frame, duration=1000) def getdata(im, offset=(0, 0), **params): """ Legacy Method @@ -1000,10 +998,13 @@ def getdata(im, offset=(0, 0), **params): The first string is a local image header, the rest contains encoded image data. + To specify duration, add the time in milliseconds, + e.g. ``getdata(im_frame, duration=1000)`` + :param im: Image object - :param offset: Tuple of (x, y) pixels. Defaults to (0,0) - :param \\**params: E.g. duration or other encoder info parameters - :returns: List of Bytes containing gif encoded frame data + :param offset: Tuple of (x, y) pixels. Defaults to (0, 0) + :param \\**params: e.g. duration or other encoder info parameters + :returns: List of bytes containing GIF encoded frame data """ From 965df6df6f806887fca3f7b5c6dd6c70a20e803e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 20:55:53 +0300 Subject: [PATCH 067/294] Add missing paramters to docstrings --- docs/reference/TiffTags.rst | 14 ++++++++++++++ src/PIL/GdImageFile.py | 2 +- src/PIL/ImageChops.py | 1 + src/PIL/ImageColor.py | 3 ++- src/PIL/ImageFont.py | 1 + src/PIL/ImageTk.py | 1 + src/PIL/TiffTags.py | 8 ++++++-- 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/reference/TiffTags.rst b/docs/reference/TiffTags.rst index 1441185dd..7cb7d16ae 100644 --- a/docs/reference/TiffTags.rst +++ b/docs/reference/TiffTags.rst @@ -10,6 +10,10 @@ metadata tag numbers, names, and type information. .. method:: lookup(tag) :param tag: Integer tag number + :param group: Which :py:data:`~PIL.TiffTags.TAGS_V2_GROUPS` to look in + + .. versionadded:: 8.3.0 + :returns: Taginfo namedtuple, From the :py:data:`~PIL.TiffTags.TAGS_V2` info if possible, otherwise just populating the value and name from :py:data:`~PIL.TiffTags.TAGS`. If the tag is not recognized, "unknown" is returned for the name @@ -42,6 +46,16 @@ metadata tag numbers, names, and type information. .. versionadded:: 3.0.0 +.. py:data:: PIL.TiffTags.TAGS_V2_GROUPS + :type: dict + + :py:data:`~PIL.TiffTags.TAGS_V2` is one dimensional and + doesn't account for the fact that tags actually exist in + `different groups `_. + This dictionary is used when the tag in question is part of a group. + +.. versionadded:: 8.3.0 + .. py:data:: PIL.TiffTags.TAGS :type: dict diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index 9c34adaa6..f6cf86af6 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -75,7 +75,7 @@ def open(fp, mode="r"): """ Load texture from a GD image file. - :param filename: GD file name, or an opened file handle. + :param fp: GD file name, or an opened file handle. :param mode: Optional mode. In this version, if the mode argument is given, it must be "r". :returns: An image instance. diff --git a/src/PIL/ImageChops.py b/src/PIL/ImageChops.py index 61d3a295b..fec4694b2 100644 --- a/src/PIL/ImageChops.py +++ b/src/PIL/ImageChops.py @@ -316,6 +316,7 @@ def offset(image, xoffset, yoffset=None): distances. Data wraps around the edges. If ``yoffset`` is omitted, it is assumed to be equal to ``xoffset``. + :param image: Input image. :param xoffset: The horizontal distance. :param yoffset: The vertical distance. If omitted, both distances are set to the same value. diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py index 13510e4d4..958635bf9 100644 --- a/src/PIL/ImageColor.py +++ b/src/PIL/ImageColor.py @@ -121,12 +121,13 @@ def getrgb(color): def getcolor(color, mode): """ Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a - greyscale value if the mode is not color or a palette image. If the string + greyscale value if ``mode`` is not color or a palette image. If the string cannot be parsed, this function raises a :py:exc:`ValueError` exception. .. versionadded:: 1.1.4 :param color: A color string + :param mode: Convert result to this mode :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` """ # same as getrgb, but converts the result to the given mode diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index f73d4a30e..8edf73b6a 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -634,6 +634,7 @@ class FreeTypeFont: should have mode ``RGBA``. Otherwise, it should have mode ``1``. :param text: Text to render. + :param fill: A fill function. :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 diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index c9a9135b0..51a4db5af 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -184,6 +184,7 @@ class PhotoImage: :param im: A PIL image. The size must match the target region. If the mode does not match, the image is converted to the mode of the bitmap image. + :param box: Deprecated. """ if box is not None: diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index b37c8cf5f..e3094b4db 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -36,8 +36,12 @@ class TagInfo(namedtuple("_TagInfo", "value name type length enum")): def lookup(tag, group=None): """ :param tag: Integer tag number - :returns: Taginfo namedtuple, From the TAGS_V2 info if possible, - otherwise just populating the value and name from TAGS. + :param group: Which :py:data:`~PIL.TiffTags.TAGS_V2_GROUPS` to look in + + .. versionadded:: 8.3.0 + + :returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible, + otherwise just populating the value and name from ``TAGS``. If the tag is not recognized, "unknown" is returned for the name """ From d3c9a6504e84f87379554b6b671a1fb6c66a449e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 21:23:31 +0300 Subject: [PATCH 068/294] Variable in function should be snake_case --- Tests/test_color_lut.py | 26 ++++---- Tests/test_file_mpo.py | 6 +- Tests/test_file_pdf.py | 6 +- Tests/test_file_tiff_metadata.py | 44 ++++++------- Tests/test_image_copy.py | 12 ++-- Tests/test_image_filter.py | 4 +- Tests/test_imagefile.py | 4 +- Tests/test_imagesequence.py | 8 +-- Tests/test_mode_i16.py | 52 ++++++++-------- src/PIL/GdImageFile.py | 15 +++-- src/PIL/ImageDraw.py | 6 +- src/PIL/ImageFilter.py | 30 ++++----- src/PIL/ImageStat.py | 6 +- src/PIL/PcfFontFile.py | 22 +++---- src/PIL/PdfImagePlugin.py | 28 ++++----- src/PIL/SgiImagePlugin.py | 10 +-- src/PIL/TiffImagePlugin.py | 102 +++++++++++++++---------------- 17 files changed, 193 insertions(+), 188 deletions(-) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 120ff777e..abe13cf13 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -15,27 +15,27 @@ except ImportError: class TestColorLut3DCoreAPI: def generate_identity_table(self, channels, size): if isinstance(size, tuple): - size1D, size2D, size3D = size + size_1d, size_2d, size_3d = size else: - size1D, size2D, size3D = (size, size, size) + size_1d, size_2d, size_3d = (size, size, size) table = [ [ - r / (size1D - 1) if size1D != 1 else 0, - g / (size2D - 1) if size2D != 1 else 0, - b / (size3D - 1) if size3D != 1 else 0, - r / (size1D - 1) if size1D != 1 else 0, - g / (size2D - 1) if size2D != 1 else 0, + r / (size_1d - 1) if size_1d != 1 else 0, + g / (size_2d - 1) if size_2d != 1 else 0, + b / (size_3d - 1) if size_3d != 1 else 0, + r / (size_1d - 1) if size_1d != 1 else 0, + g / (size_2d - 1) if size_2d != 1 else 0, ][:channels] - for b in range(size3D) - for g in range(size2D) - for r in range(size1D) + for b in range(size_3d) + for g in range(size_2d) + for r in range(size_1d) ] return ( channels, - size1D, - size2D, - size3D, + size_1d, + size_2d, + size_3d, [item for sublist in table for item in sublist], ) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index 0fa3b6382..ca3ea8419 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -145,10 +145,10 @@ def test_mp_attribute(): for test_file in test_files: with Image.open(test_file) as im: mpinfo = im._getmp() - frameNumber = 0 + frame_number = 0 for mpentry in mpinfo[0xB002]: mpattr = mpentry["Attribute"] - if frameNumber: + if frame_number: assert not mpattr["RepresentativeImageFlag"] else: assert mpattr["RepresentativeImageFlag"] @@ -157,7 +157,7 @@ def test_mp_attribute(): assert mpattr["ImageDataFormat"] == "JPEG" assert mpattr["MPType"] == "Multi-Frame Image: (Disparity)" assert mpattr["Reserved"] == 0 - frameNumber += 1 + frame_number += 1 def test_seek(): diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 10daa414b..b6f327844 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -253,9 +253,9 @@ def test_pdf_append(tmp_path): check_pdf_pages_consistency(pdf) # append two images - mode_CMYK = hopper("CMYK") - mode_P = hopper("P") - mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P]) + mode_cmyk = hopper("CMYK") + mode_p = hopper("P") + mode_cmyk.save(pdf_filename, append=True, save_all=True, append_images=[mode_p]) # open the PDF again, check pages and info again with PdfParser.PdfParser(pdf_filename) as pdf: diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 056295516..b11ab1643 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -28,26 +28,26 @@ def test_rt_metadata(tmp_path): # For text items, we still have to decode('ascii','replace') because # the tiff file format can't take 8 bit bytes in that field. - basetextdata = "This is some arbitrary metadata for a text field" - bindata = basetextdata.encode("ascii") + b" \xff" - textdata = basetextdata + " " + chr(255) - reloaded_textdata = basetextdata + " ?" - floatdata = 12.345 - doubledata = 67.89 + base_text_data = "This is some arbitrary metadata for a text field" + bin_data = base_text_data.encode("ascii") + b" \xff" + text_data = base_text_data + " " + chr(255) + reloaded_text_data = base_text_data + " ?" + float_data = 12.345 + double_data = 67.89 info = TiffImagePlugin.ImageFileDirectory() ImageJMetaData = TAG_IDS["ImageJMetaData"] ImageJMetaDataByteCounts = TAG_IDS["ImageJMetaDataByteCounts"] ImageDescription = TAG_IDS["ImageDescription"] - info[ImageJMetaDataByteCounts] = len(bindata) - info[ImageJMetaData] = bindata - info[TAG_IDS["RollAngle"]] = floatdata + info[ImageJMetaDataByteCounts] = len(bin_data) + info[ImageJMetaData] = bin_data + info[TAG_IDS["RollAngle"]] = float_data info.tagtype[TAG_IDS["RollAngle"]] = 11 - info[TAG_IDS["YawAngle"]] = doubledata + info[TAG_IDS["YawAngle"]] = double_data info.tagtype[TAG_IDS["YawAngle"]] = 12 - info[ImageDescription] = textdata + info[ImageDescription] = text_data f = str(tmp_path / "temp.tif") @@ -55,28 +55,28 @@ def test_rt_metadata(tmp_path): with Image.open(f) as loaded: - assert loaded.tag[ImageJMetaDataByteCounts] == (len(bindata),) - assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bindata),) + assert loaded.tag[ImageJMetaDataByteCounts] == (len(bin_data),) + assert loaded.tag_v2[ImageJMetaDataByteCounts] == (len(bin_data),) - assert loaded.tag[ImageJMetaData] == bindata - assert loaded.tag_v2[ImageJMetaData] == bindata + assert loaded.tag[ImageJMetaData] == bin_data + assert loaded.tag_v2[ImageJMetaData] == bin_data - assert loaded.tag[ImageDescription] == (reloaded_textdata,) - assert loaded.tag_v2[ImageDescription] == reloaded_textdata + assert loaded.tag[ImageDescription] == (reloaded_text_data,) + assert loaded.tag_v2[ImageDescription] == reloaded_text_data loaded_float = loaded.tag[TAG_IDS["RollAngle"]][0] - assert round(abs(loaded_float - floatdata), 5) == 0 + assert round(abs(loaded_float - float_data), 5) == 0 loaded_double = loaded.tag[TAG_IDS["YawAngle"]][0] - assert round(abs(loaded_double - doubledata), 7) == 0 + assert round(abs(loaded_double - double_data), 7) == 0 # check with 2 element ImageJMetaDataByteCounts, issue #2006 - info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8) + info[ImageJMetaDataByteCounts] = (8, len(bin_data) - 8) img.save(f, tiffinfo=info) with Image.open(f) as loaded: - assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) - assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bindata) - 8) + assert loaded.tag[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8) + assert loaded.tag_v2[ImageJMetaDataByteCounts] == (8, len(bin_data) - 8) def test_read_metadata(): diff --git a/Tests/test_image_copy.py b/Tests/test_image_copy.py index ad0391dbe..21e438654 100644 --- a/Tests/test_image_copy.py +++ b/Tests/test_image_copy.py @@ -6,8 +6,8 @@ from .helper import hopper def test_copy(): - croppedCoordinates = (10, 10, 20, 20) - croppedSize = (10, 10) + 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) @@ -23,15 +23,15 @@ def test_copy(): # Internal copy method on a cropped image im = hopper(mode) - out = im.crop(croppedCoordinates).copy() + out = im.crop(cropped_coordinates).copy() assert out.mode == im.mode - assert out.size == croppedSize + assert out.size == cropped_size # Python's copy method on a cropped image im = hopper(mode) - out = copy.copy(im.crop(croppedCoordinates)) + out = copy.copy(im.crop(cropped_coordinates)) assert out.mode == im.mode - assert out.size == croppedSize + assert out.size == cropped_size def test_copy_zero(): diff --git a/Tests/test_image_filter.py b/Tests/test_image_filter.py index df8c353f3..14a8da9f1 100644 --- a/Tests/test_image_filter.py +++ b/Tests/test_image_filter.py @@ -99,10 +99,10 @@ def test_rankfilter_properties(): def test_builtinfilter_p(): - builtinFilter = ImageFilter.BuiltinFilter() + builtin_filter = ImageFilter.BuiltinFilter() with pytest.raises(ValueError): - builtinFilter.filter(hopper("P")) + builtin_filter.filter(hopper("P")) def test_kernel_not_enough_coefficients(): diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 1c444fe27..0b3aecd08 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -35,9 +35,9 @@ class TestImageFile: parser = ImageFile.Parser() parser.feed(data) - imOut = parser.close() + im_out = parser.close() - return im, imOut + return im, im_out assert_image_equal(*roundtrip("BMP")) im1, im2 = roundtrip("GIF") diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 7cf237b46..628120cc4 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -65,12 +65,12 @@ def test_libtiff(): def test_consecutive(): with Image.open("Tests/images/multipage.tiff") as im: - firstFrame = None + first_frame = None for frame in ImageSequence.Iterator(im): - if firstFrame is None: - firstFrame = frame.copy() + if first_frame is None: + first_frame = frame.copy() for frame in ImageSequence.Iterator(im): - assert_image_equal(frame, firstFrame) + assert_image_equal(frame, first_frame) break diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 688af7113..6e8a2ac58 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -26,51 +26,51 @@ def test_basic(tmp_path): def basic(mode): - imIn = original.convert(mode) - verify(imIn) + im_in = original.convert(mode) + verify(im_in) - w, h = imIn.size + w, h = im_in.size - imOut = imIn.copy() - verify(imOut) # copy + im_out = im_in.copy() + verify(im_out) # copy - imOut = imIn.transform((w, h), Image.Transform.EXTENT, (0, 0, w, h)) - verify(imOut) # transform + im_out = im_in.transform((w, h), Image.Transform.EXTENT, (0, 0, w, h)) + verify(im_out) # transform filename = str(tmp_path / "temp.im") - imIn.save(filename) + im_in.save(filename) - with Image.open(filename) as imOut: + with Image.open(filename) as im_out: - verify(imIn) - verify(imOut) + verify(im_in) + verify(im_out) - imOut = imIn.crop((0, 0, w, h)) - verify(imOut) + im_out = im_in.crop((0, 0, w, h)) + verify(im_out) - imOut = Image.new(mode, (w, h), None) - imOut.paste(imIn.crop((0, 0, w // 2, h)), (0, 0)) - imOut.paste(imIn.crop((w // 2, 0, w, h)), (w // 2, 0)) + im_out = Image.new(mode, (w, h), None) + im_out.paste(im_in.crop((0, 0, w // 2, h)), (0, 0)) + im_out.paste(im_in.crop((w // 2, 0, w, h)), (w // 2, 0)) - verify(imIn) - verify(imOut) + verify(im_in) + verify(im_out) - imIn = Image.new(mode, (1, 1), 1) - assert imIn.getpixel((0, 0)) == 1 + im_in = Image.new(mode, (1, 1), 1) + assert im_in.getpixel((0, 0)) == 1 - imIn.putpixel((0, 0), 2) - assert imIn.getpixel((0, 0)) == 2 + im_in.putpixel((0, 0), 2) + assert im_in.getpixel((0, 0)) == 2 if mode == "L": maximum = 255 else: maximum = 32767 - imIn = Image.new(mode, (1, 1), 256) - assert imIn.getpixel((0, 0)) == min(256, maximum) + im_in = Image.new(mode, (1, 1), 256) + assert im_in.getpixel((0, 0)) == min(256, maximum) - imIn.putpixel((0, 0), 512) - assert imIn.getpixel((0, 0)) == min(512, maximum) + im_in.putpixel((0, 0), 512) + assert im_in.getpixel((0, 0)) == min(512, maximum) basic("L") diff --git a/src/PIL/GdImageFile.py b/src/PIL/GdImageFile.py index 9c34adaa6..1965c7fbd 100644 --- a/src/PIL/GdImageFile.py +++ b/src/PIL/GdImageFile.py @@ -54,20 +54,25 @@ class GdImageFile(ImageFile.ImageFile): self.mode = "L" # FIXME: "P" self._size = i16(s, 2), i16(s, 4) - trueColor = s[6] - trueColorOffset = 2 if trueColor else 0 + true_color = s[6] + true_color_offset = 2 if true_color else 0 # transparency index - tindex = i32(s, 7 + trueColorOffset) + tindex = i32(s, 7 + true_color_offset) if tindex < 256: self.info["transparency"] = tindex self.palette = ImagePalette.raw( - "XBGR", s[7 + trueColorOffset + 4 : 7 + trueColorOffset + 4 + 256 * 4] + "XBGR", s[7 + true_color_offset + 4 : 7 + true_color_offset + 4 + 256 * 4] ) self.tile = [ - ("raw", (0, 0) + self.size, 7 + trueColorOffset + 4 + 256 * 4, ("L", 0, 1)) + ( + "raw", + (0, 0) + self.size, + 7 + true_color_offset + 4 + 256 * 4, + ("L", 0, 1), + ) ] diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 610ccd4c7..f9782bc50 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -197,18 +197,18 @@ class ImageDraw: if width > 8: # Cover potential gaps between the line and the joint if flipped: - gapCoords = [ + gap_coords = [ coord_at_angle(point, angles[0] + 90), point, coord_at_angle(point, angles[1] + 90), ] else: - gapCoords = [ + gap_coords = [ coord_at_angle(point, angles[0] - 90), point, coord_at_angle(point, angles[1] - 90), ] - self.line(gapCoords, fill, width=3) + self.line(gap_coords, fill, width=3) def shape(self, shape, fill=None, outline=None): """(Experimental) Draw a shape.""" diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index 1320af8f9..af33c547e 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -439,22 +439,22 @@ class Color3DLUT(MultibandFilter): :param target_mode: Passed to the constructor of the resulting lookup table. """ - size1D, size2D, size3D = cls._check_size(size) + size_1d, size_2d, size_3d = cls._check_size(size) if channels not in (3, 4): raise ValueError("Only 3 or 4 output channels are supported") - table = [0] * (size1D * size2D * size3D * channels) + table = [0] * (size_1d * size_2d * size_3d * channels) idx_out = 0 - for b in range(size3D): - for g in range(size2D): - for r in range(size1D): + for b in range(size_3d): + for g in range(size_2d): + for r in range(size_1d): table[idx_out : idx_out + channels] = callback( - r / (size1D - 1), g / (size2D - 1), b / (size3D - 1) + r / (size_1d - 1), g / (size_2d - 1), b / (size_3d - 1) ) idx_out += channels return cls( - (size1D, size2D, size3D), + (size_1d, size_2d, size_3d), table, channels=channels, target_mode=target_mode, @@ -484,20 +484,20 @@ class Color3DLUT(MultibandFilter): raise ValueError("Only 3 or 4 output channels are supported") ch_in = self.channels ch_out = channels or ch_in - size1D, size2D, size3D = self.size + size_1d, size_2d, size_3d = self.size - table = [0] * (size1D * size2D * size3D * ch_out) + table = [0] * (size_1d * size_2d * size_3d * ch_out) idx_in = 0 idx_out = 0 - for b in range(size3D): - for g in range(size2D): - for r in range(size1D): + for b in range(size_3d): + for g in range(size_2d): + for r in range(size_1d): values = self.table[idx_in : idx_in + ch_in] if with_normals: values = callback( - r / (size1D - 1), - g / (size2D - 1), - b / (size3D - 1), + r / (size_1d - 1), + g / (size_2d - 1), + b / (size_3d - 1), *values, ) else: diff --git a/src/PIL/ImageStat.py b/src/PIL/ImageStat.py index ef4a1d633..1baef7db4 100644 --- a/src/PIL/ImageStat.py +++ b/src/PIL/ImageStat.py @@ -78,10 +78,10 @@ class Stat: v = [] for i in range(0, len(self.h), 256): - layerSum = 0.0 + layer_sum = 0.0 for j in range(256): - layerSum += j * self.h[i + j] - v.append(layerSum) + layer_sum += j * self.h[i + j] + v.append(layer_sum) return v def _getsum2(self): diff --git a/src/PIL/PcfFontFile.py b/src/PIL/PcfFontFile.py index 6a4eb22a6..0556c2bbc 100644 --- a/src/PIL/PcfFontFile.py +++ b/src/PIL/PcfFontFile.py @@ -193,15 +193,15 @@ class PcfFontFile(FontFile.FontFile): for i in range(nbitmaps): offsets.append(i32(fp.read(4))) - bitmapSizes = [] + bitmap_sizes = [] for i in range(4): - bitmapSizes.append(i32(fp.read(4))) + bitmap_sizes.append(i32(fp.read(4))) # byteorder = format & 4 # non-zero => MSB bitorder = format & 8 # non-zero => MSB padindex = format & 3 - bitmapsize = bitmapSizes[padindex] + bitmapsize = bitmap_sizes[padindex] offsets.append(bitmapsize) data = fp.read(bitmapsize) @@ -225,22 +225,22 @@ class PcfFontFile(FontFile.FontFile): fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) - firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2)) - firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2)) + first_col, last_col = i16(fp.read(2)), i16(fp.read(2)) + first_row, last_row = i16(fp.read(2)), i16(fp.read(2)) i16(fp.read(2)) # default - nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1) + nencoding = (last_col - first_col + 1) * (last_row - first_row + 1) - encodingOffsets = [i16(fp.read(2)) for _ in range(nencoding)] + encoding_offsets = [i16(fp.read(2)) for _ in range(nencoding)] - for i in range(firstCol, len(encoding)): + for i in range(first_col, len(encoding)): try: - encodingOffset = encodingOffsets[ + encoding_offset = encoding_offsets[ ord(bytearray([i]).decode(self.charset_encoding)) ] - if encodingOffset != 0xFFFF: - encoding[i] = encodingOffset + if encoding_offset != 0xFFFF: + encoding[i] = encoding_offset except UnicodeDecodeError: # character is not supported in selected encoding pass diff --git a/src/PIL/PdfImagePlugin.py b/src/PIL/PdfImagePlugin.py index 544035c8b..2109a6f52 100644 --- a/src/PIL/PdfImagePlugin.py +++ b/src/PIL/PdfImagePlugin.py @@ -87,21 +87,21 @@ def _save(im, fp, filename, save_all=False): for append_im in append_images: append_im.encoderinfo = im.encoderinfo.copy() ims.append(append_im) - numberOfPages = 0 + number_of_pages = 0 image_refs = [] page_refs = [] contents_refs = [] for im in ims: - im_numberOfPages = 1 + im_number_of_pages = 1 if save_all: try: - im_numberOfPages = im.n_frames + im_number_of_pages = im.n_frames except AttributeError: # Image format does not have n_frames. # It is a single frame image pass - numberOfPages += im_numberOfPages - for i in range(im_numberOfPages): + number_of_pages += im_number_of_pages + for i in range(im_number_of_pages): image_refs.append(existing_pdf.next_object_id(0)) page_refs.append(existing_pdf.next_object_id(0)) contents_refs.append(existing_pdf.next_object_id(0)) @@ -111,9 +111,9 @@ def _save(im, fp, filename, save_all=False): # catalog and list of pages existing_pdf.write_catalog() - pageNumber = 0 - for imSequence in ims: - im_pages = ImageSequence.Iterator(imSequence) if save_all else [imSequence] + page_number = 0 + for im_sequence in ims: + im_pages = ImageSequence.Iterator(im_sequence) if save_all else [im_sequence] for im in im_pages: # FIXME: Should replace ASCIIHexDecode with RunLengthDecode # (packbits) or LZWDecode (tiff/lzw compression). Note that @@ -176,7 +176,7 @@ def _save(im, fp, filename, save_all=False): width, height = im.size existing_pdf.write_obj( - image_refs[pageNumber], + image_refs[page_number], stream=op.getvalue(), Type=PdfParser.PdfName("XObject"), Subtype=PdfParser.PdfName("Image"), @@ -193,10 +193,10 @@ def _save(im, fp, filename, save_all=False): # page existing_pdf.write_page( - page_refs[pageNumber], + page_refs[page_number], Resources=PdfParser.PdfDict( ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], - XObject=PdfParser.PdfDict(image=image_refs[pageNumber]), + XObject=PdfParser.PdfDict(image=image_refs[page_number]), ), MediaBox=[ 0, @@ -204,7 +204,7 @@ def _save(im, fp, filename, save_all=False): width * 72.0 / resolution, height * 72.0 / resolution, ], - Contents=contents_refs[pageNumber], + Contents=contents_refs[page_number], ) # @@ -215,9 +215,9 @@ def _save(im, fp, filename, save_all=False): height * 72.0 / resolution, ) - existing_pdf.write_obj(contents_refs[pageNumber], stream=page_contents) + existing_pdf.write_obj(contents_refs[page_number], stream=page_contents) - pageNumber += 1 + page_number += 1 # # trailer diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 5f1ef6edc..0ef58cf41 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -138,7 +138,7 @@ def _save(im, fp, filename): # Flip the image, since the origin of SGI file is the bottom-left corner orientation = -1 # Define the file as SGI File Format - magicNumber = 474 + magic_number = 474 # Run-Length Encoding Compression - Unsupported at this time rle = 0 @@ -167,11 +167,11 @@ def _save(im, fp, filename): # Maximum Byte value (255 = 8bits per pixel) pinmax = 255 # Image name (79 characters max, truncated below in write) - imgName = os.path.splitext(os.path.basename(filename))[0] - imgName = imgName.encode("ascii", "ignore") + img_name = os.path.splitext(os.path.basename(filename))[0] + img_name = img_name.encode("ascii", "ignore") # Standard representation of pixel in the file colormap = 0 - fp.write(struct.pack(">h", magicNumber)) + fp.write(struct.pack(">h", magic_number)) fp.write(o8(rle)) fp.write(o8(bpc)) fp.write(struct.pack(">H", dim)) @@ -181,7 +181,7 @@ def _save(im, fp, filename): fp.write(struct.pack(">l", pinmin)) fp.write(struct.pack(">l", pinmax)) fp.write(struct.pack("4s", b"")) # dummy - fp.write(struct.pack("79s", imgName)) # truncates to 79 chars + fp.write(struct.pack("79s", img_name)) # truncates to 79 chars fp.write(struct.pack("s", b"")) # force null byte after imgname fp.write(struct.pack(">l", colormap)) fp.write(struct.pack("404s", b"")) # dummy diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index e96f58440..8b7144c83 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1336,14 +1336,14 @@ class TiffImageFile(ImageFile.ImageFile): logger.debug(f"- size: {self.size}") - sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,)) - if len(sampleFormat) > 1 and max(sampleFormat) == min(sampleFormat) == 1: + sample_format = self.tag_v2.get(SAMPLEFORMAT, (1,)) + if len(sample_format) > 1 and max(sample_format) == min(sample_format) == 1: # SAMPLEFORMAT is properly per band, so an RGB image will # be (1,1,1). But, we don't support per band pixel types, # and anything more than one band is a uint8. So, just # take the first element. Revisit this if adding support # for more exotic images. - sampleFormat = (1,) + sample_format = (1,) bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) @@ -1364,18 +1364,18 @@ class TiffImageFile(ImageFile.ImageFile): # presume it is the same number of bits for all of the samples. bps_tuple = bps_tuple * bps_count - samplesPerPixel = self.tag_v2.get( + samples_per_pixel = self.tag_v2.get( SAMPLESPERPIXEL, 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, ) - if len(bps_tuple) != samplesPerPixel: + if len(bps_tuple) != samples_per_pixel: raise SyntaxError("unknown data organization") # mode: check photometric interpretation and bits per pixel key = ( self.tag_v2.prefix, photo, - sampleFormat, + sample_format, fillorder, bps_tuple, extra_tuple, @@ -1904,20 +1904,20 @@ class AppendingTiffWriter: # fix offsets self.f.seek(self.offsetOfNewPage) - IIMM = self.f.read(4) - if not IIMM: + iimm = self.f.read(4) + if not iimm: # raise RuntimeError("nothing written into new page") # Make it easy to finish a frame without committing to a new one. return - if IIMM != self.IIMM: + if iimm != self.IIMM: raise RuntimeError("IIMM of new page doesn't match IIMM of first page") - IFDoffset = self.readLong() - IFDoffset += self.offsetOfNewPage + ifd_offset = self.readLong() + ifd_offset += self.offsetOfNewPage self.f.seek(self.whereToWriteNewIFDOffset) - self.writeLong(IFDoffset) - self.f.seek(IFDoffset) + self.writeLong(ifd_offset) + self.f.seek(ifd_offset) self.fixIFD() def newFrame(self): @@ -1948,9 +1948,9 @@ class AppendingTiffWriter: pos = self.f.tell() # pad to 16 byte boundary - padBytes = 16 - pos % 16 - if 0 < padBytes < 16: - self.f.write(bytes(padBytes)) + pad_bytes = 16 - pos % 16 + if 0 < pad_bytes < 16: + self.f.write(bytes(pad_bytes)) self.offsetOfNewPage = self.f.tell() def setEndian(self, endian): @@ -1961,14 +1961,14 @@ class AppendingTiffWriter: def skipIFDs(self): while True: - IFDoffset = self.readLong() - if IFDoffset == 0: + ifd_offset = self.readLong() + if ifd_offset == 0: self.whereToWriteNewIFDOffset = self.f.tell() - 4 break - self.f.seek(IFDoffset) - numTags = self.readShort() - self.f.seek(numTags * 12, os.SEEK_CUR) + self.f.seek(ifd_offset) + num_tags = self.readShort() + self.f.seek(num_tags * 12, os.SEEK_CUR) def write(self, data): return self.f.write(data) @@ -1983,68 +1983,68 @@ class AppendingTiffWriter: def rewriteLastShortToLong(self, value): self.f.seek(-2, os.SEEK_CUR) - bytesWritten = self.f.write(struct.pack(self.longFmt, value)) - if bytesWritten is not None and bytesWritten != 4: - raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + bytes_written = self.f.write(struct.pack(self.longFmt, value)) + if bytes_written is not None and bytes_written != 4: + raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 4") def rewriteLastShort(self, value): self.f.seek(-2, os.SEEK_CUR) - bytesWritten = self.f.write(struct.pack(self.shortFmt, value)) - if bytesWritten is not None and bytesWritten != 2: - raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 2") + bytes_written = self.f.write(struct.pack(self.shortFmt, value)) + if bytes_written is not None and bytes_written != 2: + raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 2") def rewriteLastLong(self, value): self.f.seek(-4, os.SEEK_CUR) - bytesWritten = self.f.write(struct.pack(self.longFmt, value)) - if bytesWritten is not None and bytesWritten != 4: - raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + bytes_written = self.f.write(struct.pack(self.longFmt, value)) + if bytes_written is not None and bytes_written != 4: + raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 4") def writeShort(self, value): - bytesWritten = self.f.write(struct.pack(self.shortFmt, value)) - if bytesWritten is not None and bytesWritten != 2: - raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 2") + bytes_written = self.f.write(struct.pack(self.shortFmt, value)) + if bytes_written is not None and bytes_written != 2: + raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 2") def writeLong(self, value): - bytesWritten = self.f.write(struct.pack(self.longFmt, value)) - if bytesWritten is not None and bytesWritten != 4: - raise RuntimeError(f"wrote only {bytesWritten} bytes but wanted 4") + bytes_written = self.f.write(struct.pack(self.longFmt, value)) + if bytes_written is not None and bytes_written != 4: + raise RuntimeError(f"wrote only {bytes_written} bytes but wanted 4") def close(self): self.finalize() self.f.close() def fixIFD(self): - numTags = self.readShort() + num_tags = self.readShort() - for i in range(numTags): - tag, fieldType, count = struct.unpack(self.tagFormat, self.f.read(8)) + for i in range(num_tags): + tag, field_type, count = struct.unpack(self.tagFormat, self.f.read(8)) - fieldSize = self.fieldSizes[fieldType] - totalSize = fieldSize * count - isLocal = totalSize <= 4 - if not isLocal: + field_size = self.fieldSizes[field_type] + total_size = field_size * count + is_local = total_size <= 4 + if not is_local: offset = self.readLong() offset += self.offsetOfNewPage self.rewriteLastLong(offset) if tag in self.Tags: - curPos = self.f.tell() + cur_pos = self.f.tell() - if isLocal: + if is_local: self.fixOffsets( - count, isShort=(fieldSize == 2), isLong=(fieldSize == 4) + count, isShort=(field_size == 2), isLong=(field_size == 4) ) - self.f.seek(curPos + 4) + self.f.seek(cur_pos + 4) else: self.f.seek(offset) self.fixOffsets( - count, isShort=(fieldSize == 2), isLong=(fieldSize == 4) + count, isShort=(field_size == 2), isLong=(field_size == 4) ) - self.f.seek(curPos) + self.f.seek(cur_pos) - offset = curPos = None + offset = cur_pos = None - elif isLocal: + elif is_local: # skip the locally stored value that is not an offset self.f.seek(4, os.SEEK_CUR) From d241e381200b79343755d5d8a1c70051ac5b3723 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 21:20:48 +0300 Subject: [PATCH 069/294] [Private] function names should be snake_case --- Tests/test_util.py | 10 +++++----- src/PIL/Image.py | 8 ++++---- src/PIL/ImageFile.py | 4 ++-- src/PIL/ImageFont.py | 8 ++++---- src/PIL/ImageQt.py | 4 ++-- src/PIL/_util.py | 8 ++++---- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Tests/test_util.py b/Tests/test_util.py index b5bfca012..58a8ff4f6 100644 --- a/Tests/test_util.py +++ b/Tests/test_util.py @@ -8,7 +8,7 @@ def test_is_path(): fp = "filename.ext" # Act - it_is = _util.isPath(fp) + it_is = _util.is_path(fp) # Assert assert it_is @@ -21,7 +21,7 @@ def test_path_obj_is_path(): test_path = Path("filename.ext") # Act - it_is = _util.isPath(test_path) + it_is = _util.is_path(test_path) # Assert assert it_is @@ -33,7 +33,7 @@ def test_is_not_path(tmp_path): pass # Act - it_is_not = _util.isPath(fp) + it_is_not = _util.is_path(fp) # Assert assert not it_is_not @@ -44,7 +44,7 @@ def test_is_directory(): directory = "Tests" # Act - it_is = _util.isDirectory(directory) + it_is = _util.is_directory(directory) # Assert assert it_is @@ -55,7 +55,7 @@ def test_is_not_directory(): text = "abc" # Act - it_is_not = _util.isDirectory(text) + it_is_not = _util.is_directory(text) # Assert assert not it_is_not diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ab391b9b4..504c57643 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -50,7 +50,7 @@ except ImportError: # Use __version__ instead. from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins from ._binary import i32le, o32be, o32le -from ._util import deferred_error, isPath +from ._util import deferred_error, is_path def __getattr__(name): @@ -2251,7 +2251,7 @@ class Image: if isinstance(fp, Path): filename = str(fp) open_fp = True - elif isPath(fp): + elif is_path(fp): filename = fp open_fp = True elif fp == sys.stdout: @@ -2259,7 +2259,7 @@ class Image: fp = sys.stdout.buffer except AttributeError: pass - if not filename and hasattr(fp, "name") and isPath(fp.name): + if not filename and hasattr(fp, "name") and is_path(fp.name): # only set the name for metadata purposes filename = fp.name @@ -3065,7 +3065,7 @@ def open(fp, mode="r", formats=None): filename = "" if isinstance(fp, Path): filename = str(fp.resolve()) - elif isPath(fp): + elif is_path(fp): filename = fp if filename: diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 34f344f1d..a4e7f625e 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -33,7 +33,7 @@ import struct import sys from . import Image -from ._util import isPath +from ._util import is_path MAXBLOCK = 65536 @@ -99,7 +99,7 @@ class ImageFile(Image.Image): self.decoderconfig = () self.decodermaxblock = MAXBLOCK - if isPath(fp): + if is_path(fp): # filename self.fp = open(fp, "rb") self.filename = fp diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index f21b6de71..e9f1ab544 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -33,7 +33,7 @@ from enum import IntEnum from io import BytesIO from . import Image -from ._util import isDirectory, isPath +from ._util import is_directory, is_path class Layout(IntEnum): @@ -212,7 +212,7 @@ class FreeTypeFont: "", size, index, encoding, self.font_bytes, layout_engine ) - if isPath(font): + if is_path(font): if sys.platform == "win32": font_bytes_path = font if isinstance(font, bytes) else font.encode() try: @@ -877,7 +877,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None): try: return freetype(font) except OSError: - if not isPath(font): + if not is_path(font): raise ttf_filename = os.path.basename(font) @@ -931,7 +931,7 @@ def load_path(filename): :exception OSError: If the file could not be read. """ for directory in sys.path: - if isDirectory(directory): + if is_directory(directory): if not isinstance(filename, str): filename = filename.decode("utf-8") try: diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index db8fa0fa9..c8143d394 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -20,7 +20,7 @@ import sys from io import BytesIO from . import Image -from ._util import isPath +from ._util import is_path qt_versions = [ ["6", "PyQt6"], @@ -140,7 +140,7 @@ def _toqclass_helper(im): if hasattr(im, "toUtf8"): # FIXME - is this really the best way to do this? im = str(im.toUtf8(), "utf-8") - if isPath(im): + if is_path(im): im = Image.open(im) exclusive_fp = True diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 0c5d3892e..48d41c752 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -2,13 +2,13 @@ import os from pathlib import Path -def isPath(f): +def is_path(f): return isinstance(f, (bytes, str, Path)) -# Checks if an object is a string, and that it points to a directory. -def isDirectory(f): - return isPath(f) and os.path.isdir(f) +def is_directory(f): + """Checks if an object is a string, and that it points to a directory.""" + return is_path(f) and os.path.isdir(f) class deferred_error: From 7fa92c67b1471a66739c4768cdef616c27675981 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 21:21:50 +0300 Subject: [PATCH 070/294] [Private] class names should be CamelCase --- Tests/test_util.py | 2 +- src/PIL/Image.py | 6 +++--- src/PIL/ImageCms.py | 4 ++-- src/PIL/ImageFont.py | 4 ++-- src/PIL/PyAccess.py | 4 ++-- src/PIL/_util.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Tests/test_util.py b/Tests/test_util.py index 58a8ff4f6..9efbdd1f3 100644 --- a/Tests/test_util.py +++ b/Tests/test_util.py @@ -65,7 +65,7 @@ def test_deferred_error(): # Arrange # Act - thing = _util.deferred_error(ValueError("Some error text")) + thing = _util.DeferredError(ValueError("Some error text")) # Assert with pytest.raises(ValueError): diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 504c57643..a117dfada 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -50,7 +50,7 @@ except ImportError: # Use __version__ instead. from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins from ._binary import i32le, o32be, o32le -from ._util import deferred_error, is_path +from ._util import DeferredError, is_path def __getattr__(name): @@ -139,7 +139,7 @@ try: ) except ImportError as v: - core = deferred_error(ImportError("The _imaging C module is not installed.")) + core = DeferredError(ImportError("The _imaging C module is not installed.")) # Explanations for ways that we know we might have an import error if str(v).startswith("Module use of python"): # The _imaging C module is present, but not compiled for @@ -613,7 +613,7 @@ class Image: # Instead of simply setting to None, we're setting up a # deferred error that will better explain that the core image # object is gone. - self.im = deferred_error(ValueError("Operation on closed image")) + self.im = DeferredError(ValueError("Operation on closed image")) def _copy(self): self.load() diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index ea328e149..0da39b513 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -26,9 +26,9 @@ try: except ImportError as ex: # Allow error import for doc purposes, but error out when accessing # anything in core. - from ._util import deferred_error + from ._util import DeferredError - _imagingcms = deferred_error(ex) + _imagingcms = DeferredError(ex) DESCRIPTION = """ pyCMS diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index e9f1ab544..8af13255c 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -64,7 +64,7 @@ def __getattr__(name): raise AttributeError(f"module '{__name__}' has no attribute '{name}'") -class _imagingft_not_installed: +class _ImagingFtNotInstalled: # module placeholder def __getattr__(self, id): raise ImportError("The _imagingft C module is not installed") @@ -73,7 +73,7 @@ class _imagingft_not_installed: try: from . import _imagingft as core except ImportError: - core = _imagingft_not_installed() + core = _ImagingFtNotInstalled() # FIXME: add support for pilfont2 format (see FontFile.py) diff --git a/src/PIL/PyAccess.py b/src/PIL/PyAccess.py index eeaa0ccc4..b508cda9a 100644 --- a/src/PIL/PyAccess.py +++ b/src/PIL/PyAccess.py @@ -39,9 +39,9 @@ try: except ImportError as ex: # Allow error import for doc purposes, but error out when accessing # anything in core. - from ._util import deferred_error + from ._util import DeferredError - FFI = ffi = deferred_error(ex) + FFI = ffi = DeferredError(ex) logger = logging.getLogger(__name__) diff --git a/src/PIL/_util.py b/src/PIL/_util.py index 48d41c752..ba27b7e49 100644 --- a/src/PIL/_util.py +++ b/src/PIL/_util.py @@ -11,7 +11,7 @@ def is_directory(f): return is_path(f) and os.path.isdir(f) -class deferred_error: +class DeferredError: def __init__(self, ex): self.ex = ex From fe4c859fc1b497a82e4903ba5491e7a4f937c4e3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 21:24:15 +0300 Subject: [PATCH 071/294] [Private] argument name should be snake_case --- src/PIL/GifImagePlugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index b798bb969..d36d8c61a 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -883,10 +883,10 @@ def _get_palette_bytes(im): return im.palette.palette -def _get_background(im, infoBackground): +def _get_background(im, info_background): background = 0 - if infoBackground: - background = infoBackground + if info_background: + background = info_background if isinstance(background, tuple): # WebPImagePlugin stores an RGBA value in info["background"] # So it must be converted to the same format as GifImagePlugin's From ee59485683b801611ed97de2826185ea90b64216 Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 11:53:03 +0430 Subject: [PATCH 072/294] Change pre/code to head and items --- CHANGES.rst | 1139 ++++++++++++++++++++++++++------------------------- 1 file changed, 575 insertions(+), 564 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index dc69e4587..5b381c6c0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4785,10 +4785,10 @@ Changelog (Pillow) [aclark4life] Pre-fork --------- +======== 0.2b5-1.1.7 -+++++++++++ +----------- :: @@ -4822,7 +4822,7 @@ Pre-fork Ka-Ping Yee, and many others (if your name should be on this list, let me know.) - *** Changes from release 1.1.6 to 1.1.7 *** +Changes from release 1.1.6 to 1.1.7 *** This section may not be fully complete. For changes since this file was last updated, see the repository revision history: @@ -5747,815 +5747,826 @@ Pre-fork appropriate), rather than writing weird things to disk for "xv" to choke upon. (bug reported by Les Schaffer). - (1.0b2 released) +1.0b2 released +-------------- - + Major speedups for rotate, transform(EXTENT), and transform(AFFINE) - when using nearest neighbour resampling. ++ Major speedups for rotate, transform(EXTENT), and transform(AFFINE) + when using nearest neighbour resampling. - + Modified ImageDraw to be compatible with the Arrow graphics - interface. See the handbook for details. ++ Modified ImageDraw to be compatible with the Arrow graphics + interface. See the handbook for details. - + PIL now automatically loads file codecs when used as a package - (from The Dragon De Monsyne). Also included an __init__.py file - in the standard distribution. ++ PIL now automatically loads file codecs when used as a package + (from The Dragon De Monsyne). Also included an __init__.py file + in the standard distribution. - + The GIF encoder has been modified to produce much smaller files. ++ The GIF encoder has been modified to produce much smaller files. - PIL now uses a run-length encoding method to encode GIF files. - On a random selection of GIF images grabbed from the web, this - version makes the images about twice as large as the original - LZW files, where the earlier version made them over 5 times - larger. YMMV, of course. + PIL now uses a run-length encoding method to encode GIF files. + On a random selection of GIF images grabbed from the web, this + version makes the images about twice as large as the original + LZW files, where the earlier version made them over 5 times + larger. YMMV, of course. - + Added PCX write support (works with "1", "P", "L", and "RGB") ++ Added PCX write support (works with "1", "P", "L", and "RGB") - + Added "bitmap" and "textsize" methods to ImageDraw. ++ Added "bitmap" and "textsize" methods to ImageDraw. - + Improved font rendering code. Fixed a bug or two, and moved - most of the time critical stuff to C. ++ Improved font rendering code. Fixed a bug or two, and moved + most of the time critical stuff to C. - + Removed "bdf2pil.py". Use "pilfont.py" instead! ++ Removed "bdf2pil.py". Use "pilfont.py" instead! - + Improved 16-bit support (still experimental, though). ++ Improved 16-bit support (still experimental, though). - The following methods now support "I;16" and "I;16B" images: - "getpixel", "copy", "convert" (to and from mode "I"), "resize", - "rotate", and "transform" with nearest neighbour filters, and - "save" using the IM format. The "new" and "open" functions - also work as expected. On Windows, 16-bit files are memory - mapped. + The following methods now support "I;16" and "I;16B" images: + "getpixel", "copy", "convert" (to and from mode "I"), "resize", + "rotate", and "transform" with nearest neighbour filters, and + "save" using the IM format. The "new" and "open" functions + also work as expected. On Windows, 16-bit files are memory + mapped. - NOTE: ALL other operations are still UNDEFINED on 16-bit images. + NOTE: ALL other operations are still UNDEFINED on 16-bit images. - + The "paste" method now supports constant sources. ++ The "paste" method now supports constant sources. - Just pass a colour value (a number or a tuple, depending on - the target image mode) instead of the source image. + Just pass a colour value (a number or a tuple, depending on + the target image mode) instead of the source image. - This was in fact implemented in an inefficient way in - earlier versions (the "paste" method generated a temporary - source image if you passed it a colour instead of an image). - In this version, this is handled on the C level instead. + This was in fact implemented in an inefficient way in + earlier versions (the "paste" method generated a temporary + source image if you passed it a colour instead of an image). + In this version, this is handled on the C level instead. - + Added experimental "RGBa" mode support. ++ Added experimental "RGBa" mode support. - An "RGBa" image is an RGBA image where the colour components - have have been premultiplied with the alpha value. PIL allows - you to convert an RGBA image to an RGBa image, and to paste - RGBa images on top of RGB images. Since this saves a bunch - of multiplications and shifts, it is typically about twice - as fast an ordinary RGBA paste. + An "RGBa" image is an RGBA image where the colour components + have have been premultiplied with the alpha value. PIL allows + you to convert an RGBA image to an RGBa image, and to paste + RGBa images on top of RGB images. Since this saves a bunch + of multiplications and shifts, it is typically about twice + as fast an ordinary RGBA paste. - + Eliminated extra conversion step when pasting "RGBA" or "RGBa" - images on top of "RGB" images. ++ Eliminated extra conversion step when pasting "RGBA" or "RGBa" + images on top of "RGB" images. - + Fixed Image.BICUBIC resampling for "RGB" images. ++ Fixed Image.BICUBIC resampling for "RGB" images. - + Fixed PCX image file handler to properly read 8-bit PCX - files (bug introduced in 1.0b1, reported by Bernhard - Herzog) ++ Fixed PCX image file handler to properly read 8-bit PCX + files (bug introduced in 1.0b1, reported by Bernhard + Herzog) - + Fixed PSDraw "image" method to restore the coordinate - system. ++ Fixed PSDraw "image" method to restore the coordinate + system. - + Fixed "blend" problem when applied to images that was - not already loaded (reported by Edward C. Jones) ++ Fixed "blend" problem when applied to images that was + not already loaded (reported by Edward C. Jones) - + Fixed -f option to "pilconvert.py" (from Anthony Baxter) ++ Fixed -f option to "pilconvert.py" (from Anthony Baxter) - (1.0b1 released) +1.0b1 released +-------------- - + Added Toby J. Sargeant's quantization package. To enable - quantization, use the "palette" option to "convert": ++ Added Toby J. Sargeant's quantization package. To enable + quantization, use the "palette" option to "convert": - imOut = im.convert("P", palette=Image.ADAPTIVE) + imOut = im.convert("P", palette=Image.ADAPTIVE) - This can be used with "L", "P", and "RGB" images. In this - version, dithering cannot be used with adaptive palettes. + This can be used with "L", "P", and "RGB" images. In this + version, dithering cannot be used with adaptive palettes. - Note: ADAPTIVE currently maps to median cut quantization - with 256 colours. The quantization package also contains - a maximum coverage quantizer, which will be supported by - future versions of PIL. + Note: ADAPTIVE currently maps to median cut quantization + with 256 colours. The quantization package also contains + a maximum coverage quantizer, which will be supported by + future versions of PIL. - + Added Eric S. Raymond's "pildriver" image calculator to the - distribution. See the docstring for more information. ++ Added Eric S. Raymond's "pildriver" image calculator to the + distribution. See the docstring for more information. - + The "offset" method no longer dumps core if given positive - offsets (from Charles Waldman). ++ The "offset" method no longer dumps core if given positive + offsets (from Charles Waldman). - + Fixed a resource leak that could cause ImageWin to run out of - GDI resources (from Roger Burnham). ++ Fixed a resource leak that could cause ImageWin to run out of + GDI resources (from Roger Burnham). - + Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired - by code contributed by Richard Jones). ++ Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired + by code contributed by Richard Jones). - + Added experimental 16-bit support, via modes "I;16" (little endian - data) and "I;16B" (big endian). Only a few methods properly support - such images (see above). ++ Added experimental 16-bit support, via modes "I;16" (little endian + data) and "I;16B" (big endian). Only a few methods properly support + such images (see above). - + Added XV thumbnail file handler (from Gene Cash). ++ Added XV thumbnail file handler (from Gene Cash). - + Fixed BMP image file handler to handle palette images with small - palettes (from Rob Hooft). ++ Fixed BMP image file handler to handle palette images with small + palettes (from Rob Hooft). - + Fixed Sun raster file handler for palette images (from Charles - Waldman). ++ Fixed Sun raster file handler for palette images (from Charles + Waldman). - + Improved various internal error messages. ++ Improved various internal error messages. - + Fixed Path constructor to handle arbitrary sequence objects. This - also affects the ImageDraw class (from Richard Jones). ++ Fixed Path constructor to handle arbitrary sequence objects. This + also affects the ImageDraw class (from Richard Jones). - + Fixed a bug in JpegDecode that caused PIL to report "decoder error - -2" for some progressive JPEG files (reported by Magnus Källström, - who also provided samples). ++ Fixed a bug in JpegDecode that caused PIL to report "decoder error + -2" for some progressive JPEG files (reported by Magnus Källström, + who also provided samples). - + Fixed a bug in JpegImagePlugin that caused PIL to hang when loading - JPEG files using 16-bit quantization tables. ++ Fixed a bug in JpegImagePlugin that caused PIL to hang when loading + JPEG files using 16-bit quantization tables. - + The Image "transform" method now supports Image.QUAD transforms. - The data argument is an 8-tuple giving the upper left, lower - left, lower right, and upper right corner of the source quadri- - lateral. Also added Image.MESH transform which takes a list - of quadrilaterals. ++ The Image "transform" method now supports Image.QUAD transforms. + The data argument is an 8-tuple giving the upper left, lower + left, lower right, and upper right corner of the source quadri- + lateral. Also added Image.MESH transform which takes a list + of quadrilaterals. - + The Image "resize", "rotate", and "transform" methods now support - Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. - Filters can be used with all transform methods. ++ The Image "resize", "rotate", and "transform" methods now support + Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. + Filters can be used with all transform methods. - + The ImageDraw "rectangle" method now includes both the right - and the bottom edges when drawing filled rectangles. ++ The ImageDraw "rectangle" method now includes both the right + and the bottom edges when drawing filled rectangles. - + The TGA decoder now works properly for runlength encoded images - which have more than one byte per pixel. ++ The TGA decoder now works properly for runlength encoded images + which have more than one byte per pixel. - + "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") ++ "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") - + Some file drivers didn't handle the optional "modify" argument - to the load method. This resulted in exceptions when you used - "paste" (and other methods that modify an image in place) on a - newly opened file. ++ Some file drivers didn't handle the optional "modify" argument + to the load method. This resulted in exceptions when you used + "paste" (and other methods that modify an image in place) on a + newly opened file. - *** Changes from release 0.2 (b5) to 0.3 (b2) *** +0.3b2 released +-------------- - (0.3b2 released) +The test suite includes 825 individual tests. - The test suite includes 825 individual tests. ++ An Image "getbands" method has been added. It returns a tuple + containing the individual band names for this image. To figure + out how many bands an image has, use "len(im.getbands())". - + An Image "getbands" method has been added. It returns a tuple - containing the individual band names for this image. To figure - out how many bands an image has, use "len(im.getbands())". ++ An Image "putpixel" method has been added. - + An Image "putpixel" method has been added. ++ The Image "point" method can now be used to convert "L" images + to any other format, via a lookup table. That table should + contain 256 values for each band in the output image. - + The Image "point" method can now be used to convert "L" images - to any other format, via a lookup table. That table should - contain 256 values for each band in the output image. ++ Some file drivers (including FLI/FLC, GIF, and IM) accidentally + overwrote the offset method with an internal attribute. All + drivers have been updated to use private attributes where + possible. - + Some file drivers (including FLI/FLC, GIF, and IM) accidentally - overwrote the offset method with an internal attribute. All - drivers have been updated to use private attributes where - possible. ++ The Image "histogram" method now works for "I" and "F" images. + For these modes, PIL divides the range between the min and + max values used in the image into 256 bins. You can also + pass in your own min and max values via the "extrema" option: - + The Image "histogram" method now works for "I" and "F" images. - For these modes, PIL divides the range between the min and - max values used in the image into 256 bins. You can also - pass in your own min and max values via the "extrema" option: + h = im.histogram(extrema=(0, 255)) - h = im.histogram(extrema=(0, 255)) ++ An Image "getextrema" method has been added. It returns the + min and max values used in the image. In this release, this + works for single band images only. - + An Image "getextrema" method has been added. It returns the - min and max values used in the image. In this release, this - works for single band images only. ++ Changed the PNG driver to load and save mode "I" images as + 16-bit images. When saving, values outside the range 0..65535 + are clipped. - + Changed the PNG driver to load and save mode "I" images as - 16-bit images. When saving, values outside the range 0..65535 - are clipped. ++ Fixed ImageFont.py to work with the new "pilfont" compiler. - + Fixed ImageFont.py to work with the new "pilfont" compiler. ++ Added JPEG "save" and "draft" support for mode "YCbCr" images. + Note that if you save an "YCbCr" image as a JPEG file and read + it back, it is read as an RGB file. To get around this, you + can use the "draft" method: - + Added JPEG "save" and "draft" support for mode "YCbCr" images. - Note that if you save an "YCbCr" image as a JPEG file and read - it back, it is read as an RGB file. To get around this, you - can use the "draft" method: + im = Image.open("color.jpg") + im.draft("YCbCr", im.size) - im = Image.open("color.jpg") - im.draft("YCbCr", im.size) ++ Read "RGBA" TGA images. Also fixed the orientation bug; all + images should now come out the right way. - + Read "RGBA" TGA images. Also fixed the orientation bug; all - images should now come out the right way. ++ Changed mode name (and internal representation) from "YCrCb" + to "YCbCr" (!) + *** WARNING: MAY BREAK EXISTING CODE *** - + Changed mode name (and internal representation) from "YCrCb" - to "YCbCr" (!) - *** WARNING: MAY BREAK EXISTING CODE *** +0.3b1 released +-------------- - (0.3b1 released) +The test suite includes 750 individual tests. - The test suite includes 750 individual tests. ++ The "pilfont" package is now included in the standard PIL + distribution. The pilfont utility can be used to convert + X BDF and PCF raster font files to a format understood by + the ImageFont module. - + The "pilfont" package is now included in the standard PIL - distribution. The pilfont utility can be used to convert - X BDF and PCF raster font files to a format understood by - the ImageFont module. ++ GIF files are now interlaced by default. To write a + non-interlaced file, pass interlace=0 to the "save" + method. - + GIF files are now interlaced by default. To write a - non-interlaced file, pass interlace=0 to the "save" - method. ++ The default string format has changed for the "fromstring" + and "tostring" methods. + *** WARNING: MAY BREAK EXISTING CODE *** - + The default string format has changed for the "fromstring" - and "tostring" methods. - *** WARNING: MAY BREAK EXISTING CODE *** + NOTE: If no extra arguments are given, the first line in + the string buffer is the top line of the image, instead of + the bottom line. For RGB images, the string now contains + 3 bytes per pixel instead of 4. These changes were made + to make the methods compatible with the "fromstring" + factory function. - NOTE: If no extra arguments are given, the first line in - the string buffer is the top line of the image, instead of - the bottom line. For RGB images, the string now contains - 3 bytes per pixel instead of 4. These changes were made - to make the methods compatible with the "fromstring" - factory function. + To get the old behaviour, use the following syntax: - To get the old behaviour, use the following syntax: + data = im.tostring("raw", "RGBX", 0, -1) + im.fromstring(data, "raw", "RGBX", 0, -1) - data = im.tostring("raw", "RGBX", 0, -1) - im.fromstring(data, "raw", "RGBX", 0, -1) ++ "new" no longer gives a MemoryError if the width or height + is zero (this only happened on platforms where malloc(0) + or calloc(0) returns NULL). - + "new" no longer gives a MemoryError if the width or height - is zero (this only happened on platforms where malloc(0) - or calloc(0) returns NULL). ++ "new" now adds a default palette object to "P" images. - + "new" now adds a default palette object to "P" images. ++ You can now convert directly between all modes supported by + PIL. When converting colour images to "P", PIL defaults to + a "web" palette and dithering. When converting greyscale + images to "1", PIL uses a thresholding and dithering. - + You can now convert directly between all modes supported by - PIL. When converting colour images to "P", PIL defaults to - a "web" palette and dithering. When converting greyscale - images to "1", PIL uses a thresholding and dithering. ++ Added a "dither" option to "convert". By default, "convert" + uses floyd-steinberg error diffusion for "P" and "1" targets, + so this option is only used to *disable* dithering. Allowed + values are NONE (no dithering) or FLOYDSTEINBERG (default). - + Added a "dither" option to "convert". By default, "convert" - uses floyd-steinberg error diffusion for "P" and "1" targets, - so this option is only used to *disable* dithering. Allowed - values are NONE (no dithering) or FLOYDSTEINBERG (default). + imOut = im.convert("P", dither=Image.NONE) - imOut = im.convert("P", dither=Image.NONE) ++ Added a full set of "I" decoders. You can use "fromstring" + (and file decoders) to read any standard integer type as an + "I" image. - + Added a full set of "I" decoders. You can use "fromstring" - (and file decoders) to read any standard integer type as an - "I" image. ++ Added some support for "YCbCr" images (creation, conversion + from/to "L" and "RGB", IM YCC load/save) - + Added some support for "YCbCr" images (creation, conversion - from/to "L" and "RGB", IM YCC load/save) ++ "getpixel" now works properly with fractional coordinates. - + "getpixel" now works properly with fractional coordinates. ++ ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", + "RGBX", "CMYK", and "YCbCr" images. - + ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", - "RGBX", "CMYK", and "YCbCr" images. ++ ImImagePlugin no longer attaches palettes to "RGB" images. - + ImImagePlugin no longer attaches palettes to "RGB" images. ++ Various minor fixes. - + Various minor fixes. +0.3a4 released +-------------- - (0.3a4 released) ++ Added experimental IPTC/NAA support. - + Added experimental IPTC/NAA support. ++ Eliminated AttributeError exceptions after "crop" (from + Skip Montanaro) - + Eliminated AttributeError exceptions after "crop" (from - Skip Montanaro) ++ Reads some uncompressed formats via memory mapping (this + is currently supported on Win32 only) - + Reads some uncompressed formats via memory mapping (this - is currently supported on Win32 only) ++ Fixed some last minute glitches in the last alpha release + (Types instead of types in Image.py, version numbers, etc.) - + Fixed some last minute glitches in the last alpha release - (Types instead of types in Image.py, version numbers, etc.) ++ Eliminated some more bogus compiler warnings. - + Eliminated some more bogus compiler warnings. ++ Various fixes to make PIL compile and run smoother on Macs + (from Jack Jansen). - + Various fixes to make PIL compile and run smoother on Macs - (from Jack Jansen). ++ Fixed "fromstring" and "tostring" for mode "I" images. - + Fixed "fromstring" and "tostring" for mode "I" images. +0.3a3 released +-------------- - (0.3a3 released) +The test suite includes 530 individual tests. - The test suite includes 530 individual tests. ++ Eliminated unexpected side-effect in "paste" with matte. "paste" + now works properly also if compiled with "gcc". - + Eliminated unexpected side-effect in "paste" with matte. "paste" - now works properly also if compiled with "gcc". ++ Adapted to Python 1.5 (build issues only) - + Adapted to Python 1.5 (build issues only) ++ Fixed the ImageDraw "point" method to draw also the last + point (!). - + Fixed the ImageDraw "point" method to draw also the last - point (!). ++ Added "I" and "RGBX" support to Image.new. - + Added "I" and "RGBX" support to Image.new. ++ The plugin path is now properly prepended to the module search + path when a plugin module is imported. - + The plugin path is now properly prepended to the module search - path when a plugin module is imported. ++ Added "draw" method to the ImageWin.Dib class. This is used by + Topaz to print images on Windows printers. - + Added "draw" method to the ImageWin.Dib class. This is used by - Topaz to print images on Windows printers. ++ "convert" now supports conversions from "P" to "1" and "F". - + "convert" now supports conversions from "P" to "1" and "F". ++ "paste" can now take a colour instead of an image as the first argument. + The colour must match the colour argument given to the new function, and + match the mode of the target image. - + "paste" can now take a colour instead of an image as the first argument. - The colour must match the colour argument given to the new function, and - match the mode of the target image. ++ Fixed "paste" to allow a mask also for mode "F" images. - + Fixed "paste" to allow a mask also for mode "F" images. ++ The BMP driver now saves mode "1" images. When loading images, the mode + is set to "L" for 8-bit files with greyscale palettes, and to "P" for + other 8-bit files. - + The BMP driver now saves mode "1" images. When loading images, the mode - is set to "L" for 8-bit files with greyscale palettes, and to "P" for - other 8-bit files. ++ The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). - + The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). ++ The JPEG and GIF drivers now saves "1" images. For JPEG, the image + is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the + image will be loaded as a "P" image. - + The JPEG and GIF drivers now saves "1" images. For JPEG, the image - is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the - image will be loaded as a "P" image. ++ Fixed a potential buffer overrun in the GIF encoder. - + Fixed a potential buffer overrun in the GIF encoder. +0.3a2 released +-------------- - (0.3a2 released) +The test suite includes 400 individual tests. - The test suite includes 400 individual tests. ++ Improvements to the test suite revealed a number of minor bugs, which + are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont + are still weak spots in this release. - + Improvements to the test suite revealed a number of minor bugs, which - are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont - are still weak spots in this release. ++ Added "putpalette" method to the Image class. You can use this + to add or modify the palette for "P" and "L" images. If a palette + is added to an "L" image, it is automatically converted to a "P" + image. - + Added "putpalette" method to the Image class. You can use this - to add or modify the palette for "P" and "L" images. If a palette - is added to an "L" image, it is automatically converted to a "P" - image. ++ Fixed ImageDraw to properly handle 32-bit image memories + ("RGB", "RGBA", "CMYK", "F") - + Fixed ImageDraw to properly handle 32-bit image memories - ("RGB", "RGBA", "CMYK", "F") ++ Fixed "fromstring" and "tostring" not to mess up the mode attribute + in default mode. - + Fixed "fromstring" and "tostring" not to mess up the mode attribute - in default mode. ++ Changed ImPlatform.h to work on CRAY's (don't have one at home, so I + haven't tried it). The previous version assumed that either "short" + or "int" were 16-bit wide. PIL still won't compile on platforms where + neither "short", "int" nor "long" are 32-bit wide. - + Changed ImPlatform.h to work on CRAY's (don't have one at home, so I - haven't tried it). The previous version assumed that either "short" - or "int" were 16-bit wide. PIL still won't compile on platforms where - neither "short", "int" nor "long" are 32-bit wide. ++ Added file= and data= keyword arguments to PhotoImage and BitmapImage. + This allows you to use them as drop-in replacements for the corre- + sponding Tkinter classes. - + Added file= and data= keyword arguments to PhotoImage and BitmapImage. - This allows you to use them as drop-in replacements for the corre- - sponding Tkinter classes. ++ Removed bogus references to the crack coder (ImagingCrack). - + Removed bogus references to the crack coder (ImagingCrack). +0.3a1 released +-------------- - (0.3a1 released) ++ Make sure image is loaded in "tostring". - + Make sure image is loaded in "tostring". ++ Added floating point packer (native 32-bit floats only). - + Added floating point packer (native 32-bit floats only). +0.1b1 to 0.2 (b5) +------------ - *** Changes from release 0.1b1 to 0.2 (b5) *** ++ Modified "fromstring" and "tostring" methods to use file codecs. +Also added "fromstring" factory method to create an image directly +from data in a string. - + Modified "fromstring" and "tostring" methods to use file codecs. - Also added "fromstring" factory method to create an image directly - from data in a string. ++ Added support for 32-bit floating point images (mode "F"). You +can convert between "L" and "F" images, and apply a subset of the +available image processing methods on the "F" image. You can also +read virtually any data format into a floating point image memory; +see the section on "Decoding Floating Point Data" in the handbook +for more information. - + Added support for 32-bit floating point images (mode "F"). You - can convert between "L" and "F" images, and apply a subset of the - available image processing methods on the "F" image. You can also - read virtually any data format into a floating point image memory; - see the section on "Decoding Floating Point Data" in the handbook - for more information. +0.2b5 released; on windows only +------------------------------- - (0.2b5 released; on windows only) ++ Fixed the tobitmap() method to work properly for small bitmaps. - + Fixed the tobitmap() method to work properly for small bitmaps. ++ Added RMS and standard deviation to the ImageStat.Stat class. Also +modified the constructor to take an optional feature mask, and also +to accept either an image or a list containing the histogram data. - + Added RMS and standard deviation to the ImageStat.Stat class. Also - modified the constructor to take an optional feature mask, and also - to accept either an image or a list containing the histogram data. ++ The BitmapImage code in ImageTk can now use a special bitmap +decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" +file for details. If not installed, bitmaps are transferred to Tk as +XBM strings. - + The BitmapImage code in ImageTk can now use a special bitmap - decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" - file for details. If not installed, bitmaps are transferred to Tk as - XBM strings. ++ The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") +instead of a special image type. This gives somewhat better performance, +and also allows PIL to support transparency. +*** WARNING: TKAPPINIT MUST BE MODIFIED *** - + The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") - instead of a special image type. This gives somewhat better performance, - and also allows PIL to support transparency. - *** WARNING: TKAPPINIT MUST BE MODIFIED *** ++ ImageTk now honours the alpha layer in RGBA images. Only fully +transparent pixels are made transparent (that is, the alpha layer +is treated as a mask). To treat the alpha laters as a matte, you +must paste the image on the background before handing it over to +ImageTk. - + ImageTk now honours the alpha layer in RGBA images. Only fully - transparent pixels are made transparent (that is, the alpha layer - is treated as a mask). To treat the alpha laters as a matte, you - must paste the image on the background before handing it over to - ImageTk. ++ Added McIdas reader (supports 8-bit images only). - + Added McIdas reader (supports 8-bit images only). ++ PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As +long as you only load and save these formats, you don't have to +wait for a full scan for drivers. To force scanning, call the +Image.init() function. - + PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As - long as you only load and save these formats, you don't have to - wait for a full scan for drivers. To force scanning, call the - Image.init() function. ++ The "seek" and "tell" methods are now always available, also for +single-frame images. - + The "seek" and "tell" methods are now always available, also for - single-frame images. ++ Added optional mask argument to histogram method. The mask may +be an "1" or "L" image with the same size as the original image. +Only pixels where the mask is non-zero are included in the +histogram. - + Added optional mask argument to histogram method. The mask may - be an "1" or "L" image with the same size as the original image. - Only pixels where the mask is non-zero are included in the - histogram. ++ The "paste" method now allows you to specify only the lower left +corner (a 2-tuple), instead of the full region (a 4-tuple). - + The "paste" method now allows you to specify only the lower left - corner (a 2-tuple), instead of the full region (a 4-tuple). ++ Reverted to old plugin scanning model; now scans all directory +names in the path when looking for plugins. - + Reverted to old plugin scanning model; now scans all directory - names in the path when looking for plugins. ++ Added PIXAR raster support. Only uncompressed ("dumped") RGB +images can currently be read (based on information provided +by Greg Coats). - + Added PIXAR raster support. Only uncompressed ("dumped") RGB - images can currently be read (based on information provided - by Greg Coats). ++ Added FlashPix (FPX) read support. Reads all pixel formats, but +only the highest resolution is read, and the viewing transform is +currently ignored. - + Added FlashPix (FPX) read support. Reads all pixel formats, but - only the highest resolution is read, and the viewing transform is - currently ignored. ++ Made PNG encoding somewhat more efficient in "optimize" mode; a +bug in 0.2b4 didn't enable all predictor filters when optimized +storage were requested. - + Made PNG encoding somewhat more efficient in "optimize" mode; a - bug in 0.2b4 didn't enable all predictor filters when optimized - storage were requested. ++ Added Microsoft Image Composer (MIC) read support. When opened, +the first sprite in the file is loaded. You can use the seek method +to load additional sprites from the file. - + Added Microsoft Image Composer (MIC) read support. When opened, - the first sprite in the file is loaded. You can use the seek method - to load additional sprites from the file. ++ Properly reads "P" and "CMYK" PSD images. - + Properly reads "P" and "CMYK" PSD images. ++ "pilconvert" no longer optimizes by default; use the -o option to +make the file as small as possible (at the expense of speed); use +the -q option to set the quality when compressing to JPEG. - + "pilconvert" no longer optimizes by default; use the -o option to - make the file as small as possible (at the expense of speed); use - the -q option to set the quality when compressing to JPEG. ++ Fixed "crop" not to drop the palette for "P" images. - + Fixed "crop" not to drop the palette for "P" images. ++ Added and verified FLC support. - + Added and verified FLC support. ++ Paste with "L" or "RGBA" alpha is now several times faster on most +platforms. - + Paste with "L" or "RGBA" alpha is now several times faster on most - platforms. ++ Changed Image.new() to initialize the image to black, as described +in the handbook. To get an uninitialized image, use None as the +colour. - + Changed Image.new() to initialize the image to black, as described - in the handbook. To get an uninitialized image, use None as the - colour. ++ Fixed the PDF encoder to produce a valid header; Acrobat no longer +complains when you load PDF images created by PIL. - + Fixed the PDF encoder to produce a valid header; Acrobat no longer - complains when you load PDF images created by PIL. ++ PIL only scans fully-qualified directory names in the path when +looking for plugins. +*** WARNING: MAY BREAK EXISTING CODE *** - + PIL only scans fully-qualified directory names in the path when - looking for plugins. - *** WARNING: MAY BREAK EXISTING CODE *** ++ Faster implementation of "save" used when filename is given, +or when file object has "fileno" and "flush" methods. - + Faster implementation of "save" used when filename is given, - or when file object has "fileno" and "flush" methods. ++ Don't crash in "crop" if region extends outside the source image. - + Don't crash in "crop" if region extends outside the source image. ++ Eliminated a massive memory leak in the "save" function. - + Eliminated a massive memory leak in the "save" function. ++ The GIF decoder doesn't crash if the code size is set to an illegal +value. This could happen since another bug didn't handle local +palettes properly if they didn't have the same size as the +global palette (not very common). - + The GIF decoder doesn't crash if the code size is set to an illegal - value. This could happen since another bug didn't handle local - palettes properly if they didn't have the same size as the - global palette (not very common). ++ Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. - + Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. ++ Fixed palette and padding problems in BMP driver. Now properly +writes "1", "L", "P" and "RGB" images. - + Fixed palette and padding problems in BMP driver. Now properly - writes "1", "L", "P" and "RGB" images. ++ Fixed getpixel()/getdata() to return correct pixel values. - + Fixed getpixel()/getdata() to return correct pixel values. ++ Added PSD (PhotoShop) read support. Reads both uncompressed +and compressed images of most types. - + Added PSD (PhotoShop) read support. Reads both uncompressed - and compressed images of most types. ++ Added GIF write support (writes "uncompressed" GIF files only, +due to unresolvable licensing issues). The "gifmaker.py" script +can be used to create GIF animations. - + Added GIF write support (writes "uncompressed" GIF files only, - due to unresolvable licensing issues). The "gifmaker.py" script - can be used to create GIF animations. ++ Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" +images. - + Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" - images. ++ Added FLI read support. This driver has only been tested +on a few FLI samples. - + Added FLI read support. This driver has only been tested - on a few FLI samples. ++ Reads 2-bit and 4-bit PCX images. - + Reads 2-bit and 4-bit PCX images. ++ Added MSP read and write support. Both version 1 and 2 can be +read, but only version 1 (uncompressed) files are written. - + Added MSP read and write support. Both version 1 and 2 can be - read, but only version 1 (uncompressed) files are written. ++ Fixed a bug in the FLI/FLC identification code that caused the +driver to raise an exception when parsing valid FLI/FLC files. - + Fixed a bug in the FLI/FLC identification code that caused the - driver to raise an exception when parsing valid FLI/FLC files. ++ Improved performance when loading file format plugins, and when +opening files. - + Improved performance when loading file format plugins, and when - opening files. ++ Added GIF animation support, via the "seek" and "tell" methods. +You can use "player.py" to play an animated GIF file. - + Added GIF animation support, via the "seek" and "tell" methods. - You can use "player.py" to play an animated GIF file. ++ Removed MNG support, since the spec is changing faster than I +can change the code. I've added support for the experimental +ARG format instead. Contact me for more information on this +format. - + Removed MNG support, since the spec is changing faster than I - can change the code. I've added support for the experimental - ARG format instead. Contact me for more information on this - format. ++ Added keyword options to the "save" method. The following options +are currently supported: - + Added keyword options to the "save" method. The following options - are currently supported: + Format Option Description + -------------------------------------------------------- + JPEG optimize Minimize output file at the + expense of compression speed. - Format Option Description - -------------------------------------------------------- - JPEG optimize Minimize output file at the - expense of compression speed. + JPEG progressive Enable progressive output. + The option value is ignored. - JPEG progressive Enable progressive output. - The option value is ignored. + JPEG quality Set compression quality (1-100). + The default value is 75. - JPEG quality Set compression quality (1-100). - The default value is 75. + JPEG smooth Smooth dithered images. + Value is strength (1-100). + Default is off (0). - JPEG smooth Smooth dithered images. - Value is strength (1-100). - Default is off (0). + PNG optimize Minimize output file at the + expense of compression speed. - PNG optimize Minimize output file at the - expense of compression speed. +Expect more options in future releases. Also note that +file writers silently ignore unknown options. - Expect more options in future releases. Also note that - file writers silently ignore unknown options. ++ Plugged memory leaks in the PNG and TIFF decoders. - + Plugged memory leaks in the PNG and TIFF decoders. ++ Added PNG write support. - + Added PNG write support. ++ (internal) RGB unpackers and converters now set the pad byte +to 255 (full opacity). - + (internal) RGB unpackers and converters now set the pad byte - to 255 (full opacity). ++ Properly handles the "transparency" property for GIF, PNG +and XPM files. - + Properly handles the "transparency" property for GIF, PNG - and XPM files. ++ Added a "putalpha" method, allowing you to attach a "1" or "L" +image as the alpha layer to an "RGBA" image. - + Added a "putalpha" method, allowing you to attach a "1" or "L" - image as the alpha layer to an "RGBA" image. ++ Various improvements to the sample scripts: - + Various improvements to the sample scripts: +"pilconvert" Carries out some extra tricks in order to make + the resulting file as small as possible. - "pilconvert" Carries out some extra tricks in order to make - the resulting file as small as possible. +"explode" (NEW) Split an image sequence into individual frames. - "explode" (NEW) Split an image sequence into individual frames. +"gifmaker" (NEW) Convert a sequence file into a GIF animation. + Note that the GIF encoder create "uncompressed" GIF + files, so animations created by this script are + rather large (typically 2-5 times the compressed + sizes). - "gifmaker" (NEW) Convert a sequence file into a GIF animation. - Note that the GIF encoder create "uncompressed" GIF - files, so animations created by this script are - rather large (typically 2-5 times the compressed - sizes). +"image2py" (NEW) Convert a single image to a python module. See + comments in this script for details. - "image2py" (NEW) Convert a single image to a python module. See - comments in this script for details. +"player" If multiple images are given on the command line, + they are interpreted as frames in a sequence. The + script assumes that they all have the same size. + Also note that this script now can play FLI/FLC + and GIF animations. - "player" If multiple images are given on the command line, - they are interpreted as frames in a sequence. The - script assumes that they all have the same size. - Also note that this script now can play FLI/FLC - and GIF animations. + This player can also execute embedded Python + animation applets (ARG format only). - This player can also execute embedded Python - animation applets (ARG format only). +"viewer" Transparent images ("P" with transparency property, + and "RGBA") are superimposed on the standard Tk back- + ground. - "viewer" Transparent images ("P" with transparency property, - and "RGBA") are superimposed on the standard Tk back- - ground. ++ Fixed colour argument to "new". For multilayer images, pass a +tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, +Magenta, Yellow, Black). - + Fixed colour argument to "new". For multilayer images, pass a - tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, - Magenta, Yellow, Black). ++ Added XPM (X pixmap) read support. - + Added XPM (X pixmap) read support. +0.2b3 released +-------------- - (0.2b3 released) ++ Added MNG (multi-image network graphics) read support. "Ming" + is a proposed animation standard, based on the PNG file format. - + Added MNG (multi-image network graphics) read support. "Ming" - is a proposed animation standard, based on the PNG file format. + You can use the "player" sample script to display some flavours + of this format. The MNG standard is still under development, + as is this driver. More information, including sample files, + can be found at - You can use the "player" sample script to display some flavours - of this format. The MNG standard is still under development, - as is this driver. More information, including sample files, - can be found at ++ Added a "verify" method to images loaded from file. This method + scans the file for errors, without actually decoding the image + data, and raises a suitable exception if it finds any problems. + Currently implemented for PNG and MNG files only. - + Added a "verify" method to images loaded from file. This method - scans the file for errors, without actually decoding the image - data, and raises a suitable exception if it finds any problems. - Currently implemented for PNG and MNG files only. ++ Added support for interlaced GIF images. - + Added support for interlaced GIF images. ++ Added PNG read support -- if linked with the ZLIB compression library, + PIL reads all kinds of PNG images, except interlaced files. - + Added PNG read support -- if linked with the ZLIB compression library, - PIL reads all kinds of PNG images, except interlaced files. ++ Improved PNG identification support -- doesn't mess up on unknown + chunks, identifies all possible PNG modes, and verifies checksum + on PNG header chunks. - + Improved PNG identification support -- doesn't mess up on unknown - chunks, identifies all possible PNG modes, and verifies checksum - on PNG header chunks. ++ Added an experimental reader for placable Windows Meta Files (WMF). + This reader is still very incomplete, but it illustrates how PIL's + drawing capabilities can be used to render vector and metafile + formats. - + Added an experimental reader for placable Windows Meta Files (WMF). - This reader is still very incomplete, but it illustrates how PIL's - drawing capabilities can be used to render vector and metafile - formats. ++ Added restricted drivers for images from Image Tools (greyscale + only) and LabEye/IFUNC (common interchange modes only). - + Added restricted drivers for images from Image Tools (greyscale - only) and LabEye/IFUNC (common interchange modes only). ++ Some minor improvements to the sample scripts provided in the + "Scripts" directory. - + Some minor improvements to the sample scripts provided in the - "Scripts" directory. ++ The test images have been moved to the "Images" directory. - + The test images have been moved to the "Images" directory. +0.2b2, 0.2b1 released; Windows only +----------------------------------- - (0.2b2 released) - (0.2b1 released; Windows only) ++ Fixed filling of complex polygons. The ImageDraw "line" and + "polygon" methods also accept Path objects. - + Fixed filling of complex polygons. The ImageDraw "line" and - "polygon" methods also accept Path objects. ++ The ImageTk "PhotoImage" object can now be constructed directly + from an image. You can also pass the object itself to Tkinter, + instead of using the "image" attribute. Finally, using "paste" + on a displayed image automatically updates the display. - + The ImageTk "PhotoImage" object can now be constructed directly - from an image. You can also pass the object itself to Tkinter, - instead of using the "image" attribute. Finally, using "paste" - on a displayed image automatically updates the display. ++ The ImageTk "BitmapImage" object allows you to create transparent + overlays from 1-bit images. You can pass the object itself to + Tkinter. The constructor takes the same arguments as the Tkinter + BitmapImage class; use the "foreground" option to set the colour + of the overlay. - + The ImageTk "BitmapImage" object allows you to create transparent - overlays from 1-bit images. You can pass the object itself to - Tkinter. The constructor takes the same arguments as the Tkinter - BitmapImage class; use the "foreground" option to set the colour - of the overlay. ++ Added a "putdata" method to the Image class. This can be used to + load a 1-layer image with data from a sequence object or a string. + An optional floating point scale and offset can be used to adjust + the data to fit into the 8-bit pixel range. Also see the "getdata" + method. - + Added a "putdata" method to the Image class. This can be used to - load a 1-layer image with data from a sequence object or a string. - An optional floating point scale and offset can be used to adjust - the data to fit into the 8-bit pixel range. Also see the "getdata" - method. ++ Added the EXTENT method to the Image "transform" method. This can + be used to quickly crop, stretch, shrink, or mirror a subregion + from another image. - + Added the EXTENT method to the Image "transform" method. This can - be used to quickly crop, stretch, shrink, or mirror a subregion - from another image. ++ Adapted to Python 1.4. - + Adapted to Python 1.4. ++ Added a project makefile for Visual C++ 4.x. This allows you to + easily build a dynamically linked version of PIL for Windows 95 + and NT. - + Added a project makefile for Visual C++ 4.x. This allows you to - easily build a dynamically linked version of PIL for Windows 95 - and NT. ++ A Tk "booster" patch for Windows is available. It gives dramatic + performance improvements for some displays. Has been tested with + Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk + subdirectory for details. - + A Tk "booster" patch for Windows is available. It gives dramatic - performance improvements for some displays. Has been tested with - Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk - subdirectory for details. ++ You can now save 1-bit images in the XBM format. In addition, the + Image class now provides a "tobitmap" method which returns a string + containing an XBM representation of the image. Quite handy to use + with Tk. - + You can now save 1-bit images in the XBM format. In addition, the - Image class now provides a "tobitmap" method which returns a string - containing an XBM representation of the image. Quite handy to use - with Tk. ++ More conversions, including "RGB" to "1" and more. - + More conversions, including "RGB" to "1" and more. +0.2a1 released +-------------- - (0.2a1 released) ++ Where earlier versions accepted lists, this version accepts arbitrary + Python sequences (including strings, in some cases). A few resource + leaks were plugged in the process. - + Where earlier versions accepted lists, this version accepts arbitrary - Python sequences (including strings, in some cases). A few resource - leaks were plugged in the process. ++ The Image "paste" method now allows the box to extend outside + the target image. The size of the box, the image to be pasted, + and the optional mask must still match. - + The Image "paste" method now allows the box to extend outside - the target image. The size of the box, the image to be pasted, - and the optional mask must still match. ++ The ImageDraw module now supports filled polygons, outlined and + filled ellipses, and text. Font support is rudimentary, though. - + The ImageDraw module now supports filled polygons, outlined and - filled ellipses, and text. Font support is rudimentary, though. ++ The Image "point" method now takes an optional mode argument, + allowing you to convert the image while translating it. Currently, + this can only be used to convert "L" or "P" images to "1" images + (creating thresholded images or "matte" masks). - + The Image "point" method now takes an optional mode argument, - allowing you to convert the image while translating it. Currently, - this can only be used to convert "L" or "P" images to "1" images - (creating thresholded images or "matte" masks). ++ An Image "getpixel" method has been added. For single band images, + it returns the pixel value at a given position as an integer. + For n-band images, it returns an n-tuple of integers. - + An Image "getpixel" method has been added. For single band images, - it returns the pixel value at a given position as an integer. - For n-band images, it returns an n-tuple of integers. ++ An Image "getdata" method has been added. It returns a sequence + object representing the image as a 1-dimensional array. Only len() + and [] can be used with this sequence. This method returns a + reference to the existing image data, so changes in the image + will be immediately reflected in the sequence object. - + An Image "getdata" method has been added. It returns a sequence - object representing the image as a 1-dimensional array. Only len() - and [] can be used with this sequence. This method returns a - reference to the existing image data, so changes in the image - will be immediately reflected in the sequence object. ++ Fixed alignment problems in the Windows BMP writer. - + Fixed alignment problems in the Windows BMP writer. ++ If converting an "RGB" image to "RGB" or "L", you can give a second + argument containing a colour conversion matrix. - + If converting an "RGB" image to "RGB" or "L", you can give a second - argument containing a colour conversion matrix. ++ An Image "getbbox" method has been added. It returns the bounding + box of data in an image, considering the value 0 as background. - + An Image "getbbox" method has been added. It returns the bounding - box of data in an image, considering the value 0 as background. ++ An Image "offset" method has been added. It returns a new image + where the contents of the image have been offset the given distance + in X and/or Y direction. Data wraps between edges. - + An Image "offset" method has been added. It returns a new image - where the contents of the image have been offset the given distance - in X and/or Y direction. Data wraps between edges. ++ Saves PDF images. The driver creates a binary PDF 1.1 files, using + JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding + (same as for PostScript) for other formats. - + Saves PDF images. The driver creates a binary PDF 1.1 files, using - JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding - (same as for PostScript) for other formats. ++ The "paste" method now accepts "1" masks. Zero means transparent, + any other pixel value means opaque. This is faster than using an + "L" transparency mask. - + The "paste" method now accepts "1" masks. Zero means transparent, - any other pixel value means opaque. This is faster than using an - "L" transparency mask. ++ Properly writes EPS files (and properly prints images to PostScript + printers as well). - + Properly writes EPS files (and properly prints images to PostScript - printers as well). ++ Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR + files. Cursor animations are not supported. - + Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR - files. Cursor animations are not supported. ++ Fixed alignment problems in the Sun raster loader. - + Fixed alignment problems in the Sun raster loader. ++ Added "draft" and "thumbnail" methods. The draft method is used + to optimize loading of JPEG and PCD files, the thumbnail method is + used to create a thumbnail representation of an image. - + Added "draft" and "thumbnail" methods. The draft method is used - to optimize loading of JPEG and PCD files, the thumbnail method is - used to create a thumbnail representation of an image. ++ Added Windows display support, via the ImageWin class (see the + handbook for details). - + Added Windows display support, via the ImageWin class (see the - handbook for details). ++ Added raster conversion for EPS files. This requires GNU or Aladdin + Ghostscript, and probably works on UNIX only. - + Added raster conversion for EPS files. This requires GNU or Aladdin - Ghostscript, and probably works on UNIX only. ++ Reads PhotoCD (PCD) images. The base resolution (768x512) can be + read from a PhotoCD file. - + Reads PhotoCD (PCD) images. The base resolution (768x512) can be - read from a PhotoCD file. ++ Eliminated some compiler warnings. Bindings now compile cleanly in C++ + mode. Note that the Imaging library itself must be compiled in C mode. - + Eliminated some compiler warnings. Bindings now compile cleanly in C++ - mode. Note that the Imaging library itself must be compiled in C mode. ++ Added "bdf2pil.py", which converts BDF fonts into images with associated + metrics. This is definitely work in progress. For info, see description + in script for details. - + Added "bdf2pil.py", which converts BDF fonts into images with associated - metrics. This is definitely work in progress. For info, see description - in script for details. ++ Fixed a bug in the "ImageEnhance.py" module. - + Fixed a bug in the "ImageEnhance.py" module. ++ Fixed a bug in the netpbm save hack in "GifImagePlugin.py" - + Fixed a bug in the netpbm save hack in "GifImagePlugin.py" ++ Fixed 90 and 270 degree rotation of rectangular images. - + Fixed 90 and 270 degree rotation of rectangular images. ++ Properly reads 8-bit TIFF palette-color images. - + Properly reads 8-bit TIFF palette-color images. ++ Reads plane separated RGB and CMYK TIFF images. - + Reads plane separated RGB and CMYK TIFF images. ++ Added driver debug mode. This is enabled by setting Image.DEBUG + to a non-zero value. Try the -D option to "pilfile.py" and see what + happens. - + Added driver debug mode. This is enabled by setting Image.DEBUG - to a non-zero value. Try the -D option to "pilfile.py" and see what - happens. ++ Don't crash on "atend" constructs in PostScript files. - + Don't crash on "atend" constructs in PostScript files. ++ Only the Image module imports _imaging directly. Other modules + should refer to the binding module as "Image.core". - + Only the Image module imports _imaging directly. Other modules - should refer to the binding module as "Image.core". +0.0 to 0.1 (b1) +------------ - *** Changes from release 0.0 to 0.1 (b1) *** ++ A handbook is available (distributed separately). - + A handbook is available (distributed separately). ++ The coordinate system is changed so that (0,0) is now located + in the upper left corner. This is in compliancy with ISO 12087 + and 90% of all other image processing and graphics libraries. - + The coordinate system is changed so that (0,0) is now located - in the upper left corner. This is in compliancy with ISO 12087 - and 90% of all other image processing and graphics libraries. ++ Modes "1" (bilevel) and "P" (palette) have been introduced. Note + that bilevel images are stored with one byte per pixel. - + Modes "1" (bilevel) and "P" (palette) have been introduced. Note - that bilevel images are stored with one byte per pixel. ++ The Image "crop" and "paste" methods now accepts None as the + box argument, to refer to the full image (self, that is). - + The Image "crop" and "paste" methods now accepts None as the - box argument, to refer to the full image (self, that is). ++ The Image "crop" method now works properly. - + The Image "crop" method now works properly. ++ The Image "point" method is now available. You can use either a + lookup table or a function taking one argument. - + The Image "point" method is now available. You can use either a - lookup table or a function taking one argument. ++ The Image join function has been renamed to "merge". - + The Image join function has been renamed to "merge". ++ An Image "composite" function has been added. It is identical + to copy() followed by paste(mask). - + An Image "composite" function has been added. It is identical - to copy() followed by paste(mask). ++ An Image "eval" function has been added. It is currently identical + to point(function); that is, only a single image can be processed. - + An Image "eval" function has been added. It is currently identical - to point(function); that is, only a single image can be processed. ++ A set of channel operations has been added. See the "ImageChops" + module, test_chops.py, and the handbook for details. - + A set of channel operations has been added. See the "ImageChops" - module, test_chops.py, and the handbook for details. ++ Added the "pilconvert" utility, which converts image files. Note + that the number of output formats are still quite restricted. - + Added the "pilconvert" utility, which converts image files. Note - that the number of output formats are still quite restricted. ++ Added the "pilfile" utility, which quickly identifies image files + (without loading them, in most cases). - + Added the "pilfile" utility, which quickly identifies image files - (without loading them, in most cases). ++ Added the "pilprint" utility, which prints image files to PostScript + printers. - + Added the "pilprint" utility, which prints image files to PostScript - printers. ++ Added a rudimentary version of the "pilview" utility, which is + simple image viewer based on Tk. Only File/Exit and Image/Next + works properly. - + Added a rudimentary version of the "pilview" utility, which is - simple image viewer based on Tk. Only File/Exit and Image/Next - works properly. ++ An interface to Tk has been added. See "Lib/ImageTk.py" and README + for details. - + An interface to Tk has been added. See "Lib/ImageTk.py" and README - for details. ++ An interface to Jack Jansen's Img library has been added (thanks to + Jack). This allows you to read images through the Img extensions file + format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. - + An interface to Jack Jansen's Img library has been added (thanks to - Jack). This allows you to read images through the Img extensions file - format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. - - + PostScript printing is provided through the PSDraw module. See the - handbook for details. ++ PostScript printing is provided through the PSDraw module. See the + handbook for details. From edfed60183d5f9ff4b547f0fe18d115c81d30c61 Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 11:56:34 +0430 Subject: [PATCH 073/294] Next: working on CHANGES --- CHANGES.rst | 1236 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 807 insertions(+), 429 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5b381c6c0..6755657ab 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5115,637 +5115,663 @@ Changes from release 1.1.6 to 1.1.7 *** + Added support for duplex scanning to the Sane interface (Abel Deuring). - (1.1.6a1 released) +1.1.6a1 released +---------------- - + Fixed a memory leak in "convert(mode)", when converting from - L to P. ++ Fixed a memory leak in "convert(mode)", when converting from + L to P. - + Added pixel access object. The "load" method now returns a - access object that can be used to directly get and set pixel - values, using ordinary [x, y] notation: ++ Added pixel access object. The "load" method now returns a + access object that can be used to directly get and set pixel + values, using ordinary [x, y] notation: - pixel = im.load() - v = pixel[x, y] - pixel[x, y] = v + pixel = im.load() + v = pixel[x, y] + pixel[x, y] = v - If you're accessing more than a few pixels, this is a lot - faster than using getpixel/putpixel. + If you're accessing more than a few pixels, this is a lot + faster than using getpixel/putpixel. - + Fixed building on Cygwin (from Miki Tebeka). ++ Fixed building on Cygwin (from Miki Tebeka). - + Fixed "point(callable)" on unloaded images (reported by Håkan - Karlsson). ++ Fixed "point(callable)" on unloaded images (reported by Håkan + Karlsson). - + Fixed size bug in ImageWin.ImageWindow constructor (from Victor - Reijs) ++ Fixed size bug in ImageWin.ImageWindow constructor (from Victor + Reijs) - + Fixed ImageMath float() and int() operations for Python 2.4 - (reported by Don Rozenberg). ++ Fixed ImageMath float() and int() operations for Python 2.4 + (reported by Don Rozenberg). - + Fixed "RuntimeError: encoder error -8 in tostring" problem for - wide "RGB", "I", and "F" images. ++ Fixed "RuntimeError: encoder error -8 in tostring" problem for + wide "RGB", "I", and "F" images. - + Fixed line width calculation. ++ Fixed line width calculation. - (1.1.6a0 released) +1.1.6a0 released +---------------- - + Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). ++ Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). - + Fixed off-by-0.5 errors in the ANTIALIAS code (based on input - from Douglas Bagnall). ++ Fixed off-by-0.5 errors in the ANTIALIAS code (based on input + from Douglas Bagnall). - + Added buffer interface support to the Path constructor. If - a buffer is provided, it is assumed to contain a flat array - of float coordinates (e.g. array.array('f', seq)). ++ Added buffer interface support to the Path constructor. If + a buffer is provided, it is assumed to contain a flat array + of float coordinates (e.g. array.array('f', seq)). - + Added new ImageMath module. ++ Added new ImageMath module. - + Fixed ImageOps.equalize when used with a small number of distinct - values (reported by David Kirtley). ++ Fixed ImageOps.equalize when used with a small number of distinct + values (reported by David Kirtley). - + Fixed potential integer division in PSDraw.image (from Eric Etheridge). ++ Fixed potential integer division in PSDraw.image (from Eric Etheridge). - *** Changes from release 1.1 to 1.1.5 *** +1.1.5c2 and 1.1.5 final released +-------------------------------- - (1.1.5c2 and 1.1.5 final released) ++ Added experimental PERSPECTIVE transform method (from Jeff Breiden- + bach). - + Added experimental PERSPECTIVE transform method (from Jeff Breiden- - bach). +1.1.5c1 released +---------------- - (1.1.5c1 released) ++ Make sure "thumbnail" never generates zero-wide or zero-high images + (reported by Gene Skonicki) - + Make sure "thumbnail" never generates zero-wide or zero-high images - (reported by Gene Skonicki) ++ Fixed a "getcolors" bug that could result in a zero count for some + colors (reported by Richard Oudkerk). - + Fixed a "getcolors" bug that could result in a zero count for some - colors (reported by Richard Oudkerk). ++ Changed default "convert" palette to avoid "rounding errors" when + round-tripping white source pixels (reported by Henryk Gerlach and + Jeff Epler). - + Changed default "convert" palette to avoid "rounding errors" when - round-tripping white source pixels (reported by Henryk Gerlach and - Jeff Epler). +1.1.5b3 released +---------------- - (1.1.5b3 released) ++ Don't crash in "quantize" method if the number of colors requested + is larger than 256. This release raises a ValueError exception; + future versions may return a mode "RGB" image instead (reported + by Richard Oudkerk). - + Don't crash in "quantize" method if the number of colors requested - is larger than 256. This release raises a ValueError exception; - future versions may return a mode "RGB" image instead (reported - by Richard Oudkerk). ++ Added WBMP read/write support (based on code by Duncan Booth). - + Added WBMP read/write support (based on code by Duncan Booth). +1.1.5b2 released +---------------- - (1.1.5b2 released) ++ Added DPI read/write support to the PNG codec. The decoder sets + the info["dpi"] attribute for PNG files with appropriate resolution + settings. The encoder uses the "dpi" option (based on code by Niki + Spahiev). - + Added DPI read/write support to the PNG codec. The decoder sets - the info["dpi"] attribute for PNG files with appropriate resolution - settings. The encoder uses the "dpi" option (based on code by Niki - Spahiev). ++ Added limited support for "point" mappings from mode "I" to mode "L". + Only 16-bit values are supported (other values are clipped), the lookup + table must contain exactly 65536 entries, and the mode argument must be + set to "L". - + Added limited support for "point" mappings from mode "I" to mode "L". - Only 16-bit values are supported (other values are clipped), the lookup - table must contain exactly 65536 entries, and the mode argument must be - set to "L". ++ Added support for Mac OS X icns files (based on code by Bob Ippolito). - + Added support for Mac OS X icns files (based on code by Bob Ippolito). ++ Added "ModeFilter" support to the ImageFilter module. - + Added "ModeFilter" support to the ImageFilter module. ++ Added support for Spider images (from William Baxter). See the + comments in PIL/SpiderImagePlugin.py for more information on this + format. - + Added support for Spider images (from William Baxter). See the - comments in PIL/SpiderImagePlugin.py for more information on this - format. +1.1.5b1 released +---------------- - (1.1.5b1 released) ++ Added new Sane release (from Ralph Heinkel). See the Sane/README + and Sane/CHANGES files for more information. - + Added new Sane release (from Ralph Heinkel). See the Sane/README - and Sane/CHANGES files for more information. ++ Added experimental PngInfo chunk container to the PngImageFile + module. This can be used to add arbitrary chunks to a PNG file. + Create a PngInfo instance, use "add" or "add_text" to add chunks, + and pass the instance as the "pnginfo" option when saving the + file. - + Added experimental PngInfo chunk container to the PngImageFile - module. This can be used to add arbitrary chunks to a PNG file. - Create a PngInfo instance, use "add" or "add_text" to add chunks, - and pass the instance as the "pnginfo" option when saving the - file. ++ Added "getpalette" method. This returns the palette as a list, + or None if the image has no palette. To modify the palette, use + "getpalette" to fetch the current palette, modify the list, and + put it back using "putpalette". - + Added "getpalette" method. This returns the palette as a list, - or None if the image has no palette. To modify the palette, use - "getpalette" to fetch the current palette, modify the list, and - put it back using "putpalette". ++ Added optional flattening to the ImagePath "tolist" method. + tolist() or tolist(0) returns a list of 2-tuples, as before. + tolist(1) returns a flattened list instead. - + Added optional flattening to the ImagePath "tolist" method. - tolist() or tolist(0) returns a list of 2-tuples, as before. - tolist(1) returns a flattened list instead. +1.1.5a5 released +---------------- - (1.1.5a5 released) ++ Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". - + Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". ++ Added "getcolors()" method. This is similar to the existing histo- + gram method, but looks at color values instead of individual layers, + and returns an unsorted list of (count, color) tuples. - + Added "getcolors()" method. This is similar to the existing histo- - gram method, but looks at color values instead of individual layers, - and returns an unsorted list of (count, color) tuples. + By default, the method returns None if finds more than 256 colors. + If you need to look for more colors, you can pass in a limit (this + is used to allocate internal tables, so you probably don't want to + pass in too large values). - By default, the method returns None if finds more than 256 colors. - If you need to look for more colors, you can pass in a limit (this - is used to allocate internal tables, so you probably don't want to - pass in too large values). ++ Build improvements: Fixed building under AIX, improved detection of + FreeType2 and Mac OS X framework libraries, and more. Many thanks + to everyone who helped test the new "setup.py" script! - + Build improvements: Fixed building under AIX, improved detection of - FreeType2 and Mac OS X framework libraries, and more. Many thanks - to everyone who helped test the new "setup.py" script! +1.1.5a4 released +---------------- - (1.1.5a4 released) ++ The "save" method now looks for a file format driver before + creating the file. - + The "save" method now looks for a file format driver before - creating the file. ++ Don't use antialiased truetype fonts when drawing in mode "P", "I", + and "F" images. - + Don't use antialiased truetype fonts when drawing in mode "P", "I", - and "F" images. ++ Rewrote the "setup.py" file. The new version scans for available + support libraries, and configures both the libImaging core library + and the bindings in one step. - + Rewrote the "setup.py" file. The new version scans for available - support libraries, and configures both the libImaging core library - and the bindings in one step. + To use specific versions of the libraries, edit the ROOT variables + in the setup.py file. - To use specific versions of the libraries, edit the ROOT variables - in the setup.py file. ++ Removed threaded "show" viewer; use the old "show" implementation + instead (Windows). - + Removed threaded "show" viewer; use the old "show" implementation - instead (Windows). ++ Added deprecation warnings to Image.offset, ImageDraw.setink, and + ImageDraw.setfill. - + Added deprecation warnings to Image.offset, ImageDraw.setink, and - ImageDraw.setfill. ++ Added width option to ImageDraw.line(). The current implementation + works best for straight lines; it does not support line joins, so + polylines won't look good. - + Added width option to ImageDraw.line(). The current implementation - works best for straight lines; it does not support line joins, so - polylines won't look good. ++ ImageDraw.Draw is now a factory function instead of a class. If + you need to create custom draw classes, inherit from the ImageDraw + class. All other code should use the factory function. - + ImageDraw.Draw is now a factory function instead of a class. If - you need to create custom draw classes, inherit from the ImageDraw - class. All other code should use the factory function. ++ Fixed loading of certain PCX files (problem reported by Greg + Hamilton, who also provided samples). - + Fixed loading of certain PCX files (problem reported by Greg - Hamilton, who also provided samples). ++ Changed _imagingft.c to require FreeType 2.1 or newer. The + module can still be built with earlier versions; see comments + in _imagingft.c for details. - + Changed _imagingft.c to require FreeType 2.1 or newer. The - module can still be built with earlier versions; see comments - in _imagingft.c for details. +1.1.5a3 released +---------------- - (1.1.5a3 released) ++ Added 'getim' method, which returns a PyCObject wrapping an + Imaging pointer. The description string is set to IMAGING_MAGIC. + See Imaging.h for pointer and string declarations. - + Added 'getim' method, which returns a PyCObject wrapping an - Imaging pointer. The description string is set to IMAGING_MAGIC. - See Imaging.h for pointer and string declarations. ++ Fixed reading of TIFF JPEG images (problem reported by Ulrik + Svensson). - + Fixed reading of TIFF JPEG images (problem reported by Ulrik - Svensson). ++ Made ImageColor work under Python 1.5.2 - + Made ImageColor work under Python 1.5.2 ++ Fixed division by zero "equalize" on very small images (from + Douglas Bagnall). - + Fixed division by zero "equalize" on very small images (from - Douglas Bagnall). +1.1.5a2 released +---------------- - (1.1.5a2 released) ++ The "paste" method now supports the alternative "paste(im, mask)" + syntax (in this case, the box defaults to im's bounding box). - + The "paste" method now supports the alternative "paste(im, mask)" - syntax (in this case, the box defaults to im's bounding box). ++ The "ImageFile.Parser" class now works also for PNG files with + more than one IDAT block. - + The "ImageFile.Parser" class now works also for PNG files with - more than one IDAT block. ++ Added DPI read/write to the TIFF codec, and fixed writing of + rational values. The decoder sets the info["dpi"] attribute + for TIFF files with appropriate resolution settings. The + encoder uses the "dpi" option. - + Added DPI read/write to the TIFF codec, and fixed writing of - rational values. The decoder sets the info["dpi"] attribute - for TIFF files with appropriate resolution settings. The - encoder uses the "dpi" option. ++ Disable interlacing for small (or narrow) GIF images, to + work around what appears to be a hard-to-find bug in PIL's + GIF encoder. - + Disable interlacing for small (or narrow) GIF images, to - work around what appears to be a hard-to-find bug in PIL's - GIF encoder. ++ Fixed writing of mode "P" PDF images. Made mode "1" PDF + images smaller. - + Fixed writing of mode "P" PDF images. Made mode "1" PDF - images smaller. ++ Made the XBM reader a bit more robust; the file may now start + with a few whitespace characters. - + Made the XBM reader a bit more robust; the file may now start - with a few whitespace characters. ++ Added support for enhanced metafiles to the WMF driver. The + separate PILWMF kit lets you render both placeable WMF files + and EMF files as raster images. See - + Added support for enhanced metafiles to the WMF driver. The - separate PILWMF kit lets you render both placeable WMF files - and EMF files as raster images. See + http://effbot.org/downloads#pilwmf - http://effbot.org/downloads#pilwmf +1.1.5a1 released +---------------- - (1.1.5a1 released) ++ Replaced broken WMF driver with a WMF stub plugin (see below). - + Replaced broken WMF driver with a WMF stub plugin (see below). ++ Fixed writing of mode "1", "L", and "CMYK" PDF images (based on + input from Nicholas Riley and others). - + Fixed writing of mode "1", "L", and "CMYK" PDF images (based on - input from Nicholas Riley and others). ++ Fixed adaptive palette conversion for zero-width or zero-height + images (from Chris Cogdon) - + Fixed adaptive palette conversion for zero-width or zero-height - images (from Chris Cogdon) ++ Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) - + Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) ++ Added support for StubImageFile plugins, including stub plugins + for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify + a given file format, but relies on application code to open and + save files in that format. - + Added support for StubImageFile plugins, including stub plugins - for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify - a given file format, but relies on application code to open and - save files in that format. ++ Added optional "encoding" argument to the ImageFont.truetype + factory. This argument can be used to specify non-Unicode character + maps for fonts that support that. For example, to draw text using + the Microsoft Symbol font, use: - + Added optional "encoding" argument to the ImageFont.truetype - factory. This argument can be used to specify non-Unicode character - maps for fonts that support that. For example, to draw text using - the Microsoft Symbol font, use: + font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") + draw.text((0, 0), unichr(0xF000 + 0xAA)) - font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") - draw.text((0, 0), unichr(0xF000 + 0xAA)) + (note that the symbol font uses characters in the 0xF000-0xF0FF + range) - (note that the symbol font uses characters in the 0xF000-0xF0FF - range) + Common encodings are "unic" (Unicode), "symb" (Microsoft Symbol), + "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple + Roman). See the FreeType documentation for more information. - Common encodings are "unic" (Unicode), "symb" (Microsoft Symbol), - "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple - Roman). See the FreeType documentation for more information. ++ Made "putalpha" a bit more robust; you can now attach an alpha + layer to a plain "L" or "RGB" image, and you can also specify + constant alphas instead of alpha layers (using integers or colour + names). - + Made "putalpha" a bit more robust; you can now attach an alpha - layer to a plain "L" or "RGB" image, and you can also specify - constant alphas instead of alpha layers (using integers or colour - names). ++ Added experimental "LA" mode support. - + Added experimental "LA" mode support. + An "LA" image is an "L" image with an attached transparency layer. + Note that support for "LA" is not complete; some operations may + fail or produce unexpected results. - An "LA" image is an "L" image with an attached transparency layer. - Note that support for "LA" is not complete; some operations may - fail or produce unexpected results. ++ Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" + classes to the ImageFilter module. - + Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" - classes to the ImageFilter module. ++ Improved support for applications using multiple threads; PIL + now releases the global interpreter lock for many CPU-intensive + operations (based on work by Kevin Cazabon). - + Improved support for applications using multiple threads; PIL - now releases the global interpreter lock for many CPU-intensive - operations (based on work by Kevin Cazabon). ++ Ignore Unicode characters in the PCF loader (from Andres Polit) - + Ignore Unicode characters in the PCF loader (from Andres Polit) ++ Fixed typo in OleFileIO.loadfat, which could affect loading of + FlashPix and Image Composer images (Daniel Haertle) - + Fixed typo in OleFileIO.loadfat, which could affect loading of - FlashPix and Image Composer images (Daniel Haertle) ++ Fixed building on platforms that have Freetype but don't have + Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) - + Fixed building on platforms that have Freetype but don't have - Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) ++ Added EXIF GPSInfo read support for JPEG files. To extract + GPSInfo information, open the file, extract the exif dictionary, + and check for the key 0x8825 (GPSInfo). If present, it contains + a dictionary mapping GPS keys to GPS values. For a list of keys, + see the EXIF specification. - + Added EXIF GPSInfo read support for JPEG files. To extract - GPSInfo information, open the file, extract the exif dictionary, - and check for the key 0x8825 (GPSInfo). If present, it contains - a dictionary mapping GPS keys to GPS values. For a list of keys, - see the EXIF specification. + The "ExifTags" module contains a GPSTAGS dictionary mapping GPS + tags to tag names. - The "ExifTags" module contains a GPSTAGS dictionary mapping GPS - tags to tag names. ++ Added DPI read support to the PCX and DCX codecs (info["dpi"]). - + Added DPI read support to the PCX and DCX codecs (info["dpi"]). ++ The "show" methods now uses a built-in image viewer on Windows. + This viewer creates an instance of the ImageWindow class (see + below) and keeps it running in a separate thread. NOTE: This + was disabled in 1.1.5a4. - + The "show" methods now uses a built-in image viewer on Windows. - This viewer creates an instance of the ImageWindow class (see - below) and keeps it running in a separate thread. NOTE: This - was disabled in 1.1.5a4. ++ Added experimental "Window" and "ImageWindow" classes to the + ImageWin module. These classes allow you to create a WCK-style + toplevel window, and use it to display raster data. - + Added experimental "Window" and "ImageWindow" classes to the - ImageWin module. These classes allow you to create a WCK-style - toplevel window, and use it to display raster data. ++ Fixed some Python 1.5.2 issues (to build under 1.5.2, use the + Makefile.pre.in/Setup.in approach) - + Fixed some Python 1.5.2 issues (to build under 1.5.2, use the - Makefile.pre.in/Setup.in approach) ++ Added support for the TIFF FillOrder tag. PIL can read mode "1", + "L", "P" and "RGB" images with non-standard FillOrder (based on + input from Jeff Breidenbach). - + Added support for the TIFF FillOrder tag. PIL can read mode "1", - "L", "P" and "RGB" images with non-standard FillOrder (based on - input from Jeff Breidenbach). +1.1.4 final released +-------------------- - (1.1.4 final released) ++ Fixed ImageTk build problem on Unix. - + Fixed ImageTk build problem on Unix. +1.1.4b2 released +---------------- - (1.1.4b2 released) ++ Improved building on Mac OS X (from Jack Jansen). - + Improved building on Mac OS X (from Jack Jansen). ++ Improved building on Windows with MinGW (from Klamer Shutte). - + Improved building on Windows with MinGW (from Klamer Shutte). ++ If no font is specified, ImageDraw now uses the embedded default + font. Use the "load" or "truetype" methods to load a real font. - + If no font is specified, ImageDraw now uses the embedded default - font. Use the "load" or "truetype" methods to load a real font. ++ Added embedded default font to the ImageFont module (currently + an 8-pixel Courier font, taken from the X window distribution). - + Added embedded default font to the ImageFont module (currently - an 8-pixel Courier font, taken from the X window distribution). +1.1.4b1 released +---------------- - (1.1.4b1 released) ++ Added experimental EXIF support for JPEG files. To extract EXIF + information from a JPEG file, open the file as usual, and call the + "_getexif" method. If successful, this method returns a dictionary + mapping EXIF TIFF tags to values. If the file does not contain EXIF + data, the "_getexif" method returns None. - + Added experimental EXIF support for JPEG files. To extract EXIF - information from a JPEG file, open the file as usual, and call the - "_getexif" method. If successful, this method returns a dictionary - mapping EXIF TIFF tags to values. If the file does not contain EXIF - data, the "_getexif" method returns None. + The "ExifTags" module contains a dictionary mapping tags to tag + names. - The "ExifTags" module contains a dictionary mapping tags to tag - names. + This interface will most likely change in future versions. - This interface will most likely change in future versions. ++ Fixed a bug when using the "transparency" option with the GIF + writer. - + Fixed a bug when using the "transparency" option with the GIF - writer. ++ Added limited support for "bitfield compression" in BMP files + and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This + also fixes a problem with ImageGrab module when copying screen- + dumps from the clipboard on 15/16/32-bit displays. - + Added limited support for "bitfield compression" in BMP files - and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This - also fixes a problem with ImageGrab module when copying screen- - dumps from the clipboard on 15/16/32-bit displays. ++ Added experimental WAL (Quake 2 textures) loader. To use this + loader, import WalImageFile and call the "open" method in that + module. - + Added experimental WAL (Quake 2 textures) loader. To use this - loader, import WalImageFile and call the "open" method in that - module. +1.1.4a4 released +---------------- - (1.1.4a4 released) ++ Added updated SANE driver (Andrew Kuchling, Abel Deuring) - + Added updated SANE driver (Andrew Kuchling, Abel Deuring) ++ Use Python's "mmap" module on non-Windows platforms to read some + uncompressed formats using memory mapping. Also added a "frombuffer" + function that allows you to access the contents of an existing string + or buffer object as if it were an image object. - + Use Python's "mmap" module on non-Windows platforms to read some - uncompressed formats using memory mapping. Also added a "frombuffer" - function that allows you to access the contents of an existing string - or buffer object as if it were an image object. ++ Fixed a memory leak that could appear when processing mode "P" + images (from Pier Paolo Glave) - + Fixed a memory leak that could appear when processing mode "P" - images (from Pier Paolo Glave) ++ Ignore Unicode characters in the BDF loader (from Graham Dumpleton) - + Ignore Unicode characters in the BDF loader (from Graham Dumpleton) +1.1.4a3 released; windows only +----------------------------- - (1.1.4a3 released; windows only) ++ Added experimental RGBA-on-RGB drawing support. To use RGBA + colours on an RGB image, pass "RGBA" as the second string to + the ImageDraw.Draw constructor. - + Added experimental RGBA-on-RGB drawing support. To use RGBA - colours on an RGB image, pass "RGBA" as the second string to - the ImageDraw.Draw constructor. ++ Added support for non-ASCII strings (Latin-1) and Unicode + to the truetype font renderer. - + Added support for non-ASCII strings (Latin-1) and Unicode - to the truetype font renderer. ++ The ImageWin "Dib" object can now be constructed directly from + an image object. - + The ImageWin "Dib" object can now be constructed directly from - an image object. ++ The ImageWin module now allows you use window handles as well + as device contexts. To use a window handle, wrap the handle in + an ImageWin.HWND object, and pass in this object instead of the + device context. - + The ImageWin module now allows you use window handles as well - as device contexts. To use a window handle, wrap the handle in - an ImageWin.HWND object, and pass in this object instead of the - device context. +1.1.4a2 released +---------------- - (1.1.4a2 released) ++ Improved support for 16-bit unsigned integer images (mode "I;16"). + This includes TIFF reader support, and support for "getextrema" + and "point" (from Klamer Shutte). - + Improved support for 16-bit unsigned integer images (mode "I;16"). - This includes TIFF reader support, and support for "getextrema" - and "point" (from Klamer Shutte). ++ Made the BdfFontFile reader a bit more robust (from Kevin Cazabon + and Dmitry Vasiliev) - + Made the BdfFontFile reader a bit more robust (from Kevin Cazabon - and Dmitry Vasiliev) ++ Changed TIFF writer to always write Compression tag, even when + using the default compression (from Greg Couch). - + Changed TIFF writer to always write Compression tag, even when - using the default compression (from Greg Couch). ++ Added "show" support for Mac OS X (from Dan Wolfe). - + Added "show" support for Mac OS X (from Dan Wolfe). ++ Added clipboard support to the "ImageGrab" module (Windows only). + The "grabclipboard" function returns an Image object, a list of + filenames (not in 1.1.4), or None if neither was found. - + Added clipboard support to the "ImageGrab" module (Windows only). - The "grabclipboard" function returns an Image object, a list of - filenames (not in 1.1.4), or None if neither was found. +1.1.4a1 released +---------------- - (1.1.4a1 released) ++ Improved support for drawing RGB data in palette images. You can + now use RGB tuples or colour names (see below) when drawing in a + mode "P" image. The drawing layer automatically assigns color + indexes, as long as you don't use more than 256 unique colours. - + Improved support for drawing RGB data in palette images. You can - now use RGB tuples or colour names (see below) when drawing in a - mode "P" image. The drawing layer automatically assigns color - indexes, as long as you don't use more than 256 unique colours. ++ Moved self test from MiniTest/test.py to ./selftest.py. - + Moved self test from MiniTest/test.py to ./selftest.py. ++ Added support for CSS3-style color strings to most places that + accept colour codes/tuples. This includes the "ImageDraw" module, + the Image "new" function, and the Image "paste" method. - + Added support for CSS3-style color strings to most places that - accept colour codes/tuples. This includes the "ImageDraw" module, - the Image "new" function, and the Image "paste" method. + Colour strings can use one of the following formats: "#f00", + "#ff0000", "rgb(255,0,0)", "rgb(100%,0%,0%)", "hsl(0, 100%, 50%)", + or "red" (most X11-style colour names are supported). See the + documentation for the "ImageColor" module for more information. - Colour strings can use one of the following formats: "#f00", - "#ff0000", "rgb(255,0,0)", "rgb(100%,0%,0%)", "hsl(0, 100%, 50%)", - or "red" (most X11-style colour names are supported). See the - documentation for the "ImageColor" module for more information. ++ Fixed DCX decoder (based on input from Larry Bates) - + Fixed DCX decoder (based on input from Larry Bates) ++ Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA + newsphoto properties from JPEG, TIFF, or IPTC files. - + Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA - newsphoto properties from JPEG, TIFF, or IPTC files. ++ Support for TrueType/OpenType fonts has been added to + the standard distribution. You need the freetype 2.0 + library. - + Support for TrueType/OpenType fonts has been added to - the standard distribution. You need the freetype 2.0 - library. ++ Made the PCX reader a bit more robust when reading 2-bit + and 4-bit PCX images with odd image sizes. - + Made the PCX reader a bit more robust when reading 2-bit - and 4-bit PCX images with odd image sizes. ++ Added "Kernel" class to the ImageFilter module. This class + allows you to filter images with user-defined 3x3 and 5x5 + convolution kernels. - + Added "Kernel" class to the ImageFilter module. This class - allows you to filter images with user-defined 3x3 and 5x5 - convolution kernels. ++ Added "putdata" support for mode "I", "F" and "RGB". - + Added "putdata" support for mode "I", "F" and "RGB". ++ The GIF writer now supports the transparency option (from + Denis Benoit). - + The GIF writer now supports the transparency option (from - Denis Benoit). ++ A HTML version of the module documentation is now shipped + with the source code distribution. You'll find the files in + the Doc subdirectory. - + A HTML version of the module documentation is now shipped - with the source code distribution. You'll find the files in - the Doc subdirectory. ++ Added support for Palm pixmaps (from Bill Janssen). This + change was listed for 1.1.3, but the "PalmImagePlugin" driver + didn't make it into the distribution. - + Added support for Palm pixmaps (from Bill Janssen). This - change was listed for 1.1.3, but the "PalmImagePlugin" driver - didn't make it into the distribution. ++ Improved decoder error messages. - + Improved decoder error messages. +1.1.3 final released +------------------- - (1.1.3 final released) ++ Made setup.py look for old versions of zlib. For some back- + ground, see: https://zlib.net/advisory-2002-03-11.txt - + Made setup.py look for old versions of zlib. For some back- - ground, see: https://zlib.net/advisory-2002-03-11.txt +1.1.3c2 released +----------------- - (1.1.3c2 released) ++ Added setup.py file (tested on Unix and Windows). You still + need to build libImaging/imaging.lib in the traditional way, + but the setup.py script takes care of the rest. - + Added setup.py file (tested on Unix and Windows). You still - need to build libImaging/imaging.lib in the traditional way, - but the setup.py script takes care of the rest. + The old Setup.in/Makefile.pre.in build method is still + supported. - The old Setup.in/Makefile.pre.in build method is still - supported. ++ Fixed segmentation violation in ANTIALIAS filter (an internal + buffer wasn't properly allocated). - + Fixed segmentation violation in ANTIALIAS filter (an internal - buffer wasn't properly allocated). +1.1.3c1 released +---------------- - (1.1.3c1 released) ++ Added ANTIALIAS downsampling filter for high-quality "resize" + and "thumbnail" operations. Also added filter option to the + "thumbnail" operation; the default value is NEAREST, but this + will most likely change in future versions. - + Added ANTIALIAS downsampling filter for high-quality "resize" - and "thumbnail" operations. Also added filter option to the - "thumbnail" operation; the default value is NEAREST, but this - will most likely change in future versions. ++ Fixed plugin loader to be more robust if the __file__ + variable isn't set. - + Fixed plugin loader to be more robust if the __file__ - variable isn't set. ++ Added seek/tell support (for layers) to the PhotoShop + loader. Layer 0 is the main image. - + Added seek/tell support (for layers) to the PhotoShop - loader. Layer 0 is the main image. ++ Added new (but experimental) "ImageOps" module, which provides + shortcuts for commonly used operations on entire images. - + Added new (but experimental) "ImageOps" module, which provides - shortcuts for commonly used operations on entire images. ++ Don't mess up when loading PNG images if the decoder leaves + data in the output buffer. This could cause internal errors + on some PNG images, with some versions of ZLIB. (Bug report + and patch provided by Bernhard Herzog.) - + Don't mess up when loading PNG images if the decoder leaves - data in the output buffer. This could cause internal errors - on some PNG images, with some versions of ZLIB. (Bug report - and patch provided by Bernhard Herzog.) ++ Don't mess up on Unicode filenames. - + Don't mess up on Unicode filenames. ++ Don't mess up when drawing on big endian platforms. - + Don't mess up when drawing on big endian platforms. ++ Made the TIFF loader a bit more robust; it can now read some + more slightly broken TIFF files (based on input from Ted Wright, + Bob Klimek, and D. Alan Stewart) - + Made the TIFF loader a bit more robust; it can now read some - more slightly broken TIFF files (based on input from Ted Wright, - Bob Klimek, and D. Alan Stewart) ++ Added OS/2 EMX build files (from Andrew MacIntyre) - + Added OS/2 EMX build files (from Andrew MacIntyre) ++ Change "ImageFont" to reject image files if they don't have the + right mode. Older versions could leak memory for "P" images. + (Bug reported by Markus Gritsch). - + Change "ImageFont" to reject image files if they don't have the - right mode. Older versions could leak memory for "P" images. - (Bug reported by Markus Gritsch). ++ Renamed some internal functions to avoid potential build + problem on Mac OS X. - + Renamed some internal functions to avoid potential build - problem on Mac OS X. ++ Added DL_EXPORT where relevant (for Cygwin, based on input + from Robert Yodlowski) - + Added DL_EXPORT where relevant (for Cygwin, based on input - from Robert Yodlowski) ++ (re)moved bogus __init__ call in BdfFontFile (bug spotted + by Fred Clare) - + (re)moved bogus __init__ call in BdfFontFile (bug spotted - by Fred Clare) ++ Added "ImageGrab" support (Windows only) - + Added "ImageGrab" support (Windows only) ++ Added support for XBM hotspots (based on code contributed by + Bernhard Herzog). - + Added support for XBM hotspots (based on code contributed by - Bernhard Herzog). ++ Added write support for more TIFF tags, namely the Artist, + Copyright, DateTime, ResolutionUnit, Software, XResolution and + YResolution tags (from Greg Couch) - + Added write support for more TIFF tags, namely the Artist, - Copyright, DateTime, ResolutionUnit, Software, XResolution and - YResolution tags (from Greg Couch) ++ Added TransposedFont wrapper to ImageFont module - + Added TransposedFont wrapper to ImageFont module ++ Added "optimize" flag to GIF encoder. If optimize is present + and non-zero, PIL will work harder to create a small file. - + Added "optimize" flag to GIF encoder. If optimize is present - and non-zero, PIL will work harder to create a small file. ++ Raise "EOFError" (not IndexError) when reading beyond the + end of a TIFF sequence. - + Raise "EOFError" (not IndexError) when reading beyond the - end of a TIFF sequence. ++ Support rewind ("seek(0)") for GIF and TIFF sequences. - + Support rewind ("seek(0)") for GIF and TIFF sequences. ++ Load grayscale GIF images as mode "L" - + Load grayscale GIF images as mode "L" ++ Added DPI read/write support to the JPEG codec. The decoder + sets the info["dpi"] attribute for JPEG files with JFIF dpi + settings. The encoder uses the "dpi" option: - + Added DPI read/write support to the JPEG codec. The decoder - sets the info["dpi"] attribute for JPEG files with JFIF dpi - settings. The encoder uses the "dpi" option: + im = Image.open("file.jpg") + dpi = im.info["dpi"] # raises KeyError if DPI not known + im.save("out.jpg", dpi=dpi) - im = Image.open("file.jpg") - dpi = im.info["dpi"] # raises KeyError if DPI not known - im.save("out.jpg", dpi=dpi) + Note that PIL doesn't always preserve the "info" attribute + for normal image operations. - Note that PIL doesn't always preserve the "info" attribute - for normal image operations. +1.1.2c1 and 1.1.2 final released +---------------- - (1.1.2c1 and 1.1.2 final released) ++ Adapted to Python 2.1. Among other things, all uses of the + "regex" module have been replaced with "re". - + Adapted to Python 2.1. Among other things, all uses of the - "regex" module have been replaced with "re". ++ Fixed attribute error when reading large PNG files (this bug + was introduced in maintenance code released after the 1.1.1 + release) - + Fixed attribute error when reading large PNG files (this bug - was introduced in maintenance code released after the 1.1.1 - release) ++ Ignore non-string objects in sys.path - + Ignore non-string objects in sys.path ++ Fixed Image.transform(EXTENT) for negative xoffsets - + Fixed Image.transform(EXTENT) for negative xoffsets ++ Fixed loading of image plugins if PIL is installed as a package. + (The plugin loader now always looks in the directory where the + Image.py module itself is found, even if that directory isn't on + the standard search path) - + Fixed loading of image plugins if PIL is installed as a package. - (The plugin loader now always looks in the directory where the - Image.py module itself is found, even if that directory isn't on - the standard search path) ++ The Png plugin has been added to the list of preloaded standard + formats - + The Png plugin has been added to the list of preloaded standard - formats ++ Fixed bitmap/text drawing in fill mode. - + Fixed bitmap/text drawing in fill mode. ++ Fixed "getextrema" to work also for multiband images. - + Fixed "getextrema" to work also for multiband images. ++ Added transparency support for L and P images to the PNG codec. - + Added transparency support for L and P images to the PNG codec. ++ Improved support for read-only images. The "load" method now + sets the "readonly" attribute for memory-mapped images. Operations + that modifies an image in place (such as "paste" and drawing operations) + creates an in-memory copy of the image, if necessary. (before this + change, any attempt to modify a memory-mapped image resulted in a + core dump...) - + Improved support for read-only images. The "load" method now - sets the "readonly" attribute for memory-mapped images. Operations - that modifies an image in place (such as "paste" and drawing operations) - creates an in-memory copy of the image, if necessary. (before this - change, any attempt to modify a memory-mapped image resulted in a - core dump...) ++ Added special cases for lists everywhere PIL expects a sequence. + This should speed up things like "putdata" and drawing operations. - + Added special cases for lists everywhere PIL expects a sequence. - This should speed up things like "putdata" and drawing operations. ++ The Image.offset method is deprecated. Use the ImageChops.offset + function instead. - + The Image.offset method is deprecated. Use the ImageChops.offset - function instead. ++ Changed ImageChops operators to copy palette and info dictionary + from the first image argument. - + Changed ImageChops operators to copy palette and info dictionary - from the first image argument. +1.1.1 released +-------------- - (1.1.1 released) ++ Additional fixes for Python 1.6/2.0, including TIFF "save" bug. - + Additional fixes for Python 1.6/2.0, including TIFF "save" bug. ++ Changed "init" to properly load plugins when PIL is used as a + package. - + Changed "init" to properly load plugins when PIL is used as a - package. ++ Fixed broken "show" method (on Unix) - + Fixed broken "show" method (on Unix) +1.0 to 1.1 +---------- - *** Changes from release 1.0 to 1.1 *** ++ Adapted to Python 1.6 ("append" and other method changes) - + Adapted to Python 1.6 ("append" and other method changes) ++ Fixed Image.paste when pasting with solid colour and matte + layers ("L" or "RGBA" masks) (bug reported by Robert Kern) - + Fixed Image.paste when pasting with solid colour and matte - layers ("L" or "RGBA" masks) (bug reported by Robert Kern) ++ To make it easier to distribute prebuilt versions of PIL, + the tkinit binding stuff has been moved to a separate + extension module, named "_imagingtk". - + To make it easier to distribute prebuilt versions of PIL, - the tkinit binding stuff has been moved to a separate - extension module, named "_imagingtk". +0.3b2 to 1.0 final +------ - *** Changes from release 0.3b2 to 1.0 final *** ++ If there's no 16-bit integer (like on a Cray T3E), set + INT16 to the smallest integer available. Most of the + library works just fine anyway (from Bill Crutchfield) - + If there's no 16-bit integer (like on a Cray T3E), set - INT16 to the smallest integer available. Most of the - library works just fine anyway (from Bill Crutchfield) ++ Tweaks to make drawing work on big-endian platforms. - + Tweaks to make drawing work on big-endian platforms. +1.0c2 released +-------------- - (1.0c2 released) ++ If PIL is built with the WITH_TKINTER flag, ImageTk can + automatically hook into a standard Tkinter build. You + no longer need to build your own Tkinter to use the + ImageTk module. - + If PIL is built with the WITH_TKINTER flag, ImageTk can - automatically hook into a standard Tkinter build. You - no longer need to build your own Tkinter to use the - ImageTk module. + The old way still works, though. For more information, + see Tk/install.txt. - The old way still works, though. For more information, - see Tk/install.txt. ++ Some tweaks to ImageTk to support multiple Tk interpreters + (from Greg Couch). - + Some tweaks to ImageTk to support multiple Tk interpreters - (from Greg Couch). ++ ImageFont "load_path" now scans directory mentioned in .pth + files (from Richard Jones). - + ImageFont "load_path" now scans directory mentioned in .pth - files (from Richard Jones). +1.0c1 released +-------------- - (1.0c1 released) ++ The TIFF plugin has been rewritten. The new plugin fully + supports all major PIL image modes (including F and I). - + The TIFF plugin has been rewritten. The new plugin fully - supports all major PIL image modes (including F and I). ++ The ImageFile module now includes a Parser class, which can + be used to incrementally decode an image file (while down- + loading it from the net, for example). See the handbook for + details. - + The ImageFile module now includes a Parser class, which can - be used to incrementally decode an image file (while down- - loading it from the net, for example). See the handbook for - details. - - + "show" now converts non-standard modes to "L" or "RGB" (as - appropriate), rather than writing weird things to disk for - "xv" to choke upon. (bug reported by Les Schaffer). ++ "show" now converts non-standard modes to "L" or "RGB" (as + appropriate), rather than writing weird things to disk for + "xv" to choke upon. (bug reported by Les Schaffer). 1.0b2 released -------------- @@ -6570,3 +6596,355 @@ Magenta, Yellow, Black). + PostScript printing is provided through the PSDraw module. See the handbook for details. +3708 + [sircinnamon, radarhere] + +- Added ImageSequence all_frames #3778 + [radarhere] + +- Use unsigned int to store TIFF IFD offsets #3923 + [cgohlke] + +- Include CPPFLAGS when searching for libraries #3819 + [jefferyto] + +- Updated TIFF tile descriptors to match current decoding functionality #3795 + [dmnisson] + +- Added an ``image.entropy()`` method (second revision) #3608 + [fish2000] + +- Pass the correct types to PyArg_ParseTuple #3880 + [QuLogic] + +- Fixed crash when loading non-font bytes #3912 + [radarhere] + +- Fix SPARC memory alignment issues in Pack/Unpack functions #3858 + [kulikjak] + +- Added CMYK;16B and CMYK;16N unpackers #3913 + [radarhere] + +- Fixed bugs in calculating text size #3864 + [radarhere] + +- Add __main__.py to output basic format and support information #3870 + [jdufresne] + +- Added variation font support #3802 + [radarhere] + +- Do not down-convert if image is LA when showing with PNG format #3869 + [radarhere] + +- Improve handling of PSD frames #3759 + [radarhere] + +- Improved ICO and ICNS loading #3897 + [radarhere] + +- Changed Preview application path so that it is no longer static #3896 + [radarhere] + +- Corrected ttb text positioning #3856 + [radarhere] + +- Handle unexpected ICO image sizes #3836 + [radarhere] + +- Fixed bits value for RGB;16N unpackers #3837 + [kkopachev] + +- Travis CI: Add Fedora 30, remove Fedora 28 #3821 + [hugovk] + +- Added reading of CMYK;16L TIFF images #3817 + [radarhere] + +- Fixed dimensions of 1-bit PDFs #3827 + [radarhere] + +- Fixed opening mmap image through Path on Windows #3825 + [radarhere] + +- Fixed ImageDraw arc gaps #3824 + [radarhere] + +- Expand GIF to include frames with extents outside the image size #3822 + [radarhere] + +- Fixed ImageTk getimage #3814 + [radarhere] + +- Fixed bug in decoding large images #3791 + [radarhere] + +- Fixed reading APP13 marker without Photoshop data #3771 + [radarhere] + +- Added option to include layered windows in ImageGrab.grab on Windows #3808 + [radarhere] + +- Detect libimagequant when installed by pacman on MingW #3812 + [radarhere] + +- Fixed raqm layout bug #3787 + [radarhere] + +- Fixed loading font with non-Unicode path on Windows #3785 + [radarhere] + +- Travis CI: Upgrade PyPy from 6.0.0 to 7.1.1 #3783 + [hugovk, johnthagen] + +- Depends: Updated openjpeg to 2.3.1 #3794, raqm to 0.7.0 #3877, libimagequant to 2.12.3 #3889 + [radarhere] + +- Fix numpy bool bug #3790 + [radarhere] + +6.0.0 (2019-04-01) +------------------ + +- Python 2.7 support will be removed in Pillow 7.0.0 #3682 + [hugovk] + +- Add EXIF class #3625 + [radarhere] + +- Add ImageOps exif_transpose method #3687 + [radarhere] + +- Added warnings to deprecated CMSProfile attributes #3615 + [hugovk] + +- Documented reading TIFF multiframe images #3720 + [akuchling] + +- Improved speed of opening an MPO file #3658 + [Glandos] + +- Update palette in quantize #3721 + [radarhere] + +- Improvements to TIFF is_animated and n_frames #3714 + [radarhere] + +- Fixed incompatible pointer type warnings #3754 + [radarhere] + +- Improvements to PA and LA conversion and palette operations #3728 + [radarhere] + +- Consistent DPI rounding #3709 + [radarhere] + +- Change size of MPO image to match frame #3588 + [radarhere] + +- Read Photoshop resolution data #3701 + [radarhere] + +- Ensure image is mutable before saving #3724 + [radarhere] + +- Correct remap_palette documentation #3740 + [radarhere] + +- Promote P images to PA in putalpha #3726 + [radarhere] + +- Allow RGB and RGBA values for new P images #3719 + [radarhere] + +- Fixed TIFF bug when seeking backwards and then forwards #3713 + [radarhere] + +- Cache EXIF information #3498 + [Glandos] + +- Added transparency for all PNG greyscale modes #3744 + [radarhere] + +- Fix deprecation warnings in Python 3.8 #3749 + [radarhere] + +- Fixed GIF bug when rewinding to a non-zero frame #3716 + [radarhere] + +- Only close original fp in __del__ and __exit__ if original fp is exclusive #3683 + [radarhere] + +- Fix BytesWarning in Tests/test_numpy.py #3725 + [jdufresne] + +- Add missing MIME types and extensions #3520 + [pirate486743186] + +- Add I;16 PNG save #3566 + [radarhere] + +- Add support for BMP RGBA bitfield compression #3705 + [radarhere] + +- Added ability to set language for text rendering #3693 + [iwsfutcmd] + +- Only close exclusive fp on Image __exit__ #3698 + [radarhere] + +- Changed EPS subprocess stdout from devnull to None #3635 + [radarhere] + +- Add reading old-JPEG compressed TIFFs #3489 + [kkopachev] + +- Add EXIF support for PNG #3674 + [radarhere] + +- Add option to set dither param on quantize #3699 + [glasnt] + +- Add reading of DDS uncompressed RGB data #3673 + [radarhere] + +- Correct length of Tiff BYTE tags #3672 + [radarhere] + +- Add DIB saving and loading through Image open #3691 + [radarhere] + +- Removed deprecated VERSION #3624 + [hugovk] + +- Fix 'BytesWarning: Comparison between bytes and string' in PdfDict #3580 + [jdufresne] + +- Do not resize in Image.thumbnail if already the destination size #3632 + [radarhere] + +- Replace .seek() magic numbers with io.SEEK_* constants #3572 + [jdufresne] + +- Make ContainerIO.isatty() return a bool, not int #3568 + [jdufresne] + +- Add support to all transpose operations for I;16 modes #3563, #3741 + [radarhere] + +- Deprecate support for PyQt4 and PySide #3655 + [hugovk, radarhere] + +- Add TIFF compression codecs: LZMA, Zstd, WebP #3555 + [cgohlke] + +- Fixed pickling of iTXt class with protocol > 1 #3537 + [radarhere] + +- _util.isPath returns True for pathlib.Path objects #3616 + [wbadart] + +- Remove unnecessary unittest.main() boilerplate from test files #3631 + [jdufresne] + +- Exif: Seek to IFD offset #3584 + [radarhere] + +- Deprecate PIL.*ImagePlugin.__version__ attributes #3628 + [jdufresne] + +- Docs: Add note about ImageDraw operations that exceed image bounds #3620 + [radarhere] + +- Allow for unknown PNG chunks after image data #3558 + [radarhere] + +- Changed EPS subprocess stdin from devnull to None #3611 + [radarhere] + +- Fix possible integer overflow #3609 + [cgohlke] + +- Catch BaseException for resource cleanup handlers #3574 + [jdufresne] + +- Improve pytest configuration to allow specific tests as CLI args #3579 + [jdufresne] + +- Drop support for Python 3.4 #3596 + [hugovk] + +- Remove deprecated PIL.OleFileIO #3598 + [hugovk] + +- Remove deprecated ImageOps undocumented functions #3599 + [hugovk] + +- Depends: Update libwebp to 1.0.2 #3602 + [radarhere] + +- Detect MIME types #3525 + [radarhere] + +5.4.1 (2019-01-06) +------------------ + +- File closing: Only close __fp if not fp #3540 + [radarhere] + +- Fix build for Termux #3529 + [pslacerda] + +- PNG: Detect MIME types #3525 + [radarhere] + +- PNG: Handle IDAT chunks after image end #3532 + [radarhere] + +5.4.0 (2019-01-01) +------------------ + +- Docs: Improved ImageChops documentation #3522 + [radarhere] + +- Allow RGB and RGBA values for P image putpixel #3519 + [radarhere] + +- Add APNG extension to PNG plugin #3501 + [pirate486743186, radarhere] + +- Lookup ld.so.cache instead of hardcoding search paths #3245 + [pslacerda] + +- Added custom string TIFF tags #3513 + [radarhere] + +- Improve setup.py configuration #3395 + [diorcety] + +- Read textual chunks located after IDAT chunks for PNG #3506 + [radarhere] + +- Performance: Don't try to hash value if enum is empty #3503 + [Glandos] + +- Added custom int and float TIFF tags #3350 + [radarhere] + +- Fixes for issues reported by static code analysis #3393 + [frenzymadness] + +- GIF: Wait until mode is normalized to copy im.info into encoderinfo #3187 + [radarhere] + +- Docs: Add page of deprecations and removals #3486 + [hugovk] + +- Travis CI: Upgrade PyPy from 5.8.0 to 6.0 #3488 + [hugovk] + +- Travis CI: Allow lint job to fail #3467 + [hugovk] + +- Re From 386c64bb439fc1c70af98885730df57638a985b3 Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 11:58:16 +0430 Subject: [PATCH 074/294] Next update: complete CHANGES --- CHANGES.rst | 1715 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 1215 insertions(+), 500 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6755657ab..22fd1ddbc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,3 @@ - Changelog (Pillow) ================== @@ -4822,298 +4821,304 @@ Pre-fork Ka-Ping Yee, and many others (if your name should be on this list, let me know.) -Changes from release 1.1.6 to 1.1.7 *** - This section may not be fully complete. For changes since this file - was last updated, see the repository revision history: +1.1.6 to 1.1.7 +-------------- - http://svn.effbot.org/public/pil/ +This section may not be fully complete. For changes since this file +was last updated, see the repository revision history: - (1.1.7 final) + http://svn.effbot.org/public/pil/ - + Set GIF loop info property to the number of iterations if a NETSCAPE - loop extension is present, instead of always setting it to 1 (from - Valentino Volonghi). +1.1.7 final +----------- - (1.1.7c1 released) ++ Set GIF loop info property to the number of iterations if a NETSCAPE + loop extension is present, instead of always setting it to 1 (from + Valentino Volonghi). - + Improved PNG compression (from Alexey Borzenkov). +1.1.7c1 released +---------------- - + Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) ++ Improved PNG compression (from Alexey Borzenkov). - + Added various TGA improvements from Alexey Borzenkov, including - support for specifying image orientation. ++ Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) - + Bumped block threshold to 16 megabytes, made size estimation a bit - more accurate. This speeds up allocation of large images. ++ Added various TGA improvements from Alexey Borzenkov, including + support for specifying image orientation. - + Fixed rounding error in ImagingDrawWideLine. ++ Bumped block threshold to 16 megabytes, made size estimation a bit + more accurate. This speeds up allocation of large images. - "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every - version I've seen, which leads to different width lines depending on - the order of the points in the line. This is especially bad at some - angles where a 'width=2' line can completely disappear. ++ Fixed rounding error in ImagingDrawWideLine. - + Added support for RGBA mode to the SGI module (based on code by - Karsten Hiddemann). + "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every + version I've seen, which leads to different width lines depending on + the order of the points in the line. This is especially bad at some + angles where a 'width=2' line can completely disappear. - + Handle repeated IPTC tags (adapted from a patch by Eric Bruning). ++ Added support for RGBA mode to the SGI module (based on code by + Karsten Hiddemann). - Eric writes: According to the specification, some IPTC tags can be - repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last - instance of that tag. Below is a patch to store all tags. If there are - multiple tag instances, they are stored in a (python) list. Single tag - instances remain as strings. ++ Handle repeated IPTC tags (adapted from a patch by Eric Bruning). - + Fixed potential crash in ImageFilter for small target images - (reported by Zac Burns and Daniel Fetchinson). + Eric writes: According to the specification, some IPTC tags can be + repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last + instance of that tag. Below is a patch to store all tags. If there are + multiple tag instances, they are stored in a (python) list. Single tag + instances remain as strings. - + Use BMP instead of JPEG as temporary show format on Mac OS X. ++ Fixed potential crash in ImageFilter for small target images + (reported by Zac Burns and Daniel Fetchinson). - + Fixed putpixel/new for I;16 with colors > 255. ++ Use BMP instead of JPEG as temporary show format on Mac OS X. - + Added integer power support to ImagingMath. ++ Fixed putpixel/new for I;16 with colors > 255. - + Added limited support for I;16L mode (explicit little endian). ++ Added integer power support to ImagingMath. - + Moved WMF support into Image.core; enable WMF rendering by default - if renderer is available. ++ Added limited support for I;16L mode (explicit little endian). - + Mark the ARG plugin as obsolete. ++ Moved WMF support into Image.core; enable WMF rendering by default + if renderer is available. - + Added version query mechanism to ImageCms and ImageFont, for - debugging. ++ Mark the ARG plugin as obsolete. - + Added (experimental) ImageCms function for fetching the ICC profile - for the current display (currently Windows only). ++ Added version query mechanism to ImageCms and ImageFont, for + debugging. - Added HWND/HDC support to ImageCms.get_display_profile(). ++ Added (experimental) ImageCms function for fetching the ICC profile + for the current display (currently Windows only). - + Added WMF renderer (Windows only). + Added HWND/HDC support to ImageCms.get_display_profile(). - + Added ImagePointHandler and ImageTransformHandler mixins; made - ImageCmsTransform work with im.point. ++ Added WMF renderer (Windows only). - + Fixed potential endless loop in the XVThumbnail reader (from Nikolai - Ugelvik). ++ Added ImagePointHandler and ImageTransformHandler mixins; made + ImageCmsTransform work with im.point. - + Added Kevin Cazabon's pyCMS package. ++ Fixed potential endless loop in the XVThumbnail reader (from Nikolai + Ugelvik). - The C code has been moved to _imagingcms.c, the Python interface - module is installed as PIL.ImageCMS. ++ Added Kevin Cazabon's pyCMS package. - Added support for in-memory ICC profiles. + The C code has been moved to _imagingcms.c, the Python interface + module is installed as PIL.ImageCMS. - Unified buildTransform and buildTransformFromOpenProfiles. + Added support for in-memory ICC profiles. - The profile can now be either a filename, a profile object, or a - file-like object containing an in-memory profile. + Unified buildTransform and buildTransformFromOpenProfiles. - Additional fixes from Florian Böch: + The profile can now be either a filename, a profile object, or a + file-like object containing an in-memory profile. - Very nice - it just needs LCMS flags support so we can use black - point compensation and softproofing :) See attached patches. They - also fix a naming issue which could cause confusion - display - profile (ImageCms wording) actually means proof profile (lcms - wording), so I changed variable names and docstrings where - applicable. Patches are tested under Python 2.6. + Additional fixes from Florian Böch: - + Improved support for layer names in PSD files (from Sylvain Baubeau) + Very nice - it just needs LCMS flags support so we can use black + point compensation and softproofing :) See attached patches. They + also fix a naming issue which could cause confusion - display + profile (ImageCms wording) actually means proof profile (lcms + wording), so I changed variable names and docstrings where + applicable. Patches are tested under Python 2.6. - Sylvain writes: I needed to be able to retrieve the names of the - layers in a PSD files. But PsdImagePlugin.py didn't do the job so I - wrote this very small patch. ++ Improved support for layer names in PSD files (from Sylvain Baubeau) - + Improved RGBA support for ImageTk for 8.4 and newer (from Con - Radchenko). + Sylvain writes: I needed to be able to retrieve the names of the + layers in a PSD files. But PsdImagePlugin.py didn't do the job so I + wrote this very small patch. - This replaces the slow run-length based encoding model with true - compositing at the Tk level. ++ Improved RGBA support for ImageTk for 8.4 and newer (from Con + Radchenko). - + Added support for 16- and 32-bit images to McIdas loader. + This replaces the slow run-length based encoding model with true + compositing at the Tk level. - Based on file samples and stand-alone reader code provided by Craig - Swank. ++ Added support for 16- and 32-bit images to McIdas loader. - + Added ImagePalette support to putpalette. + Based on file samples and stand-alone reader code provided by Craig + Swank. - + Fixed problem with incremental parsing of PNG files. ++ Added ImagePalette support to putpalette. - + Make selftest.py report non-zero status on failure (from Mark - Sienkiewicz) ++ Fixed problem with incremental parsing of PNG files. - + Add big endian save support and multipage infrastructure to the TIFF - writer (from Sebastian Haase). ++ Make selftest.py report non-zero status on failure (from Mark + Sienkiewicz) - + Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt - Schwehr). ++ Add big endian save support and multipage infrastructure to the TIFF + writer (from Sebastian Haase). - + Added zTXT support (from Andrew Kuchling via Lowell Alleman). ++ Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt + Schwehr). - + Fixed potential infinite loop bug in ImageFont (from Guilherme Polo). ++ Added zTXT support (from Andrew Kuchling via Lowell Alleman). - + Added sample ICC profiles (from Kevin Cazabon) ++ Fixed potential infinite loop bug in ImageFont (from Guilherme Polo). - + Fixed array interface for I, F, and RGBA/RGBX images. ++ Added sample ICC profiles (from Kevin Cazabon) - + Added Chroma subsampling support for JPEG (from Justin Huff). ++ Fixed array interface for I, F, and RGBA/RGBX images. - Justin writes: Attached is a patch (against PIL 1.1.6) to provide - control over the chroma subsampling done by the JPEG encoder. This - is often useful for reducing compression artifacts around edges of - clipart and text. ++ Added Chroma subsampling support for JPEG (from Justin Huff). - + Added USM/Gaussian Blur code from Kevin Cazabon. + Justin writes: Attached is a patch (against PIL 1.1.6) to provide + control over the chroma subsampling done by the JPEG encoder. This + is often useful for reducing compression artifacts around edges of + clipart and text. - + Fixed bug w. uninitialized image data when cropping outside the - source image. ++ Added USM/Gaussian Blur code from Kevin Cazabon. - + Use ImageShow to implement the Image.show method. ++ Fixed bug w. uninitialized image data when cropping outside the + source image. - Most notably, this picks the 'display' utility when available. It - also allows application code to register new display utilities via - the ImageShow registry. ++ Use ImageShow to implement the Image.show method. - + Release the GIL in the PNG compressor (from Michael van Tellingen). + Most notably, this picks the 'display' utility when available. It + also allows application code to register new display utilities via + the ImageShow registry. - + Revised JPEG CMYK handling. ++ Release the GIL in the PNG compressor (from Michael van Tellingen). - Always assume Adobe behaviour, both when reading and writing (based on - a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and - additional debugging by Michael van Tellingen). ++ Revised JPEG CMYK handling. - + Support for preserving ICC profiles (by Florian Böch via Tim Hatch). + Always assume Adobe behaviour, both when reading and writing (based on + a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and + additional debugging by Michael van Tellingen). - Florian writes: ++ Support for preserving ICC profiles (by Florian Böch via Tim Hatch). - It's a beta, so still needs some testing, but should allow you to: - - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. - Existing code doesn't need to be changed. - - access embedded profiles in JPEG, PNG, PSD, TIFF. + Florian writes: - It also includes patches for TIFF to retain IPTC, Photoshop and XMP - metadata when saving as TIFF again, read/write TIFF resolution - information correctly, and to correct inverted CMYK JPEG files. + It's a beta, so still needs some testing, but should allow you to: + - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. + Existing code doesn't need to be changed. + - access embedded profiles in JPEG, PNG, PSD, TIFF. - + Fixed potential memory leak in median cut quantizer (from Evgeny Salmin). + It also includes patches for TIFF to retain IPTC, Photoshop and XMP + metadata when saving as TIFF again, read/write TIFF resolution + information correctly, and to correct inverted CMYK JPEG files. - + Fixed OverflowError when reading upside-down BMP images. ++ Fixed potential memory leak in median cut quantizer (from Evgeny Salmin). - + Added resolution save option for PDF files. ++ Fixed OverflowError when reading upside-down BMP images. - Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py - based on 1.1.6 as included in Ubuntu, that supports a "resolution" - save option. Not great, but it makes the PDF saving more useful by - allowing PDFs that are not exactly 72dpi. ++ Added resolution save option for PDF files. - + Look for Tcl/Tk include files in version-specific include directory - (from Encolpe Degoute). + Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py + based on 1.1.6 as included in Ubuntu, that supports a "resolution" + save option. Not great, but it makes the PDF saving more useful by + allowing PDFs that are not exactly 72dpi. - + Fixed grayscale rounding error in ImageColor.getcolor (from Tim - Hatch). ++ Look for Tcl/Tk include files in version-specific include directory + (from Encolpe Degoute). - + Fixed calculation of mean value in ImageEnhance.Contrast (reported - by "roop" and Scott David Daniels). ++ Fixed grayscale rounding error in ImageColor.getcolor (from Tim + Hatch). - + Fixed truetype positioning when first character has a negative left - bearing (from Ned Batchelder): ++ Fixed calculation of mean value in ImageEnhance.Contrast (reported + by "roop" and Scott David Daniels). - Ned writes: In PIL 1.1.6, ImageDraw.text will position the string - incorrectly if the first character has a negative left bearing. To - see the problem, show a string like "///" in an italic font. The - first slash will be clipped at the left, and the string will be - mis-positioned. ++ Fixed truetype positioning when first character has a negative left + bearing (from Ned Batchelder): - + Fixed resolution unit bug in tiff reader/writer (based on code by - Florian Höch, Gary Bloom, and others). + Ned writes: In PIL 1.1.6, ImageDraw.text will position the string + incorrectly if the first character has a negative left bearing. To + see the problem, show a string like "///" in an italic font. The + first slash will be clipped at the left, and the string will be + mis-positioned. - + Added simple transparency support for RGB images (reported by - Sebastian Spaeth). ++ Fixed resolution unit bug in tiff reader/writer (based on code by + Florian Höch, Gary Bloom, and others). - + Added support for Unicode filenames in ImageFont.truetype (from Donn - Ingle). ++ Added simple transparency support for RGB images (reported by + Sebastian Spaeth). - + Fixed potential crash in ImageFont.getname method (from Donn Ingle). ++ Added support for Unicode filenames in ImageFont.truetype (from Donn + Ingle). - + Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). ++ Fixed potential crash in ImageFont.getname method (from Donn Ingle). - *** Changes from release 1.1.5 to 1.1.6 *** ++ Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). - (1.1.6 released) +1.1.6 released +-------------- - + Fixed some 64-bit compatibility warnings for Python 2.5. ++ Fixed some 64-bit compatibility warnings for Python 2.5. - + Added threading support for the Sane driver (from Abel Deuring). ++ Added threading support for the Sane driver (from Abel Deuring). - (1.1.6b2 released) +1.1.6b2 released +---------------- - + Added experimental "floodfill" function to the ImageDraw module - (based on code by Eric Raymond). ++ Added experimental "floodfill" function to the ImageDraw module + (based on code by Eric Raymond). - + The default arguments for "frombuffer" doesn't match "fromstring" - and the documentation; this is a bug, and will most likely be fixed - in a future version. In this release, PIL prints a warning message - instead. To silence the warning, change any calls of the form - "frombuffer(mode, size, data)" to ++ The default arguments for "frombuffer" doesn't match "fromstring" + and the documentation; this is a bug, and will most likely be fixed + in a future version. In this release, PIL prints a warning message + instead. To silence the warning, change any calls of the form + "frombuffer(mode, size, data)" to - frombuffer(mode, size, data, "raw", mode, 0, 1) + frombuffer(mode, size, data, "raw", mode, 0, 1) - + Added "fromarray" function, which takes an object implementing the - NumPy array interface and creates a PIL Image from it. (from Travis - Oliphant). ++ Added "fromarray" function, which takes an object implementing the + NumPy array interface and creates a PIL Image from it. (from Travis + Oliphant). - + Added NumPy array interface support (__array_interface__) to the - Image class (based on code by Travis Oliphant). ++ Added NumPy array interface support (__array_interface__) to the + Image class (based on code by Travis Oliphant). - This allows you to easily convert between PIL image memories and - NumPy arrays: + This allows you to easily convert between PIL image memories and + NumPy arrays: - import numpy, Image + import numpy, Image - im = Image.open('hopper.jpg') + im = Image.open('hopper.jpg') - a = numpy.asarray(im) # a is readonly + a = numpy.asarray(im) # a is readonly - im = Image.fromarray(a) + im = Image.fromarray(a) - + Fixed CMYK polarity for JPEG images, by treating all images as - "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon - for samples, debugging, and patches). ++ Fixed CMYK polarity for JPEG images, by treating all images as + "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon + for samples, debugging, and patches). - (1.1.6b1 released) +1.1.6b1 released +---------------- - + Added 'expand' option to the Image 'rotate' method. If true, the - output image is made large enough to hold the entire rotated image. ++ Added 'expand' option to the Image 'rotate' method. If true, the + output image is made large enough to hold the entire rotated image. - + Changed the ImageDraw 'line' method to always draw the last pixel in - a polyline, independent of line angle. ++ Changed the ImageDraw 'line' method to always draw the last pixel in + a polyline, independent of line angle. - + Fixed bearing calculation and clipping in the ImageFont truetype - renderer; this could lead to clipped text, or crashes in the low- - level _imagingft module. (based on input from Adam Twardoch and - others). ++ Fixed bearing calculation and clipping in the ImageFont truetype + renderer; this could lead to clipped text, or crashes in the low- + level _imagingft module. (based on input from Adam Twardoch and + others). - + Added ImageQt wrapper module, for converting PIL Image objects to - QImage objects in an efficient way. ++ Added ImageQt wrapper module, for converting PIL Image objects to + QImage objects in an efficient way. - + Fixed 'getmodebands' to return the number of bands also for "PA" - and "LA" modes. Added 'getmodebandnames' helper that return the - band names. ++ Fixed 'getmodebands' to return the number of bands also for "PA" + and "LA" modes. Added 'getmodebandnames' helper that return the + band names. - (1.1.6a2 released) +1.1.6a2 released +---------------- - + Added float/double support to the TIFF loader (from Russell - Nelson). ++ Added float/double support to the TIFF loader (from Russell + Nelson). - + Fixed broken use of realloc() in path.c (from Jan Matejek) ++ Fixed broken use of realloc() in path.c (from Jan Matejek) - + Added save support for Spider images (from William Baxter). ++ Added save support for Spider images (from William Baxter). - + Fixed broken 'paste' and 'resize' operations in pildriver - (from Bill Janssen). ++ Fixed broken 'paste' and 'resize' operations in pildriver + (from Bill Janssen). - + Added support for duplex scanning to the Sane interface (Abel - Deuring). ++ Added support for duplex scanning to the Sane interface (Abel + Deuring). 1.1.6a1 released ---------------- @@ -6596,355 +6601,1065 @@ Magenta, Yellow, Black). + PostScript printing is provided through the PSDraw module. See the handbook for details. -3708 - [sircinnamon, radarhere] - -- Added ImageSequence all_frames #3778 +Qt use of unicode() for 2/3 compatibility #1218 [radarhere] -- Use unsigned int to store TIFF IFD offsets #3923 - [cgohlke] - -- Include CPPFLAGS when searching for libraries #3819 - [jefferyto] - -- Updated TIFF tile descriptors to match current decoding functionality #3795 - [dmnisson] - -- Added an ``image.entropy()`` method (second revision) #3608 - [fish2000] - -- Pass the correct types to PyArg_ParseTuple #3880 - [QuLogic] - -- Fixed crash when loading non-font bytes #3912 - [radarhere] - -- Fix SPARC memory alignment issues in Pack/Unpack functions #3858 - [kulikjak] - -- Added CMYK;16B and CMYK;16N unpackers #3913 - [radarhere] - -- Fixed bugs in calculating text size #3864 - [radarhere] - -- Add __main__.py to output basic format and support information #3870 - [jdufresne] - -- Added variation font support #3802 - [radarhere] - -- Do not down-convert if image is LA when showing with PNG format #3869 - [radarhere] - -- Improve handling of PSD frames #3759 - [radarhere] - -- Improved ICO and ICNS loading #3897 - [radarhere] - -- Changed Preview application path so that it is no longer static #3896 - [radarhere] - -- Corrected ttb text positioning #3856 - [radarhere] - -- Handle unexpected ICO image sizes #3836 - [radarhere] - -- Fixed bits value for RGB;16N unpackers #3837 - [kkopachev] - -- Travis CI: Add Fedora 30, remove Fedora 28 #3821 +- Identify XBM file created with filename including underscore #1230 (fixes #1229) [hugovk] -- Added reading of CMYK;16L TIFF images #3817 +- Copy image when saving in GifImagePlugin #1231 (fixes #718) [radarhere] -- Fixed dimensions of 1-bit PDFs #3827 +- Removed support for FreeType 2.0 #1247 [radarhere] -- Fixed opening mmap image through Path on Windows #3825 +- Added background saving to GifImagePlugin #1273 [radarhere] -- Fixed ImageDraw arc gaps #3824 +- Provide n_frames attribute to multi-frame formats #1261 + [anntzer, radarhere] + +- Add duration and loop set to GifImagePlugin #1172, #1269 [radarhere] -- Expand GIF to include frames with extents outside the image size #3822 +- Ico files are little endian #1232 + [wiredfool] + +- Upgrade olefile from 0.30 to 0.42b #1226 + [radarhere, decalage2] + +- Setting transparency value to 0 when the tRNS contains only null byte(s) #1239 + [juztin] + +- Separated out feature checking from selftest #1233 [radarhere] -- Fixed ImageTk getimage #3814 +- Style/health fixes [radarhere] -- Fixed bug in decoding large images #3791 +- Update WebP from 0.4.1 to 0.4.3 #1235 [radarhere] -- Fixed reading APP13 marker without Photoshop data #3771 +- Release GIL during image load (decode) #1224 + [lkesteloot] + +- Added icns save #1185 [radarhere] -- Added option to include layered windows in ImageGrab.grab on Windows #3808 - [radarhere] +- Fix putdata memory leak #1196 + [benoit-pierre] -- Detect libimagequant when installed by pacman on MingW #3812 - [radarhere] +- Keep user-specified ordering of icon sizes #1193 + [karimbahgat] -- Fixed raqm layout bug #3787 - [radarhere] +- Tiff: allow writing floating point tag values #1113 + [bpedersen2] -- Fixed loading font with non-Unicode path on Windows #3785 - [radarhere] - -- Travis CI: Upgrade PyPy from 6.0.0 to 7.1.1 #3783 - [hugovk, johnthagen] - -- Depends: Updated openjpeg to 2.3.1 #3794, raqm to 0.7.0 #3877, libimagequant to 2.12.3 #3889 - [radarhere] - -- Fix numpy bool bug #3790 - [radarhere] - -6.0.0 (2019-04-01) +2.8.2 (2015-06-06) ------------------ -- Python 2.7 support will be removed in Pillow 7.0.0 #3682 - [hugovk] - -- Add EXIF class #3625 +- Bug fix: Fixed Tiff handling of bad EXIF data [radarhere] -- Add ImageOps exif_transpose method #3687 - [radarhere] - -- Added warnings to deprecated CMSProfile attributes #3615 - [hugovk] - -- Documented reading TIFF multiframe images #3720 - [akuchling] - -- Improved speed of opening an MPO file #3658 - [Glandos] - -- Update palette in quantize #3721 - [radarhere] - -- Improvements to TIFF is_animated and n_frames #3714 - [radarhere] - -- Fixed incompatible pointer type warnings #3754 - [radarhere] - -- Improvements to PA and LA conversion and palette operations #3728 - [radarhere] - -- Consistent DPI rounding #3709 - [radarhere] - -- Change size of MPO image to match frame #3588 - [radarhere] - -- Read Photoshop resolution data #3701 - [radarhere] - -- Ensure image is mutable before saving #3724 - [radarhere] - -- Correct remap_palette documentation #3740 - [radarhere] - -- Promote P images to PA in putalpha #3726 - [radarhere] - -- Allow RGB and RGBA values for new P images #3719 - [radarhere] - -- Fixed TIFF bug when seeking backwards and then forwards #3713 - [radarhere] - -- Cache EXIF information #3498 - [Glandos] - -- Added transparency for all PNG greyscale modes #3744 - [radarhere] - -- Fix deprecation warnings in Python 3.8 #3749 - [radarhere] - -- Fixed GIF bug when rewinding to a non-zero frame #3716 - [radarhere] - -- Only close original fp in __del__ and __exit__ if original fp is exclusive #3683 - [radarhere] - -- Fix BytesWarning in Tests/test_numpy.py #3725 - [jdufresne] - -- Add missing MIME types and extensions #3520 - [pirate486743186] - -- Add I;16 PNG save #3566 - [radarhere] - -- Add support for BMP RGBA bitfield compression #3705 - [radarhere] - -- Added ability to set language for text rendering #3693 - [iwsfutcmd] - -- Only close exclusive fp on Image __exit__ #3698 - [radarhere] - -- Changed EPS subprocess stdout from devnull to None #3635 - [radarhere] - -- Add reading old-JPEG compressed TIFFs #3489 - [kkopachev] - -- Add EXIF support for PNG #3674 - [radarhere] - -- Add option to set dither param on quantize #3699 - [glasnt] - -- Add reading of DDS uncompressed RGB data #3673 - [radarhere] - -- Correct length of Tiff BYTE tags #3672 - [radarhere] - -- Add DIB saving and loading through Image open #3691 - [radarhere] - -- Removed deprecated VERSION #3624 - [hugovk] - -- Fix 'BytesWarning: Comparison between bytes and string' in PdfDict #3580 - [jdufresne] - -- Do not resize in Image.thumbnail if already the destination size #3632 - [radarhere] - -- Replace .seek() magic numbers with io.SEEK_* constants #3572 - [jdufresne] - -- Make ContainerIO.isatty() return a bool, not int #3568 - [jdufresne] - -- Add support to all transpose operations for I;16 modes #3563, #3741 - [radarhere] - -- Deprecate support for PyQt4 and PySide #3655 - [hugovk, radarhere] - -- Add TIFF compression codecs: LZMA, Zstd, WebP #3555 - [cgohlke] - -- Fixed pickling of iTXt class with protocol > 1 #3537 - [radarhere] - -- _util.isPath returns True for pathlib.Path objects #3616 - [wbadart] - -- Remove unnecessary unittest.main() boilerplate from test files #3631 - [jdufresne] - -- Exif: Seek to IFD offset #3584 - [radarhere] - -- Deprecate PIL.*ImagePlugin.__version__ attributes #3628 - [jdufresne] - -- Docs: Add note about ImageDraw operations that exceed image bounds #3620 - [radarhere] - -- Allow for unknown PNG chunks after image data #3558 - [radarhere] - -- Changed EPS subprocess stdin from devnull to None #3611 - [radarhere] - -- Fix possible integer overflow #3609 - [cgohlke] - -- Catch BaseException for resource cleanup handlers #3574 - [jdufresne] - -- Improve pytest configuration to allow specific tests as CLI args #3579 - [jdufresne] - -- Drop support for Python 3.4 #3596 - [hugovk] - -- Remove deprecated PIL.OleFileIO #3598 - [hugovk] - -- Remove deprecated ImageOps undocumented functions #3599 - [hugovk] - -- Depends: Update libwebp to 1.0.2 #3602 - [radarhere] - -- Detect MIME types #3525 - [radarhere] - -5.4.1 (2019-01-06) +2.8.1 (2015-04-02) ------------------ -- File closing: Only close __fp if not fp #3540 - [radarhere] +- Bug fix: Catch struct.error on invalid JPEG, fixes #1163. #1165 + [wiredfool, hugovk] -- Fix build for Termux #3529 - [pslacerda] - -- PNG: Detect MIME types #3525 - [radarhere] - -- PNG: Handle IDAT chunks after image end #3532 - [radarhere] - -5.4.0 (2019-01-01) +2.8.0 (2015-04-01) ------------------ -- Docs: Improved ImageChops documentation #3522 - [radarhere] +- Fix 32-bit BMP loading (RGBA or RGBX) #1125 + [artscoop] -- Allow RGB and RGBA values for P image putpixel #3519 - [radarhere] +- Fix UnboundLocalError in ImageFile #1131 + [davarisg] -- Add APNG extension to PNG plugin #3501 - [pirate486743186, radarhere] +- Re-enable test image caching #982 + [hugovk, homm] -- Lookup ld.so.cache instead of hardcoding search paths #3245 - [pslacerda] - -- Added custom string TIFF tags #3513 - [radarhere] - -- Improve setup.py configuration #3395 - [diorcety] - -- Read textual chunks located after IDAT chunks for PNG #3506 - [radarhere] - -- Performance: Don't try to hash value if enum is empty #3503 - [Glandos] - -- Added custom int and float TIFF tags #3350 - [radarhere] - -- Fixes for issues reported by static code analysis #3393 - [frenzymadness] - -- GIF: Wait until mode is normalized to copy im.info into encoderinfo #3187 - [radarhere] - -- Docs: Add page of deprecations and removals #3486 +- Fix: Cannot identify EPS images #1152 (fixes #1104) [hugovk] -- Travis CI: Upgrade PyPy from 5.8.0 to 6.0 #3488 +- Configure setuptools to run nosetests, fixes #729 + [aclark4life] + +- Style/health fixes + [radarhere, hugovk] + +- Add support for HTTP response objects to Image.open() #1151 + [mfitzp] + +- Improve reference docs for PIL.ImageDraw.Draw.pieslice() #1145 + [audreyr] + +- Added copy method font_variant() and accessible properties to truetype() #1123 + [radarhere] + +- Fix ImagingEffectNoise #1128 [hugovk] -- Travis CI: Allow lint job to fail #3467 +- Remove unreachable code #1126 [hugovk] -- Re +- Let Python do the endian stuff + tests #1121 + [amoibos, radarhere] + +- Fix webp decode memory leak #1114 + [benoit-pierre] + +- Fast path for opaque pixels in RGBa unpacker #1088 + [bgilbert] + +- Enable basic support for 'RGBa' raw encoding/decoding #1096 + [immerrr] + +- Fix pickling L mode images with no palette, #1095 + [hugovk] + +- iPython display hook #1091 + [wiredfool] + +- Adjust buffer size when quality=keep #1079 (fixes #148 again) + [wiredfool] + +- Fix for corrupted bitmaps embedded in truetype fonts #1072 + [jackyyf, wiredfool] + +2.7.0 (2015-01-01) +------------------ + +- Split Sane into a separate repo: https://github.com/python-pillow/Sane + [hugovk] + +- Look for OS X and Linux fonts in common places #1054 + [charleslaw] + +- Fix CVE-2014-9601, potential PNG decompression DOS #1060 + [wiredfool] + +- Use underscores, not spaces, in TIFF tag kwargs #1044, #1058 + [anntzer, hugovk] + +- Update PSDraw for Python3, add tests #1055 + [hugovk] + +- Use Bicubic filtering by default for thumbnails. Don't use Jpeg Draft mode for thumbnails #1029 + [homm] + +- Fix MSVC compiler error: Use Py_ssize_t instead of ssize_t #1051 + [cgohlke] + +- Fix compiler error: MSVC needs variables defined at the start of the block #1048 + [cgohlke] + +- The GIF Palette optimization algorithm is only applicable to mode='P' or 'L' #993 + [moriyoshi] + +- Use PySide as an alternative to PyQt4/5 #1024 + [holg] + +- Replace affine-based im.resize implementation with convolution-based im.stretch #997 + [homm] + +- Replace Gaussian Blur implementation with iterated fast box blur. #961 Note: Radius parameter is interpreted differently than before. + [homm] + +- Better docs explaining import _imaging failure #1016, build #1017, mode #1018, PyAccess, PixelAccess objects #1019 Image.quantize #1020 and Image.save #1021 + [wiredfool] + +- Fix for saving TIFF image into an io.BytesIO buffer #1011 + [mfergie] + +- Fix antialias compilation on debug versions of Python #1010 + [wiredfool] + +- Fix for Image.putdata segfault #1009 + [wiredfool] + +- Ico save, additional tests #1007 + [exherb] + +- Use PyQt4 if it has already been imported, otherwise prefer PyQt5 #1003 + [AurelienBallier] + +- Speedup resample implementation up to 2.5 times #977 + [homm] + +- Speed up rotation by using cache aware loops, added transpose to rotations #994 + [homm] + +- Fix Bicubic interpolation #970 + [homm] + +- Support for 4-bit greyscale TIFF images #980 + [hugovk] + +- Updated manifest #957 + [wiredfool] + +- Fix PyPy 2.4 regression #958 + [wiredfool] + +- Webp Metadata Skip Test comments #954 + [wiredfool] + +- Fixes for things rpmlint complains about #942 + [manisandro] + +2.6.2 (2015-01-01) +------------------ + +- Fix CVE-2014-9601, potential PNG decompression DOS #1060 + [wiredfool] + +- Fix Regression in PyPy 2.4 in streamio #958 + [wiredfool] + +2.6.1 (2014-10-11) +------------------ + +- Fix SciPy regression in Image.resize #945 + [wiredfool] + +- Fix manifest to include all test files. + [aclark4life] + +2.6.0 (2014-10-01) +------------------ + +- Relax precision of ImageDraw tests for x86, GimpGradient for PPC #930 + [wiredfool] + +2.6.0-rc1 (2014-09-29) +---------------------- + +- Use redistributable image for testing #884 + [hugovk] + +- Use redistributable ICC profiles for testing, skip if not available #923 + [wiredfool] + +- Additional documentation for JPEG info and save options #922 + [wiredfool] + +- Fix JPEG Encoding memory leak when exif or qtables were specified #921 + [wiredfool] + +- Image.tobytes() and Image.tostring() documentation update #916 #917 + [mgedmin] + +- On Windows, do not execute convert.exe without specifying path #912 + [cgohlke] + +- Fix msvc build error #911 + [cgohlke] + +- Fix for handling P + transparency -> RGBA conversions #904 + [wiredfool] + +- Retain alpha in ImageEnhance operations #909 + [wiredfool] + +- Jpeg2k Decode/encode memory leak fix #898 + [joshware, wiredfool] + +- EpsFilePlugin Speed improvements #886 + [wiredfool, karstenw] + +- Don't resize if already the right size #892 + [radarhere] + +- Fix for reading multipage TIFFs #885 + [kostrom, wiredfool] + +- Correctly handle saving gray and CMYK JPEGs with quality=keep #857 + [etienned] + +- Correct duplicate Tiff Metadata and Exif tag values + [hugovk] + +- Windows fixes #871 + [wiredfool] + +- Fix TGA files with image ID field #856 + [megabuz] + +- Fixed wrong P-mode of small, unoptimized L-mode GIF #843 + [uvNikita] + +- Fixed CVE-2014-3598, a DOS in the Jpeg2KImagePlugin + [Andrew Drake] + +- Fixed CVE-2014-3589, a DOS in the IcnsImagePlugin + [Andrew Drake] + +- setup.py: Close open file handle before deleting #844 + [divergentdave] + +- Return Profile with Transformed Images #837 + [wiredfool] + +- Changed docstring to refer to the correct function #836 + [MatMoore] + +- Adding coverage support for C code tests #833 + [wiredfool] + +- PyPy performance improvements #821 + [wiredfool] + +- Added support for reading MPO files #822 + [Feneric] + +- Added support for encoding and decoding iTXt chunks #818 + [dolda2000] + +- HSV Support #816 + [wiredfool] + +- Removed unusable ImagePalette.new() + [hugovk] + +- Fix Scrambled XPM #808 + [wiredfool] + +- Doc cleanup + [wiredfool] + +- Fix ``ImageStat`` docs #796 + [akx] + +- Added docs for ExifTags #794 + [Wintermute3] + +- More tests for CurImagePlugin, DcxImagePlugin, Effects.c, GimpGradientFile, ImageFont, ImageMath, ImagePalette, IptcImagePlugin, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util + [hugovk] + +- Fix return value of FreeTypeFont.textsize() does not include font offsets #784 + [tk0miya] + +- Fix dispose calculations for animated GIFs #765 + [larsjsol] + +- Added class checking to Image __eq__ function #775 + [radarhere, hugovk] + +- Test PalmImagePlugin and method to skip known bad tests #776 + [hugovk, wiredfool] + +2.5.3 (2014-08-18) +------------------ + +- Fixed CVE-2014-3598, a DOS in the Jpeg2KImagePlugin (backport) + [Andrew Drake] + + +2.5.2 (2014-08-13) +------------------ + +- Fixed CVE-2014-3589, a DOS in the IcnsImagePlugin (backport) + [Andrew Drake] + +2.5.1 (2014-07-10) +------------------ + +- Fixed install issue if Multiprocessing.Pool is not available + [wiredfool] + +- 32bit mult overflow fix #782 + [wiredfool] + +2.5.0 (2014-07-01) +------------------ + +- Imagedraw rewrite #737 + [terseus, wiredfool] + +- Add support for multithreaded test execution #755 + [wiredfool] + +- Prevent shell injection #748 + [mbrown1413, wiredfool] + +- Support for Resolution in BMP files #734 + [gcq] + +- Fix error in setup.py for Python 3 #744 + [matthew-brett] + +- Pyroma fix and add Python 3.4 to setup metadata #742 + [wirefool] + +- Top level flake8 fixes #741 + [aclark4life] + +- Remove obsolete Animated Raster Graphics (ARG) support #736 + [hugovk] + +- Fix test_imagedraw failures #727 + [cgohlke] + +- Fix AttributeError: class Image has no attribute 'DEBUG' #726 + [cgohlke] + +- Fix msvc warning: 'inline' : macro redefinition #725 + [cgohlke] + +- Cleanup #654 + [dvska, hugovk, wiredfool] + +- 16-bit monochrome support for JPEG2000 #730 + [videan42] + +- Fixed ImagePalette.save + [brightpisces] + +- Support JPEG qtables #677 + [csinchok] + +- Add binary morphology addon + [dov, wiredfool] + +- Decompression bomb protection #674 + [hugovk] + +- Put images in a single directory #708 + [hugovk] + +- Support OpenJpeg 2.1 #681 + [al45tair, wiredfool] + +- Remove unistd.h #include for all platforms #704 + [wiredfool] + +- Use unittest for tests + [hugovk] + +- ImageCms fixes + [hugovk] + +- Added more ImageDraw tests + [hugovk] + +- Added tests for Spider files + [hugovk] + +- Use libtiff to write any compressed tiff files #669 + [wiredfool] + +- Support for pickling Image objects + [hugovk] + +- Fixed resolution handling for EPS thumbnails #619 + [eliempje] + +- Fixed rendering of some binary EPS files (Issue #302) + [eliempje] + +- Rename variables not to use built-in function names #670 + [hugovk] + +- Ignore junk JPEG markers + [hugovk] + +- Change default interpolation for Image.thumbnail to Image.ANTIALIAS + [hugovk] + +- Add tests and fixes for saving PDFs + [hugovk] + +- Remove transparency resource after P->RGBA conversion + [hugovk] + +- Clean up preprocessor cruft for Windows #652 + [CounterPillow] + +- Adjust Homebrew freetype detection logic #656 + [jacknagel] + +- Added Image.close, context manager support + [wiredfool] + +- Added support for 16 bit PGM files + [wiredfool] + +- Updated OleFileIO to version 0.30 from upstream #618 + [hugovk] + +- Added support for additional TIFF floating point format + [Hijackal] + +- Have the tempfile use a suffix with a dot + [wiredfool] + +- Fix variable name used for transparency manipulations #604 + [nijel] + +2.4.0 (2014-04-01) +------------------ + +- Indexed Transparency handled for conversions between L, RGB, and P modes #574 (fixes #510) + [wiredfool] + +- Conversions enabled from RGBA->P #574 (fixes #544) + [wiredfool] + +- Improved icns support #565 + [al45tair] + +- Fix libtiff leaking open files #580 (fixes #526) + [wiredfool] + +- Fixes for Jpeg encoding in Python 3 #578 (fixes #577) + [wiredfool] + +- Added support for JPEG 2000 #547 + [al45tair] + +- Add more detailed error messages to Image.py #566 + [larsmans] + +- Avoid conflicting _expand functions in PIL & MINGW, fixes #538 + [aclark4life] + +- Merge from Philippe Lagadec’s OleFileIO_PL fork #512 + [vadmium] + +- Fix ImageColor.getcolor #534 + [homm] + +- Make ICO files work with the ImageFile.Parser interface #525 (fixes #522) + [wiredfool] + +- Handle 32bit compiled python on 64bit architecture #521 + [choppsv1] + +- Fix support for characters >128 using .pcf or .pil fonts in Py3k #517 (fixes #505) + [wiredfool] + +- Skip CFFI test earlier if it's not installed #516 + [wiredfool] + +- Fixed opening and saving odd sized .pcx files #535 (fixes #523) + [wiredfool] + +- Fixed palette handling when converting from mode P->RGB->P + [d-schmidt] + +- Fixed saving mode P image as a PNG with transparency = palette color 0 + [d-schmidt] + +- Improve heuristic used when saving progressive and optimized JPEGs with high quality values #504 + [e98cuenc] + +- Fixed DOS with invalid palette size or invalid image size in BMP file + [wiredfool] + +- Added support for BMP version 4 and 5 + [eddwardo, wiredfool] + +- Fix segfault in getfont when passed a memory resident font + [wiredfool] + +- Fix crash on Saving a PNG when icc-profile is None #496 + [brutasse] + +- Cffi+Python implementation of the PixelAccess object + [wiredfool] + +- PixelAccess returns unsigned ints for I16 mode + [wiredfool] + +- Minor patch on booleans + Travis #474 + [sciunto] + +- Look in multiarch paths in GNU platforms #511 + [pinotree] + +- Add arch support for pcc64, s390, s390x, armv7l, aarch64 #475 + [manisandro] + +- Add arch support for ppc + [wiredfool] + +- Correctly quote file names for WindowsViewer command + [cgohlke] + +- Prefer homebrew freetype over X11 freetype (but still allow both) #466 + [dmckeone] + +2.3.2 (2014-08-13) +------------------ + +- Fixed CVE-2014-3589, a DOS in the IcnsImagePlugin (backport) + [Andrew Drake] + +2.3.1 (2014-03-14) +------------------ + +- Fix insecure use of tempfile.mktemp (CVE-2014-1932 CVE-2014-1933) + [wiredfool] + +2.3.0 (2014-01-01) +------------------ + +- Stop leaking filename parameter passed to getfont #459 + [jpharvey] + +- Report availability of LIBTIFF during setup and selftest + [cgohlke] + +- Fix msvc build error C1189: "No Target Architecture" #460 + [cgohlke] + +- Fix memory leak in font_getsize + [wiredfool] + +- Correctly prioritize include and library paths #442 + [ohanar] + +- Image.point fixes for numpy.array and docs #441 + [wiredfool] + +- Save the transparency header by default for PNGs #424 + [wiredfool] + +- Support for PNG tRNS header when converting from RGB->RGBA #423 + [wiredfool] + +- PyQT5 Support #418 + [wiredfool] + +- Updates for saving color tiffs w/compression using libtiff #417 + [wiredfool] + +- 2gigapix image fixes and redux + [wiredfool] + +- Save arbitrary tags in Tiff image files #369 + [wiredfool] + +- Quote filenames and title before using on command line #398 + [tmccombs] + +- Fixed Viewer.show to return properly #399 + [tmccombs] + +- Documentation fixes + [wiredfool] + +- Fixed memory leak saving images as webp when webpmux is available #429 + [cezarsa] + +- Fix compiling with FreeType 2.5.1 #427 + [stromnov] + +- Adds directories for NetBSD #411 + [deepy] + +- Support RGBA TIFF with missing ExtraSamples tag #393 + [cgohlke] + +- Lossless WEBP Support #390 + [wiredfool] + +- Take compression as an option in the save call for tiffs #389 + [wiredfool] + +- Add support for saving lossless WebP. Just pass 'lossless=True' to save() #386 + [liftoff] + +- LCMS support upgraded from version 1 to version 2 #380 (fixes #343) + [wiredfool] + +- Added more raw decoder 16 bit pixel formats #379 + [svanheulen] + +- Document remaining Image* modules listed in PIL handbook + [irksep] + +- Document ImageEnhance, ImageFile, ImageFilter, ImageFont, ImageGrab, ImageMath, and ImageOps + [irksep] + +- Port and update docs for Image, ImageChops, ImageColor, and ImageDraw + [irksep] + +- Move or copy content from README.rst to docs/ + [irksep] + +- Respect CFLAGS/LDFLAGS when searching for headers/libs + [iElectric] + +- Port PIL Handbook tutorial and appendices + [irksep] + +- Alpha Premultiplication support for transform and resize #364 + [wiredfool] + +- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 #359 + [wiredfool] + +2.2.2 (2013-12-11) +------------------ + +- Fix compiling with FreeType 2.5.1 #427 + [stromnov] + +2.2.1 (2013-10-02) +------------------ + +- Error installing Pillow 2.2.0 on Mac OS X (due to hard dep on brew) #357 (fixes #356) + [wiredfool] + +2.2.0 (2013-10-02) +------------------ + +- Bug in image transformations resulting from uninitialized memory #348 (fixes #254) + [nikmolnar] + +- Fix for encoding of b_whitespace #346 (similar to closed issue #272) + [mhogg] + +- Add numpy array interface support for 16 and 32 bit integer modes #347 (fixes #273) + [cgohlke] + +- Partial fix for #290: Add preliminary support for TIFF tags. + [wiredfool] + +- Fix #251 and #326: circumvent classification of pngtest_bad.png as malware + [cgohlke] + +- Add typedef uint64_t for MSVC #339 + [cgohlke] + +- setup.py: better support for C_INCLUDE_PATH, LD_RUN_PATH, etc. #336 (fixes #329) + [nu774] + +- _imagingcms.c: include windef.h to fix build issue on MSVC #335 (fixes #328) + [nu774] + +- Automatically discover homebrew include/ and lib/ paths on OS X #330 + [donspaulding] + +- Fix bytes which should be bytearray #325 + [manisandro] + +- Add respective paths for C_INCLUDE_PATH, LD_RUN_PATH (rpath) to build + if specified as environment variables #324 + [seanupton] + +- Fix #312 + gif optimize improvement + [d-schmidt] + +- Be more tolerant of tag read failures #320 + [ericbuehl] + +- Catch truncated zTXt errors #321 (fixes #318) + [vytisb] + +- Fix IOError when saving progressive JPEGs #313 + [e98cuenc] + +- Add RGBA support to ImageColor #309 + [yoavweiss] + +- Test for ``str``, not ``"utf-8"`` #306 (fixes #304) + [mjpieters] + +- Fix missing import os in _util.py #303 + [mnowotka] + +- Added missing exif tags #300 + [freyes] + +- Fail on all import errors #298, #299 (fixes #297) + [macfreek, wiredfool] + +- Fixed Windows fallback (wasn't using correct file in Windows fonts) #295 + [lmollea] + +- Moved ImageFile and ImageFileIO comments to docstrings #293 + [freyes] + +- Restore compatibility with ISO C #289 + [cgohlke] + +- Use correct format character for C int type #288 + [cgohlke] + +- Allocate enough memory to hold pointers in encode.c #287 + [cgohlke] + +- Fillorder double shuffling bug when FillOrder ==2 and decoding using libtiff #284 (fixes #279) + [wiredfool] + +- Moved Image module comments to docstrings. + [freyes] + +- Add 16-bit TIFF support #277 (fixes #274) + [wiredfool] + +- Ignore high ascii characters in string.whitespace #276 (fixes #272) + [wiredfool] + +- Added clean/build to tox to make it behave like Travis #275 + [freyes] + +- Adding support for metadata in webp images #271 + [heynemann] + +2.1.0 (2013-07-02) +------------------ + +- Add /usr/bin/env python shebangs to all scripts in /Scripts #197 + [mgorny] + +- Add several TIFF decoders and encoders #268 + [megabuz] + +- Added support for alpha transparent webp images. + +- Adding Python 3 support for StringIO. + +- Adding Python3 basestring compatibility without changing basestring. + +- Fix webp encode errors on win-amd64 #259 + [cgohlke] + +- Better fix for ZeroDivisionError in ImageOps.fit for image.size height is 1 #267 + [chrispbailey] + +- Better support for ICO images. + +- Changed PY_VERSION_HEX #190 (fixes #166) + +- Changes to put everything under the PIL namespace #191 + [wiredfool] + +- Changing StringIO to BytesIO. + +- Cleanup whitespace. + [Arfrever] + +- Don't skip 'import site' on initialization when running tests for inplace builds. + [cgohlke] + +- Enable warnings for test suite #227 + [wiredfool] + +- Fix for ZeroDivisionError in ImageOps.fit for image.size == (1,1) #255 + [pterk] + +- Fix for if isinstance(filter, collections.Callable) crash. Python bug #7624 on <2.6.6 + +- Remove double typedef declaration #194 (fixes #193) + [evertrol] + +- Fix msvc compile errors (#230). + +- Fix rendered characters have been chipped for some TrueType fonts + [tk0miya] + +- Fix usage of pilfont.py script #184 + [fabiomcosta] + +- Fresh start for docs, generated by sphinx-apidoc. + +- Introduce --enable-x and fail if it is given and x is not available. + +- Partial work to add a wrapper for WebPGetFeatures to correctly support #220 (fixes #204) + +- Significant performance improvement of ``alpha_composite`` function #156 + [homm] + +- Support explicitly disabling features via --disable-* options #240 + [mgorny] + +- Support selftest.py --installed, fixes #263 + +- Transparent WebP Support #220 (fixes #204) + [euangoddard, wiredfool] + +- Use PyCapsule for py3.1 #238 (fixes #237) + [wiredfool] + +- Workaround for: https://bugs.python.org/issue16754 in 3.2.x < 3.2.4 and 3.3.0. + +2.0.0 (2013-03-15) +------------------ + +.. Note:: Special thanks to Christoph Gohlke and Eric Soroos for assisting with a pre-PyCon 2013 release! + +- Many other bug fixes and enhancements by many other people. + +- Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.) + [fluggo] + +- Add PyPy support (experimental, please see #67) + +- Add WebP support #96 + [lqs] + +- Add Tiff G3/G4 support (experimental) + [wiredfool] + +- Backport PIL's PNG/Zip improvements #95, #97 + [olt] + +- Various 64-bit and Windows fixes. + [cgohlke] + +- Add testing suite. + [cgohlke, fluggo] + +- Added support for PNG images with transparency palette. + [d-schmidt] + +1.7.8 (2012-11-01) +------------------ + +- Removed doctests.py that made tests of other packages fail. + [thomasdesvenain] + +- Fix opening psd files with RGBA layers when A mode is not of type 65535 but 3. + Fixes #3 + [thomasdesvenain] + + +1.7.7 (2012-04-04) +------------------ + +- UNDEF more types before including windows headers + [mattip] + +1.7.6 (2012-01-20) +------------------ + +- Bug fix: freetype not found on Mac OS X with case-sensitive filesystem + [gjo] + +- Bug fix: Backport fix to split() after open() (regression introduced in PIL 1.1.7). + [sfllaw] + +1.7.5 (2011-09-07) +------------------ + +- Fix for sys.platform = "linux3" + [blueyed] + +- Package cleanup and additional documentation + [aclark4life] + +1.7.4 (2011-07-21) +------------------ + +- Fix brown bag release + [aclark4life] + +1.7.3 (2011-07-20) +------------------ + +- Fix : resize need int values, append int conversion in thumbnail method + [harobed] + +1.7.2 (2011-06-02) +------------------ + +- Bug fix: Python 2.4 compat + [aclark4life] + +1.7.1 (2011-05-31) +------------------ + +- More multi-arch support + [SteveM, regebro, barry, aclark4life] + +1.7.0 (2011-05-27) +------------------ + +- Add support for multi-arch library directory /usr/lib/x86_64-linux-gnu + [aclark4life] + +1.6 (12/01/2010) +---------------- + +- Bug fix: /usr/x11/include should be added to include_dirs not library_dirs + [elro] + +- Doc fixes + [aclark4life] + +1.5 (11/28/2010) +---------------- + +- Module and package fixes + [aclark4life] + +1.4 (11/28/2010) +---------------- + +- Doc fixes + [aclark4life] + +1.3 (11/28/2010) +---------------- + +- Add support for /lib64 and /usr/lib64 library directories on Linux + [aclark4life] + +- Doc fixes + [aclark4life] + +1.2 (08/02/2010) +---------------- + +- On OS X also check for freetype2 in the X11 path + [jezdez] + +- Doc fixes + [aclark4life] + +1.1 (07/31/2010) +---------------- + +- Removed setuptools_hg requirement + [aclark4life] + +- Doc fixes + [aclark4life] + +1.0 (07/30/2010) +---------------- + +- Remove support for ``import Image``. ``from PIL import Image`` now required. +- Forked PIL based on `Chris McDonough and Hanno Schlichting's setuptools compatible re-packaging `_ + [aclark4life] + +Pre-fork +======== + +0.2b5-1.1.7 +----------- + +:: + + -*- coding: utf-8 -*- + + The Python Imaging Library + $Id$ + + ACKNOWLEDGEMENTS: PIL wouldn't be what it is without the help of: + David Ascher, Phil Austin, Douglas Bagnall, Larry Bates, Anthony + Baxter, William Baxter, Denis Benoit, Jan Blom, Duncan Booth, Alexey + Borzenkov, Jeff Breidenbach, Roger Burnham, Zac Burns, Gene Cash, + Kevin Cazabon, Fred Clare, Greg Coats, Chris Cogdon, Greg Couch, Bill + Crutchfield, Abel Deuring, Tim Docker, Fred Drake, Graham Dumpleton, + Matthew Ellis, Eric Etheridge, Daniel Fetchinson, Robin Friedrich, + Pier Paolo Glave, Federico Di Gregorio, Markus Gritsch, Daniel + Haertle, Greg Hamilton, Mark Hammond, Bernhard Herzog, Rob Hooft, Bob + Ippolito, Jack Jansen, Bill Janssen, Edward Jones, Richard Jones, + Håkan Karlsson, Robert Kern, David Kirtley, Bob Klimek, Matthias + Klose, Andrew Kuchling, Magnus Källström, Victor Lacina, Ben Last, + Hamish Lawson, Cesare Leonardi, Andrew MacIntyre, Jan Matejek, Naveen From 5e1c7d79142f1b820d8c0fd4e22538ed97400e9b Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:00:05 +0430 Subject: [PATCH 075/294] Update CHANGES.rst --- CHANGES.rst | 1063 +-------------------------------------------------- 1 file changed, 1 insertion(+), 1062 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 22fd1ddbc..22b3fa230 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,4 @@ + Changelog (Pillow) ================== @@ -6601,1065 +6602,3 @@ Magenta, Yellow, Black). + PostScript printing is provided through the PSDraw module. See the handbook for details. -Qt use of unicode() for 2/3 compatibility #1218 - [radarhere] - -- Identify XBM file created with filename including underscore #1230 (fixes #1229) - [hugovk] - -- Copy image when saving in GifImagePlugin #1231 (fixes #718) - [radarhere] - -- Removed support for FreeType 2.0 #1247 - [radarhere] - -- Added background saving to GifImagePlugin #1273 - [radarhere] - -- Provide n_frames attribute to multi-frame formats #1261 - [anntzer, radarhere] - -- Add duration and loop set to GifImagePlugin #1172, #1269 - [radarhere] - -- Ico files are little endian #1232 - [wiredfool] - -- Upgrade olefile from 0.30 to 0.42b #1226 - [radarhere, decalage2] - -- Setting transparency value to 0 when the tRNS contains only null byte(s) #1239 - [juztin] - -- Separated out feature checking from selftest #1233 - [radarhere] - -- Style/health fixes - [radarhere] - -- Update WebP from 0.4.1 to 0.4.3 #1235 - [radarhere] - -- Release GIL during image load (decode) #1224 - [lkesteloot] - -- Added icns save #1185 - [radarhere] - -- Fix putdata memory leak #1196 - [benoit-pierre] - -- Keep user-specified ordering of icon sizes #1193 - [karimbahgat] - -- Tiff: allow writing floating point tag values #1113 - [bpedersen2] - -2.8.2 (2015-06-06) ------------------- - -- Bug fix: Fixed Tiff handling of bad EXIF data - [radarhere] - -2.8.1 (2015-04-02) ------------------- - -- Bug fix: Catch struct.error on invalid JPEG, fixes #1163. #1165 - [wiredfool, hugovk] - -2.8.0 (2015-04-01) ------------------- - -- Fix 32-bit BMP loading (RGBA or RGBX) #1125 - [artscoop] - -- Fix UnboundLocalError in ImageFile #1131 - [davarisg] - -- Re-enable test image caching #982 - [hugovk, homm] - -- Fix: Cannot identify EPS images #1152 (fixes #1104) - [hugovk] - -- Configure setuptools to run nosetests, fixes #729 - [aclark4life] - -- Style/health fixes - [radarhere, hugovk] - -- Add support for HTTP response objects to Image.open() #1151 - [mfitzp] - -- Improve reference docs for PIL.ImageDraw.Draw.pieslice() #1145 - [audreyr] - -- Added copy method font_variant() and accessible properties to truetype() #1123 - [radarhere] - -- Fix ImagingEffectNoise #1128 - [hugovk] - -- Remove unreachable code #1126 - [hugovk] - -- Let Python do the endian stuff + tests #1121 - [amoibos, radarhere] - -- Fix webp decode memory leak #1114 - [benoit-pierre] - -- Fast path for opaque pixels in RGBa unpacker #1088 - [bgilbert] - -- Enable basic support for 'RGBa' raw encoding/decoding #1096 - [immerrr] - -- Fix pickling L mode images with no palette, #1095 - [hugovk] - -- iPython display hook #1091 - [wiredfool] - -- Adjust buffer size when quality=keep #1079 (fixes #148 again) - [wiredfool] - -- Fix for corrupted bitmaps embedded in truetype fonts #1072 - [jackyyf, wiredfool] - -2.7.0 (2015-01-01) ------------------- - -- Split Sane into a separate repo: https://github.com/python-pillow/Sane - [hugovk] - -- Look for OS X and Linux fonts in common places #1054 - [charleslaw] - -- Fix CVE-2014-9601, potential PNG decompression DOS #1060 - [wiredfool] - -- Use underscores, not spaces, in TIFF tag kwargs #1044, #1058 - [anntzer, hugovk] - -- Update PSDraw for Python3, add tests #1055 - [hugovk] - -- Use Bicubic filtering by default for thumbnails. Don't use Jpeg Draft mode for thumbnails #1029 - [homm] - -- Fix MSVC compiler error: Use Py_ssize_t instead of ssize_t #1051 - [cgohlke] - -- Fix compiler error: MSVC needs variables defined at the start of the block #1048 - [cgohlke] - -- The GIF Palette optimization algorithm is only applicable to mode='P' or 'L' #993 - [moriyoshi] - -- Use PySide as an alternative to PyQt4/5 #1024 - [holg] - -- Replace affine-based im.resize implementation with convolution-based im.stretch #997 - [homm] - -- Replace Gaussian Blur implementation with iterated fast box blur. #961 Note: Radius parameter is interpreted differently than before. - [homm] - -- Better docs explaining import _imaging failure #1016, build #1017, mode #1018, PyAccess, PixelAccess objects #1019 Image.quantize #1020 and Image.save #1021 - [wiredfool] - -- Fix for saving TIFF image into an io.BytesIO buffer #1011 - [mfergie] - -- Fix antialias compilation on debug versions of Python #1010 - [wiredfool] - -- Fix for Image.putdata segfault #1009 - [wiredfool] - -- Ico save, additional tests #1007 - [exherb] - -- Use PyQt4 if it has already been imported, otherwise prefer PyQt5 #1003 - [AurelienBallier] - -- Speedup resample implementation up to 2.5 times #977 - [homm] - -- Speed up rotation by using cache aware loops, added transpose to rotations #994 - [homm] - -- Fix Bicubic interpolation #970 - [homm] - -- Support for 4-bit greyscale TIFF images #980 - [hugovk] - -- Updated manifest #957 - [wiredfool] - -- Fix PyPy 2.4 regression #958 - [wiredfool] - -- Webp Metadata Skip Test comments #954 - [wiredfool] - -- Fixes for things rpmlint complains about #942 - [manisandro] - -2.6.2 (2015-01-01) ------------------- - -- Fix CVE-2014-9601, potential PNG decompression DOS #1060 - [wiredfool] - -- Fix Regression in PyPy 2.4 in streamio #958 - [wiredfool] - -2.6.1 (2014-10-11) ------------------- - -- Fix SciPy regression in Image.resize #945 - [wiredfool] - -- Fix manifest to include all test files. - [aclark4life] - -2.6.0 (2014-10-01) ------------------- - -- Relax precision of ImageDraw tests for x86, GimpGradient for PPC #930 - [wiredfool] - -2.6.0-rc1 (2014-09-29) ----------------------- - -- Use redistributable image for testing #884 - [hugovk] - -- Use redistributable ICC profiles for testing, skip if not available #923 - [wiredfool] - -- Additional documentation for JPEG info and save options #922 - [wiredfool] - -- Fix JPEG Encoding memory leak when exif or qtables were specified #921 - [wiredfool] - -- Image.tobytes() and Image.tostring() documentation update #916 #917 - [mgedmin] - -- On Windows, do not execute convert.exe without specifying path #912 - [cgohlke] - -- Fix msvc build error #911 - [cgohlke] - -- Fix for handling P + transparency -> RGBA conversions #904 - [wiredfool] - -- Retain alpha in ImageEnhance operations #909 - [wiredfool] - -- Jpeg2k Decode/encode memory leak fix #898 - [joshware, wiredfool] - -- EpsFilePlugin Speed improvements #886 - [wiredfool, karstenw] - -- Don't resize if already the right size #892 - [radarhere] - -- Fix for reading multipage TIFFs #885 - [kostrom, wiredfool] - -- Correctly handle saving gray and CMYK JPEGs with quality=keep #857 - [etienned] - -- Correct duplicate Tiff Metadata and Exif tag values - [hugovk] - -- Windows fixes #871 - [wiredfool] - -- Fix TGA files with image ID field #856 - [megabuz] - -- Fixed wrong P-mode of small, unoptimized L-mode GIF #843 - [uvNikita] - -- Fixed CVE-2014-3598, a DOS in the Jpeg2KImagePlugin - [Andrew Drake] - -- Fixed CVE-2014-3589, a DOS in the IcnsImagePlugin - [Andrew Drake] - -- setup.py: Close open file handle before deleting #844 - [divergentdave] - -- Return Profile with Transformed Images #837 - [wiredfool] - -- Changed docstring to refer to the correct function #836 - [MatMoore] - -- Adding coverage support for C code tests #833 - [wiredfool] - -- PyPy performance improvements #821 - [wiredfool] - -- Added support for reading MPO files #822 - [Feneric] - -- Added support for encoding and decoding iTXt chunks #818 - [dolda2000] - -- HSV Support #816 - [wiredfool] - -- Removed unusable ImagePalette.new() - [hugovk] - -- Fix Scrambled XPM #808 - [wiredfool] - -- Doc cleanup - [wiredfool] - -- Fix ``ImageStat`` docs #796 - [akx] - -- Added docs for ExifTags #794 - [Wintermute3] - -- More tests for CurImagePlugin, DcxImagePlugin, Effects.c, GimpGradientFile, ImageFont, ImageMath, ImagePalette, IptcImagePlugin, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util - [hugovk] - -- Fix return value of FreeTypeFont.textsize() does not include font offsets #784 - [tk0miya] - -- Fix dispose calculations for animated GIFs #765 - [larsjsol] - -- Added class checking to Image __eq__ function #775 - [radarhere, hugovk] - -- Test PalmImagePlugin and method to skip known bad tests #776 - [hugovk, wiredfool] - -2.5.3 (2014-08-18) ------------------- - -- Fixed CVE-2014-3598, a DOS in the Jpeg2KImagePlugin (backport) - [Andrew Drake] - - -2.5.2 (2014-08-13) ------------------- - -- Fixed CVE-2014-3589, a DOS in the IcnsImagePlugin (backport) - [Andrew Drake] - -2.5.1 (2014-07-10) ------------------- - -- Fixed install issue if Multiprocessing.Pool is not available - [wiredfool] - -- 32bit mult overflow fix #782 - [wiredfool] - -2.5.0 (2014-07-01) ------------------- - -- Imagedraw rewrite #737 - [terseus, wiredfool] - -- Add support for multithreaded test execution #755 - [wiredfool] - -- Prevent shell injection #748 - [mbrown1413, wiredfool] - -- Support for Resolution in BMP files #734 - [gcq] - -- Fix error in setup.py for Python 3 #744 - [matthew-brett] - -- Pyroma fix and add Python 3.4 to setup metadata #742 - [wirefool] - -- Top level flake8 fixes #741 - [aclark4life] - -- Remove obsolete Animated Raster Graphics (ARG) support #736 - [hugovk] - -- Fix test_imagedraw failures #727 - [cgohlke] - -- Fix AttributeError: class Image has no attribute 'DEBUG' #726 - [cgohlke] - -- Fix msvc warning: 'inline' : macro redefinition #725 - [cgohlke] - -- Cleanup #654 - [dvska, hugovk, wiredfool] - -- 16-bit monochrome support for JPEG2000 #730 - [videan42] - -- Fixed ImagePalette.save - [brightpisces] - -- Support JPEG qtables #677 - [csinchok] - -- Add binary morphology addon - [dov, wiredfool] - -- Decompression bomb protection #674 - [hugovk] - -- Put images in a single directory #708 - [hugovk] - -- Support OpenJpeg 2.1 #681 - [al45tair, wiredfool] - -- Remove unistd.h #include for all platforms #704 - [wiredfool] - -- Use unittest for tests - [hugovk] - -- ImageCms fixes - [hugovk] - -- Added more ImageDraw tests - [hugovk] - -- Added tests for Spider files - [hugovk] - -- Use libtiff to write any compressed tiff files #669 - [wiredfool] - -- Support for pickling Image objects - [hugovk] - -- Fixed resolution handling for EPS thumbnails #619 - [eliempje] - -- Fixed rendering of some binary EPS files (Issue #302) - [eliempje] - -- Rename variables not to use built-in function names #670 - [hugovk] - -- Ignore junk JPEG markers - [hugovk] - -- Change default interpolation for Image.thumbnail to Image.ANTIALIAS - [hugovk] - -- Add tests and fixes for saving PDFs - [hugovk] - -- Remove transparency resource after P->RGBA conversion - [hugovk] - -- Clean up preprocessor cruft for Windows #652 - [CounterPillow] - -- Adjust Homebrew freetype detection logic #656 - [jacknagel] - -- Added Image.close, context manager support - [wiredfool] - -- Added support for 16 bit PGM files - [wiredfool] - -- Updated OleFileIO to version 0.30 from upstream #618 - [hugovk] - -- Added support for additional TIFF floating point format - [Hijackal] - -- Have the tempfile use a suffix with a dot - [wiredfool] - -- Fix variable name used for transparency manipulations #604 - [nijel] - -2.4.0 (2014-04-01) ------------------- - -- Indexed Transparency handled for conversions between L, RGB, and P modes #574 (fixes #510) - [wiredfool] - -- Conversions enabled from RGBA->P #574 (fixes #544) - [wiredfool] - -- Improved icns support #565 - [al45tair] - -- Fix libtiff leaking open files #580 (fixes #526) - [wiredfool] - -- Fixes for Jpeg encoding in Python 3 #578 (fixes #577) - [wiredfool] - -- Added support for JPEG 2000 #547 - [al45tair] - -- Add more detailed error messages to Image.py #566 - [larsmans] - -- Avoid conflicting _expand functions in PIL & MINGW, fixes #538 - [aclark4life] - -- Merge from Philippe Lagadec’s OleFileIO_PL fork #512 - [vadmium] - -- Fix ImageColor.getcolor #534 - [homm] - -- Make ICO files work with the ImageFile.Parser interface #525 (fixes #522) - [wiredfool] - -- Handle 32bit compiled python on 64bit architecture #521 - [choppsv1] - -- Fix support for characters >128 using .pcf or .pil fonts in Py3k #517 (fixes #505) - [wiredfool] - -- Skip CFFI test earlier if it's not installed #516 - [wiredfool] - -- Fixed opening and saving odd sized .pcx files #535 (fixes #523) - [wiredfool] - -- Fixed palette handling when converting from mode P->RGB->P - [d-schmidt] - -- Fixed saving mode P image as a PNG with transparency = palette color 0 - [d-schmidt] - -- Improve heuristic used when saving progressive and optimized JPEGs with high quality values #504 - [e98cuenc] - -- Fixed DOS with invalid palette size or invalid image size in BMP file - [wiredfool] - -- Added support for BMP version 4 and 5 - [eddwardo, wiredfool] - -- Fix segfault in getfont when passed a memory resident font - [wiredfool] - -- Fix crash on Saving a PNG when icc-profile is None #496 - [brutasse] - -- Cffi+Python implementation of the PixelAccess object - [wiredfool] - -- PixelAccess returns unsigned ints for I16 mode - [wiredfool] - -- Minor patch on booleans + Travis #474 - [sciunto] - -- Look in multiarch paths in GNU platforms #511 - [pinotree] - -- Add arch support for pcc64, s390, s390x, armv7l, aarch64 #475 - [manisandro] - -- Add arch support for ppc - [wiredfool] - -- Correctly quote file names for WindowsViewer command - [cgohlke] - -- Prefer homebrew freetype over X11 freetype (but still allow both) #466 - [dmckeone] - -2.3.2 (2014-08-13) ------------------- - -- Fixed CVE-2014-3589, a DOS in the IcnsImagePlugin (backport) - [Andrew Drake] - -2.3.1 (2014-03-14) ------------------- - -- Fix insecure use of tempfile.mktemp (CVE-2014-1932 CVE-2014-1933) - [wiredfool] - -2.3.0 (2014-01-01) ------------------- - -- Stop leaking filename parameter passed to getfont #459 - [jpharvey] - -- Report availability of LIBTIFF during setup and selftest - [cgohlke] - -- Fix msvc build error C1189: "No Target Architecture" #460 - [cgohlke] - -- Fix memory leak in font_getsize - [wiredfool] - -- Correctly prioritize include and library paths #442 - [ohanar] - -- Image.point fixes for numpy.array and docs #441 - [wiredfool] - -- Save the transparency header by default for PNGs #424 - [wiredfool] - -- Support for PNG tRNS header when converting from RGB->RGBA #423 - [wiredfool] - -- PyQT5 Support #418 - [wiredfool] - -- Updates for saving color tiffs w/compression using libtiff #417 - [wiredfool] - -- 2gigapix image fixes and redux - [wiredfool] - -- Save arbitrary tags in Tiff image files #369 - [wiredfool] - -- Quote filenames and title before using on command line #398 - [tmccombs] - -- Fixed Viewer.show to return properly #399 - [tmccombs] - -- Documentation fixes - [wiredfool] - -- Fixed memory leak saving images as webp when webpmux is available #429 - [cezarsa] - -- Fix compiling with FreeType 2.5.1 #427 - [stromnov] - -- Adds directories for NetBSD #411 - [deepy] - -- Support RGBA TIFF with missing ExtraSamples tag #393 - [cgohlke] - -- Lossless WEBP Support #390 - [wiredfool] - -- Take compression as an option in the save call for tiffs #389 - [wiredfool] - -- Add support for saving lossless WebP. Just pass 'lossless=True' to save() #386 - [liftoff] - -- LCMS support upgraded from version 1 to version 2 #380 (fixes #343) - [wiredfool] - -- Added more raw decoder 16 bit pixel formats #379 - [svanheulen] - -- Document remaining Image* modules listed in PIL handbook - [irksep] - -- Document ImageEnhance, ImageFile, ImageFilter, ImageFont, ImageGrab, ImageMath, and ImageOps - [irksep] - -- Port and update docs for Image, ImageChops, ImageColor, and ImageDraw - [irksep] - -- Move or copy content from README.rst to docs/ - [irksep] - -- Respect CFLAGS/LDFLAGS when searching for headers/libs - [iElectric] - -- Port PIL Handbook tutorial and appendices - [irksep] - -- Alpha Premultiplication support for transform and resize #364 - [wiredfool] - -- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 #359 - [wiredfool] - -2.2.2 (2013-12-11) ------------------- - -- Fix compiling with FreeType 2.5.1 #427 - [stromnov] - -2.2.1 (2013-10-02) ------------------- - -- Error installing Pillow 2.2.0 on Mac OS X (due to hard dep on brew) #357 (fixes #356) - [wiredfool] - -2.2.0 (2013-10-02) ------------------- - -- Bug in image transformations resulting from uninitialized memory #348 (fixes #254) - [nikmolnar] - -- Fix for encoding of b_whitespace #346 (similar to closed issue #272) - [mhogg] - -- Add numpy array interface support for 16 and 32 bit integer modes #347 (fixes #273) - [cgohlke] - -- Partial fix for #290: Add preliminary support for TIFF tags. - [wiredfool] - -- Fix #251 and #326: circumvent classification of pngtest_bad.png as malware - [cgohlke] - -- Add typedef uint64_t for MSVC #339 - [cgohlke] - -- setup.py: better support for C_INCLUDE_PATH, LD_RUN_PATH, etc. #336 (fixes #329) - [nu774] - -- _imagingcms.c: include windef.h to fix build issue on MSVC #335 (fixes #328) - [nu774] - -- Automatically discover homebrew include/ and lib/ paths on OS X #330 - [donspaulding] - -- Fix bytes which should be bytearray #325 - [manisandro] - -- Add respective paths for C_INCLUDE_PATH, LD_RUN_PATH (rpath) to build - if specified as environment variables #324 - [seanupton] - -- Fix #312 + gif optimize improvement - [d-schmidt] - -- Be more tolerant of tag read failures #320 - [ericbuehl] - -- Catch truncated zTXt errors #321 (fixes #318) - [vytisb] - -- Fix IOError when saving progressive JPEGs #313 - [e98cuenc] - -- Add RGBA support to ImageColor #309 - [yoavweiss] - -- Test for ``str``, not ``"utf-8"`` #306 (fixes #304) - [mjpieters] - -- Fix missing import os in _util.py #303 - [mnowotka] - -- Added missing exif tags #300 - [freyes] - -- Fail on all import errors #298, #299 (fixes #297) - [macfreek, wiredfool] - -- Fixed Windows fallback (wasn't using correct file in Windows fonts) #295 - [lmollea] - -- Moved ImageFile and ImageFileIO comments to docstrings #293 - [freyes] - -- Restore compatibility with ISO C #289 - [cgohlke] - -- Use correct format character for C int type #288 - [cgohlke] - -- Allocate enough memory to hold pointers in encode.c #287 - [cgohlke] - -- Fillorder double shuffling bug when FillOrder ==2 and decoding using libtiff #284 (fixes #279) - [wiredfool] - -- Moved Image module comments to docstrings. - [freyes] - -- Add 16-bit TIFF support #277 (fixes #274) - [wiredfool] - -- Ignore high ascii characters in string.whitespace #276 (fixes #272) - [wiredfool] - -- Added clean/build to tox to make it behave like Travis #275 - [freyes] - -- Adding support for metadata in webp images #271 - [heynemann] - -2.1.0 (2013-07-02) ------------------- - -- Add /usr/bin/env python shebangs to all scripts in /Scripts #197 - [mgorny] - -- Add several TIFF decoders and encoders #268 - [megabuz] - -- Added support for alpha transparent webp images. - -- Adding Python 3 support for StringIO. - -- Adding Python3 basestring compatibility without changing basestring. - -- Fix webp encode errors on win-amd64 #259 - [cgohlke] - -- Better fix for ZeroDivisionError in ImageOps.fit for image.size height is 1 #267 - [chrispbailey] - -- Better support for ICO images. - -- Changed PY_VERSION_HEX #190 (fixes #166) - -- Changes to put everything under the PIL namespace #191 - [wiredfool] - -- Changing StringIO to BytesIO. - -- Cleanup whitespace. - [Arfrever] - -- Don't skip 'import site' on initialization when running tests for inplace builds. - [cgohlke] - -- Enable warnings for test suite #227 - [wiredfool] - -- Fix for ZeroDivisionError in ImageOps.fit for image.size == (1,1) #255 - [pterk] - -- Fix for if isinstance(filter, collections.Callable) crash. Python bug #7624 on <2.6.6 - -- Remove double typedef declaration #194 (fixes #193) - [evertrol] - -- Fix msvc compile errors (#230). - -- Fix rendered characters have been chipped for some TrueType fonts - [tk0miya] - -- Fix usage of pilfont.py script #184 - [fabiomcosta] - -- Fresh start for docs, generated by sphinx-apidoc. - -- Introduce --enable-x and fail if it is given and x is not available. - -- Partial work to add a wrapper for WebPGetFeatures to correctly support #220 (fixes #204) - -- Significant performance improvement of ``alpha_composite`` function #156 - [homm] - -- Support explicitly disabling features via --disable-* options #240 - [mgorny] - -- Support selftest.py --installed, fixes #263 - -- Transparent WebP Support #220 (fixes #204) - [euangoddard, wiredfool] - -- Use PyCapsule for py3.1 #238 (fixes #237) - [wiredfool] - -- Workaround for: https://bugs.python.org/issue16754 in 3.2.x < 3.2.4 and 3.3.0. - -2.0.0 (2013-03-15) ------------------- - -.. Note:: Special thanks to Christoph Gohlke and Eric Soroos for assisting with a pre-PyCon 2013 release! - -- Many other bug fixes and enhancements by many other people. - -- Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.) - [fluggo] - -- Add PyPy support (experimental, please see #67) - -- Add WebP support #96 - [lqs] - -- Add Tiff G3/G4 support (experimental) - [wiredfool] - -- Backport PIL's PNG/Zip improvements #95, #97 - [olt] - -- Various 64-bit and Windows fixes. - [cgohlke] - -- Add testing suite. - [cgohlke, fluggo] - -- Added support for PNG images with transparency palette. - [d-schmidt] - -1.7.8 (2012-11-01) ------------------- - -- Removed doctests.py that made tests of other packages fail. - [thomasdesvenain] - -- Fix opening psd files with RGBA layers when A mode is not of type 65535 but 3. - Fixes #3 - [thomasdesvenain] - - -1.7.7 (2012-04-04) ------------------- - -- UNDEF more types before including windows headers - [mattip] - -1.7.6 (2012-01-20) ------------------- - -- Bug fix: freetype not found on Mac OS X with case-sensitive filesystem - [gjo] - -- Bug fix: Backport fix to split() after open() (regression introduced in PIL 1.1.7). - [sfllaw] - -1.7.5 (2011-09-07) ------------------- - -- Fix for sys.platform = "linux3" - [blueyed] - -- Package cleanup and additional documentation - [aclark4life] - -1.7.4 (2011-07-21) ------------------- - -- Fix brown bag release - [aclark4life] - -1.7.3 (2011-07-20) ------------------- - -- Fix : resize need int values, append int conversion in thumbnail method - [harobed] - -1.7.2 (2011-06-02) ------------------- - -- Bug fix: Python 2.4 compat - [aclark4life] - -1.7.1 (2011-05-31) ------------------- - -- More multi-arch support - [SteveM, regebro, barry, aclark4life] - -1.7.0 (2011-05-27) ------------------- - -- Add support for multi-arch library directory /usr/lib/x86_64-linux-gnu - [aclark4life] - -1.6 (12/01/2010) ----------------- - -- Bug fix: /usr/x11/include should be added to include_dirs not library_dirs - [elro] - -- Doc fixes - [aclark4life] - -1.5 (11/28/2010) ----------------- - -- Module and package fixes - [aclark4life] - -1.4 (11/28/2010) ----------------- - -- Doc fixes - [aclark4life] - -1.3 (11/28/2010) ----------------- - -- Add support for /lib64 and /usr/lib64 library directories on Linux - [aclark4life] - -- Doc fixes - [aclark4life] - -1.2 (08/02/2010) ----------------- - -- On OS X also check for freetype2 in the X11 path - [jezdez] - -- Doc fixes - [aclark4life] - -1.1 (07/31/2010) ----------------- - -- Removed setuptools_hg requirement - [aclark4life] - -- Doc fixes - [aclark4life] - -1.0 (07/30/2010) ----------------- - -- Remove support for ``import Image``. ``from PIL import Image`` now required. -- Forked PIL based on `Chris McDonough and Hanno Schlichting's setuptools compatible re-packaging `_ - [aclark4life] - -Pre-fork -======== - -0.2b5-1.1.7 ------------ - -:: - - -*- coding: utf-8 -*- - - The Python Imaging Library - $Id$ - - ACKNOWLEDGEMENTS: PIL wouldn't be what it is without the help of: - David Ascher, Phil Austin, Douglas Bagnall, Larry Bates, Anthony - Baxter, William Baxter, Denis Benoit, Jan Blom, Duncan Booth, Alexey - Borzenkov, Jeff Breidenbach, Roger Burnham, Zac Burns, Gene Cash, - Kevin Cazabon, Fred Clare, Greg Coats, Chris Cogdon, Greg Couch, Bill - Crutchfield, Abel Deuring, Tim Docker, Fred Drake, Graham Dumpleton, - Matthew Ellis, Eric Etheridge, Daniel Fetchinson, Robin Friedrich, - Pier Paolo Glave, Federico Di Gregorio, Markus Gritsch, Daniel - Haertle, Greg Hamilton, Mark Hammond, Bernhard Herzog, Rob Hooft, Bob - Ippolito, Jack Jansen, Bill Janssen, Edward Jones, Richard Jones, - Håkan Karlsson, Robert Kern, David Kirtley, Bob Klimek, Matthias - Klose, Andrew Kuchling, Magnus Källström, Victor Lacina, Ben Last, - Hamish Lawson, Cesare Leonardi, Andrew MacIntyre, Jan Matejek, Naveen From 63b33aab3978829061e7cf65a5531c4081efc36a Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:01:49 +0430 Subject: [PATCH 076/294] Change + to - --- CHANGES.rst | 864 ++++++++++++++++++++++++++-------------------------- 1 file changed, 432 insertions(+), 432 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 22b3fa230..6ef0c0ec8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4834,34 +4834,34 @@ was last updated, see the repository revision history: 1.1.7 final ----------- -+ Set GIF loop info property to the number of iterations if a NETSCAPE +- Set GIF loop info property to the number of iterations if a NETSCAPE loop extension is present, instead of always setting it to 1 (from Valentino Volonghi). 1.1.7c1 released ---------------- -+ Improved PNG compression (from Alexey Borzenkov). +- Improved PNG compression (from Alexey Borzenkov). -+ Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) +- Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) -+ Added various TGA improvements from Alexey Borzenkov, including +- Added various TGA improvements from Alexey Borzenkov, including support for specifying image orientation. -+ Bumped block threshold to 16 megabytes, made size estimation a bit +- Bumped block threshold to 16 megabytes, made size estimation a bit more accurate. This speeds up allocation of large images. -+ Fixed rounding error in ImagingDrawWideLine. +- Fixed rounding error in ImagingDrawWideLine. "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every version I've seen, which leads to different width lines depending on the order of the points in the line. This is especially bad at some angles where a 'width=2' line can completely disappear. -+ Added support for RGBA mode to the SGI module (based on code by +- Added support for RGBA mode to the SGI module (based on code by Karsten Hiddemann). -+ Handle repeated IPTC tags (adapted from a patch by Eric Bruning). +- Handle repeated IPTC tags (adapted from a patch by Eric Bruning). Eric writes: According to the specification, some IPTC tags can be repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last @@ -4869,39 +4869,39 @@ was last updated, see the repository revision history: multiple tag instances, they are stored in a (python) list. Single tag instances remain as strings. -+ Fixed potential crash in ImageFilter for small target images +- Fixed potential crash in ImageFilter for small target images (reported by Zac Burns and Daniel Fetchinson). -+ Use BMP instead of JPEG as temporary show format on Mac OS X. +- Use BMP instead of JPEG as temporary show format on Mac OS X. -+ Fixed putpixel/new for I;16 with colors > 255. +- Fixed putpixel/new for I;16 with colors > 255. -+ Added integer power support to ImagingMath. +- Added integer power support to ImagingMath. -+ Added limited support for I;16L mode (explicit little endian). +- Added limited support for I;16L mode (explicit little endian). -+ Moved WMF support into Image.core; enable WMF rendering by default +- Moved WMF support into Image.core; enable WMF rendering by default if renderer is available. -+ Mark the ARG plugin as obsolete. +- Mark the ARG plugin as obsolete. -+ Added version query mechanism to ImageCms and ImageFont, for +- Added version query mechanism to ImageCms and ImageFont, for debugging. -+ Added (experimental) ImageCms function for fetching the ICC profile +- Added (experimental) ImageCms function for fetching the ICC profile for the current display (currently Windows only). Added HWND/HDC support to ImageCms.get_display_profile(). -+ Added WMF renderer (Windows only). +- Added WMF renderer (Windows only). -+ Added ImagePointHandler and ImageTransformHandler mixins; made +- Added ImagePointHandler and ImageTransformHandler mixins; made ImageCmsTransform work with im.point. -+ Fixed potential endless loop in the XVThumbnail reader (from Nikolai +- Fixed potential endless loop in the XVThumbnail reader (from Nikolai Ugelvik). -+ Added Kevin Cazabon's pyCMS package. +- Added Kevin Cazabon's pyCMS package. The C code has been moved to _imagingcms.c, the Python interface module is installed as PIL.ImageCMS. @@ -4922,71 +4922,71 @@ was last updated, see the repository revision history: wording), so I changed variable names and docstrings where applicable. Patches are tested under Python 2.6. -+ Improved support for layer names in PSD files (from Sylvain Baubeau) +- Improved support for layer names in PSD files (from Sylvain Baubeau) Sylvain writes: I needed to be able to retrieve the names of the layers in a PSD files. But PsdImagePlugin.py didn't do the job so I wrote this very small patch. -+ Improved RGBA support for ImageTk for 8.4 and newer (from Con +- Improved RGBA support for ImageTk for 8.4 and newer (from Con Radchenko). This replaces the slow run-length based encoding model with true compositing at the Tk level. -+ Added support for 16- and 32-bit images to McIdas loader. +- Added support for 16- and 32-bit images to McIdas loader. Based on file samples and stand-alone reader code provided by Craig Swank. -+ Added ImagePalette support to putpalette. +- Added ImagePalette support to putpalette. -+ Fixed problem with incremental parsing of PNG files. +- Fixed problem with incremental parsing of PNG files. -+ Make selftest.py report non-zero status on failure (from Mark +- Make selftest.py report non-zero status on failure (from Mark Sienkiewicz) -+ Add big endian save support and multipage infrastructure to the TIFF +- Add big endian save support and multipage infrastructure to the TIFF writer (from Sebastian Haase). -+ Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt +- Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt Schwehr). -+ Added zTXT support (from Andrew Kuchling via Lowell Alleman). +- Added zTXT support (from Andrew Kuchling via Lowell Alleman). -+ Fixed potential infinite loop bug in ImageFont (from Guilherme Polo). +- Fixed potential infinite loop bug in ImageFont (from Guilherme Polo). -+ Added sample ICC profiles (from Kevin Cazabon) +- Added sample ICC profiles (from Kevin Cazabon) -+ Fixed array interface for I, F, and RGBA/RGBX images. +- Fixed array interface for I, F, and RGBA/RGBX images. -+ Added Chroma subsampling support for JPEG (from Justin Huff). +- Added Chroma subsampling support for JPEG (from Justin Huff). Justin writes: Attached is a patch (against PIL 1.1.6) to provide control over the chroma subsampling done by the JPEG encoder. This is often useful for reducing compression artifacts around edges of clipart and text. -+ Added USM/Gaussian Blur code from Kevin Cazabon. +- Added USM/Gaussian Blur code from Kevin Cazabon. -+ Fixed bug w. uninitialized image data when cropping outside the +- Fixed bug w. uninitialized image data when cropping outside the source image. -+ Use ImageShow to implement the Image.show method. +- Use ImageShow to implement the Image.show method. Most notably, this picks the 'display' utility when available. It also allows application code to register new display utilities via the ImageShow registry. -+ Release the GIL in the PNG compressor (from Michael van Tellingen). +- Release the GIL in the PNG compressor (from Michael van Tellingen). -+ Revised JPEG CMYK handling. +- Revised JPEG CMYK handling. Always assume Adobe behaviour, both when reading and writing (based on a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and additional debugging by Michael van Tellingen). -+ Support for preserving ICC profiles (by Florian Böch via Tim Hatch). +- Support for preserving ICC profiles (by Florian Böch via Tim Hatch). Florian writes: @@ -4999,27 +4999,27 @@ was last updated, see the repository revision history: metadata when saving as TIFF again, read/write TIFF resolution information correctly, and to correct inverted CMYK JPEG files. -+ Fixed potential memory leak in median cut quantizer (from Evgeny Salmin). +- Fixed potential memory leak in median cut quantizer (from Evgeny Salmin). -+ Fixed OverflowError when reading upside-down BMP images. +- Fixed OverflowError when reading upside-down BMP images. -+ Added resolution save option for PDF files. +- Added resolution save option for PDF files. Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py based on 1.1.6 as included in Ubuntu, that supports a "resolution" save option. Not great, but it makes the PDF saving more useful by allowing PDFs that are not exactly 72dpi. -+ Look for Tcl/Tk include files in version-specific include directory +- Look for Tcl/Tk include files in version-specific include directory (from Encolpe Degoute). -+ Fixed grayscale rounding error in ImageColor.getcolor (from Tim +- Fixed grayscale rounding error in ImageColor.getcolor (from Tim Hatch). -+ Fixed calculation of mean value in ImageEnhance.Contrast (reported +- Fixed calculation of mean value in ImageEnhance.Contrast (reported by "roop" and Scott David Daniels). -+ Fixed truetype positioning when first character has a negative left +- Fixed truetype positioning when first character has a negative left bearing (from Ned Batchelder): Ned writes: In PIL 1.1.6, ImageDraw.text will position the string @@ -5028,33 +5028,33 @@ was last updated, see the repository revision history: first slash will be clipped at the left, and the string will be mis-positioned. -+ Fixed resolution unit bug in tiff reader/writer (based on code by +- Fixed resolution unit bug in tiff reader/writer (based on code by Florian Höch, Gary Bloom, and others). -+ Added simple transparency support for RGB images (reported by +- Added simple transparency support for RGB images (reported by Sebastian Spaeth). -+ Added support for Unicode filenames in ImageFont.truetype (from Donn +- Added support for Unicode filenames in ImageFont.truetype (from Donn Ingle). -+ Fixed potential crash in ImageFont.getname method (from Donn Ingle). +- Fixed potential crash in ImageFont.getname method (from Donn Ingle). -+ Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). +- Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). 1.1.6 released -------------- -+ Fixed some 64-bit compatibility warnings for Python 2.5. +- Fixed some 64-bit compatibility warnings for Python 2.5. -+ Added threading support for the Sane driver (from Abel Deuring). +- Added threading support for the Sane driver (from Abel Deuring). 1.1.6b2 released ---------------- -+ Added experimental "floodfill" function to the ImageDraw module +- Added experimental "floodfill" function to the ImageDraw module (based on code by Eric Raymond). -+ The default arguments for "frombuffer" doesn't match "fromstring" +- The default arguments for "frombuffer" doesn't match "fromstring" and the documentation; this is a bug, and will most likely be fixed in a future version. In this release, PIL prints a warning message instead. To silence the warning, change any calls of the form @@ -5062,11 +5062,11 @@ was last updated, see the repository revision history: frombuffer(mode, size, data, "raw", mode, 0, 1) -+ Added "fromarray" function, which takes an object implementing the +- Added "fromarray" function, which takes an object implementing the NumPy array interface and creates a PIL Image from it. (from Travis Oliphant). -+ Added NumPy array interface support (__array_interface__) to the +- Added NumPy array interface support (__array_interface__) to the Image class (based on code by Travis Oliphant). This allows you to easily convert between PIL image memories and @@ -5080,54 +5080,54 @@ was last updated, see the repository revision history: im = Image.fromarray(a) -+ Fixed CMYK polarity for JPEG images, by treating all images as +- Fixed CMYK polarity for JPEG images, by treating all images as "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon for samples, debugging, and patches). 1.1.6b1 released ---------------- -+ Added 'expand' option to the Image 'rotate' method. If true, the +- Added 'expand' option to the Image 'rotate' method. If true, the output image is made large enough to hold the entire rotated image. -+ Changed the ImageDraw 'line' method to always draw the last pixel in +- Changed the ImageDraw 'line' method to always draw the last pixel in a polyline, independent of line angle. -+ Fixed bearing calculation and clipping in the ImageFont truetype +- Fixed bearing calculation and clipping in the ImageFont truetype renderer; this could lead to clipped text, or crashes in the low- level _imagingft module. (based on input from Adam Twardoch and others). -+ Added ImageQt wrapper module, for converting PIL Image objects to +- Added ImageQt wrapper module, for converting PIL Image objects to QImage objects in an efficient way. -+ Fixed 'getmodebands' to return the number of bands also for "PA" +- Fixed 'getmodebands' to return the number of bands also for "PA" and "LA" modes. Added 'getmodebandnames' helper that return the band names. 1.1.6a2 released ---------------- -+ Added float/double support to the TIFF loader (from Russell +- Added float/double support to the TIFF loader (from Russell Nelson). -+ Fixed broken use of realloc() in path.c (from Jan Matejek) +- Fixed broken use of realloc() in path.c (from Jan Matejek) -+ Added save support for Spider images (from William Baxter). +- Added save support for Spider images (from William Baxter). -+ Fixed broken 'paste' and 'resize' operations in pildriver +- Fixed broken 'paste' and 'resize' operations in pildriver (from Bill Janssen). -+ Added support for duplex scanning to the Sane interface (Abel +- Added support for duplex scanning to the Sane interface (Abel Deuring). 1.1.6a1 released ---------------- -+ Fixed a memory leak in "convert(mode)", when converting from +- Fixed a memory leak in "convert(mode)", when converting from L to P. -+ Added pixel access object. The "load" method now returns a +- Added pixel access object. The "load" method now returns a access object that can be used to directly get and set pixel values, using ordinary [x, y] notation: @@ -5138,118 +5138,118 @@ was last updated, see the repository revision history: If you're accessing more than a few pixels, this is a lot faster than using getpixel/putpixel. -+ Fixed building on Cygwin (from Miki Tebeka). +- Fixed building on Cygwin (from Miki Tebeka). -+ Fixed "point(callable)" on unloaded images (reported by Håkan +- Fixed "point(callable)" on unloaded images (reported by Håkan Karlsson). -+ Fixed size bug in ImageWin.ImageWindow constructor (from Victor +- Fixed size bug in ImageWin.ImageWindow constructor (from Victor Reijs) -+ Fixed ImageMath float() and int() operations for Python 2.4 +- Fixed ImageMath float() and int() operations for Python 2.4 (reported by Don Rozenberg). -+ Fixed "RuntimeError: encoder error -8 in tostring" problem for +- Fixed "RuntimeError: encoder error -8 in tostring" problem for wide "RGB", "I", and "F" images. -+ Fixed line width calculation. +- Fixed line width calculation. 1.1.6a0 released ---------------- -+ Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). +- Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). -+ Fixed off-by-0.5 errors in the ANTIALIAS code (based on input +- Fixed off-by-0.5 errors in the ANTIALIAS code (based on input from Douglas Bagnall). -+ Added buffer interface support to the Path constructor. If +- Added buffer interface support to the Path constructor. If a buffer is provided, it is assumed to contain a flat array of float coordinates (e.g. array.array('f', seq)). -+ Added new ImageMath module. +- Added new ImageMath module. -+ Fixed ImageOps.equalize when used with a small number of distinct +- Fixed ImageOps.equalize when used with a small number of distinct values (reported by David Kirtley). -+ Fixed potential integer division in PSDraw.image (from Eric Etheridge). +- Fixed potential integer division in PSDraw.image (from Eric Etheridge). 1.1.5c2 and 1.1.5 final released -------------------------------- -+ Added experimental PERSPECTIVE transform method (from Jeff Breiden- +- Added experimental PERSPECTIVE transform method (from Jeff Breiden- bach). 1.1.5c1 released ---------------- -+ Make sure "thumbnail" never generates zero-wide or zero-high images +- Make sure "thumbnail" never generates zero-wide or zero-high images (reported by Gene Skonicki) -+ Fixed a "getcolors" bug that could result in a zero count for some +- Fixed a "getcolors" bug that could result in a zero count for some colors (reported by Richard Oudkerk). -+ Changed default "convert" palette to avoid "rounding errors" when +- Changed default "convert" palette to avoid "rounding errors" when round-tripping white source pixels (reported by Henryk Gerlach and Jeff Epler). 1.1.5b3 released ---------------- -+ Don't crash in "quantize" method if the number of colors requested +- Don't crash in "quantize" method if the number of colors requested is larger than 256. This release raises a ValueError exception; future versions may return a mode "RGB" image instead (reported by Richard Oudkerk). -+ Added WBMP read/write support (based on code by Duncan Booth). +- Added WBMP read/write support (based on code by Duncan Booth). 1.1.5b2 released ---------------- -+ Added DPI read/write support to the PNG codec. The decoder sets +- Added DPI read/write support to the PNG codec. The decoder sets the info["dpi"] attribute for PNG files with appropriate resolution settings. The encoder uses the "dpi" option (based on code by Niki Spahiev). -+ Added limited support for "point" mappings from mode "I" to mode "L". +- Added limited support for "point" mappings from mode "I" to mode "L". Only 16-bit values are supported (other values are clipped), the lookup table must contain exactly 65536 entries, and the mode argument must be set to "L". -+ Added support for Mac OS X icns files (based on code by Bob Ippolito). +- Added support for Mac OS X icns files (based on code by Bob Ippolito). -+ Added "ModeFilter" support to the ImageFilter module. +- Added "ModeFilter" support to the ImageFilter module. -+ Added support for Spider images (from William Baxter). See the +- Added support for Spider images (from William Baxter). See the comments in PIL/SpiderImagePlugin.py for more information on this format. 1.1.5b1 released ---------------- -+ Added new Sane release (from Ralph Heinkel). See the Sane/README +- Added new Sane release (from Ralph Heinkel). See the Sane/README and Sane/CHANGES files for more information. -+ Added experimental PngInfo chunk container to the PngImageFile +- Added experimental PngInfo chunk container to the PngImageFile module. This can be used to add arbitrary chunks to a PNG file. Create a PngInfo instance, use "add" or "add_text" to add chunks, and pass the instance as the "pnginfo" option when saving the file. -+ Added "getpalette" method. This returns the palette as a list, +- Added "getpalette" method. This returns the palette as a list, or None if the image has no palette. To modify the palette, use "getpalette" to fetch the current palette, modify the list, and put it back using "putpalette". -+ Added optional flattening to the ImagePath "tolist" method. +- Added optional flattening to the ImagePath "tolist" method. tolist() or tolist(0) returns a list of 2-tuples, as before. tolist(1) returns a flattened list instead. 1.1.5a5 released ---------------- -+ Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". +- Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". -+ Added "getcolors()" method. This is similar to the existing histo- +- Added "getcolors()" method. This is similar to the existing histo- gram method, but looks at color values instead of individual layers, and returns an unsorted list of (count, color) tuples. @@ -5258,87 +5258,87 @@ was last updated, see the repository revision history: is used to allocate internal tables, so you probably don't want to pass in too large values). -+ Build improvements: Fixed building under AIX, improved detection of +- Build improvements: Fixed building under AIX, improved detection of FreeType2 and Mac OS X framework libraries, and more. Many thanks to everyone who helped test the new "setup.py" script! 1.1.5a4 released ---------------- -+ The "save" method now looks for a file format driver before +- The "save" method now looks for a file format driver before creating the file. -+ Don't use antialiased truetype fonts when drawing in mode "P", "I", +- Don't use antialiased truetype fonts when drawing in mode "P", "I", and "F" images. -+ Rewrote the "setup.py" file. The new version scans for available +- Rewrote the "setup.py" file. The new version scans for available support libraries, and configures both the libImaging core library and the bindings in one step. To use specific versions of the libraries, edit the ROOT variables in the setup.py file. -+ Removed threaded "show" viewer; use the old "show" implementation +- Removed threaded "show" viewer; use the old "show" implementation instead (Windows). -+ Added deprecation warnings to Image.offset, ImageDraw.setink, and +- Added deprecation warnings to Image.offset, ImageDraw.setink, and ImageDraw.setfill. -+ Added width option to ImageDraw.line(). The current implementation +- Added width option to ImageDraw.line(). The current implementation works best for straight lines; it does not support line joins, so polylines won't look good. -+ ImageDraw.Draw is now a factory function instead of a class. If +- ImageDraw.Draw is now a factory function instead of a class. If you need to create custom draw classes, inherit from the ImageDraw class. All other code should use the factory function. -+ Fixed loading of certain PCX files (problem reported by Greg +- Fixed loading of certain PCX files (problem reported by Greg Hamilton, who also provided samples). -+ Changed _imagingft.c to require FreeType 2.1 or newer. The +- Changed _imagingft.c to require FreeType 2.1 or newer. The module can still be built with earlier versions; see comments in _imagingft.c for details. 1.1.5a3 released ---------------- -+ Added 'getim' method, which returns a PyCObject wrapping an +- Added 'getim' method, which returns a PyCObject wrapping an Imaging pointer. The description string is set to IMAGING_MAGIC. See Imaging.h for pointer and string declarations. -+ Fixed reading of TIFF JPEG images (problem reported by Ulrik +- Fixed reading of TIFF JPEG images (problem reported by Ulrik Svensson). -+ Made ImageColor work under Python 1.5.2 +- Made ImageColor work under Python 1.5.2 -+ Fixed division by zero "equalize" on very small images (from +- Fixed division by zero "equalize" on very small images (from Douglas Bagnall). 1.1.5a2 released ---------------- -+ The "paste" method now supports the alternative "paste(im, mask)" +- The "paste" method now supports the alternative "paste(im, mask)" syntax (in this case, the box defaults to im's bounding box). -+ The "ImageFile.Parser" class now works also for PNG files with +- The "ImageFile.Parser" class now works also for PNG files with more than one IDAT block. -+ Added DPI read/write to the TIFF codec, and fixed writing of +- Added DPI read/write to the TIFF codec, and fixed writing of rational values. The decoder sets the info["dpi"] attribute for TIFF files with appropriate resolution settings. The encoder uses the "dpi" option. -+ Disable interlacing for small (or narrow) GIF images, to +- Disable interlacing for small (or narrow) GIF images, to work around what appears to be a hard-to-find bug in PIL's GIF encoder. -+ Fixed writing of mode "P" PDF images. Made mode "1" PDF +- Fixed writing of mode "P" PDF images. Made mode "1" PDF images smaller. -+ Made the XBM reader a bit more robust; the file may now start +- Made the XBM reader a bit more robust; the file may now start with a few whitespace characters. -+ Added support for enhanced metafiles to the WMF driver. The +- Added support for enhanced metafiles to the WMF driver. The separate PILWMF kit lets you render both placeable WMF files and EMF files as raster images. See @@ -5347,22 +5347,22 @@ was last updated, see the repository revision history: 1.1.5a1 released ---------------- -+ Replaced broken WMF driver with a WMF stub plugin (see below). +- Replaced broken WMF driver with a WMF stub plugin (see below). -+ Fixed writing of mode "1", "L", and "CMYK" PDF images (based on +- Fixed writing of mode "1", "L", and "CMYK" PDF images (based on input from Nicholas Riley and others). -+ Fixed adaptive palette conversion for zero-width or zero-height +- Fixed adaptive palette conversion for zero-width or zero-height images (from Chris Cogdon) -+ Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) +- Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) -+ Added support for StubImageFile plugins, including stub plugins +- Added support for StubImageFile plugins, including stub plugins for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify a given file format, but relies on application code to open and save files in that format. -+ Added optional "encoding" argument to the ImageFont.truetype +- Added optional "encoding" argument to the ImageFont.truetype factory. This argument can be used to specify non-Unicode character maps for fonts that support that. For example, to draw text using the Microsoft Symbol font, use: @@ -5377,33 +5377,33 @@ was last updated, see the repository revision history: "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). See the FreeType documentation for more information. -+ Made "putalpha" a bit more robust; you can now attach an alpha +- Made "putalpha" a bit more robust; you can now attach an alpha layer to a plain "L" or "RGB" image, and you can also specify constant alphas instead of alpha layers (using integers or colour names). -+ Added experimental "LA" mode support. +- Added experimental "LA" mode support. An "LA" image is an "L" image with an attached transparency layer. Note that support for "LA" is not complete; some operations may fail or produce unexpected results. -+ Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" +- Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" classes to the ImageFilter module. -+ Improved support for applications using multiple threads; PIL +- Improved support for applications using multiple threads; PIL now releases the global interpreter lock for many CPU-intensive operations (based on work by Kevin Cazabon). -+ Ignore Unicode characters in the PCF loader (from Andres Polit) +- Ignore Unicode characters in the PCF loader (from Andres Polit) -+ Fixed typo in OleFileIO.loadfat, which could affect loading of +- Fixed typo in OleFileIO.loadfat, which could affect loading of FlashPix and Image Composer images (Daniel Haertle) -+ Fixed building on platforms that have Freetype but don't have +- Fixed building on platforms that have Freetype but don't have Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) -+ Added EXIF GPSInfo read support for JPEG files. To extract +- Added EXIF GPSInfo read support for JPEG files. To extract GPSInfo information, open the file, extract the exif dictionary, and check for the key 0x8825 (GPSInfo). If present, it contains a dictionary mapping GPS keys to GPS values. For a list of keys, @@ -5412,46 +5412,46 @@ was last updated, see the repository revision history: The "ExifTags" module contains a GPSTAGS dictionary mapping GPS tags to tag names. -+ Added DPI read support to the PCX and DCX codecs (info["dpi"]). +- Added DPI read support to the PCX and DCX codecs (info["dpi"]). -+ The "show" methods now uses a built-in image viewer on Windows. +- The "show" methods now uses a built-in image viewer on Windows. This viewer creates an instance of the ImageWindow class (see below) and keeps it running in a separate thread. NOTE: This was disabled in 1.1.5a4. -+ Added experimental "Window" and "ImageWindow" classes to the +- Added experimental "Window" and "ImageWindow" classes to the ImageWin module. These classes allow you to create a WCK-style toplevel window, and use it to display raster data. -+ Fixed some Python 1.5.2 issues (to build under 1.5.2, use the +- Fixed some Python 1.5.2 issues (to build under 1.5.2, use the Makefile.pre.in/Setup.in approach) -+ Added support for the TIFF FillOrder tag. PIL can read mode "1", +- Added support for the TIFF FillOrder tag. PIL can read mode "1", "L", "P" and "RGB" images with non-standard FillOrder (based on input from Jeff Breidenbach). 1.1.4 final released -------------------- -+ Fixed ImageTk build problem on Unix. +- Fixed ImageTk build problem on Unix. 1.1.4b2 released ---------------- -+ Improved building on Mac OS X (from Jack Jansen). +- Improved building on Mac OS X (from Jack Jansen). -+ Improved building on Windows with MinGW (from Klamer Shutte). +- Improved building on Windows with MinGW (from Klamer Shutte). -+ If no font is specified, ImageDraw now uses the embedded default +- If no font is specified, ImageDraw now uses the embedded default font. Use the "load" or "truetype" methods to load a real font. -+ Added embedded default font to the ImageFont module (currently +- Added embedded default font to the ImageFont module (currently an 8-pixel Courier font, taken from the X window distribution). 1.1.4b1 released ---------------- -+ Added experimental EXIF support for JPEG files. To extract EXIF +- Added experimental EXIF support for JPEG files. To extract EXIF information from a JPEG file, open the file as usual, and call the "_getexif" method. If successful, this method returns a dictionary mapping EXIF TIFF tags to values. If the file does not contain EXIF @@ -5462,47 +5462,47 @@ was last updated, see the repository revision history: This interface will most likely change in future versions. -+ Fixed a bug when using the "transparency" option with the GIF +- Fixed a bug when using the "transparency" option with the GIF writer. -+ Added limited support for "bitfield compression" in BMP files +- Added limited support for "bitfield compression" in BMP files and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This also fixes a problem with ImageGrab module when copying screen- dumps from the clipboard on 15/16/32-bit displays. -+ Added experimental WAL (Quake 2 textures) loader. To use this +- Added experimental WAL (Quake 2 textures) loader. To use this loader, import WalImageFile and call the "open" method in that module. 1.1.4a4 released ---------------- -+ Added updated SANE driver (Andrew Kuchling, Abel Deuring) +- Added updated SANE driver (Andrew Kuchling, Abel Deuring) -+ Use Python's "mmap" module on non-Windows platforms to read some +- Use Python's "mmap" module on non-Windows platforms to read some uncompressed formats using memory mapping. Also added a "frombuffer" function that allows you to access the contents of an existing string or buffer object as if it were an image object. -+ Fixed a memory leak that could appear when processing mode "P" +- Fixed a memory leak that could appear when processing mode "P" images (from Pier Paolo Glave) -+ Ignore Unicode characters in the BDF loader (from Graham Dumpleton) +- Ignore Unicode characters in the BDF loader (from Graham Dumpleton) 1.1.4a3 released; windows only ----------------------------- -+ Added experimental RGBA-on-RGB drawing support. To use RGBA +- Added experimental RGBA-on-RGB drawing support. To use RGBA colours on an RGB image, pass "RGBA" as the second string to the ImageDraw.Draw constructor. -+ Added support for non-ASCII strings (Latin-1) and Unicode +- Added support for non-ASCII strings (Latin-1) and Unicode to the truetype font renderer. -+ The ImageWin "Dib" object can now be constructed directly from +- The ImageWin "Dib" object can now be constructed directly from an image object. -+ The ImageWin module now allows you use window handles as well +- The ImageWin module now allows you use window handles as well as device contexts. To use a window handle, wrap the handle in an ImageWin.HWND object, and pass in this object instead of the device context. @@ -5510,33 +5510,33 @@ was last updated, see the repository revision history: 1.1.4a2 released ---------------- -+ Improved support for 16-bit unsigned integer images (mode "I;16"). +- Improved support for 16-bit unsigned integer images (mode "I;16"). This includes TIFF reader support, and support for "getextrema" and "point" (from Klamer Shutte). -+ Made the BdfFontFile reader a bit more robust (from Kevin Cazabon +- Made the BdfFontFile reader a bit more robust (from Kevin Cazabon and Dmitry Vasiliev) -+ Changed TIFF writer to always write Compression tag, even when +- Changed TIFF writer to always write Compression tag, even when using the default compression (from Greg Couch). -+ Added "show" support for Mac OS X (from Dan Wolfe). +- Added "show" support for Mac OS X (from Dan Wolfe). -+ Added clipboard support to the "ImageGrab" module (Windows only). +- Added clipboard support to the "ImageGrab" module (Windows only). The "grabclipboard" function returns an Image object, a list of filenames (not in 1.1.4), or None if neither was found. 1.1.4a1 released ---------------- -+ Improved support for drawing RGB data in palette images. You can +- Improved support for drawing RGB data in palette images. You can now use RGB tuples or colour names (see below) when drawing in a mode "P" image. The drawing layer automatically assigns color indexes, as long as you don't use more than 256 unique colours. -+ Moved self test from MiniTest/test.py to ./selftest.py. +- Moved self test from MiniTest/test.py to ./selftest.py. -+ Added support for CSS3-style color strings to most places that +- Added support for CSS3-style color strings to most places that accept colour codes/tuples. This includes the "ImageDraw" module, the Image "new" function, and the Image "paste" method. @@ -5545,123 +5545,123 @@ was last updated, see the repository revision history: or "red" (most X11-style colour names are supported). See the documentation for the "ImageColor" module for more information. -+ Fixed DCX decoder (based on input from Larry Bates) +- Fixed DCX decoder (based on input from Larry Bates) -+ Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA +- Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA newsphoto properties from JPEG, TIFF, or IPTC files. -+ Support for TrueType/OpenType fonts has been added to +- Support for TrueType/OpenType fonts has been added to the standard distribution. You need the freetype 2.0 library. -+ Made the PCX reader a bit more robust when reading 2-bit +- Made the PCX reader a bit more robust when reading 2-bit and 4-bit PCX images with odd image sizes. -+ Added "Kernel" class to the ImageFilter module. This class +- Added "Kernel" class to the ImageFilter module. This class allows you to filter images with user-defined 3x3 and 5x5 convolution kernels. -+ Added "putdata" support for mode "I", "F" and "RGB". +- Added "putdata" support for mode "I", "F" and "RGB". -+ The GIF writer now supports the transparency option (from +- The GIF writer now supports the transparency option (from Denis Benoit). -+ A HTML version of the module documentation is now shipped +- A HTML version of the module documentation is now shipped with the source code distribution. You'll find the files in the Doc subdirectory. -+ Added support for Palm pixmaps (from Bill Janssen). This +- Added support for Palm pixmaps (from Bill Janssen). This change was listed for 1.1.3, but the "PalmImagePlugin" driver didn't make it into the distribution. -+ Improved decoder error messages. +- Improved decoder error messages. 1.1.3 final released ------------------- -+ Made setup.py look for old versions of zlib. For some back- +- Made setup.py look for old versions of zlib. For some back- ground, see: https://zlib.net/advisory-2002-03-11.txt 1.1.3c2 released ----------------- -+ Added setup.py file (tested on Unix and Windows). You still +- Added setup.py file (tested on Unix and Windows). You still need to build libImaging/imaging.lib in the traditional way, but the setup.py script takes care of the rest. The old Setup.in/Makefile.pre.in build method is still supported. -+ Fixed segmentation violation in ANTIALIAS filter (an internal +- Fixed segmentation violation in ANTIALIAS filter (an internal buffer wasn't properly allocated). 1.1.3c1 released ---------------- -+ Added ANTIALIAS downsampling filter for high-quality "resize" +- Added ANTIALIAS downsampling filter for high-quality "resize" and "thumbnail" operations. Also added filter option to the "thumbnail" operation; the default value is NEAREST, but this will most likely change in future versions. -+ Fixed plugin loader to be more robust if the __file__ +- Fixed plugin loader to be more robust if the __file__ variable isn't set. -+ Added seek/tell support (for layers) to the PhotoShop +- Added seek/tell support (for layers) to the PhotoShop loader. Layer 0 is the main image. -+ Added new (but experimental) "ImageOps" module, which provides +- Added new (but experimental) "ImageOps" module, which provides shortcuts for commonly used operations on entire images. -+ Don't mess up when loading PNG images if the decoder leaves +- Don't mess up when loading PNG images if the decoder leaves data in the output buffer. This could cause internal errors on some PNG images, with some versions of ZLIB. (Bug report and patch provided by Bernhard Herzog.) -+ Don't mess up on Unicode filenames. +- Don't mess up on Unicode filenames. -+ Don't mess up when drawing on big endian platforms. +- Don't mess up when drawing on big endian platforms. -+ Made the TIFF loader a bit more robust; it can now read some +- Made the TIFF loader a bit more robust; it can now read some more slightly broken TIFF files (based on input from Ted Wright, Bob Klimek, and D. Alan Stewart) -+ Added OS/2 EMX build files (from Andrew MacIntyre) +- Added OS/2 EMX build files (from Andrew MacIntyre) -+ Change "ImageFont" to reject image files if they don't have the +- Change "ImageFont" to reject image files if they don't have the right mode. Older versions could leak memory for "P" images. (Bug reported by Markus Gritsch). -+ Renamed some internal functions to avoid potential build +- Renamed some internal functions to avoid potential build problem on Mac OS X. -+ Added DL_EXPORT where relevant (for Cygwin, based on input +- Added DL_EXPORT where relevant (for Cygwin, based on input from Robert Yodlowski) -+ (re)moved bogus __init__ call in BdfFontFile (bug spotted +- (re)moved bogus __init__ call in BdfFontFile (bug spotted by Fred Clare) -+ Added "ImageGrab" support (Windows only) +- Added "ImageGrab" support (Windows only) -+ Added support for XBM hotspots (based on code contributed by +- Added support for XBM hotspots (based on code contributed by Bernhard Herzog). -+ Added write support for more TIFF tags, namely the Artist, +- Added write support for more TIFF tags, namely the Artist, Copyright, DateTime, ResolutionUnit, Software, XResolution and YResolution tags (from Greg Couch) -+ Added TransposedFont wrapper to ImageFont module +- Added TransposedFont wrapper to ImageFont module -+ Added "optimize" flag to GIF encoder. If optimize is present +- Added "optimize" flag to GIF encoder. If optimize is present and non-zero, PIL will work harder to create a small file. -+ Raise "EOFError" (not IndexError) when reading beyond the +- Raise "EOFError" (not IndexError) when reading beyond the end of a TIFF sequence. -+ Support rewind ("seek(0)") for GIF and TIFF sequences. +- Support rewind ("seek(0)") for GIF and TIFF sequences. -+ Load grayscale GIF images as mode "L" +- Load grayscale GIF images as mode "L" -+ Added DPI read/write support to the JPEG codec. The decoder +- Added DPI read/write support to the JPEG codec. The decoder sets the info["dpi"] attribute for JPEG files with JFIF dpi settings. The encoder uses the "dpi" option: @@ -5675,82 +5675,82 @@ was last updated, see the repository revision history: 1.1.2c1 and 1.1.2 final released ---------------- -+ Adapted to Python 2.1. Among other things, all uses of the +- Adapted to Python 2.1. Among other things, all uses of the "regex" module have been replaced with "re". -+ Fixed attribute error when reading large PNG files (this bug +- Fixed attribute error when reading large PNG files (this bug was introduced in maintenance code released after the 1.1.1 release) -+ Ignore non-string objects in sys.path +- Ignore non-string objects in sys.path -+ Fixed Image.transform(EXTENT) for negative xoffsets +- Fixed Image.transform(EXTENT) for negative xoffsets -+ Fixed loading of image plugins if PIL is installed as a package. +- Fixed loading of image plugins if PIL is installed as a package. (The plugin loader now always looks in the directory where the Image.py module itself is found, even if that directory isn't on the standard search path) -+ The Png plugin has been added to the list of preloaded standard +- The Png plugin has been added to the list of preloaded standard formats -+ Fixed bitmap/text drawing in fill mode. +- Fixed bitmap/text drawing in fill mode. -+ Fixed "getextrema" to work also for multiband images. +- Fixed "getextrema" to work also for multiband images. -+ Added transparency support for L and P images to the PNG codec. +- Added transparency support for L and P images to the PNG codec. -+ Improved support for read-only images. The "load" method now +- Improved support for read-only images. The "load" method now sets the "readonly" attribute for memory-mapped images. Operations that modifies an image in place (such as "paste" and drawing operations) creates an in-memory copy of the image, if necessary. (before this change, any attempt to modify a memory-mapped image resulted in a core dump...) -+ Added special cases for lists everywhere PIL expects a sequence. +- Added special cases for lists everywhere PIL expects a sequence. This should speed up things like "putdata" and drawing operations. -+ The Image.offset method is deprecated. Use the ImageChops.offset +- The Image.offset method is deprecated. Use the ImageChops.offset function instead. -+ Changed ImageChops operators to copy palette and info dictionary +- Changed ImageChops operators to copy palette and info dictionary from the first image argument. 1.1.1 released -------------- -+ Additional fixes for Python 1.6/2.0, including TIFF "save" bug. +- Additional fixes for Python 1.6/2.0, including TIFF "save" bug. -+ Changed "init" to properly load plugins when PIL is used as a +- Changed "init" to properly load plugins when PIL is used as a package. -+ Fixed broken "show" method (on Unix) +- Fixed broken "show" method (on Unix) 1.0 to 1.1 ---------- -+ Adapted to Python 1.6 ("append" and other method changes) +- Adapted to Python 1.6 ("append" and other method changes) -+ Fixed Image.paste when pasting with solid colour and matte +- Fixed Image.paste when pasting with solid colour and matte layers ("L" or "RGBA" masks) (bug reported by Robert Kern) -+ To make it easier to distribute prebuilt versions of PIL, +- To make it easier to distribute prebuilt versions of PIL, the tkinit binding stuff has been moved to a separate extension module, named "_imagingtk". 0.3b2 to 1.0 final ------ -+ If there's no 16-bit integer (like on a Cray T3E), set +- If there's no 16-bit integer (like on a Cray T3E), set INT16 to the smallest integer available. Most of the library works just fine anyway (from Bill Crutchfield) -+ Tweaks to make drawing work on big-endian platforms. +- Tweaks to make drawing work on big-endian platforms. 1.0c2 released -------------- -+ If PIL is built with the WITH_TKINTER flag, ImageTk can +- If PIL is built with the WITH_TKINTER flag, ImageTk can automatically hook into a standard Tkinter build. You no longer need to build your own Tkinter to use the ImageTk module. @@ -5758,41 +5758,41 @@ was last updated, see the repository revision history: The old way still works, though. For more information, see Tk/install.txt. -+ Some tweaks to ImageTk to support multiple Tk interpreters +- Some tweaks to ImageTk to support multiple Tk interpreters (from Greg Couch). -+ ImageFont "load_path" now scans directory mentioned in .pth +- ImageFont "load_path" now scans directory mentioned in .pth files (from Richard Jones). 1.0c1 released -------------- -+ The TIFF plugin has been rewritten. The new plugin fully +- The TIFF plugin has been rewritten. The new plugin fully supports all major PIL image modes (including F and I). -+ The ImageFile module now includes a Parser class, which can +- The ImageFile module now includes a Parser class, which can be used to incrementally decode an image file (while down- loading it from the net, for example). See the handbook for details. -+ "show" now converts non-standard modes to "L" or "RGB" (as +- "show" now converts non-standard modes to "L" or "RGB" (as appropriate), rather than writing weird things to disk for "xv" to choke upon. (bug reported by Les Schaffer). 1.0b2 released -------------- -+ Major speedups for rotate, transform(EXTENT), and transform(AFFINE) +- Major speedups for rotate, transform(EXTENT), and transform(AFFINE) when using nearest neighbour resampling. -+ Modified ImageDraw to be compatible with the Arrow graphics +- Modified ImageDraw to be compatible with the Arrow graphics interface. See the handbook for details. -+ PIL now automatically loads file codecs when used as a package +- PIL now automatically loads file codecs when used as a package (from The Dragon De Monsyne). Also included an __init__.py file in the standard distribution. -+ The GIF encoder has been modified to produce much smaller files. +- The GIF encoder has been modified to produce much smaller files. PIL now uses a run-length encoding method to encode GIF files. On a random selection of GIF images grabbed from the web, this @@ -5800,16 +5800,16 @@ was last updated, see the repository revision history: LZW files, where the earlier version made them over 5 times larger. YMMV, of course. -+ Added PCX write support (works with "1", "P", "L", and "RGB") +- Added PCX write support (works with "1", "P", "L", and "RGB") -+ Added "bitmap" and "textsize" methods to ImageDraw. +- Added "bitmap" and "textsize" methods to ImageDraw. -+ Improved font rendering code. Fixed a bug or two, and moved +- Improved font rendering code. Fixed a bug or two, and moved most of the time critical stuff to C. -+ Removed "bdf2pil.py". Use "pilfont.py" instead! +- Removed "bdf2pil.py". Use "pilfont.py" instead! -+ Improved 16-bit support (still experimental, though). +- Improved 16-bit support (still experimental, though). The following methods now support "I;16" and "I;16B" images: "getpixel", "copy", "convert" (to and from mode "I"), "resize", @@ -5820,7 +5820,7 @@ was last updated, see the repository revision history: NOTE: ALL other operations are still UNDEFINED on 16-bit images. -+ The "paste" method now supports constant sources. +- The "paste" method now supports constant sources. Just pass a colour value (a number or a tuple, depending on the target image mode) instead of the source image. @@ -5830,7 +5830,7 @@ was last updated, see the repository revision history: source image if you passed it a colour instead of an image). In this version, this is handled on the C level instead. -+ Added experimental "RGBa" mode support. +- Added experimental "RGBa" mode support. An "RGBa" image is an RGBA image where the colour components have have been premultiplied with the alpha value. PIL allows @@ -5839,27 +5839,27 @@ was last updated, see the repository revision history: of multiplications and shifts, it is typically about twice as fast an ordinary RGBA paste. -+ Eliminated extra conversion step when pasting "RGBA" or "RGBa" +- Eliminated extra conversion step when pasting "RGBA" or "RGBa" images on top of "RGB" images. -+ Fixed Image.BICUBIC resampling for "RGB" images. +- Fixed Image.BICUBIC resampling for "RGB" images. -+ Fixed PCX image file handler to properly read 8-bit PCX +- Fixed PCX image file handler to properly read 8-bit PCX files (bug introduced in 1.0b1, reported by Bernhard Herzog) -+ Fixed PSDraw "image" method to restore the coordinate +- Fixed PSDraw "image" method to restore the coordinate system. -+ Fixed "blend" problem when applied to images that was +- Fixed "blend" problem when applied to images that was not already loaded (reported by Edward C. Jones) -+ Fixed -f option to "pilconvert.py" (from Anthony Baxter) +- Fixed -f option to "pilconvert.py" (from Anthony Baxter) 1.0b1 released -------------- -+ Added Toby J. Sargeant's quantization package. To enable +- Added Toby J. Sargeant's quantization package. To enable quantization, use the "palette" option to "convert": imOut = im.convert("P", palette=Image.ADAPTIVE) @@ -5872,61 +5872,61 @@ was last updated, see the repository revision history: a maximum coverage quantizer, which will be supported by future versions of PIL. -+ Added Eric S. Raymond's "pildriver" image calculator to the +- Added Eric S. Raymond's "pildriver" image calculator to the distribution. See the docstring for more information. -+ The "offset" method no longer dumps core if given positive +- The "offset" method no longer dumps core if given positive offsets (from Charles Waldman). -+ Fixed a resource leak that could cause ImageWin to run out of +- Fixed a resource leak that could cause ImageWin to run out of GDI resources (from Roger Burnham). -+ Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired +- Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired by code contributed by Richard Jones). -+ Added experimental 16-bit support, via modes "I;16" (little endian +- Added experimental 16-bit support, via modes "I;16" (little endian data) and "I;16B" (big endian). Only a few methods properly support such images (see above). -+ Added XV thumbnail file handler (from Gene Cash). +- Added XV thumbnail file handler (from Gene Cash). -+ Fixed BMP image file handler to handle palette images with small +- Fixed BMP image file handler to handle palette images with small palettes (from Rob Hooft). -+ Fixed Sun raster file handler for palette images (from Charles +- Fixed Sun raster file handler for palette images (from Charles Waldman). -+ Improved various internal error messages. +- Improved various internal error messages. -+ Fixed Path constructor to handle arbitrary sequence objects. This +- Fixed Path constructor to handle arbitrary sequence objects. This also affects the ImageDraw class (from Richard Jones). -+ Fixed a bug in JpegDecode that caused PIL to report "decoder error +- Fixed a bug in JpegDecode that caused PIL to report "decoder error -2" for some progressive JPEG files (reported by Magnus Källström, who also provided samples). -+ Fixed a bug in JpegImagePlugin that caused PIL to hang when loading +- Fixed a bug in JpegImagePlugin that caused PIL to hang when loading JPEG files using 16-bit quantization tables. -+ The Image "transform" method now supports Image.QUAD transforms. +- The Image "transform" method now supports Image.QUAD transforms. The data argument is an 8-tuple giving the upper left, lower left, lower right, and upper right corner of the source quadri- lateral. Also added Image.MESH transform which takes a list of quadrilaterals. -+ The Image "resize", "rotate", and "transform" methods now support +- The Image "resize", "rotate", and "transform" methods now support Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. Filters can be used with all transform methods. -+ The ImageDraw "rectangle" method now includes both the right +- The ImageDraw "rectangle" method now includes both the right and the bottom edges when drawing filled rectangles. -+ The TGA decoder now works properly for runlength encoded images +- The TGA decoder now works properly for runlength encoded images which have more than one byte per pixel. -+ "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") +- "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") -+ Some file drivers didn't handle the optional "modify" argument +- Some file drivers didn't handle the optional "modify" argument to the load method. This resulted in exceptions when you used "paste" (and other methods that modify an image in place) on a newly opened file. @@ -5936,39 +5936,39 @@ was last updated, see the repository revision history: The test suite includes 825 individual tests. -+ An Image "getbands" method has been added. It returns a tuple +- An Image "getbands" method has been added. It returns a tuple containing the individual band names for this image. To figure out how many bands an image has, use "len(im.getbands())". -+ An Image "putpixel" method has been added. +- An Image "putpixel" method has been added. -+ The Image "point" method can now be used to convert "L" images +- The Image "point" method can now be used to convert "L" images to any other format, via a lookup table. That table should contain 256 values for each band in the output image. -+ Some file drivers (including FLI/FLC, GIF, and IM) accidentally +- Some file drivers (including FLI/FLC, GIF, and IM) accidentally overwrote the offset method with an internal attribute. All drivers have been updated to use private attributes where possible. -+ The Image "histogram" method now works for "I" and "F" images. +- The Image "histogram" method now works for "I" and "F" images. For these modes, PIL divides the range between the min and max values used in the image into 256 bins. You can also pass in your own min and max values via the "extrema" option: h = im.histogram(extrema=(0, 255)) -+ An Image "getextrema" method has been added. It returns the +- An Image "getextrema" method has been added. It returns the min and max values used in the image. In this release, this works for single band images only. -+ Changed the PNG driver to load and save mode "I" images as +- Changed the PNG driver to load and save mode "I" images as 16-bit images. When saving, values outside the range 0..65535 are clipped. -+ Fixed ImageFont.py to work with the new "pilfont" compiler. +- Fixed ImageFont.py to work with the new "pilfont" compiler. -+ Added JPEG "save" and "draft" support for mode "YCbCr" images. +- Added JPEG "save" and "draft" support for mode "YCbCr" images. Note that if you save an "YCbCr" image as a JPEG file and read it back, it is read as an RGB file. To get around this, you can use the "draft" method: @@ -5976,10 +5976,10 @@ The test suite includes 825 individual tests. im = Image.open("color.jpg") im.draft("YCbCr", im.size) -+ Read "RGBA" TGA images. Also fixed the orientation bug; all +- Read "RGBA" TGA images. Also fixed the orientation bug; all images should now come out the right way. -+ Changed mode name (and internal representation) from "YCrCb" +- Changed mode name (and internal representation) from "YCrCb" to "YCbCr" (!) *** WARNING: MAY BREAK EXISTING CODE *** @@ -5988,16 +5988,16 @@ The test suite includes 825 individual tests. The test suite includes 750 individual tests. -+ The "pilfont" package is now included in the standard PIL +- The "pilfont" package is now included in the standard PIL distribution. The pilfont utility can be used to convert X BDF and PCF raster font files to a format understood by the ImageFont module. -+ GIF files are now interlaced by default. To write a +- GIF files are now interlaced by default. To write a non-interlaced file, pass interlace=0 to the "save" method. -+ The default string format has changed for the "fromstring" +- The default string format has changed for the "fromstring" and "tostring" methods. *** WARNING: MAY BREAK EXISTING CODE *** @@ -6013,148 +6013,148 @@ The test suite includes 750 individual tests. data = im.tostring("raw", "RGBX", 0, -1) im.fromstring(data, "raw", "RGBX", 0, -1) -+ "new" no longer gives a MemoryError if the width or height +- "new" no longer gives a MemoryError if the width or height is zero (this only happened on platforms where malloc(0) or calloc(0) returns NULL). -+ "new" now adds a default palette object to "P" images. +- "new" now adds a default palette object to "P" images. -+ You can now convert directly between all modes supported by +- You can now convert directly between all modes supported by PIL. When converting colour images to "P", PIL defaults to a "web" palette and dithering. When converting greyscale images to "1", PIL uses a thresholding and dithering. -+ Added a "dither" option to "convert". By default, "convert" +- Added a "dither" option to "convert". By default, "convert" uses floyd-steinberg error diffusion for "P" and "1" targets, so this option is only used to *disable* dithering. Allowed values are NONE (no dithering) or FLOYDSTEINBERG (default). imOut = im.convert("P", dither=Image.NONE) -+ Added a full set of "I" decoders. You can use "fromstring" +- Added a full set of "I" decoders. You can use "fromstring" (and file decoders) to read any standard integer type as an "I" image. -+ Added some support for "YCbCr" images (creation, conversion +- Added some support for "YCbCr" images (creation, conversion from/to "L" and "RGB", IM YCC load/save) -+ "getpixel" now works properly with fractional coordinates. +- "getpixel" now works properly with fractional coordinates. -+ ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", +- ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", "RGBX", "CMYK", and "YCbCr" images. -+ ImImagePlugin no longer attaches palettes to "RGB" images. +- ImImagePlugin no longer attaches palettes to "RGB" images. -+ Various minor fixes. +- Various minor fixes. 0.3a4 released -------------- -+ Added experimental IPTC/NAA support. +- Added experimental IPTC/NAA support. -+ Eliminated AttributeError exceptions after "crop" (from +- Eliminated AttributeError exceptions after "crop" (from Skip Montanaro) -+ Reads some uncompressed formats via memory mapping (this +- Reads some uncompressed formats via memory mapping (this is currently supported on Win32 only) -+ Fixed some last minute glitches in the last alpha release +- Fixed some last minute glitches in the last alpha release (Types instead of types in Image.py, version numbers, etc.) -+ Eliminated some more bogus compiler warnings. +- Eliminated some more bogus compiler warnings. -+ Various fixes to make PIL compile and run smoother on Macs +- Various fixes to make PIL compile and run smoother on Macs (from Jack Jansen). -+ Fixed "fromstring" and "tostring" for mode "I" images. +- Fixed "fromstring" and "tostring" for mode "I" images. 0.3a3 released -------------- The test suite includes 530 individual tests. -+ Eliminated unexpected side-effect in "paste" with matte. "paste" +- Eliminated unexpected side-effect in "paste" with matte. "paste" now works properly also if compiled with "gcc". -+ Adapted to Python 1.5 (build issues only) +- Adapted to Python 1.5 (build issues only) -+ Fixed the ImageDraw "point" method to draw also the last +- Fixed the ImageDraw "point" method to draw also the last point (!). -+ Added "I" and "RGBX" support to Image.new. +- Added "I" and "RGBX" support to Image.new. -+ The plugin path is now properly prepended to the module search +- The plugin path is now properly prepended to the module search path when a plugin module is imported. -+ Added "draw" method to the ImageWin.Dib class. This is used by +- Added "draw" method to the ImageWin.Dib class. This is used by Topaz to print images on Windows printers. -+ "convert" now supports conversions from "P" to "1" and "F". +- "convert" now supports conversions from "P" to "1" and "F". -+ "paste" can now take a colour instead of an image as the first argument. +- "paste" can now take a colour instead of an image as the first argument. The colour must match the colour argument given to the new function, and match the mode of the target image. -+ Fixed "paste" to allow a mask also for mode "F" images. +- Fixed "paste" to allow a mask also for mode "F" images. -+ The BMP driver now saves mode "1" images. When loading images, the mode +- The BMP driver now saves mode "1" images. When loading images, the mode is set to "L" for 8-bit files with greyscale palettes, and to "P" for other 8-bit files. -+ The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). +- The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). -+ The JPEG and GIF drivers now saves "1" images. For JPEG, the image +- The JPEG and GIF drivers now saves "1" images. For JPEG, the image is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the image will be loaded as a "P" image. -+ Fixed a potential buffer overrun in the GIF encoder. +- Fixed a potential buffer overrun in the GIF encoder. 0.3a2 released -------------- The test suite includes 400 individual tests. -+ Improvements to the test suite revealed a number of minor bugs, which +- Improvements to the test suite revealed a number of minor bugs, which are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont are still weak spots in this release. -+ Added "putpalette" method to the Image class. You can use this +- Added "putpalette" method to the Image class. You can use this to add or modify the palette for "P" and "L" images. If a palette is added to an "L" image, it is automatically converted to a "P" image. -+ Fixed ImageDraw to properly handle 32-bit image memories +- Fixed ImageDraw to properly handle 32-bit image memories ("RGB", "RGBA", "CMYK", "F") -+ Fixed "fromstring" and "tostring" not to mess up the mode attribute +- Fixed "fromstring" and "tostring" not to mess up the mode attribute in default mode. -+ Changed ImPlatform.h to work on CRAY's (don't have one at home, so I +- Changed ImPlatform.h to work on CRAY's (don't have one at home, so I haven't tried it). The previous version assumed that either "short" or "int" were 16-bit wide. PIL still won't compile on platforms where neither "short", "int" nor "long" are 32-bit wide. -+ Added file= and data= keyword arguments to PhotoImage and BitmapImage. +- Added file= and data= keyword arguments to PhotoImage and BitmapImage. This allows you to use them as drop-in replacements for the corre- sponding Tkinter classes. -+ Removed bogus references to the crack coder (ImagingCrack). +- Removed bogus references to the crack coder (ImagingCrack). 0.3a1 released -------------- -+ Make sure image is loaded in "tostring". +- Make sure image is loaded in "tostring". -+ Added floating point packer (native 32-bit floats only). +- Added floating point packer (native 32-bit floats only). 0.1b1 to 0.2 (b5) ------------ -+ Modified "fromstring" and "tostring" methods to use file codecs. +- Modified "fromstring" and "tostring" methods to use file codecs. Also added "fromstring" factory method to create an image directly from data in a string. -+ Added support for 32-bit floating point images (mode "F"). You +- Added support for 32-bit floating point images (mode "F"). You can convert between "L" and "F" images, and apply a subset of the available image processing methods on the "F" image. You can also read virtually any data format into a floating point image memory; @@ -6164,141 +6164,141 @@ for more information. 0.2b5 released; on windows only ------------------------------- -+ Fixed the tobitmap() method to work properly for small bitmaps. +- Fixed the tobitmap() method to work properly for small bitmaps. -+ Added RMS and standard deviation to the ImageStat.Stat class. Also +- Added RMS and standard deviation to the ImageStat.Stat class. Also modified the constructor to take an optional feature mask, and also to accept either an image or a list containing the histogram data. -+ The BitmapImage code in ImageTk can now use a special bitmap +- The BitmapImage code in ImageTk can now use a special bitmap decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" file for details. If not installed, bitmaps are transferred to Tk as XBM strings. -+ The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") +- The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") instead of a special image type. This gives somewhat better performance, and also allows PIL to support transparency. *** WARNING: TKAPPINIT MUST BE MODIFIED *** -+ ImageTk now honours the alpha layer in RGBA images. Only fully +- ImageTk now honours the alpha layer in RGBA images. Only fully transparent pixels are made transparent (that is, the alpha layer is treated as a mask). To treat the alpha laters as a matte, you must paste the image on the background before handing it over to ImageTk. -+ Added McIdas reader (supports 8-bit images only). +- Added McIdas reader (supports 8-bit images only). -+ PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As +- PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As long as you only load and save these formats, you don't have to wait for a full scan for drivers. To force scanning, call the Image.init() function. -+ The "seek" and "tell" methods are now always available, also for +- The "seek" and "tell" methods are now always available, also for single-frame images. -+ Added optional mask argument to histogram method. The mask may +- Added optional mask argument to histogram method. The mask may be an "1" or "L" image with the same size as the original image. Only pixels where the mask is non-zero are included in the histogram. -+ The "paste" method now allows you to specify only the lower left +- The "paste" method now allows you to specify only the lower left corner (a 2-tuple), instead of the full region (a 4-tuple). -+ Reverted to old plugin scanning model; now scans all directory +- Reverted to old plugin scanning model; now scans all directory names in the path when looking for plugins. -+ Added PIXAR raster support. Only uncompressed ("dumped") RGB +- Added PIXAR raster support. Only uncompressed ("dumped") RGB images can currently be read (based on information provided by Greg Coats). -+ Added FlashPix (FPX) read support. Reads all pixel formats, but +- Added FlashPix (FPX) read support. Reads all pixel formats, but only the highest resolution is read, and the viewing transform is currently ignored. -+ Made PNG encoding somewhat more efficient in "optimize" mode; a +- Made PNG encoding somewhat more efficient in "optimize" mode; a bug in 0.2b4 didn't enable all predictor filters when optimized storage were requested. -+ Added Microsoft Image Composer (MIC) read support. When opened, +- Added Microsoft Image Composer (MIC) read support. When opened, the first sprite in the file is loaded. You can use the seek method to load additional sprites from the file. -+ Properly reads "P" and "CMYK" PSD images. +- Properly reads "P" and "CMYK" PSD images. -+ "pilconvert" no longer optimizes by default; use the -o option to +- "pilconvert" no longer optimizes by default; use the -o option to make the file as small as possible (at the expense of speed); use the -q option to set the quality when compressing to JPEG. -+ Fixed "crop" not to drop the palette for "P" images. +- Fixed "crop" not to drop the palette for "P" images. -+ Added and verified FLC support. +- Added and verified FLC support. -+ Paste with "L" or "RGBA" alpha is now several times faster on most +- Paste with "L" or "RGBA" alpha is now several times faster on most platforms. -+ Changed Image.new() to initialize the image to black, as described +- Changed Image.new() to initialize the image to black, as described in the handbook. To get an uninitialized image, use None as the colour. -+ Fixed the PDF encoder to produce a valid header; Acrobat no longer +- Fixed the PDF encoder to produce a valid header; Acrobat no longer complains when you load PDF images created by PIL. -+ PIL only scans fully-qualified directory names in the path when +- PIL only scans fully-qualified directory names in the path when looking for plugins. *** WARNING: MAY BREAK EXISTING CODE *** -+ Faster implementation of "save" used when filename is given, +- Faster implementation of "save" used when filename is given, or when file object has "fileno" and "flush" methods. -+ Don't crash in "crop" if region extends outside the source image. +- Don't crash in "crop" if region extends outside the source image. -+ Eliminated a massive memory leak in the "save" function. +- Eliminated a massive memory leak in the "save" function. -+ The GIF decoder doesn't crash if the code size is set to an illegal +- The GIF decoder doesn't crash if the code size is set to an illegal value. This could happen since another bug didn't handle local palettes properly if they didn't have the same size as the global palette (not very common). -+ Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. +- Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. -+ Fixed palette and padding problems in BMP driver. Now properly +- Fixed palette and padding problems in BMP driver. Now properly writes "1", "L", "P" and "RGB" images. -+ Fixed getpixel()/getdata() to return correct pixel values. +- Fixed getpixel()/getdata() to return correct pixel values. -+ Added PSD (PhotoShop) read support. Reads both uncompressed +- Added PSD (PhotoShop) read support. Reads both uncompressed and compressed images of most types. -+ Added GIF write support (writes "uncompressed" GIF files only, +- Added GIF write support (writes "uncompressed" GIF files only, due to unresolvable licensing issues). The "gifmaker.py" script can be used to create GIF animations. -+ Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" +- Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" images. -+ Added FLI read support. This driver has only been tested +- Added FLI read support. This driver has only been tested on a few FLI samples. -+ Reads 2-bit and 4-bit PCX images. +- Reads 2-bit and 4-bit PCX images. -+ Added MSP read and write support. Both version 1 and 2 can be +- Added MSP read and write support. Both version 1 and 2 can be read, but only version 1 (uncompressed) files are written. -+ Fixed a bug in the FLI/FLC identification code that caused the +- Fixed a bug in the FLI/FLC identification code that caused the driver to raise an exception when parsing valid FLI/FLC files. -+ Improved performance when loading file format plugins, and when +- Improved performance when loading file format plugins, and when opening files. -+ Added GIF animation support, via the "seek" and "tell" methods. +- Added GIF animation support, via the "seek" and "tell" methods. You can use "player.py" to play an animated GIF file. -+ Removed MNG support, since the spec is changing faster than I +- Removed MNG support, since the spec is changing faster than I can change the code. I've added support for the experimental ARG format instead. Contact me for more information on this format. -+ Added keyword options to the "save" method. The following options +- Added keyword options to the "save" method. The following options are currently supported: Format Option Description @@ -6322,20 +6322,20 @@ are currently supported: Expect more options in future releases. Also note that file writers silently ignore unknown options. -+ Plugged memory leaks in the PNG and TIFF decoders. +- Plugged memory leaks in the PNG and TIFF decoders. -+ Added PNG write support. +- Added PNG write support. -+ (internal) RGB unpackers and converters now set the pad byte +- (internal) RGB unpackers and converters now set the pad byte to 255 (full opacity). -+ Properly handles the "transparency" property for GIF, PNG +- Properly handles the "transparency" property for GIF, PNG and XPM files. -+ Added a "putalpha" method, allowing you to attach a "1" or "L" +- Added a "putalpha" method, allowing you to attach a "1" or "L" image as the alpha layer to an "RGBA" image. -+ Various improvements to the sample scripts: +- Various improvements to the sample scripts: "pilconvert" Carries out some extra tricks in order to make the resulting file as small as possible. @@ -6364,16 +6364,16 @@ image as the alpha layer to an "RGBA" image. and "RGBA") are superimposed on the standard Tk back- ground. -+ Fixed colour argument to "new". For multilayer images, pass a +- Fixed colour argument to "new". For multilayer images, pass a tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, Magenta, Yellow, Black). -+ Added XPM (X pixmap) read support. +- Added XPM (X pixmap) read support. 0.2b3 released -------------- -+ Added MNG (multi-image network graphics) read support. "Ming" +- Added MNG (multi-image network graphics) read support. "Ming" is a proposed animation standard, based on the PNG file format. You can use the "player" sample script to display some flavours @@ -6381,224 +6381,224 @@ Magenta, Yellow, Black). as is this driver. More information, including sample files, can be found at -+ Added a "verify" method to images loaded from file. This method +- Added a "verify" method to images loaded from file. This method scans the file for errors, without actually decoding the image data, and raises a suitable exception if it finds any problems. Currently implemented for PNG and MNG files only. -+ Added support for interlaced GIF images. +- Added support for interlaced GIF images. -+ Added PNG read support -- if linked with the ZLIB compression library, +- Added PNG read support -- if linked with the ZLIB compression library, PIL reads all kinds of PNG images, except interlaced files. -+ Improved PNG identification support -- doesn't mess up on unknown +- Improved PNG identification support -- doesn't mess up on unknown chunks, identifies all possible PNG modes, and verifies checksum on PNG header chunks. -+ Added an experimental reader for placable Windows Meta Files (WMF). +- Added an experimental reader for placable Windows Meta Files (WMF). This reader is still very incomplete, but it illustrates how PIL's drawing capabilities can be used to render vector and metafile formats. -+ Added restricted drivers for images from Image Tools (greyscale +- Added restricted drivers for images from Image Tools (greyscale only) and LabEye/IFUNC (common interchange modes only). -+ Some minor improvements to the sample scripts provided in the +- Some minor improvements to the sample scripts provided in the "Scripts" directory. -+ The test images have been moved to the "Images" directory. +- The test images have been moved to the "Images" directory. 0.2b2, 0.2b1 released; Windows only ----------------------------------- -+ Fixed filling of complex polygons. The ImageDraw "line" and +- Fixed filling of complex polygons. The ImageDraw "line" and "polygon" methods also accept Path objects. -+ The ImageTk "PhotoImage" object can now be constructed directly +- The ImageTk "PhotoImage" object can now be constructed directly from an image. You can also pass the object itself to Tkinter, instead of using the "image" attribute. Finally, using "paste" on a displayed image automatically updates the display. -+ The ImageTk "BitmapImage" object allows you to create transparent +- The ImageTk "BitmapImage" object allows you to create transparent overlays from 1-bit images. You can pass the object itself to Tkinter. The constructor takes the same arguments as the Tkinter BitmapImage class; use the "foreground" option to set the colour of the overlay. -+ Added a "putdata" method to the Image class. This can be used to +- Added a "putdata" method to the Image class. This can be used to load a 1-layer image with data from a sequence object or a string. An optional floating point scale and offset can be used to adjust the data to fit into the 8-bit pixel range. Also see the "getdata" method. -+ Added the EXTENT method to the Image "transform" method. This can +- Added the EXTENT method to the Image "transform" method. This can be used to quickly crop, stretch, shrink, or mirror a subregion from another image. -+ Adapted to Python 1.4. +- Adapted to Python 1.4. -+ Added a project makefile for Visual C++ 4.x. This allows you to +- Added a project makefile for Visual C++ 4.x. This allows you to easily build a dynamically linked version of PIL for Windows 95 and NT. -+ A Tk "booster" patch for Windows is available. It gives dramatic +- A Tk "booster" patch for Windows is available. It gives dramatic performance improvements for some displays. Has been tested with Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk subdirectory for details. -+ You can now save 1-bit images in the XBM format. In addition, the +- You can now save 1-bit images in the XBM format. In addition, the Image class now provides a "tobitmap" method which returns a string containing an XBM representation of the image. Quite handy to use with Tk. -+ More conversions, including "RGB" to "1" and more. +- More conversions, including "RGB" to "1" and more. 0.2a1 released -------------- -+ Where earlier versions accepted lists, this version accepts arbitrary +- Where earlier versions accepted lists, this version accepts arbitrary Python sequences (including strings, in some cases). A few resource leaks were plugged in the process. -+ The Image "paste" method now allows the box to extend outside +- The Image "paste" method now allows the box to extend outside the target image. The size of the box, the image to be pasted, and the optional mask must still match. -+ The ImageDraw module now supports filled polygons, outlined and +- The ImageDraw module now supports filled polygons, outlined and filled ellipses, and text. Font support is rudimentary, though. -+ The Image "point" method now takes an optional mode argument, +- The Image "point" method now takes an optional mode argument, allowing you to convert the image while translating it. Currently, this can only be used to convert "L" or "P" images to "1" images (creating thresholded images or "matte" masks). -+ An Image "getpixel" method has been added. For single band images, +- An Image "getpixel" method has been added. For single band images, it returns the pixel value at a given position as an integer. For n-band images, it returns an n-tuple of integers. -+ An Image "getdata" method has been added. It returns a sequence +- An Image "getdata" method has been added. It returns a sequence object representing the image as a 1-dimensional array. Only len() and [] can be used with this sequence. This method returns a reference to the existing image data, so changes in the image will be immediately reflected in the sequence object. -+ Fixed alignment problems in the Windows BMP writer. +- Fixed alignment problems in the Windows BMP writer. -+ If converting an "RGB" image to "RGB" or "L", you can give a second +- If converting an "RGB" image to "RGB" or "L", you can give a second argument containing a colour conversion matrix. -+ An Image "getbbox" method has been added. It returns the bounding +- An Image "getbbox" method has been added. It returns the bounding box of data in an image, considering the value 0 as background. -+ An Image "offset" method has been added. It returns a new image +- An Image "offset" method has been added. It returns a new image where the contents of the image have been offset the given distance in X and/or Y direction. Data wraps between edges. -+ Saves PDF images. The driver creates a binary PDF 1.1 files, using +- Saves PDF images. The driver creates a binary PDF 1.1 files, using JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding (same as for PostScript) for other formats. -+ The "paste" method now accepts "1" masks. Zero means transparent, +- The "paste" method now accepts "1" masks. Zero means transparent, any other pixel value means opaque. This is faster than using an "L" transparency mask. -+ Properly writes EPS files (and properly prints images to PostScript +- Properly writes EPS files (and properly prints images to PostScript printers as well). -+ Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR +- Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR files. Cursor animations are not supported. -+ Fixed alignment problems in the Sun raster loader. +- Fixed alignment problems in the Sun raster loader. -+ Added "draft" and "thumbnail" methods. The draft method is used +- Added "draft" and "thumbnail" methods. The draft method is used to optimize loading of JPEG and PCD files, the thumbnail method is used to create a thumbnail representation of an image. -+ Added Windows display support, via the ImageWin class (see the +- Added Windows display support, via the ImageWin class (see the handbook for details). -+ Added raster conversion for EPS files. This requires GNU or Aladdin +- Added raster conversion for EPS files. This requires GNU or Aladdin Ghostscript, and probably works on UNIX only. -+ Reads PhotoCD (PCD) images. The base resolution (768x512) can be +- Reads PhotoCD (PCD) images. The base resolution (768x512) can be read from a PhotoCD file. -+ Eliminated some compiler warnings. Bindings now compile cleanly in C++ +- Eliminated some compiler warnings. Bindings now compile cleanly in C++ mode. Note that the Imaging library itself must be compiled in C mode. -+ Added "bdf2pil.py", which converts BDF fonts into images with associated +- Added "bdf2pil.py", which converts BDF fonts into images with associated metrics. This is definitely work in progress. For info, see description in script for details. -+ Fixed a bug in the "ImageEnhance.py" module. +- Fixed a bug in the "ImageEnhance.py" module. -+ Fixed a bug in the netpbm save hack in "GifImagePlugin.py" +- Fixed a bug in the netpbm save hack in "GifImagePlugin.py" -+ Fixed 90 and 270 degree rotation of rectangular images. +- Fixed 90 and 270 degree rotation of rectangular images. -+ Properly reads 8-bit TIFF palette-color images. +- Properly reads 8-bit TIFF palette-color images. -+ Reads plane separated RGB and CMYK TIFF images. +- Reads plane separated RGB and CMYK TIFF images. -+ Added driver debug mode. This is enabled by setting Image.DEBUG +- Added driver debug mode. This is enabled by setting Image.DEBUG to a non-zero value. Try the -D option to "pilfile.py" and see what happens. -+ Don't crash on "atend" constructs in PostScript files. +- Don't crash on "atend" constructs in PostScript files. -+ Only the Image module imports _imaging directly. Other modules +- Only the Image module imports _imaging directly. Other modules should refer to the binding module as "Image.core". 0.0 to 0.1 (b1) ------------ -+ A handbook is available (distributed separately). +- A handbook is available (distributed separately). -+ The coordinate system is changed so that (0,0) is now located +- The coordinate system is changed so that (0,0) is now located in the upper left corner. This is in compliancy with ISO 12087 and 90% of all other image processing and graphics libraries. -+ Modes "1" (bilevel) and "P" (palette) have been introduced. Note +- Modes "1" (bilevel) and "P" (palette) have been introduced. Note that bilevel images are stored with one byte per pixel. -+ The Image "crop" and "paste" methods now accepts None as the +- The Image "crop" and "paste" methods now accepts None as the box argument, to refer to the full image (self, that is). -+ The Image "crop" method now works properly. +- The Image "crop" method now works properly. -+ The Image "point" method is now available. You can use either a +- The Image "point" method is now available. You can use either a lookup table or a function taking one argument. -+ The Image join function has been renamed to "merge". +- The Image join function has been renamed to "merge". -+ An Image "composite" function has been added. It is identical +- An Image "composite" function has been added. It is identical to copy() followed by paste(mask). -+ An Image "eval" function has been added. It is currently identical +- An Image "eval" function has been added. It is currently identical to point(function); that is, only a single image can be processed. -+ A set of channel operations has been added. See the "ImageChops" +- A set of channel operations has been added. See the "ImageChops" module, test_chops.py, and the handbook for details. -+ Added the "pilconvert" utility, which converts image files. Note +- Added the "pilconvert" utility, which converts image files. Note that the number of output formats are still quite restricted. -+ Added the "pilfile" utility, which quickly identifies image files +- Added the "pilfile" utility, which quickly identifies image files (without loading them, in most cases). -+ Added the "pilprint" utility, which prints image files to PostScript +- Added the "pilprint" utility, which prints image files to PostScript printers. -+ Added a rudimentary version of the "pilview" utility, which is +- Added a rudimentary version of the "pilview" utility, which is simple image viewer based on Tk. Only File/Exit and Image/Next works properly. -+ An interface to Tk has been added. See "Lib/ImageTk.py" and README +- An interface to Tk has been added. See "Lib/ImageTk.py" and README for details. -+ An interface to Jack Jansen's Img library has been added (thanks to +- An interface to Jack Jansen's Img library has been added (thanks to Jack). This allows you to read images through the Img extensions file format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. -+ PostScript printing is provided through the PSDraw module. See the +- PostScript printing is provided through the PSDraw module. See the handbook for details. From 39e689c0904811e101587a0f724d2d9b9145b3b7 Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:07:12 +0430 Subject: [PATCH 077/294] CHANGES: remove unreleased from end of subtitles --- CHANGES.rst | 80 ++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6ef0c0ec8..15d63d6b1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4838,7 +4838,7 @@ was last updated, see the repository revision history: loop extension is present, instead of always setting it to 1 (from Valentino Volonghi). -1.1.7c1 released +1.1.7c1 ---------------- - Improved PNG compression (from Alexey Borzenkov). @@ -5041,14 +5041,14 @@ was last updated, see the repository revision history: - Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). -1.1.6 released +1.1.6 -------------- - Fixed some 64-bit compatibility warnings for Python 2.5. - Added threading support for the Sane driver (from Abel Deuring). -1.1.6b2 released +1.1.6b2 ---------------- - Added experimental "floodfill" function to the ImageDraw module @@ -5084,7 +5084,7 @@ was last updated, see the repository revision history: "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon for samples, debugging, and patches). -1.1.6b1 released +1.1.6b1 ---------------- - Added 'expand' option to the Image 'rotate' method. If true, the @@ -5105,7 +5105,7 @@ was last updated, see the repository revision history: and "LA" modes. Added 'getmodebandnames' helper that return the band names. -1.1.6a2 released +1.1.6a2 ---------------- - Added float/double support to the TIFF loader (from Russell @@ -5121,7 +5121,7 @@ was last updated, see the repository revision history: - Added support for duplex scanning to the Sane interface (Abel Deuring). -1.1.6a1 released +1.1.6a1 ---------------- - Fixed a memory leak in "convert(mode)", when converting from @@ -5154,7 +5154,7 @@ was last updated, see the repository revision history: - Fixed line width calculation. -1.1.6a0 released +1.1.6a0 ---------------- - Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). @@ -5173,13 +5173,13 @@ was last updated, see the repository revision history: - Fixed potential integer division in PSDraw.image (from Eric Etheridge). -1.1.5c2 and 1.1.5 final released +1.1.5c2 and 1.1.5 final -------------------------------- - Added experimental PERSPECTIVE transform method (from Jeff Breiden- bach). -1.1.5c1 released +1.1.5c1 ---------------- - Make sure "thumbnail" never generates zero-wide or zero-high images @@ -5192,7 +5192,7 @@ was last updated, see the repository revision history: round-tripping white source pixels (reported by Henryk Gerlach and Jeff Epler). -1.1.5b3 released +1.1.5b3 ---------------- - Don't crash in "quantize" method if the number of colors requested @@ -5202,7 +5202,7 @@ was last updated, see the repository revision history: - Added WBMP read/write support (based on code by Duncan Booth). -1.1.5b2 released +1.1.5b2 ---------------- - Added DPI read/write support to the PNG codec. The decoder sets @@ -5223,7 +5223,7 @@ was last updated, see the repository revision history: comments in PIL/SpiderImagePlugin.py for more information on this format. -1.1.5b1 released +1.1.5b1 ---------------- - Added new Sane release (from Ralph Heinkel). See the Sane/README @@ -5244,7 +5244,7 @@ was last updated, see the repository revision history: tolist() or tolist(0) returns a list of 2-tuples, as before. tolist(1) returns a flattened list instead. -1.1.5a5 released +1.1.5a5 ---------------- - Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". @@ -5262,7 +5262,7 @@ was last updated, see the repository revision history: FreeType2 and Mac OS X framework libraries, and more. Many thanks to everyone who helped test the new "setup.py" script! -1.1.5a4 released +1.1.5a4 ---------------- - The "save" method now looks for a file format driver before @@ -5299,7 +5299,7 @@ was last updated, see the repository revision history: module can still be built with earlier versions; see comments in _imagingft.c for details. -1.1.5a3 released +1.1.5a3 ---------------- - Added 'getim' method, which returns a PyCObject wrapping an @@ -5314,7 +5314,7 @@ was last updated, see the repository revision history: - Fixed division by zero "equalize" on very small images (from Douglas Bagnall). -1.1.5a2 released +1.1.5a2 ---------------- - The "paste" method now supports the alternative "paste(im, mask)" @@ -5344,7 +5344,7 @@ was last updated, see the repository revision history: http://effbot.org/downloads#pilwmf -1.1.5a1 released +1.1.5a1 ---------------- - Replaced broken WMF driver with a WMF stub plugin (see below). @@ -5430,12 +5430,12 @@ was last updated, see the repository revision history: "L", "P" and "RGB" images with non-standard FillOrder (based on input from Jeff Breidenbach). -1.1.4 final released +1.1.4 final -------------------- - Fixed ImageTk build problem on Unix. -1.1.4b2 released +1.1.4b2 ---------------- - Improved building on Mac OS X (from Jack Jansen). @@ -5448,7 +5448,7 @@ was last updated, see the repository revision history: - Added embedded default font to the ImageFont module (currently an 8-pixel Courier font, taken from the X window distribution). -1.1.4b1 released +1.1.4b1 ---------------- - Added experimental EXIF support for JPEG files. To extract EXIF @@ -5474,7 +5474,7 @@ was last updated, see the repository revision history: loader, import WalImageFile and call the "open" method in that module. -1.1.4a4 released +1.1.4a4 ---------------- - Added updated SANE driver (Andrew Kuchling, Abel Deuring) @@ -5507,7 +5507,7 @@ was last updated, see the repository revision history: an ImageWin.HWND object, and pass in this object instead of the device context. -1.1.4a2 released +1.1.4a2 ---------------- - Improved support for 16-bit unsigned integer images (mode "I;16"). @@ -5526,7 +5526,7 @@ was last updated, see the repository revision history: The "grabclipboard" function returns an Image object, a list of filenames (not in 1.1.4), or None if neither was found. -1.1.4a1 released +1.1.4a1 ---------------- - Improved support for drawing RGB data in palette images. You can @@ -5576,13 +5576,13 @@ was last updated, see the repository revision history: - Improved decoder error messages. -1.1.3 final released +1.1.3 final ------------------- - Made setup.py look for old versions of zlib. For some back- ground, see: https://zlib.net/advisory-2002-03-11.txt -1.1.3c2 released +1.1.3c2 ----------------- - Added setup.py file (tested on Unix and Windows). You still @@ -5595,7 +5595,7 @@ was last updated, see the repository revision history: - Fixed segmentation violation in ANTIALIAS filter (an internal buffer wasn't properly allocated). -1.1.3c1 released +1.1.3c1 ---------------- - Added ANTIALIAS downsampling filter for high-quality "resize" @@ -5672,7 +5672,7 @@ was last updated, see the repository revision history: Note that PIL doesn't always preserve the "info" attribute for normal image operations. -1.1.2c1 and 1.1.2 final released +1.1.2c1 and 1.1.2 final ---------------- - Adapted to Python 2.1. Among other things, all uses of the @@ -5716,7 +5716,7 @@ was last updated, see the repository revision history: - Changed ImageChops operators to copy palette and info dictionary from the first image argument. -1.1.1 released +1.1.1 -------------- - Additional fixes for Python 1.6/2.0, including TIFF "save" bug. @@ -5747,7 +5747,7 @@ was last updated, see the repository revision history: - Tweaks to make drawing work on big-endian platforms. -1.0c2 released +1.0c2 -------------- - If PIL is built with the WITH_TKINTER flag, ImageTk can @@ -5764,7 +5764,7 @@ was last updated, see the repository revision history: - ImageFont "load_path" now scans directory mentioned in .pth files (from Richard Jones). -1.0c1 released +1.0c1 -------------- - The TIFF plugin has been rewritten. The new plugin fully @@ -5779,7 +5779,7 @@ was last updated, see the repository revision history: appropriate), rather than writing weird things to disk for "xv" to choke upon. (bug reported by Les Schaffer). -1.0b2 released +1.0b2 -------------- - Major speedups for rotate, transform(EXTENT), and transform(AFFINE) @@ -5856,7 +5856,7 @@ was last updated, see the repository revision history: - Fixed -f option to "pilconvert.py" (from Anthony Baxter) -1.0b1 released +1.0b1 -------------- - Added Toby J. Sargeant's quantization package. To enable @@ -5931,7 +5931,7 @@ was last updated, see the repository revision history: "paste" (and other methods that modify an image in place) on a newly opened file. -0.3b2 released +0.3b2 -------------- The test suite includes 825 individual tests. @@ -5983,7 +5983,7 @@ The test suite includes 825 individual tests. to "YCbCr" (!) *** WARNING: MAY BREAK EXISTING CODE *** -0.3b1 released +0.3b1 -------------- The test suite includes 750 individual tests. @@ -6047,7 +6047,7 @@ The test suite includes 750 individual tests. - Various minor fixes. -0.3a4 released +0.3a4 -------------- - Added experimental IPTC/NAA support. @@ -6068,7 +6068,7 @@ The test suite includes 750 individual tests. - Fixed "fromstring" and "tostring" for mode "I" images. -0.3a3 released +0.3a3 -------------- The test suite includes 530 individual tests. @@ -6109,7 +6109,7 @@ The test suite includes 530 individual tests. - Fixed a potential buffer overrun in the GIF encoder. -0.3a2 released +0.3a2 -------------- The test suite includes 400 individual tests. @@ -6140,7 +6140,7 @@ The test suite includes 400 individual tests. - Removed bogus references to the crack coder (ImagingCrack). -0.3a1 released +0.3a1 -------------- - Make sure image is loaded in "tostring". @@ -6370,7 +6370,7 @@ Magenta, Yellow, Black). - Added XPM (X pixmap) read support. -0.2b3 released +0.2b3 -------------- - Added MNG (multi-image network graphics) read support. "Ming" @@ -6453,7 +6453,7 @@ Magenta, Yellow, Black). - More conversions, including "RGB" to "1" and more. -0.2a1 released +0.2a1 -------------- - Where earlier versions accepted lists, this version accepts arbitrary From 261a24752c96709aa05482ca145c9e8075db57b3 Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:13:59 +0430 Subject: [PATCH 078/294] Fix rst error: :3150: (WARNING/2) Inline emphasis start-string without end-string. --- CHANGES.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 15d63d6b1..1786c6d6d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1527,7 +1527,6 @@ Changelog (Pillow) - Deprecate PIL.*ImagePlugin.__version__ attributes #3628 [jdufresne] - - Docs: Add note about ImageDraw operations that exceed image bounds #3620 [radarhere] @@ -3139,7 +3138,6 @@ Changelog (Pillow) - Changed depends/install_*.sh urls to point to github pillow-depends repo #1983 [wiredfool] - - Allow ICC profile from ``encoderinfo`` while saving PNGs #1909 [homm] @@ -3149,7 +3147,7 @@ Changelog (Pillow) - Change function declaration to match Tcl_CmdProc type #1966 [homm] -- Integer overflow checks on all calls to *alloc #1781 +- Integer overflow checks on all calls to alloc #1781 [wiredfool] - Change equals method on Image so it short circuits #1967 From d8a69ebf7514a1a28815fdc64f750409f4e6e1c5 Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:17:44 +0430 Subject: [PATCH 079/294] Fix errors --- CHANGES.rst | 115 +++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 82 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1786c6d6d..792aa6561 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4989,8 +4989,7 @@ was last updated, see the repository revision history: Florian writes: It's a beta, so still needs some testing, but should allow you to: - - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. - Existing code doesn't need to be changed. + - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. Existing code doesn't need to be changed. - access embedded profiles in JPEG, PNG, PSD, TIFF. It also includes patches for TIFF to retain IPTC, Photoshop and XMP @@ -5488,7 +5487,7 @@ was last updated, see the repository revision history: - Ignore Unicode characters in the BDF loader (from Graham Dumpleton) 1.1.4a3 released; windows only ------------------------------ +--------------------------------- - Added experimental RGBA-on-RGB drawing support. To use RGBA colours on an RGB image, pass "RGBA" as the second string to @@ -5671,7 +5670,7 @@ was last updated, see the repository revision history: for normal image operations. 1.1.2c1 and 1.1.2 final ----------------- +------------------------ - Adapted to Python 2.1. Among other things, all uses of the "regex" module have been replaced with "re". @@ -5737,7 +5736,7 @@ was last updated, see the repository revision history: extension module, named "_imagingtk". 0.3b2 to 1.0 final ------- +------------------- - If there's no 16-bit integer (like on a Cray T3E), set INT16 to the smallest integer available. Most of the @@ -5746,7 +5745,7 @@ was last updated, see the repository revision history: - Tweaks to make drawing work on big-endian platforms. 1.0c2 --------------- +------- - If PIL is built with the WITH_TKINTER flag, ImageTk can automatically hook into a standard Tkinter build. You @@ -6146,116 +6145,72 @@ The test suite includes 400 individual tests. - Added floating point packer (native 32-bit floats only). 0.1b1 to 0.2 (b5) ------------- +------------------ -- Modified "fromstring" and "tostring" methods to use file codecs. -Also added "fromstring" factory method to create an image directly -from data in a string. +- Modified "fromstring" and "tostring" methods to use file codecs. Also added "fromstring" factory method to create an image directly from data in a string. -- Added support for 32-bit floating point images (mode "F"). You -can convert between "L" and "F" images, and apply a subset of the -available image processing methods on the "F" image. You can also -read virtually any data format into a floating point image memory; -see the section on "Decoding Floating Point Data" in the handbook -for more information. +- Added support for 32-bit floating point images (mode "F"). You can convert between "L" and "F" images, and apply a subset of the available image processing methods on the "F" image. You can also read virtually any data format into a floating point image memory; see the section on "Decoding Floating Point Data" in the handbook for more information. 0.2b5 released; on windows only ------------------------------- - Fixed the tobitmap() method to work properly for small bitmaps. -- Added RMS and standard deviation to the ImageStat.Stat class. Also -modified the constructor to take an optional feature mask, and also -to accept either an image or a list containing the histogram data. +- Added RMS and standard deviation to the ImageStat.Stat class. Also modified the constructor to take an optional feature mask, and also to accept either an image or a list containing the histogram data. -- The BitmapImage code in ImageTk can now use a special bitmap -decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" -file for details. If not installed, bitmaps are transferred to Tk as -XBM strings. +- The BitmapImage code in ImageTk can now use a special bitmap decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" file for details. If not installed, bitmaps are transferred to Tk as XBM strings. + +- The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") instead of a special image type. This gives somewhat better performance, and also allows PIL to support transparency. -- The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") -instead of a special image type. This gives somewhat better performance, -and also allows PIL to support transparency. *** WARNING: TKAPPINIT MUST BE MODIFIED *** -- ImageTk now honours the alpha layer in RGBA images. Only fully -transparent pixels are made transparent (that is, the alpha layer -is treated as a mask). To treat the alpha laters as a matte, you -must paste the image on the background before handing it over to -ImageTk. +- ImageTk now honours the alpha layer in RGBA images. Only fully transparent pixels are made transparent (that is, the alpha layer is treated as a mask). To treat the alpha laters as a matte, you must paste the image on the background before handing it over to ImageTk. - Added McIdas reader (supports 8-bit images only). -- PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As -long as you only load and save these formats, you don't have to -wait for a full scan for drivers. To force scanning, call the -Image.init() function. +- PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As long as you only load and save these formats, you don't have to wait for a full scan for drivers. To force scanning, call the Image.init() function. -- The "seek" and "tell" methods are now always available, also for -single-frame images. +- The "seek" and "tell" methods are now always available, also for single-frame images. -- Added optional mask argument to histogram method. The mask may -be an "1" or "L" image with the same size as the original image. -Only pixels where the mask is non-zero are included in the -histogram. +- Added optional mask argument to histogram method. The mask may be an "1" or "L" image with the same size as the original image. Only pixels where the mask is non-zero are included in the histogram. -- The "paste" method now allows you to specify only the lower left -corner (a 2-tuple), instead of the full region (a 4-tuple). +- The "paste" method now allows you to specify only the lower left corner (a 2-tuple), instead of the full region (a 4-tuple). -- Reverted to old plugin scanning model; now scans all directory -names in the path when looking for plugins. +- Reverted to old plugin scanning model; now scans all directory names in the path when looking for plugins. -- Added PIXAR raster support. Only uncompressed ("dumped") RGB -images can currently be read (based on information provided -by Greg Coats). +- Added PIXAR raster support. Only uncompressed ("dumped") RGB images can currently be read (based on information provided by Greg Coats). -- Added FlashPix (FPX) read support. Reads all pixel formats, but -only the highest resolution is read, and the viewing transform is -currently ignored. +- Added FlashPix (FPX) read support. Reads all pixel formats, but only the highest resolution is read, and the viewing transform is currently ignored. -- Made PNG encoding somewhat more efficient in "optimize" mode; a -bug in 0.2b4 didn't enable all predictor filters when optimized -storage were requested. +- Made PNG encoding somewhat more efficient in "optimize" mode; a bug in 0.2b4 didn't enable all predictor filters when optimized storage were requested. -- Added Microsoft Image Composer (MIC) read support. When opened, -the first sprite in the file is loaded. You can use the seek method -to load additional sprites from the file. +- Added Microsoft Image Composer (MIC) read support. When opened, the first sprite in the file is loaded. You can use the seek method to load additional sprites from the file. - Properly reads "P" and "CMYK" PSD images. -- "pilconvert" no longer optimizes by default; use the -o option to -make the file as small as possible (at the expense of speed); use -the -q option to set the quality when compressing to JPEG. +- "pilconvert" no longer optimizes by default; use the -o option to make the file as small as possible (at the expense of speed); use the -q option to set the quality when compressing to JPEG. - Fixed "crop" not to drop the palette for "P" images. - Added and verified FLC support. -- Paste with "L" or "RGBA" alpha is now several times faster on most -platforms. +- Paste with "L" or "RGBA" alpha is now several times faster on most platforms. -- Changed Image.new() to initialize the image to black, as described -in the handbook. To get an uninitialized image, use None as the -colour. +- Changed Image.new() to initialize the image to black, as described in the handbook. To get an uninitialized image, use None as the colour. -- Fixed the PDF encoder to produce a valid header; Acrobat no longer -complains when you load PDF images created by PIL. +- Fixed the PDF encoder to produce a valid header; Acrobat no longer complains when you load PDF images created by PIL. + +- PIL only scans fully-qualified directory names in the path when looking for plugins. -- PIL only scans fully-qualified directory names in the path when -looking for plugins. *** WARNING: MAY BREAK EXISTING CODE *** -- Faster implementation of "save" used when filename is given, -or when file object has "fileno" and "flush" methods. +- Faster implementation of "save" used when filename is given, or when file object has "fileno" and "flush" methods. - Don't crash in "crop" if region extends outside the source image. - Eliminated a massive memory leak in the "save" function. -- The GIF decoder doesn't crash if the code size is set to an illegal -value. This could happen since another bug didn't handle local -palettes properly if they didn't have the same size as the -global palette (not very common). +- The GIF decoder doesn't crash if the code size is set to an illegal value. This could happen since another bug didn't handle local palettes properly if they didn't have the same size as the global palette (not very common). - Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. @@ -6264,18 +6219,14 @@ writes "1", "L", "P" and "RGB" images. - Fixed getpixel()/getdata() to return correct pixel values. -- Added PSD (PhotoShop) read support. Reads both uncompressed -and compressed images of most types. +- Added PSD (PhotoShop) read support. Reads both uncompressed and compressed images of most types. -- Added GIF write support (writes "uncompressed" GIF files only, -due to unresolvable licensing issues). The "gifmaker.py" script -can be used to create GIF animations. +- Added GIF write support (writes "uncompressed" GIF files only, due to unresolvable licensing issues). The "gifmaker.py" script can be used to create GIF animations. - Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" images. -- Added FLI read support. This driver has only been tested -on a few FLI samples. +- Added FLI read support. This driver has only been tested on a few FLI samples. - Reads 2-bit and 4-bit PCX images. From e03120a474aa9c4d3e7e009278b104ee3b28d22f Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:26:08 +0430 Subject: [PATCH 080/294] errors --- CHANGES.rst | 519 ++++++++++++++++------------------------------------ 1 file changed, 155 insertions(+), 364 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 792aa6561..7607779d0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5704,22 +5704,18 @@ was last updated, see the repository revision history: change, any attempt to modify a memory-mapped image resulted in a core dump...) -- Added special cases for lists everywhere PIL expects a sequence. - This should speed up things like "putdata" and drawing operations. +- Added special cases for lists everywhere PIL expects a sequence. This should speed up things like "putdata" and drawing operations. -- The Image.offset method is deprecated. Use the ImageChops.offset - function instead. +- The Image.offset method is deprecated. Use the ImageChops.offset function instead. -- Changed ImageChops operators to copy palette and info dictionary - from the first image argument. +- Changed ImageChops operators to copy palette and info dictionary from the first image argument. 1.1.1 -------------- - Additional fixes for Python 1.6/2.0, including TIFF "save" bug. -- Changed "init" to properly load plugins when PIL is used as a - package. +- Changed "init" to properly load plugins when PIL is used as a package. - Fixed broken "show" method (on Unix) @@ -5728,38 +5724,27 @@ was last updated, see the repository revision history: - Adapted to Python 1.6 ("append" and other method changes) -- Fixed Image.paste when pasting with solid colour and matte - layers ("L" or "RGBA" masks) (bug reported by Robert Kern) +- Fixed Image.paste when pasting with solid colour and matte layers ("L" or "RGBA" masks) (bug reported by Robert Kern) -- To make it easier to distribute prebuilt versions of PIL, - the tkinit binding stuff has been moved to a separate - extension module, named "_imagingtk". +- To make it easier to distribute prebuilt versions of PIL, the tkinit binding stuff has been moved to a separate extension module, named "_imagingtk". 0.3b2 to 1.0 final ------------------- -- If there's no 16-bit integer (like on a Cray T3E), set - INT16 to the smallest integer available. Most of the - library works just fine anyway (from Bill Crutchfield) +- If there's no 16-bit integer (like on a Cray T3E), set INT16 to the smallest integer available. Most of the library works just fine anyway (from Bill Crutchfield) - Tweaks to make drawing work on big-endian platforms. 1.0c2 ------- -- If PIL is built with the WITH_TKINTER flag, ImageTk can - automatically hook into a standard Tkinter build. You - no longer need to build your own Tkinter to use the - ImageTk module. +- If PIL is built with the WITH_TKINTER flag, ImageTk can automatically hook into a standard Tkinter build. You no longer need to build your own Tkinter to use the ImageTk module. - The old way still works, though. For more information, - see Tk/install.txt. + The old way still works, though. For more information, see Tk/install.txt. -- Some tweaks to ImageTk to support multiple Tk interpreters - (from Greg Couch). +- Some tweaks to ImageTk to support multiple Tk interpreters (from Greg Couch). -- ImageFont "load_path" now scans directory mentioned in .pth - files (from Richard Jones). +- ImageFont "load_path" now scans directory mentioned in .pth files (from Richard Jones). 1.0c1 -------------- @@ -5779,15 +5764,11 @@ was last updated, see the repository revision history: 1.0b2 -------------- -- Major speedups for rotate, transform(EXTENT), and transform(AFFINE) - when using nearest neighbour resampling. +- Major speedups for rotate, transform(EXTENT), and transform(AFFINE) when using nearest neighbour resampling. -- Modified ImageDraw to be compatible with the Arrow graphics - interface. See the handbook for details. +- Modified ImageDraw to be compatible with the Arrow graphics interface. See the handbook for details. -- PIL now automatically loads file codecs when used as a package - (from The Dragon De Monsyne). Also included an __init__.py file - in the standard distribution. +- PIL now automatically loads file codecs when used as a package (from The Dragon De Monsyne). Also included an __init__.py file in the standard distribution. - The GIF encoder has been modified to produce much smaller files. @@ -5801,8 +5782,7 @@ was last updated, see the repository revision history: - Added "bitmap" and "textsize" methods to ImageDraw. -- Improved font rendering code. Fixed a bug or two, and moved - most of the time critical stuff to C. +- Improved font rendering code. Fixed a bug or two, and moved most of the time critical stuff to C. - Removed "bdf2pil.py". Use "pilfont.py" instead! @@ -5817,167 +5797,107 @@ was last updated, see the repository revision history: NOTE: ALL other operations are still UNDEFINED on 16-bit images. -- The "paste" method now supports constant sources. - - Just pass a colour value (a number or a tuple, depending on - the target image mode) instead of the source image. - - This was in fact implemented in an inefficient way in - earlier versions (the "paste" method generated a temporary - source image if you passed it a colour instead of an image). - In this version, this is handled on the C level instead. +- The "paste" method now supports constant sources. Just pass a colour value (a number or a tuple, depending on the target image mode) instead of the source image. This was in fact implemented in an inefficient way in earlier versions (the "paste" method generated a temporary source image if you passed it a colour instead of an image). In this version, this is handled on the C level instead. - Added experimental "RGBa" mode support. - An "RGBa" image is an RGBA image where the colour components - have have been premultiplied with the alpha value. PIL allows - you to convert an RGBA image to an RGBa image, and to paste - RGBa images on top of RGB images. Since this saves a bunch - of multiplications and shifts, it is typically about twice - as fast an ordinary RGBA paste. + An "RGBa" image is an RGBA image where the colour components have have been premultiplied with the alpha value. PIL allows you to convert an RGBA image to an RGBa image, and to paste RGBa images on top of RGB images. Since this saves a bunch of multiplications and shifts, it is typically about twice as fast an ordinary RGBA paste. -- Eliminated extra conversion step when pasting "RGBA" or "RGBa" - images on top of "RGB" images. +- Eliminated extra conversion step when pasting "RGBA" or "RGBa" images on top of "RGB" images. - Fixed Image.BICUBIC resampling for "RGB" images. -- Fixed PCX image file handler to properly read 8-bit PCX - files (bug introduced in 1.0b1, reported by Bernhard - Herzog) +- Fixed PCX image file handler to properly read 8-bit PCX files (bug introduced in 1.0b1, reported by Bernhard Herzog) -- Fixed PSDraw "image" method to restore the coordinate - system. +- Fixed PSDraw "image" method to restore the coordinate system. -- Fixed "blend" problem when applied to images that was - not already loaded (reported by Edward C. Jones) +- Fixed "blend" problem when applied to images that was not already loaded (reported by Edward C. Jones) - Fixed -f option to "pilconvert.py" (from Anthony Baxter) 1.0b1 -------------- -- Added Toby J. Sargeant's quantization package. To enable - quantization, use the "palette" option to "convert": +- Added Toby J. Sargeant's quantization package. To enable quantization, use the "palette" option to "convert": imOut = im.convert("P", palette=Image.ADAPTIVE) - This can be used with "L", "P", and "RGB" images. In this - version, dithering cannot be used with adaptive palettes. + This can be used with "L", "P", and "RGB" images. In this version, dithering cannot be used with adaptive palettes. - Note: ADAPTIVE currently maps to median cut quantization - with 256 colours. The quantization package also contains - a maximum coverage quantizer, which will be supported by - future versions of PIL. + Note: ADAPTIVE currently maps to median cut quantization with 256 colours. The quantization package also contains a maximum coverage quantizer, which will be supported by future versions of PIL. -- Added Eric S. Raymond's "pildriver" image calculator to the - distribution. See the docstring for more information. +- Added Eric S. Raymond's "pildriver" image calculator to the distribution. See the docstring for more information. -- The "offset" method no longer dumps core if given positive - offsets (from Charles Waldman). +- The "offset" method no longer dumps core if given positive offsets (from Charles Waldman). -- Fixed a resource leak that could cause ImageWin to run out of - GDI resources (from Roger Burnham). +- Fixed a resource leak that could cause ImageWin to run out of GDI resources (from Roger Burnham). -- Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired - by code contributed by Richard Jones). +- Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired by code contributed by Richard Jones). -- Added experimental 16-bit support, via modes "I;16" (little endian - data) and "I;16B" (big endian). Only a few methods properly support - such images (see above). +- Added experimental 16-bit support, via modes "I;16" (little endian data) and "I;16B" (big endian). Only a few methods properly support such images (see above). - Added XV thumbnail file handler (from Gene Cash). -- Fixed BMP image file handler to handle palette images with small - palettes (from Rob Hooft). +- Fixed BMP image file handler to handle palette images with small palettes (from Rob Hooft). -- Fixed Sun raster file handler for palette images (from Charles - Waldman). +- Fixed Sun raster file handler for palette images (from Charles Waldman). - Improved various internal error messages. -- Fixed Path constructor to handle arbitrary sequence objects. This - also affects the ImageDraw class (from Richard Jones). +- Fixed Path constructor to handle arbitrary sequence objects. This also affects the ImageDraw class (from Richard Jones). -- Fixed a bug in JpegDecode that caused PIL to report "decoder error - -2" for some progressive JPEG files (reported by Magnus Källström, - who also provided samples). +- Fixed a bug in JpegDecode that caused PIL to report "decoder error -2" for some progressive JPEG files (reported by Magnus Källström, who also provided samples). -- Fixed a bug in JpegImagePlugin that caused PIL to hang when loading - JPEG files using 16-bit quantization tables. +- Fixed a bug in JpegImagePlugin that caused PIL to hang when loading JPEG files using 16-bit quantization tables. -- The Image "transform" method now supports Image.QUAD transforms. - The data argument is an 8-tuple giving the upper left, lower - left, lower right, and upper right corner of the source quadri- - lateral. Also added Image.MESH transform which takes a list - of quadrilaterals. +- The Image "transform" method now supports Image.QUAD transforms. The data argument is an 8-tuple giving the upper left, lower left, lower right, and upper right corner of the source quadri-lateral. Also added Image.MESH transform which takes a list of quadrilaterals. -- The Image "resize", "rotate", and "transform" methods now support - Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. - Filters can be used with all transform methods. +- The Image "resize", "rotate", and "transform" methods now support Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. Filters can be used with all transform methods. -- The ImageDraw "rectangle" method now includes both the right - and the bottom edges when drawing filled rectangles. +- The ImageDraw "rectangle" method now includes both the right and the bottom edges when drawing filled rectangles. -- The TGA decoder now works properly for runlength encoded images - which have more than one byte per pixel. +- The TGA decoder now works properly for runlength encoded images which have more than one byte per pixel. - "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") -- Some file drivers didn't handle the optional "modify" argument - to the load method. This resulted in exceptions when you used - "paste" (and other methods that modify an image in place) on a - newly opened file. +- Some file drivers didn't handle the optional "modify" argument to the load method. This resulted in exceptions when you used "paste" (and other methods that modify an image in place) on a newly opened file. 0.3b2 -------------- The test suite includes 825 individual tests. -- An Image "getbands" method has been added. It returns a tuple - containing the individual band names for this image. To figure - out how many bands an image has, use "len(im.getbands())". +- An Image "getbands" method has been added. It returns a tuple containing the individual band names for this image. To figure out how many bands an image has, use "len(im.getbands())". - An Image "putpixel" method has been added. -- The Image "point" method can now be used to convert "L" images - to any other format, via a lookup table. That table should - contain 256 values for each band in the output image. +- The Image "point" method can now be used to convert "L" images to any other format, via a lookup table. That table should contain 256 values for each band in the output image. -- Some file drivers (including FLI/FLC, GIF, and IM) accidentally - overwrote the offset method with an internal attribute. All - drivers have been updated to use private attributes where - possible. +- Some file drivers (including FLI/FLC, GIF, and IM) accidentally overwrote the offset method with an internal attribute. All drivers have been updated to use private attributes where possible. -- The Image "histogram" method now works for "I" and "F" images. - For these modes, PIL divides the range between the min and - max values used in the image into 256 bins. You can also - pass in your own min and max values via the "extrema" option: +- The Image "histogram" method now works for "I" and "F" images. For these modes, PIL divides the range between the min and max values used in the image into 256 bins. + + You can also pass in your own min and max values via the "extrema" option: h = im.histogram(extrema=(0, 255)) -- An Image "getextrema" method has been added. It returns the - min and max values used in the image. In this release, this - works for single band images only. +- An Image "getextrema" method has been added. It returns the min and max values used in the image. In this release, this works for single band images only. -- Changed the PNG driver to load and save mode "I" images as - 16-bit images. When saving, values outside the range 0..65535 - are clipped. +- Changed the PNG driver to load and save mode "I" images as 16-bit images. When saving, values outside the range 0..65535 are clipped. - Fixed ImageFont.py to work with the new "pilfont" compiler. - Added JPEG "save" and "draft" support for mode "YCbCr" images. - Note that if you save an "YCbCr" image as a JPEG file and read - it back, it is read as an RGB file. To get around this, you - can use the "draft" method: + + Note that if you save an "YCbCr" image as a JPEG file and read it back, it is read as an RGB file. To get around this, you can use the "draft" method: im = Image.open("color.jpg") im.draft("YCbCr", im.size) -- Read "RGBA" TGA images. Also fixed the orientation bug; all - images should now come out the right way. +- Read "RGBA" TGA images. Also fixed the orientation bug; all images should now come out the right way. + +- Changed mode name (and internal representation) from "YCrCb" to "YCbCr" (!) -- Changed mode name (and internal representation) from "YCrCb" - to "YCbCr" (!) *** WARNING: MAY BREAK EXISTING CODE *** 0.3b1 @@ -5985,17 +5905,12 @@ The test suite includes 825 individual tests. The test suite includes 750 individual tests. -- The "pilfont" package is now included in the standard PIL - distribution. The pilfont utility can be used to convert - X BDF and PCF raster font files to a format understood by - the ImageFont module. +- The "pilfont" package is now included in the standard PIL distribution. The pilfont utility can be used to convert X BDF and PCF raster font files to a format understood by the ImageFont module. -- GIF files are now interlaced by default. To write a - non-interlaced file, pass interlace=0 to the "save" - method. +- GIF files are now interlaced by default. To write a non-interlaced file, pass interlace=0 to the "save" method. + +- The default string format has changed for the "fromstring" and "tostring" methods. -- The default string format has changed for the "fromstring" - and "tostring" methods. *** WARNING: MAY BREAK EXISTING CODE *** NOTE: If no extra arguments are given, the first line in @@ -6010,35 +5925,23 @@ The test suite includes 750 individual tests. data = im.tostring("raw", "RGBX", 0, -1) im.fromstring(data, "raw", "RGBX", 0, -1) -- "new" no longer gives a MemoryError if the width or height - is zero (this only happened on platforms where malloc(0) - or calloc(0) returns NULL). +- "new" no longer gives a MemoryError if the width or height is zero (this only happened on platforms where malloc(0) or calloc(0) returns NULL). - "new" now adds a default palette object to "P" images. -- You can now convert directly between all modes supported by - PIL. When converting colour images to "P", PIL defaults to - a "web" palette and dithering. When converting greyscale - images to "1", PIL uses a thresholding and dithering. +- You can now convert directly between all modes supported by PIL. When converting colour images to "P", PIL defaults to a "web" palette and dithering. When converting greyscale images to "1", PIL uses a thresholding and dithering. -- Added a "dither" option to "convert". By default, "convert" - uses floyd-steinberg error diffusion for "P" and "1" targets, - so this option is only used to *disable* dithering. Allowed - values are NONE (no dithering) or FLOYDSTEINBERG (default). +- Added a "dither" option to "convert". By default, "convert" uses floyd-steinberg error diffusion for "P" and "1" targets, so this option is only used to *disable* dithering. Allowed values are NONE (no dithering) or FLOYDSTEINBERG (default). imOut = im.convert("P", dither=Image.NONE) -- Added a full set of "I" decoders. You can use "fromstring" - (and file decoders) to read any standard integer type as an - "I" image. +- Added a full set of "I" decoders. You can use "fromstring" (and file decoders) to read any standard integer type as an "I" image. -- Added some support for "YCbCr" images (creation, conversion - from/to "L" and "RGB", IM YCC load/save) +- Added some support for "YCbCr" images (creation, conversion from/to "L" and "RGB", IM YCC load/save) - "getpixel" now works properly with fractional coordinates. -- ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", - "RGBX", "CMYK", and "YCbCr" images. +- ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", "RGBX", "CMYK", and "YCbCr" images. - ImImagePlugin no longer attaches palettes to "RGB" images. @@ -6049,19 +5952,15 @@ The test suite includes 750 individual tests. - Added experimental IPTC/NAA support. -- Eliminated AttributeError exceptions after "crop" (from - Skip Montanaro) +- Eliminated AttributeError exceptions after "crop" (from Skip Montanaro) -- Reads some uncompressed formats via memory mapping (this - is currently supported on Win32 only) +- Reads some uncompressed formats via memory mapping (this is currently supported on Win32 only) -- Fixed some last minute glitches in the last alpha release - (Types instead of types in Image.py, version numbers, etc.) +- Fixed some last minute glitches in the last alpha release (Types instead of types in Image.py, version numbers, etc.) - Eliminated some more bogus compiler warnings. -- Various fixes to make PIL compile and run smoother on Macs - (from Jack Jansen). +- Various fixes to make PIL compile and run smoother on Macs (from Jack Jansen). - Fixed "fromstring" and "tostring" for mode "I" images. @@ -6070,39 +5969,29 @@ The test suite includes 750 individual tests. The test suite includes 530 individual tests. -- Eliminated unexpected side-effect in "paste" with matte. "paste" - now works properly also if compiled with "gcc". +- Eliminated unexpected side-effect in "paste" with matte. "paste" now works properly also if compiled with "gcc". - Adapted to Python 1.5 (build issues only) -- Fixed the ImageDraw "point" method to draw also the last - point (!). +- Fixed the ImageDraw "point" method to draw also the last point (!). - Added "I" and "RGBX" support to Image.new. -- The plugin path is now properly prepended to the module search - path when a plugin module is imported. +- The plugin path is now properly prepended to the module search path when a plugin module is imported. -- Added "draw" method to the ImageWin.Dib class. This is used by - Topaz to print images on Windows printers. +- Added "draw" method to the ImageWin.Dib class. This is used by Topaz to print images on Windows printers. - "convert" now supports conversions from "P" to "1" and "F". -- "paste" can now take a colour instead of an image as the first argument. - The colour must match the colour argument given to the new function, and - match the mode of the target image. +- "paste" can now take a colour instead of an image as the first argument. The colour must match the colour argument given to the new function, and match the mode of the target image. - Fixed "paste" to allow a mask also for mode "F" images. -- The BMP driver now saves mode "1" images. When loading images, the mode - is set to "L" for 8-bit files with greyscale palettes, and to "P" for - other 8-bit files. +- The BMP driver now saves mode "1" images. When loading images, the mode is set to "L" for 8-bit files with greyscale palettes, and to "P" for other 8-bit files. - The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). -- The JPEG and GIF drivers now saves "1" images. For JPEG, the image - is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the - image will be loaded as a "P" image. +- The JPEG and GIF drivers now saves "1" images. For JPEG, the image is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the image will be loaded as a "P" image. - Fixed a potential buffer overrun in the GIF encoder. @@ -6214,8 +6103,7 @@ The test suite includes 400 individual tests. - Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. -- Fixed palette and padding problems in BMP driver. Now properly -writes "1", "L", "P" and "RGB" images. +- Fixed palette and padding problems in BMP driver. Now properly writes "1", "L", "P" and "RGB" images. - Fixed getpixel()/getdata() to return correct pixel values. @@ -6223,32 +6111,23 @@ writes "1", "L", "P" and "RGB" images. - Added GIF write support (writes "uncompressed" GIF files only, due to unresolvable licensing issues). The "gifmaker.py" script can be used to create GIF animations. -- Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" -images. +- Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" images. - Added FLI read support. This driver has only been tested on a few FLI samples. - Reads 2-bit and 4-bit PCX images. -- Added MSP read and write support. Both version 1 and 2 can be -read, but only version 1 (uncompressed) files are written. +- Added MSP read and write support. Both version 1 and 2 can be read, but only version 1 (uncompressed) files are written. -- Fixed a bug in the FLI/FLC identification code that caused the -driver to raise an exception when parsing valid FLI/FLC files. +- Fixed a bug in the FLI/FLC identification code that caused the driver to raise an exception when parsing valid FLI/FLC files. -- Improved performance when loading file format plugins, and when -opening files. +- Improved performance when loading file format plugins, and when opening files. -- Added GIF animation support, via the "seek" and "tell" methods. -You can use "player.py" to play an animated GIF file. +- Added GIF animation support, via the "seek" and "tell" methods. You can use "player.py" to play an animated GIF file. -- Removed MNG support, since the spec is changing faster than I -can change the code. I've added support for the experimental -ARG format instead. Contact me for more information on this -format. +- Removed MNG support, since the spec is changing faster than I can change the code. I've added support for the experimental ARG format instead. Contact me for more information on this format. -- Added keyword options to the "save" method. The following options -are currently supported: +- Added keyword options to the "save" method. The following options are currently supported: Format Option Description -------------------------------------------------------- @@ -6268,100 +6147,78 @@ are currently supported: PNG optimize Minimize output file at the expense of compression speed. -Expect more options in future releases. Also note that -file writers silently ignore unknown options. +Expect more options in future releases. Also note that file writers silently ignore unknown options. - Plugged memory leaks in the PNG and TIFF decoders. - Added PNG write support. -- (internal) RGB unpackers and converters now set the pad byte -to 255 (full opacity). +- (internal) RGB unpackers and converters now set the pad byte to 255 (full opacity). -- Properly handles the "transparency" property for GIF, PNG -and XPM files. +- Properly handles the "transparency" property for GIF, PNG and XPM files. -- Added a "putalpha" method, allowing you to attach a "1" or "L" -image as the alpha layer to an "RGBA" image. +- Added a "putalpha" method, allowing you to attach a "1" or "L" image as the alpha layer to an "RGBA" image. - Various improvements to the sample scripts: -"pilconvert" Carries out some extra tricks in order to make - the resulting file as small as possible. + "pilconvert" Carries out some extra tricks in order to make + the resulting file as small as possible. -"explode" (NEW) Split an image sequence into individual frames. + "explode" (NEW) Split an image sequence into individual frames. -"gifmaker" (NEW) Convert a sequence file into a GIF animation. - Note that the GIF encoder create "uncompressed" GIF - files, so animations created by this script are - rather large (typically 2-5 times the compressed - sizes). + "gifmaker" (NEW) Convert a sequence file into a GIF animation. + Note that the GIF encoder create "uncompressed" GIF + files, so animations created by this script are + rather large (typically 2-5 times the compressed + sizes). -"image2py" (NEW) Convert a single image to a python module. See - comments in this script for details. + "image2py" (NEW) Convert a single image to a python module. See + comments in this script for details. -"player" If multiple images are given on the command line, - they are interpreted as frames in a sequence. The - script assumes that they all have the same size. - Also note that this script now can play FLI/FLC - and GIF animations. + "player" If multiple images are given on the command line, + they are interpreted as frames in a sequence. The + script assumes that they all have the same size. + Also note that this script now can play FLI/FLC + and GIF animations. - This player can also execute embedded Python - animation applets (ARG format only). + This player can also execute embedded Python + animation applets (ARG format only). -"viewer" Transparent images ("P" with transparency property, - and "RGBA") are superimposed on the standard Tk back- - ground. + "viewer" Transparent images ("P" with transparency property, + and "RGBA") are superimposed on the standard Tk back- + ground. -- Fixed colour argument to "new". For multilayer images, pass a -tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, -Magenta, Yellow, Black). +- Fixed colour argument to "new". For multilayer images, pass a tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, Magenta, Yellow, Black). - Added XPM (X pixmap) read support. 0.2b3 -------------- -- Added MNG (multi-image network graphics) read support. "Ming" - is a proposed animation standard, based on the PNG file format. +- Added MNG (multi-image network graphics) read support. "Ming" is a proposed animation standard, based on the PNG file format. - You can use the "player" sample script to display some flavours - of this format. The MNG standard is still under development, - as is this driver. More information, including sample files, - can be found at + You can use the "player" sample script to display some flavours of this format. The MNG standard is still under development, as is this driver. More information, including sample files, can be found at -- Added a "verify" method to images loaded from file. This method - scans the file for errors, without actually decoding the image - data, and raises a suitable exception if it finds any problems. - Currently implemented for PNG and MNG files only. +- Added a "verify" method to images loaded from file. This method scans the file for errors, without actually decoding the image data, and raises a suitable exception if it finds any problems. Currently implemented for PNG and MNG files only. - Added support for interlaced GIF images. -- Added PNG read support -- if linked with the ZLIB compression library, - PIL reads all kinds of PNG images, except interlaced files. +- Added PNG read support -- if linked with the ZLIB compression library, PIL reads all kinds of PNG images, except interlaced files. -- Improved PNG identification support -- doesn't mess up on unknown - chunks, identifies all possible PNG modes, and verifies checksum - on PNG header chunks. +- Improved PNG identification support -- doesn't mess up on unknown chunks, identifies all possible PNG modes, and verifies checksum on PNG header chunks. -- Added an experimental reader for placable Windows Meta Files (WMF). - This reader is still very incomplete, but it illustrates how PIL's - drawing capabilities can be used to render vector and metafile - formats. +- Added an experimental reader for placable Windows Meta Files (WMF). This reader is still very incomplete, but it illustrates how PIL's drawing capabilities can be used to render vector and metafile formats. -- Added restricted drivers for images from Image Tools (greyscale - only) and LabEye/IFUNC (common interchange modes only). +- Added restricted drivers for images from Image Tools (greyscale only) and LabEye/IFUNC (common interchange modes only). -- Some minor improvements to the sample scripts provided in the - "Scripts" directory. +- Some minor improvements to the sample scripts provided in the "Scripts" directory. - The test images have been moved to the "Images" directory. 0.2b2, 0.2b1 released; Windows only ----------------------------------- -- Fixed filling of complex polygons. The ImageDraw "line" and - "polygon" methods also accept Path objects. +- Fixed filling of complex polygons. The ImageDraw "line" and "polygon" methods also accept Path objects. - The ImageTk "PhotoImage" object can now be constructed directly from an image. You can also pass the object itself to Tkinter, @@ -6374,110 +6231,64 @@ Magenta, Yellow, Black). BitmapImage class; use the "foreground" option to set the colour of the overlay. -- Added a "putdata" method to the Image class. This can be used to - load a 1-layer image with data from a sequence object or a string. - An optional floating point scale and offset can be used to adjust - the data to fit into the 8-bit pixel range. Also see the "getdata" - method. +- Added a "putdata" method to the Image class. This can be used to load a 1-layer image with data from a sequence object or a string. An optional floating point scale and offset can be used to adjust the data to fit into the 8-bit pixel range. Also see the "getdata" method. -- Added the EXTENT method to the Image "transform" method. This can - be used to quickly crop, stretch, shrink, or mirror a subregion - from another image. +- Added the EXTENT method to the Image "transform" method. This can be used to quickly crop, stretch, shrink, or mirror a subregion from another image. - Adapted to Python 1.4. -- Added a project makefile for Visual C++ 4.x. This allows you to - easily build a dynamically linked version of PIL for Windows 95 - and NT. +- Added a project makefile for Visual C++ 4.x. This allows you to easily build a dynamically linked version of PIL for Windows 95 and NT. -- A Tk "booster" patch for Windows is available. It gives dramatic - performance improvements for some displays. Has been tested with - Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk - subdirectory for details. +- A Tk "booster" patch for Windows is available. It gives dramatic performance improvements for some displays. Has been tested with Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk subdirectory for details. -- You can now save 1-bit images in the XBM format. In addition, the - Image class now provides a "tobitmap" method which returns a string - containing an XBM representation of the image. Quite handy to use - with Tk. +- You can now save 1-bit images in the XBM format. In addition, the Image class now provides a "tobitmap" method which returns a string containing an XBM representation of the image. Quite handy to use with Tk. - More conversions, including "RGB" to "1" and more. 0.2a1 -------------- -- Where earlier versions accepted lists, this version accepts arbitrary - Python sequences (including strings, in some cases). A few resource - leaks were plugged in the process. +- Where earlier versions accepted lists, this version accepts arbitrary Python sequences (including strings, in some cases). A few resource leaks were plugged in the process. -- The Image "paste" method now allows the box to extend outside - the target image. The size of the box, the image to be pasted, - and the optional mask must still match. +- The Image "paste" method now allows the box to extend outside the target image. The size of the box, the image to be pasted, and the optional mask must still match. -- The ImageDraw module now supports filled polygons, outlined and - filled ellipses, and text. Font support is rudimentary, though. +- The ImageDraw module now supports filled polygons, outlined and filled ellipses, and text. Font support is rudimentary, though. -- The Image "point" method now takes an optional mode argument, - allowing you to convert the image while translating it. Currently, - this can only be used to convert "L" or "P" images to "1" images - (creating thresholded images or "matte" masks). +- The Image "point" method now takes an optional mode argument, allowing you to convert the image while translating it. Currently, this can only be used to convert "L" or "P" images to "1" images (creating thresholded images or "matte" masks). -- An Image "getpixel" method has been added. For single band images, - it returns the pixel value at a given position as an integer. - For n-band images, it returns an n-tuple of integers. +- An Image "getpixel" method has been added. For single band images, it returns the pixel value at a given position as an integer. For n-band images, it returns an n-tuple of integers. -- An Image "getdata" method has been added. It returns a sequence - object representing the image as a 1-dimensional array. Only len() - and [] can be used with this sequence. This method returns a - reference to the existing image data, so changes in the image - will be immediately reflected in the sequence object. +- An Image "getdata" method has been added. It returns a sequence object representing the image as a 1-dimensional array. Only len() and [] can be used with this sequence. This method returns a reference to the existing image data, so changes in the image will be immediately reflected in the sequence object. - Fixed alignment problems in the Windows BMP writer. -- If converting an "RGB" image to "RGB" or "L", you can give a second - argument containing a colour conversion matrix. +- If converting an "RGB" image to "RGB" or "L", you can give a second argument containing a colour conversion matrix. -- An Image "getbbox" method has been added. It returns the bounding - box of data in an image, considering the value 0 as background. +- An Image "getbbox" method has been added. It returns the bounding box of data in an image, considering the value 0 as background. -- An Image "offset" method has been added. It returns a new image - where the contents of the image have been offset the given distance - in X and/or Y direction. Data wraps between edges. +- An Image "offset" method has been added. It returns a new image where the contents of the image have been offset the given distance in X and/or Y direction. Data wraps between edges. -- Saves PDF images. The driver creates a binary PDF 1.1 files, using - JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding - (same as for PostScript) for other formats. +- Saves PDF images. The driver creates a binary PDF 1.1 files, using JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding (same as for PostScript) for other formats. -- The "paste" method now accepts "1" masks. Zero means transparent, - any other pixel value means opaque. This is faster than using an - "L" transparency mask. +- The "paste" method now accepts "1" masks. Zero means transparent, any other pixel value means opaque. This is faster than using an "L" transparency mask. -- Properly writes EPS files (and properly prints images to PostScript - printers as well). +- Properly writes EPS files (and properly prints images to PostScript printers as well). -- Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR - files. Cursor animations are not supported. +- Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR files. Cursor animations are not supported. - Fixed alignment problems in the Sun raster loader. -- Added "draft" and "thumbnail" methods. The draft method is used - to optimize loading of JPEG and PCD files, the thumbnail method is - used to create a thumbnail representation of an image. +- Added "draft" and "thumbnail" methods. The draft method is used to optimize loading of JPEG and PCD files, the thumbnail method is used to create a thumbnail representation of an image. -- Added Windows display support, via the ImageWin class (see the - handbook for details). +- Added Windows display support, via the ImageWin class (see the handbook for details). -- Added raster conversion for EPS files. This requires GNU or Aladdin - Ghostscript, and probably works on UNIX only. +- Added raster conversion for EPS files. This requires GNU or Aladdin Ghostscript, and probably works on UNIX only. -- Reads PhotoCD (PCD) images. The base resolution (768x512) can be - read from a PhotoCD file. +- Reads PhotoCD (PCD) images. The base resolution (768x512) can be read from a PhotoCD file. -- Eliminated some compiler warnings. Bindings now compile cleanly in C++ - mode. Note that the Imaging library itself must be compiled in C mode. +- Eliminated some compiler warnings. Bindings now compile cleanly in C++ mode. Note that the Imaging library itself must be compiled in C mode. -- Added "bdf2pil.py", which converts BDF fonts into images with associated - metrics. This is definitely work in progress. For info, see description - in script for details. +- Added "bdf2pil.py", which converts BDF fonts into images with associated metrics. This is definitely work in progress. For info, see description in script for details. - Fixed a bug in the "ImageEnhance.py" module. @@ -6489,65 +6300,45 @@ Magenta, Yellow, Black). - Reads plane separated RGB and CMYK TIFF images. -- Added driver debug mode. This is enabled by setting Image.DEBUG - to a non-zero value. Try the -D option to "pilfile.py" and see what - happens. +- Added driver debug mode. This is enabled by setting Image.DEBUG to a non-zero value. Try the -D option to "pilfile.py" and see what happens. - Don't crash on "atend" constructs in PostScript files. -- Only the Image module imports _imaging directly. Other modules - should refer to the binding module as "Image.core". +- Only the Image module imports _imaging directly. Other modules should refer to the binding module as "Image.core". 0.0 to 0.1 (b1) ------------ - A handbook is available (distributed separately). -- The coordinate system is changed so that (0,0) is now located - in the upper left corner. This is in compliancy with ISO 12087 - and 90% of all other image processing and graphics libraries. +- The coordinate system is changed so that (0,0) is now located in the upper left corner. This is in compliancy with ISO 12087 and 90% of all other image processing and graphics libraries. -- Modes "1" (bilevel) and "P" (palette) have been introduced. Note - that bilevel images are stored with one byte per pixel. +- Modes "1" (bilevel) and "P" (palette) have been introduced. Note that bilevel images are stored with one byte per pixel. -- The Image "crop" and "paste" methods now accepts None as the - box argument, to refer to the full image (self, that is). +- The Image "crop" and "paste" methods now accepts None as the box argument, to refer to the full image (self, that is). - The Image "crop" method now works properly. -- The Image "point" method is now available. You can use either a - lookup table or a function taking one argument. +- The Image "point" method is now available. You can use either a lookup table or a function taking one argument. - The Image join function has been renamed to "merge". -- An Image "composite" function has been added. It is identical - to copy() followed by paste(mask). +- An Image "composite" function has been added. It is identical to copy() followed by paste(mask). -- An Image "eval" function has been added. It is currently identical - to point(function); that is, only a single image can be processed. +- An Image "eval" function has been added. It is currently identical to point(function); that is, only a single image can be processed. -- A set of channel operations has been added. See the "ImageChops" - module, test_chops.py, and the handbook for details. +- A set of channel operations has been added. See the "ImageChops" module, test_chops.py, and the handbook for details. -- Added the "pilconvert" utility, which converts image files. Note - that the number of output formats are still quite restricted. +- Added the "pilconvert" utility, which converts image files. Note that the number of output formats are still quite restricted. -- Added the "pilfile" utility, which quickly identifies image files - (without loading them, in most cases). +- Added the "pilfile" utility, which quickly identifies image files (without loading them, in most cases). -- Added the "pilprint" utility, which prints image files to PostScript - printers. +- Added the "pilprint" utility, which prints image files to PostScript printers. -- Added a rudimentary version of the "pilview" utility, which is - simple image viewer based on Tk. Only File/Exit and Image/Next - works properly. +- Added a rudimentary version of the "pilview" utility, which is simple image viewer based on Tk. Only File/Exit and Image/Next works properly. -- An interface to Tk has been added. See "Lib/ImageTk.py" and README - for details. +- An interface to Tk has been added. See "Lib/ImageTk.py" and README for details. -- An interface to Jack Jansen's Img library has been added (thanks to - Jack). This allows you to read images through the Img extensions file - format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. +- An interface to Jack Jansen's Img library has been added (thanks to Jack). This allows you to read images through the Img extensions file format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. -- PostScript printing is provided through the PSDraw module. See the - handbook for details. +- PostScript printing is provided through the PSDraw module. See the handbook for details. From a82ab509dfd284d742309b31e0ad78085f2020fa Mon Sep 17 00:00:00 2001 From: Max Base Date: Mon, 11 Apr 2022 12:30:51 +0430 Subject: [PATCH 081/294] Fix errors, complete table --- CHANGES.rst | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7607779d0..6520878d1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6129,23 +6129,28 @@ The test suite includes 400 individual tests. - Added keyword options to the "save" method. The following options are currently supported: - Format Option Description - -------------------------------------------------------- - JPEG optimize Minimize output file at the - expense of compression speed. + .. list-table:: + :widths: 25 25 50 + :header-rows: 1 - JPEG progressive Enable progressive output. - The option value is ignored. - - JPEG quality Set compression quality (1-100). - The default value is 75. - - JPEG smooth Smooth dithered images. - Value is strength (1-100). - Default is off (0). - - PNG optimize Minimize output file at the - expense of compression speed. + * - Format + - Option + - Description + * - JPEG + - optimize + - Minimize output file at the expense of compression speed. + * - JPEG + - progressive + - Enable progressive output. The option value is ignored. + * - JPEG + - quality + - Set compression quality (1-100). The default value is 75. + * - JPEG + - smooth + - Smooth dithered images. Value is strength (1-100). Default is off (0). + * - PNG + - optimize + - Minimize output file at the expense of compression speed. Expect more options in future releases. Also note that file writers silently ignore unknown options. @@ -6307,7 +6312,7 @@ Expect more options in future releases. Also note that file writers silently ig - Only the Image module imports _imaging directly. Other modules should refer to the binding module as "Image.core". 0.0 to 0.1 (b1) ------------- +--------------- - A handbook is available (distributed separately). From fca64bc0d31542ade23992f769936a5cff81f8b3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 21:26:23 +0300 Subject: [PATCH 082/294] [Test] class names should be CamelCase --- Tests/helper.py | 2 +- Tests/test_image_paste.py | 16 ++++++++-------- Tests/test_imagepath.py | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Tests/helper.py b/Tests/helper.py index feccce6bc..13c6955e4 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -324,7 +324,7 @@ def is_mingw(): return sysconfig.get_platform() == "mingw" -class cached_property: +class CachedProperty: def __init__(self, func): self.func = func diff --git a/Tests/test_image_paste.py b/Tests/test_image_paste.py index 281d5a6fb..4ea1d73ce 100644 --- a/Tests/test_image_paste.py +++ b/Tests/test_image_paste.py @@ -1,6 +1,6 @@ from PIL import Image -from .helper import assert_image_equal, cached_property +from .helper import CachedProperty, assert_image_equal class TestImagingPaste: @@ -34,7 +34,7 @@ class TestImagingPaste: im.paste(im2, mask) self.assert_9points_image(im, expected) - @cached_property + @CachedProperty def mask_1(self): mask = Image.new("1", (self.size, self.size)) px = mask.load() @@ -43,11 +43,11 @@ class TestImagingPaste: px[y, x] = (x + y) % 2 return mask - @cached_property + @CachedProperty def mask_L(self): return self.gradient_L.transpose(Image.Transpose.ROTATE_270) - @cached_property + @CachedProperty def gradient_L(self): gradient = Image.new("L", (self.size, self.size)) px = gradient.load() @@ -56,7 +56,7 @@ class TestImagingPaste: px[y, x] = (x + y) % 255 return gradient - @cached_property + @CachedProperty def gradient_RGB(self): return Image.merge( "RGB", @@ -67,7 +67,7 @@ class TestImagingPaste: ], ) - @cached_property + @CachedProperty def gradient_LA(self): return Image.merge( "LA", @@ -77,7 +77,7 @@ class TestImagingPaste: ], ) - @cached_property + @CachedProperty def gradient_RGBA(self): return Image.merge( "RGBA", @@ -89,7 +89,7 @@ class TestImagingPaste: ], ) - @cached_property + @CachedProperty def gradient_RGBa(self): return Image.merge( "RGBa", diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index e38a2068a..de3920cf5 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -174,7 +174,7 @@ def test_overflow_segfault(): # through to the sequence. Seeing this on 32-bit Windows. with pytest.raises((TypeError, MemoryError)): # post patch, this fails with a memory error - x = evil() + x = Evil() # This fails due to the invalid malloc above, # and segfaults @@ -182,7 +182,7 @@ def test_overflow_segfault(): x[i] = b"0" * 16 -class evil: +class Evil: def __init__(self): self.corrupt = Image.core.path(0x4000000000000000) From a715bf2d6fa13caabe1fc2e5b776516900ba3fde Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 22:17:35 +0300 Subject: [PATCH 083/294] [Test] function name should be snake_case --- Tests/test_box_blur.py | 32 ++++++++++++++++---------------- Tests/test_decompression_bomb.py | 2 +- Tests/test_file_gif.py | 16 ++++++++-------- Tests/test_file_pdf.py | 4 ++-- Tests/test_file_tiff.py | 4 ++-- Tests/test_file_tiff_metadata.py | 2 +- Tests/test_file_webp_animated.py | 4 ++-- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index 94f504e0b..3bdd5177d 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -25,7 +25,7 @@ def box_blur(image, radius=1, n=1): return image._new(image.im.box_blur(radius, n)) -def assertImage(im, data, delta=0): +def assert_image(im, data, delta=0): it = iter(im.getdata()) for data_row in data: im_row = [next(it) for _ in range(im.size[0])] @@ -35,12 +35,12 @@ def assertImage(im, data, delta=0): next(it) -def assertBlur(im, radius, data, passes=1, delta=0): +def assert_blur(im, radius, data, passes=1, delta=0): # check grayscale image - assertImage(box_blur(im, radius, passes), data, delta) + assert_image(box_blur(im, radius, passes), data, delta) rgba = Image.merge("RGBA", (im, im, im, im)) for band in box_blur(rgba, radius, passes).split(): - assertImage(band, data, delta) + assert_image(band, data, delta) def test_color_modes(): @@ -64,7 +64,7 @@ def test_color_modes(): def test_radius_0(): - assertBlur( + assert_blur( sample, 0, [ @@ -80,7 +80,7 @@ def test_radius_0(): def test_radius_0_02(): - assertBlur( + assert_blur( sample, 0.02, [ @@ -97,7 +97,7 @@ def test_radius_0_02(): def test_radius_0_05(): - assertBlur( + assert_blur( sample, 0.05, [ @@ -114,7 +114,7 @@ def test_radius_0_05(): def test_radius_0_1(): - assertBlur( + assert_blur( sample, 0.1, [ @@ -131,7 +131,7 @@ def test_radius_0_1(): def test_radius_0_5(): - assertBlur( + assert_blur( sample, 0.5, [ @@ -148,7 +148,7 @@ def test_radius_0_5(): def test_radius_1(): - assertBlur( + assert_blur( sample, 1, [ @@ -165,7 +165,7 @@ def test_radius_1(): def test_radius_1_5(): - assertBlur( + assert_blur( sample, 1.5, [ @@ -182,7 +182,7 @@ def test_radius_1_5(): def test_radius_bigger_then_half(): - assertBlur( + assert_blur( sample, 3, [ @@ -199,7 +199,7 @@ def test_radius_bigger_then_half(): def test_radius_bigger_then_width(): - assertBlur( + assert_blur( sample, 10, [ @@ -214,7 +214,7 @@ def test_radius_bigger_then_width(): def test_extreme_large_radius(): - assertBlur( + assert_blur( sample, 600, [ @@ -229,7 +229,7 @@ def test_extreme_large_radius(): def test_two_passes(): - assertBlur( + assert_blur( sample, 1, [ @@ -247,7 +247,7 @@ def test_two_passes(): def test_three_passes(): - assertBlur( + assert_blur( sample, 1, [ diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index 1778491ab..b590a84c5 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -78,7 +78,7 @@ class TestDecompressionCrop: def teardown_class(self): Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT - def testEnlargeCrop(self): + def test_enlarge_crop(self): # Crops can extend the extents, therefore we should have the # same decompression bomb warnings on them. with hopper() as src: diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index dffd1006f..fd30cded0 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -799,31 +799,31 @@ def test_zero_comment_subblocks(): def test_version(tmp_path): out = str(tmp_path / "temp.gif") - def assertVersionAfterSave(im, version): + def assert_version_after_save(im, version): im.save(out) with Image.open(out) as reread: assert reread.info["version"] == version # Test that GIF87a is used by default im = Image.new("L", (100, 100), "#000") - assertVersionAfterSave(im, b"GIF87a") + assert_version_after_save(im, b"GIF87a") # Test setting the version to 89a im = Image.new("L", (100, 100), "#000") im.info["version"] = b"89a" - assertVersionAfterSave(im, b"GIF89a") + assert_version_after_save(im, b"GIF89a") # Test that adding a GIF89a feature changes the version im.info["transparency"] = 1 - assertVersionAfterSave(im, b"GIF89a") + assert_version_after_save(im, b"GIF89a") # Test that a GIF87a image is also saved in that format with Image.open("Tests/images/test.colors.gif") as im: - assertVersionAfterSave(im, b"GIF87a") + assert_version_after_save(im, b"GIF87a") # Test that a GIF89a image is also saved in that format im.info["version"] = b"GIF89a" - assertVersionAfterSave(im, b"GIF87a") + assert_version_after_save(im, b"GIF87a") def test_append_images(tmp_path): @@ -838,10 +838,10 @@ def test_append_images(tmp_path): assert reread.n_frames == 3 # Tests appending using a generator - def imGenerator(ims): + def im_generator(ims): yield from ims - im.save(out, save_all=True, append_images=imGenerator(ims)) + im.save(out, save_all=True, append_images=im_generator(ims)) with Image.open(out) as reread: assert reread.n_frames == 3 diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index b6f327844..c71d4f5f2 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -131,10 +131,10 @@ def test_save_all(tmp_path): assert os.path.getsize(outfile) > 0 # Test appending using a generator - def imGenerator(ims): + def im_generator(ims): yield from ims - im.save(outfile, save_all=True, append_images=imGenerator(ims)) + im.save(outfile, save_all=True, append_images=im_generator(ims)) assert os.path.isfile(outfile) assert os.path.getsize(outfile) > 0 diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index c53bb87e8..157065ddd 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -655,11 +655,11 @@ class TestFileTiff: assert reread.n_frames == 3 # Test appending using a generator - def imGenerator(ims): + def im_generator(ims): yield from ims mp = BytesIO() - im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims)) + im.save(mp, format="TIFF", save_all=True, append_images=im_generator(ims)) mp.seek(0, os.SEEK_SET) with Image.open(mp) as reread: diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index b11ab1643..d7a0d9377 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -356,7 +356,7 @@ def test_empty_values(): assert 33432 in info -def test_PhotoshopInfo(tmp_path): +def test_photoshop_info(tmp_path): with Image.open("Tests/images/issue_2278.tif") as im: assert len(im.tag_v2[34377]) == 70 assert isinstance(im.tag_v2[34377], bytes) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index 8606f6aaf..c621df0d9 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -90,14 +90,14 @@ def test_write_animation_RGB(tmp_path): check(temp_file1) # Tests appending using a generator - def imGenerator(ims): + def im_generator(ims): yield from ims temp_file2 = str(tmp_path / "temp_generator.webp") frame1.copy().save( temp_file2, save_all=True, - append_images=imGenerator([frame2]), + append_images=im_generator([frame2]), lossless=True, ) check(temp_file2) From 9951de08c023535105833dbc48b423a624e480ea Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 10 Apr 2022 22:23:55 +0300 Subject: [PATCH 084/294] [Test] argument name should be snake_case --- Tests/test_file_tiff.py | 8 ++++---- Tests/test_imagemorph.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 157065ddd..16c43b00f 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -151,14 +151,14 @@ class TestFileTiff: assert im.info["dpi"] == (71.0, 71.0) @pytest.mark.parametrize( - "resolutionUnit, dpi", + "resolution_unit, dpi", [(None, 72.8), (2, 72.8), (3, 184.912)], ) - def test_load_float_dpi(self, resolutionUnit, dpi): + def test_load_float_dpi(self, resolution_unit, dpi): with Image.open( - "Tests/images/hopper_float_dpi_" + str(resolutionUnit) + ".tif" + "Tests/images/hopper_float_dpi_" + str(resolution_unit) + ".tif" ) as im: - assert im.tag_v2.get(RESOLUTION_UNIT) == resolutionUnit + assert im.tag_v2.get(RESOLUTION_UNIT) == resolution_unit assert im.info["dpi"] == (dpi, dpi) def test_save_float_dpi(self, tmp_path): diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 368c2bba1..7ec267bcd 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -48,12 +48,12 @@ def img_string_normalize(im): return img_to_string(string_to_img(im)) -def assert_img_equal(A, B): - assert img_to_string(A) == img_to_string(B) +def assert_img_equal(a, b): + assert img_to_string(a) == img_to_string(b) -def assert_img_equal_img_string(A, Bstring): - assert img_to_string(A) == img_string_normalize(Bstring) +def assert_img_equal_img_string(a, b_string): + assert img_to_string(a) == img_string_normalize(b_string) def test_str_to_img(): From 4258246ed5ac1a338762fb73629951a254091efe Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 12 Apr 2022 13:15:25 +0300 Subject: [PATCH 085/294] Use actions/stale to close 'Awaiting OP Action' that have had no response in 7 days --- .github/workflows/stale.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..dcff12c29 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,26 @@ +name: Close stale issues + +on: + schedule: + - cron: "10 0 * * *" + workflow_dispatch: + +permissions: + issues: write + +jobs: + stale: + if: github.repository_owner == 'python-pillow' + + runs-on: ubuntu-latest + + steps: + - name: "Check issues" + uses: actions/stale@v4 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + only-issue-labels: "Awaiting OP Action" + close-issue-message: "Closing this issue as no feedback has been received." + days-before-stale: -1 + days-before-close: 7 + labels-to-remove-when-unstale: "Awaiting OP Action" From c45a29d5fea0cbd43d703729736bf81410ebc75c Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 12 Apr 2022 13:15:44 +0300 Subject: [PATCH 086/294] TEMP testing --- .github/workflows/stale.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index dcff12c29..83f1a065c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -19,8 +19,9 @@ jobs: uses: actions/stale@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} + debug-only: true only-issue-labels: "Awaiting OP Action" close-issue-message: "Closing this issue as no feedback has been received." days-before-stale: -1 - days-before-close: 7 + days-before-close: 1 labels-to-remove-when-unstale: "Awaiting OP Action" From df51c357eb24131cd5ce861d184f1a430e7d16c5 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 12 Apr 2022 13:41:13 +0300 Subject: [PATCH 087/294] Bump to v5 Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 83f1a065c..698972304 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,7 +16,7 @@ jobs: steps: - name: "Check issues" - uses: actions/stale@v4 + uses: actions/stale@v5 with: repo-token: ${{ secrets.GITHUB_TOKEN }} debug-only: true From 4160d9145e5c90133b69a4940f2d78b7cf156e6a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 12 Apr 2022 14:53:50 +0300 Subject: [PATCH 088/294] Skip all PRs without the special label --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 83f1a065c..dabb9fb8b 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -20,7 +20,7 @@ jobs: with: repo-token: ${{ secrets.GITHUB_TOKEN }} debug-only: true - only-issue-labels: "Awaiting OP Action" + only-labels: "Awaiting OP Action" close-issue-message: "Closing this issue as no feedback has been received." days-before-stale: -1 days-before-close: 1 From d144fe325d8e619fc45256be69d83ec7923bf3db Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 12 Apr 2022 14:54:29 +0300 Subject: [PATCH 089/294] Never close PRs --- .github/workflows/stale.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index dabb9fb8b..bac723833 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -23,5 +23,6 @@ jobs: only-labels: "Awaiting OP Action" close-issue-message: "Closing this issue as no feedback has been received." days-before-stale: -1 - days-before-close: 1 + days-before-issue-close: 1 + days-before-pr-close: -1 labels-to-remove-when-unstale: "Awaiting OP Action" From 950d0ad1d37a8b68b13fef9dbab47bf5a76b552a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 12 Apr 2022 23:12:45 +1000 Subject: [PATCH 090/294] 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 3f3c16ee26560b70fc098c8e46e7922286658935 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 Apr 2022 21:52:29 +1000 Subject: [PATCH 091/294] Aligned headings [ci skip] --- CHANGES.rst | 88 ++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6520878d1..92e662485 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4837,7 +4837,7 @@ was last updated, see the repository revision history: Valentino Volonghi). 1.1.7c1 ----------------- +------- - Improved PNG compression (from Alexey Borzenkov). @@ -5039,14 +5039,14 @@ was last updated, see the repository revision history: - Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). 1.1.6 --------------- +----- - Fixed some 64-bit compatibility warnings for Python 2.5. - Added threading support for the Sane driver (from Abel Deuring). 1.1.6b2 ----------------- +------- - Added experimental "floodfill" function to the ImageDraw module (based on code by Eric Raymond). @@ -5082,7 +5082,7 @@ was last updated, see the repository revision history: for samples, debugging, and patches). 1.1.6b1 ----------------- +------- - Added 'expand' option to the Image 'rotate' method. If true, the output image is made large enough to hold the entire rotated image. @@ -5103,7 +5103,7 @@ was last updated, see the repository revision history: band names. 1.1.6a2 ----------------- +------- - Added float/double support to the TIFF loader (from Russell Nelson). @@ -5119,7 +5119,7 @@ was last updated, see the repository revision history: Deuring). 1.1.6a1 ----------------- +------- - Fixed a memory leak in "convert(mode)", when converting from L to P. @@ -5152,7 +5152,7 @@ was last updated, see the repository revision history: - Fixed line width calculation. 1.1.6a0 ----------------- +------- - Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). @@ -5171,13 +5171,13 @@ was last updated, see the repository revision history: - Fixed potential integer division in PSDraw.image (from Eric Etheridge). 1.1.5c2 and 1.1.5 final --------------------------------- +----------------------- - Added experimental PERSPECTIVE transform method (from Jeff Breiden- bach). 1.1.5c1 ----------------- +------- - Make sure "thumbnail" never generates zero-wide or zero-high images (reported by Gene Skonicki) @@ -5190,7 +5190,7 @@ was last updated, see the repository revision history: Jeff Epler). 1.1.5b3 ----------------- +------- - Don't crash in "quantize" method if the number of colors requested is larger than 256. This release raises a ValueError exception; @@ -5200,7 +5200,7 @@ was last updated, see the repository revision history: - Added WBMP read/write support (based on code by Duncan Booth). 1.1.5b2 ----------------- +------- - Added DPI read/write support to the PNG codec. The decoder sets the info["dpi"] attribute for PNG files with appropriate resolution @@ -5221,7 +5221,7 @@ was last updated, see the repository revision history: format. 1.1.5b1 ----------------- +------- - Added new Sane release (from Ralph Heinkel). See the Sane/README and Sane/CHANGES files for more information. @@ -5242,7 +5242,7 @@ was last updated, see the repository revision history: tolist(1) returns a flattened list instead. 1.1.5a5 ----------------- +------- - Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". @@ -5260,7 +5260,7 @@ was last updated, see the repository revision history: to everyone who helped test the new "setup.py" script! 1.1.5a4 ----------------- +------- - The "save" method now looks for a file format driver before creating the file. @@ -5297,7 +5297,7 @@ was last updated, see the repository revision history: in _imagingft.c for details. 1.1.5a3 ----------------- +------- - Added 'getim' method, which returns a PyCObject wrapping an Imaging pointer. The description string is set to IMAGING_MAGIC. @@ -5312,7 +5312,7 @@ was last updated, see the repository revision history: Douglas Bagnall). 1.1.5a2 ----------------- +------- - The "paste" method now supports the alternative "paste(im, mask)" syntax (in this case, the box defaults to im's bounding box). @@ -5342,7 +5342,7 @@ was last updated, see the repository revision history: http://effbot.org/downloads#pilwmf 1.1.5a1 ----------------- +------- - Replaced broken WMF driver with a WMF stub plugin (see below). @@ -5428,12 +5428,12 @@ was last updated, see the repository revision history: input from Jeff Breidenbach). 1.1.4 final --------------------- +----------- - Fixed ImageTk build problem on Unix. 1.1.4b2 ----------------- +------- - Improved building on Mac OS X (from Jack Jansen). @@ -5446,7 +5446,7 @@ was last updated, see the repository revision history: an 8-pixel Courier font, taken from the X window distribution). 1.1.4b1 ----------------- +------- - Added experimental EXIF support for JPEG files. To extract EXIF information from a JPEG file, open the file as usual, and call the @@ -5472,7 +5472,7 @@ was last updated, see the repository revision history: module. 1.1.4a4 ----------------- +------- - Added updated SANE driver (Andrew Kuchling, Abel Deuring) @@ -5487,7 +5487,7 @@ was last updated, see the repository revision history: - Ignore Unicode characters in the BDF loader (from Graham Dumpleton) 1.1.4a3 released; windows only ---------------------------------- +------------------------------ - Added experimental RGBA-on-RGB drawing support. To use RGBA colours on an RGB image, pass "RGBA" as the second string to @@ -5505,7 +5505,7 @@ was last updated, see the repository revision history: device context. 1.1.4a2 ----------------- +------- - Improved support for 16-bit unsigned integer images (mode "I;16"). This includes TIFF reader support, and support for "getextrema" @@ -5524,7 +5524,7 @@ was last updated, see the repository revision history: filenames (not in 1.1.4), or None if neither was found. 1.1.4a1 ----------------- +------- - Improved support for drawing RGB data in palette images. You can now use RGB tuples or colour names (see below) when drawing in a @@ -5574,13 +5574,13 @@ was last updated, see the repository revision history: - Improved decoder error messages. 1.1.3 final -------------------- +----------- - Made setup.py look for old versions of zlib. For some back- ground, see: https://zlib.net/advisory-2002-03-11.txt 1.1.3c2 ------------------ +------- - Added setup.py file (tested on Unix and Windows). You still need to build libImaging/imaging.lib in the traditional way, @@ -5593,7 +5593,7 @@ was last updated, see the repository revision history: buffer wasn't properly allocated). 1.1.3c1 ----------------- +------- - Added ANTIALIAS downsampling filter for high-quality "resize" and "thumbnail" operations. Also added filter option to the @@ -5670,7 +5670,7 @@ was last updated, see the repository revision history: for normal image operations. 1.1.2c1 and 1.1.2 final ------------------------- +----------------------- - Adapted to Python 2.1. Among other things, all uses of the "regex" module have been replaced with "re". @@ -5711,7 +5711,7 @@ was last updated, see the repository revision history: - Changed ImageChops operators to copy palette and info dictionary from the first image argument. 1.1.1 --------------- +----- - Additional fixes for Python 1.6/2.0, including TIFF "save" bug. @@ -5729,14 +5729,14 @@ was last updated, see the repository revision history: - To make it easier to distribute prebuilt versions of PIL, the tkinit binding stuff has been moved to a separate extension module, named "_imagingtk". 0.3b2 to 1.0 final -------------------- +------------------ - If there's no 16-bit integer (like on a Cray T3E), set INT16 to the smallest integer available. Most of the library works just fine anyway (from Bill Crutchfield) - Tweaks to make drawing work on big-endian platforms. 1.0c2 -------- +----- - If PIL is built with the WITH_TKINTER flag, ImageTk can automatically hook into a standard Tkinter build. You no longer need to build your own Tkinter to use the ImageTk module. @@ -5747,7 +5747,7 @@ was last updated, see the repository revision history: - ImageFont "load_path" now scans directory mentioned in .pth files (from Richard Jones). 1.0c1 --------------- +----- - The TIFF plugin has been rewritten. The new plugin fully supports all major PIL image modes (including F and I). @@ -5762,7 +5762,7 @@ was last updated, see the repository revision history: "xv" to choke upon. (bug reported by Les Schaffer). 1.0b2 --------------- +----- - Major speedups for rotate, transform(EXTENT), and transform(AFFINE) when using nearest neighbour resampling. @@ -5816,7 +5816,7 @@ was last updated, see the repository revision history: - Fixed -f option to "pilconvert.py" (from Anthony Baxter) 1.0b1 --------------- +----- - Added Toby J. Sargeant's quantization package. To enable quantization, use the "palette" option to "convert": @@ -5863,7 +5863,7 @@ was last updated, see the repository revision history: - Some file drivers didn't handle the optional "modify" argument to the load method. This resulted in exceptions when you used "paste" (and other methods that modify an image in place) on a newly opened file. 0.3b2 --------------- +----- The test suite includes 825 individual tests. @@ -5876,7 +5876,7 @@ The test suite includes 825 individual tests. - Some file drivers (including FLI/FLC, GIF, and IM) accidentally overwrote the offset method with an internal attribute. All drivers have been updated to use private attributes where possible. - The Image "histogram" method now works for "I" and "F" images. For these modes, PIL divides the range between the min and max values used in the image into 256 bins. - + You can also pass in your own min and max values via the "extrema" option: h = im.histogram(extrema=(0, 255)) @@ -5901,7 +5901,7 @@ The test suite includes 825 individual tests. *** WARNING: MAY BREAK EXISTING CODE *** 0.3b1 --------------- +----- The test suite includes 750 individual tests. @@ -5948,7 +5948,7 @@ The test suite includes 750 individual tests. - Various minor fixes. 0.3a4 --------------- +----- - Added experimental IPTC/NAA support. @@ -5965,7 +5965,7 @@ The test suite includes 750 individual tests. - Fixed "fromstring" and "tostring" for mode "I" images. 0.3a3 --------------- +----- The test suite includes 530 individual tests. @@ -5996,7 +5996,7 @@ The test suite includes 530 individual tests. - Fixed a potential buffer overrun in the GIF encoder. 0.3a2 --------------- +----- The test suite includes 400 individual tests. @@ -6027,14 +6027,14 @@ The test suite includes 400 individual tests. - Removed bogus references to the crack coder (ImagingCrack). 0.3a1 --------------- +----- - Make sure image is loaded in "tostring". - Added floating point packer (native 32-bit floats only). 0.1b1 to 0.2 (b5) ------------------- +----------------- - Modified "fromstring" and "tostring" methods to use file codecs. Also added "fromstring" factory method to create an image directly from data in a string. @@ -6198,7 +6198,7 @@ Expect more options in future releases. Also note that file writers silently ig - Added XPM (X pixmap) read support. 0.2b3 --------------- +----- - Added MNG (multi-image network graphics) read support. "Ming" is a proposed animation standard, based on the PNG file format. @@ -6251,7 +6251,7 @@ Expect more options in future releases. Also note that file writers silently ig - More conversions, including "RGB" to "1" and more. 0.2a1 --------------- +----- - Where earlier versions accepted lists, this version accepts arbitrary Python sequences (including strings, in some cases). A few resource leaks were plugged in the process. From fb361fc89ebabc8a12bd645947e88eeca40932bc Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 13 Apr 2022 15:57:20 +0300 Subject: [PATCH 092/294] Consider stale after some time, make available for closure immediately For https://github.com/python-pillow/Pillow/pull/6203 --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 50e28be5a..321f6f307 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -22,7 +22,7 @@ jobs: debug-only: true only-labels: "Awaiting OP Action" close-issue-message: "Closing this issue as no feedback has been received." - days-before-stale: -1 - days-before-issue-close: 1 + days-before-stale: 1 + days-before-issue-close: 0 days-before-pr-close: -1 labels-to-remove-when-unstale: "Awaiting OP Action" From 317ea9a8bbc061a8276f789d8fec626b84618673 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 13 Apr 2022 16:04:23 +0300 Subject: [PATCH 093/294] Remove debug mode so it can close stale issues --- .github/workflows/stale.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 321f6f307..89ee84485 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -19,7 +19,6 @@ jobs: uses: actions/stale@v5 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - debug-only: true only-labels: "Awaiting OP Action" close-issue-message: "Closing this issue as no feedback has been received." days-before-stale: 1 From 0329b8fa1d7ad3ae39a171a903b328a56046003a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 13 Apr 2022 16:07:20 +0300 Subject: [PATCH 094/294] Seven days before stale --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 89ee84485..cc5e0d488 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -21,7 +21,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} only-labels: "Awaiting OP Action" close-issue-message: "Closing this issue as no feedback has been received." - days-before-stale: 1 + days-before-stale: 7 days-before-issue-close: 0 days-before-pr-close: -1 labels-to-remove-when-unstale: "Awaiting OP Action" From 5f5e30880a9aff7efcdb2d0649759ccbc06c2228 Mon Sep 17 00:00:00 2001 From: Max Base Date: Thu, 14 Apr 2022 13:35:04 +0430 Subject: [PATCH 095/294] change aloc to `*aloc` --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 92e662485..bddc6a463 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3147,7 +3147,7 @@ Changelog (Pillow) - Change function declaration to match Tcl_CmdProc type #1966 [homm] -- Integer overflow checks on all calls to alloc #1781 +- Integer overflow checks on all calls to ``*alloc`` #1781 [wiredfool] - Change equals method on Image so it short circuits #1967 From ae12b1d2dccd0c7eacd471b7e44928c8379b904e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Apr 2022 18:23:02 +1000 Subject: [PATCH 096/294] Removed unused function --- Tests/test_imagemorph.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 368c2bba1..8f6ce7f8f 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -48,10 +48,6 @@ def img_string_normalize(im): return img_to_string(string_to_img(im)) -def assert_img_equal(A, B): - assert img_to_string(A) == img_to_string(B) - - def assert_img_equal_img_string(A, Bstring): assert img_to_string(A) == img_string_normalize(Bstring) From 72a59d298a578ee02452a615da16c005f28c664b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 15 Apr 2022 19:00:23 +1000 Subject: [PATCH 097/294] Variable in function should be snake_case --- src/PIL/TiffImagePlugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 8b7144c83..000429991 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1880,16 +1880,16 @@ class AppendingTiffWriter: self.whereToWriteNewIFDOffset = None self.offsetOfNewPage = 0 - self.IIMM = IIMM = self.f.read(4) - if not IIMM: + self.IIMM = iimm = self.f.read(4) + if not iimm: # empty file - first page self.isFirst = True return self.isFirst = False - if IIMM == b"II\x2a\x00": + if iimm == b"II\x2a\x00": self.setEndian("<") - elif IIMM == b"MM\x00\x2a": + elif iimm == b"MM\x00\x2a": self.setEndian(">") else: raise RuntimeError("Invalid TIFF file header") From 136d1a89dfb96a51e95ffe36139acdb6cccda55d Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 15 Apr 2022 12:08:15 +0300 Subject: [PATCH 098/294] Fix deprecation warning Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/Image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 0c674fff2..03fb63813 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -68,7 +68,7 @@ def __getattr__(name): "ANTIALIAS": "LANCZOS", } if name in old_resampling: - deprecate(name, 10, old_resampling[name]) + deprecate(name, 10, f"Resampling.{old_resampling[name]}") return Resampling[old_resampling[name]] for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): if name in enum.__members__: From 12665d8db4ad237d6f2cab8299a2ec96fe510dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hendrik=20M=C3=BCller?= <44469195+kolibril13@users.noreply.github.com> Date: Fri, 15 Apr 2022 14:02:52 +0200 Subject: [PATCH 099/294] add read from URL example --- docs/handbook/tutorial.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst index 9f185feba..1d9d97a93 100644 --- a/docs/handbook/tutorial.rst +++ b/docs/handbook/tutorial.rst @@ -504,6 +504,17 @@ image header. In addition, seek will also be used when the image data is read tar file, you can use the :py:class:`~PIL.ContainerIO` or :py:class:`~PIL.TarIO` modules to access it. +Reading from URL +^^^^^^^^^^^^^^^^ + +:: + + from PIL import Image + from urllib.request import urlopen + url = "https://raw.githubusercontent.com/python-pillow/pillow-logo/main/pillow-logo-248x250.png" + img = Image.open(urlopen(url)) + + Reading from a tar archive ^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 100/294] 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 3b1332542..c85876ad1 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 101/294] 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 c85876ad1..5597b2b34 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 e5f09dad4..f2c08488f 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 f9bf1cd59d81ba491421d2112ff2a9c54fe137e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hendrik=20M=C3=BCller?= <44469195+kolibril13@users.noreply.github.com> Date: Sat, 16 Apr 2022 08:12:54 +0200 Subject: [PATCH 102/294] Update docs/handbook/tutorial.rst Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/handbook/tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst index 1d9d97a93..50133f15e 100644 --- a/docs/handbook/tutorial.rst +++ b/docs/handbook/tutorial.rst @@ -511,7 +511,7 @@ Reading from URL from PIL import Image from urllib.request import urlopen - url = "https://raw.githubusercontent.com/python-pillow/pillow-logo/main/pillow-logo-248x250.png" + url = "https://python-pillow.org/images/pillow-logo.png" img = Image.open(urlopen(url)) From c8118438ae4ad477f8b4f7c969e2c16b892a0d8f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Apr 2022 17:07:39 +1000 Subject: [PATCH 103/294] 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 | 25 ++++++++++++++-------- src/PIL/GifImagePlugin.py | 4 ++-- 3 files changed, 18 insertions(+), 11 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 dffd1006f..d177520e2 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) +@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) + # 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 b798bb969..5c8016839 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -183,8 +183,6 @@ class GifImageFile(ImageFile.ImageFile): if not s or s == b";": raise EOFError - self.__frame = frame - self.tile = [] palette = None @@ -283,6 +281,8 @@ class GifImageFile(ImageFile.ImageFile): 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 104/294] 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 5597b2b34..8683e72d7 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 280b4e9e1adab91ec31a9b30908aaaa75be6c3be Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Apr 2022 22:15:21 +1000 Subject: [PATCH 105/294] Updated variable name in comment --- src/PIL/SgiImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/SgiImagePlugin.py b/src/PIL/SgiImagePlugin.py index 0ef58cf41..f0207bb77 100644 --- a/src/PIL/SgiImagePlugin.py +++ b/src/PIL/SgiImagePlugin.py @@ -182,7 +182,7 @@ def _save(im, fp, filename): fp.write(struct.pack(">l", pinmax)) fp.write(struct.pack("4s", b"")) # dummy fp.write(struct.pack("79s", img_name)) # truncates to 79 chars - fp.write(struct.pack("s", b"")) # force null byte after imgname + fp.write(struct.pack("s", b"")) # force null byte after img_name fp.write(struct.pack(">l", colormap)) fp.write(struct.pack("404s", b"")) # dummy From e4b39d9cc74f26dad1f5cad0851645cc2cd5356d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 16 Apr 2022 22:15:48 +1000 Subject: [PATCH 106/294] Variable in function should be snake_case --- src/PIL/ImageFilter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PIL/ImageFilter.py b/src/PIL/ImageFilter.py index af33c547e..e10c6fdf1 100644 --- a/src/PIL/ImageFilter.py +++ b/src/PIL/ImageFilter.py @@ -421,8 +421,8 @@ class Color3DLUT(MultibandFilter): except TypeError: size = (size, size, size) size = [int(x) for x in size] - for size1D in size: - if not 2 <= size1D <= 65: + for size_1d in size: + if not 2 <= size_1d <= 65: raise ValueError("Size should be in [2, 65] range.") return size From a766b5b0d5ab541dd93876f124da2b080fa79ff0 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 16 Apr 2022 17:07:32 +0200 Subject: [PATCH 107/294] deprecate fill in imageFont --- Tests/test_imagefont.py | 5 +++++ src/PIL/ImageFont.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index f9d0a4c4f..a8fdddc24 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -977,6 +977,11 @@ class TestImageFont: assert_image_similar_tofile(im, "Tests/images/colr_bungee_mask.png", 22) + def test_fill_deprecation(self): + font = self.get_font() + with pytest.warns(DeprecationWarning): + font.getmask2("Hello world", fill=Image.core.fill) + @skip_unless_feature("raqm") class TestImageFont_RaqmLayout(TestImageFont): diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index f420e197e..77dce3bc6 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -602,7 +602,7 @@ class FreeTypeFont: self, text, mode="", - fill=Image.core.fill, + fill=None, direction=None, features=None, language=None, @@ -627,6 +627,8 @@ class FreeTypeFont: .. versionadded:: 1.1.5 + :param fill: Fill function. Deprecated. + :param direction: Direction of the text. It can be 'rtl' (right to left), 'ltr' (left to right) or 'ttb' (top to bottom). Requires libraqm. @@ -674,6 +676,10 @@ class FreeTypeFont: :py:mod:`PIL.Image.core` interface module, and the text offset, the gap between the starting coordinate and the first marking """ + if fill is not None: + deprecate("fill", 10) + else: + fill = Image.core.fill size, offset = self.font.getsize( text, mode, direction, features, language, anchor ) From d088c804d0df4f790209dddf2d4b83e2be75561b Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 16 Apr 2022 17:36:21 +0200 Subject: [PATCH 108/294] release notes --- docs/releasenotes/9.2.0.rst | 49 +++++++++++++++++++++++++++++++++++++ docs/releasenotes/index.rst | 1 + 2 files changed, 50 insertions(+) create mode 100644 docs/releasenotes/9.2.0.rst diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst new file mode 100644 index 000000000..35e8ca2d5 --- /dev/null +++ b/docs/releasenotes/9.2.0.rst @@ -0,0 +1,49 @@ +9.2.0 +----- + +Backwards Incompatible Changes +============================== + +TODO +^^^^ + +Deprecations +============ + +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). + +API Changes +=========== + +TODO +^^^^ + +TODO + +API Additions +============= + +TODO +^^^^ + +TODO + +Security +======== + +TODO +^^^^ + +TODO + +Other Changes +============= + +TODO +^^^^ + +TODO diff --git a/docs/releasenotes/index.rst b/docs/releasenotes/index.rst index 656acef95..db578bdb7 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.2.0 9.1.0 9.0.1 9.0.0 From b4b8249c9d7678dc8c08efa4c32a2b4b17a26b54 Mon Sep 17 00:00:00 2001 From: nulano Date: Sat, 16 Apr 2022 17:46:43 +0200 Subject: [PATCH 109/294] None is also not a valid value --- Tests/test_imagefont.py | 3 +++ src/PIL/ImageFont.py | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index a8fdddc24..c074c5205 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -981,6 +981,9 @@ class TestImageFont: font = self.get_font() with pytest.warns(DeprecationWarning): font.getmask2("Hello world", fill=Image.core.fill) + with pytest.raises(TypeError): + with pytest.warns(DeprecationWarning): + font.getmask2("Hello world", fill=None) @skip_unless_feature("raqm") diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 77dce3bc6..7a34160f9 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -64,6 +64,9 @@ except ImportError: core = _imagingft_not_installed() +_UNSPECIFIED = object() + + # FIXME: add support for pilfont2 format (see FontFile.py) # -------------------------------------------------------------------- @@ -602,7 +605,7 @@ class FreeTypeFont: self, text, mode="", - fill=None, + fill=_UNSPECIFIED, direction=None, features=None, language=None, @@ -676,7 +679,7 @@ class FreeTypeFont: :py:mod:`PIL.Image.core` interface module, and the text offset, the gap between the starting coordinate and the first marking """ - if fill is not None: + if fill is not _UNSPECIFIED: deprecate("fill", 10) else: fill = Image.core.fill From 2ae52552f787d5916763b7fc970a27d72d612e9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 13 Apr 2022 09:54:17 +1000 Subject: [PATCH 110/294] 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 de21db8f0..d5c748226 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -57,7 +57,7 @@ class DcxImageFile(PcxImageFile): 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 @@ class DcxImageFile(PcxImageFile): 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 ea9503305..7df301904 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -91,7 +91,7 @@ class FliImageFile(ImageFile.ImageFile): # 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 @@ class FliImageFile(ImageFile.ImageFile): 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 @@ class FliImageFile(ImageFile.ImageFile): 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 @@ class FliImageFile(ImageFile.ImageFile): 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 d36d8c61a..cfb6c0355 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -102,7 +102,7 @@ class GifImageFile(ImageFile.ImageFile): 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 @@ class GifImageFile(ImageFile.ImageFile): 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 @@ class GifImageFile(ImageFile.ImageFile): 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 @@ class GifImageFile(ImageFile.ImageFile): s = None if interlace is None: - # self.__fp = None + # self._fp = None raise EOFError if not update_image: return @@ -443,14 +443,14 @@ class GifImageFile(ImageFile.ImageFile): 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 f7e690b35..3c5739f3d 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -245,7 +245,7 @@ class ImImageFile(ImageFile.ImageFile): 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 @@ class ImImageFile(ImageFile.ImageFile): 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 3c36178bd..16fad61c1 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -544,8 +544,8 @@ class Image: 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 @@ class Image: 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 9248b1b65..324c8eff4 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -62,7 +62,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): 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 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): 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 88c1bfcc5..0d61746b7 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -58,20 +58,20 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): 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 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): 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 c939b86e7..313090e8d 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -689,7 +689,7 @@ class PngImageFile(ImageFile.ImageFile): 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 @@ class PngImageFile(ImageFile.ImageFile): 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 @@ class PngImageFile(ImageFile.ImageFile): 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 @@ class PngImageFile(ImageFile.ImageFile): 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 @@ class PngImageFile(ImageFile.ImageFile): 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 @@ class PngImageFile(ImageFile.ImageFile): 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 283219579..3be9aa290 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -132,7 +132,7 @@ class PsdImageFile(ImageFile.ImageFile): 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 @@ class PsdImageFile(ImageFile.ImageFile): 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 @@ class PsdImageFile(ImageFile.ImageFile): # 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 1a72f5c04..0a65c286c 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -149,7 +149,7 @@ class SpiderImageFile(ImageFile.ImageFile): 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 @@ class SpiderImageFile(ImageFile.ImageFile): 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 @@ class SpiderImageFile(ImageFile.ImageFile): 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 000429991..ee737cb59 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1073,7 +1073,7 @@ class TiffImageFile(ImageFile.ImageFile): # 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 @@ class TiffImageFile(ImageFile.ImageFile): 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 @@ class TiffImageFile(ImageFile.ImageFile): 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 111/294] 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 d5c748226..aeed1e7c7 100644 --- a/src/PIL/DcxImagePlugin.py +++ b/src/PIL/DcxImagePlugin.py @@ -74,15 +74,6 @@ class DcxImageFile(PcxImageFile): 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 7df301904..e13b1779c 100644 --- a/src/PIL/FliImagePlugin.py +++ b/src/PIL/FliImagePlugin.py @@ -153,15 +153,6 @@ class FliImageFile(ImageFile.ImageFile): 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 cfb6c0355..33c76586b 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -443,15 +443,6 @@ class GifImageFile(ImageFile.ImageFile): 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 3c5739f3d..ee95a94cb 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -301,15 +301,6 @@ class ImImageFile(ImageFile.ImageFile): 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 16fad61c1..1409a20d2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -544,8 +544,10 @@ class Image: 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 @@ class Image: 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 324c8eff4..0de37cf37 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -89,15 +89,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): 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 0d61746b7..fc3f8556f 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -97,15 +97,6 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): 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 313090e8d..856c21802 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1006,15 +1006,6 @@ class PngImageFile(ImageFile.ImageFile): 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 3be9aa290..9622e648a 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -155,15 +155,6 @@ class PsdImageFile(ImageFile.ImageFile): # 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 0a65c286c..154008c08 100644 --- a/src/PIL/SpiderImagePlugin.py +++ b/src/PIL/SpiderImagePlugin.py @@ -191,15 +191,6 @@ class SpiderImageFile(ImageFile.ImageFile): 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 ee737cb59..c871072ad 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1515,15 +1515,6 @@ class TiffImageFile(ImageFile.ImageFile): 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 112/294] 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 0de37cf37..d4f6c90f7 100644 --- a/src/PIL/MicImagePlugin.py +++ b/src/PIL/MicImagePlugin.py @@ -62,7 +62,6 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): 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 113/294] 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 d1d5c85c1..ad61a07cc 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 c1ad4a7f0..a7d43d2e9 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 fd30cded0..db64dd1af 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 ca3ea8419..d9b59321b 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 16c43b00f..8fdae4f13 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -70,6 +70,15 @@ class TestFileTiff: 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 1409a20d2..3de2c8cd2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -547,7 +547,7 @@ class Image: 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 @@ class Image: 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 2dd848ca4f76d71271c8e635ce248a53a6d960a9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 17 Apr 2022 08:06:28 +0300 Subject: [PATCH 114/294] Include deprecation removal date Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageTk.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index 51a4db5af..59b3f8976 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -184,7 +184,8 @@ class PhotoImage: :param im: A PIL image. The size must match the target region. If the mode does not match, the image is converted to the mode of the bitmap image. - :param box: Deprecated. + :param box: Deprecated. This parameter will be removed in Pillow 10 + (2023-07-01). """ if box is not None: From 98e995f6c79378ab831a5863563784c22ecde07b Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 Apr 2022 14:23:46 +1000 Subject: [PATCH 115/294] FreeTypeFont.getmask2 fill parameter has been deprecated --- docs/deprecations.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index d4d5907ea..496a4df95 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -149,6 +149,14 @@ PhotoImage.paste box parameter The ``box`` parameter is unused. It will be removed in Pillow 10.0.0 (2023-07-01). +FreeTypeFont.getmask2 fill parameter +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 + +The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been +deprecated and will be removed in Pillow 10 (2023-07-01). + Removed features ---------------- From 2a29b2dba1d075784e53c81dafb9626f83c63e2d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 Apr 2022 14:33:16 +1000 Subject: [PATCH 116/294] Document that fill parameter is optional --- src/PIL/ImageFont.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 7a34160f9..95059d5b2 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -630,7 +630,10 @@ class FreeTypeFont: .. versionadded:: 1.1.5 - :param fill: Fill function. Deprecated. + :param fill: Optional fill function. By default, an internal Pillow function + will be used. + + Deprecated. :param direction: Direction of the text. It can be 'rtl' (right to left), 'ltr' (left to right) or 'ttb' (top to bottom). From c87c2cf5b3d836ff53577aa292c581eea6c5f62f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 17 Apr 2022 14:33:27 +1000 Subject: [PATCH 117/294] Added removal date for fill parameter --- src/PIL/ImageFont.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 95059d5b2..ef6a8d4c7 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -633,7 +633,8 @@ class FreeTypeFont: :param fill: Optional fill function. By default, an internal Pillow function will be used. - Deprecated. + Deprecated. This parameter will be removed in Pillow 10 + (2023-07-01). :param direction: Direction of the text. It can be 'rtl' (right to left), 'ltr' (left to right) or 'ttb' (top to bottom). From ab86bdda0a751c7e9357b3396288d6d96628ed15 Mon Sep 17 00:00:00 2001 From: nulano Date: Sun, 17 Apr 2022 16:02:51 +0200 Subject: [PATCH 118/294] fix deprecation test --- Tests/test_imagefont.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index c074c5205..0e1d1e637 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -981,8 +981,8 @@ class TestImageFont: font = self.get_font() with pytest.warns(DeprecationWarning): font.getmask2("Hello world", fill=Image.core.fill) - with pytest.raises(TypeError): - with pytest.warns(DeprecationWarning): + with pytest.warns(DeprecationWarning): + with pytest.raises(TypeError): font.getmask2("Hello world", fill=None) From 995be34ddb80de3fe1b806deff81d80144353601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ondrej=20Baranovi=C4=8D?= Date: Sun, 17 Apr 2022 19:27:14 +0200 Subject: [PATCH 119/294] flip so the conditional is positive Co-authored-by: Hugo van Kemenade --- src/PIL/ImageFont.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index ef6a8d4c7..7a53de53c 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -683,10 +683,10 @@ class FreeTypeFont: :py:mod:`PIL.Image.core` interface module, and the text offset, the gap between the starting coordinate and the first marking """ - if fill is not _UNSPECIFIED: - deprecate("fill", 10) - else: + if fill is _UNSPECIFIED: fill = Image.core.fill + else: + deprecate("fill", 10) size, offset = self.font.getsize( text, mode, direction, features, language, anchor ) From e8b2b9baca9d4ad8d5d4d21028e29e8a862d3503 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 18 Apr 2022 12:53:55 +1000 Subject: [PATCH 120/294] 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 6ed8bb0c5..30e262eeb 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 661c5a5ec..6e496b9cb 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 121/294] 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 6e496b9cb..992fda58b 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 122/294] 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 30e262eeb..f18a6041b 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 123/294] 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 353dd0c19..fb860bbb0 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 bf46c6a648040a51e4e5091e67c7de2014b32cb2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 18 Apr 2022 07:42:05 +0300 Subject: [PATCH 124/294] Fix docstring Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageFont.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 8edf73b6a..f73d4a30e 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -634,7 +634,6 @@ class FreeTypeFont: should have mode ``RGBA``. Otherwise, it should have mode ``1``. :param text: Text to render. - :param fill: A fill function. :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 From 4279c742890a6bb09ebb2a3597a7f4878f784507 Mon Sep 17 00:00:00 2001 From: Alex Tedeschi <60716244+AlexTedeschi@users.noreply.github.com> Date: Mon, 18 Apr 2022 16:23:50 -0400 Subject: [PATCH 125/294] Increase wait time of temporary file deletion Increase wait time to 4 seconds from 2 seconds for the deletion of the temporary file made from .show(). The Windows default image viewer at times can take longer than 2 seconds to open and display an image causing the image to never display for the user. --- 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 66f86211f..9117f57e5 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -141,7 +141,7 @@ class WindowsViewer(Viewer): def get_command(self, file, **options): return ( f'start "Pillow" /WAIT "{file}" ' - "&& ping -n 2 127.0.0.1 >NUL " + "&& ping -n 4 127.0.0.1 >NUL " f'&& del /f "{file}"' ) From b01a2effd2a719fb094bcef7c471b05c07c48dfa Mon Sep 17 00:00:00 2001 From: Ray Gardner Date: Tue, 19 Apr 2022 13:40:56 -0600 Subject: [PATCH 126/294] Update image-file-formats.rst Correct encoding of GIF files from run-length to LZW. --- 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 e5082cf09..c02965a05 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -101,7 +101,7 @@ GIF ^^^ Pillow reads GIF87a and GIF89a versions of the GIF file format. The library -writes run-length encoded files in GIF87a by default, unless GIF89a features +writes LZW encoded files in GIF87a by default, unless GIF89a features are used or GIF89a is already in use. GIF files are initially read as grayscale (``L``) or palette mode (``P``) From 110238bccb0ec6682d585a953cc3d0f4630d69ee Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 20 Apr 2022 08:37:45 +1000 Subject: [PATCH 127/294] Updated fribidi to 1.0.12 --- 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 9f9bdf198..b8c8f8dd3 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -292,9 +292,9 @@ deps = { "libs": [r"*.lib"], }, "fribidi": { - "url": "https://github.com/fribidi/fribidi/archive/v1.0.11.zip", - "filename": "fribidi-1.0.11.zip", - "dir": "fribidi-1.0.11", + "url": "https://github.com/fribidi/fribidi/archive/v1.0.12.zip", + "filename": "fribidi-1.0.12.zip", + "dir": "fribidi-1.0.12", "build": [ cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"), cmd_cmake(), From 0374d6190b1c4c6ccf3f999621007a19687d58a6 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 20 Apr 2022 11:05:12 +1000 Subject: [PATCH 128/294] Skip tests unless libtiff is available --- Tests/test_image_resize.py | 2 ++ Tests/test_image_thumbnail.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index 6961afa60..8347fabb9 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -12,6 +12,7 @@ from .helper import ( assert_image_equal_tofile, assert_image_similar, hopper, + skip_unless_feature, ) @@ -264,6 +265,7 @@ class TestImageResize: with pytest.raises(ValueError): im.resize((10, 10), "unknown") + @skip_unless_feature("libtiff") def test_load_first(self): # load() may change the size of the image # Test that resize() is calling it before getting the size diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index 858db9a0a..20cc101ed 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -7,6 +7,7 @@ from .helper import ( assert_image_similar, fromstring, hopper, + skip_unless_feature, tostring, ) @@ -88,6 +89,7 @@ def test_no_resize(): assert im.size == (64, 64) +@skip_unless_feature("libtiff") def test_load_first(): # load() may change the size of the image # Test that thumbnail() is calling it before performing size calculations 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 129/294] 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 8683e72d7..e68275e42 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 130/294] 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 e68275e42..0874bc01a 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 131/294] 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 16c43b00f..1bdc4639a 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -690,6 +690,13 @@ class TestFileTiff: 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 000429991..3a3e5c430 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 132/294] 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 0874bc01a..70667bce6 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 133/294] 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 d83c584b5..a43548ae0 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -497,8 +497,8 @@ class TestFileLibTiff(LibTiffTestCase): 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 000429991..6966467d6 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 df4ddc1d84ff143d9894413f899efaaabe9b382d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 18 Apr 2022 19:10:11 +1000 Subject: [PATCH 134/294] Run Tidelift Align on Pipfile changes --- .github/workflows/tidelift.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tidelift.yml b/.github/workflows/tidelift.yml index 2e8c9b730..9a3192f9d 100644 --- a/.github/workflows/tidelift.yml +++ b/.github/workflows/tidelift.yml @@ -4,9 +4,11 @@ on: - cron: "30 2 * * *" # daily at 02:30 UTC push: paths: + - "Pipfile*" - ".github/workflows/tidelift.yml" pull_request: paths: + - "Pipfile*" - ".github/workflows/tidelift.yml" workflow_dispatch: From de1ba373e10065e7bffe4bdb18a4aec40ef306a2 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 21 Apr 2022 22:10:55 +1000 Subject: [PATCH 135/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index dc69e4587..1f58dfe91 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Increase wait time of temporary file deletion on Windows #6224 + [AlexTedeschi] + +- Deprecate FreeTypeFont.getmask2 fill parameter #6220 + [nulano, radarhere, hugovk] + - Round lut values where necessary #6188 [radarhere] 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 136/294] 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 70667bce6..afddb8551 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 137/294] 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 0e1d1e637..0c50303f9 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -65,9 +65,12 @@ class TestImageFont: 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 81ac03fe6..4799d71fb 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -711,8 +711,13 @@ class FreeTypeFont: :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 9145c778ec380ed99e1be45bdf17a4cdd16f4e5c Mon Sep 17 00:00:00 2001 From: Dominus Iniquitatis Date: Fri, 22 Apr 2022 01:20:34 +0300 Subject: [PATCH 138/294] Fixed a typo in 9.1.0 changelog --- docs/releasenotes/9.1.0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/releasenotes/9.1.0.rst b/docs/releasenotes/9.1.0.rst index 80ce7604f..57646e558 100644 --- a/docs/releasenotes/9.1.0.rst +++ b/docs/releasenotes/9.1.0.rst @@ -76,8 +76,8 @@ Deprecated Use instead ``Image.TRANSPOSE`` ``Image.Transpose.TRANSPOSE`` ``Image.TRANSVERSE`` ``Image.Transpose.TRANSVERSE`` ``Image.BOX`` ``Image.Resampling.BOX`` -``Image.BILINEAR`` ``Image.Resampling.BILNEAR`` -``Image.LINEAR`` ``Image.Resampling.BILNEAR`` +``Image.BILINEAR`` ``Image.Resampling.BILINEAR`` +``Image.LINEAR`` ``Image.Resampling.BILINEAR`` ``Image.HAMMING`` ``Image.Resampling.HAMMING`` ``Image.BICUBIC`` ``Image.Resampling.BICUBIC`` ``Image.CUBIC`` ``Image.Resampling.BICUBIC`` From 21c91c1a36afa83223aea421c1dd879ccbfa89ff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 Apr 2022 08:46:27 +1000 Subject: [PATCH 139/294] Fixed typo [ci skip] --- docs/deprecations.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 496a4df95..479c79080 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -97,8 +97,8 @@ Deprecated Use instead ``Image.TRANSPOSE`` ``Image.Transpose.TRANSPOSE`` ``Image.TRANSVERSE`` ``Image.Transpose.TRANSVERSE`` ``Image.BOX`` ``Image.Resampling.BOX`` -``Image.BILINEAR`` ``Image.Resampling.BILNEAR`` -``Image.LINEAR`` ``Image.Resampling.BILNEAR`` +``Image.BILINEAR`` ``Image.Resampling.BILINEAR`` +``Image.LINEAR`` ``Image.Resampling.BILINEAR`` ``Image.HAMMING`` ``Image.Resampling.HAMMING`` ``Image.BICUBIC`` ``Image.Resampling.BICUBIC`` ``Image.CUBIC`` ``Image.Resampling.BICUBIC`` From a7438ecaf005de0b98d7ea2df08fa8f9be9e4c0f Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 22 Apr 2022 17:35:20 +1000 Subject: [PATCH 140/294] Added Ubuntu 22.04 to Docker jobs --- .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 fc4667387..497b994db 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -27,6 +27,7 @@ jobs: gentoo, ubuntu-18.04-bionic-amd64, ubuntu-20.04-focal-amd64, + ubuntu-22.04-jammy-amd64, ] dockerTag: [main] include: diff --git a/docs/installation.rst b/docs/installation.rst index ce28cbc91..41e99797c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -474,6 +474,8 @@ These platforms are built and tested for every change. | | 3.8 | arm64v8, ppc64le, | | | | s390x | +----------------------------------+----------------------------+---------------------+ +| Ubuntu Linux 22.04 LTS (Jammy) | 3.10 | x86-64 | ++----------------------------------+----------------------------+---------------------+ | Windows Server 2016 | 3.7 | x86-64 | +----------------------------------+----------------------------+---------------------+ | Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 | 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 141/294] 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 afddb8551..8353714af 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 f854d7f7c98c512c81d8b36b75d079260a14c1d4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 22 Apr 2022 12:06:20 +0300 Subject: [PATCH 142/294] Deprecate support for Qt 5 (PyQt5 and PySide2). Use Qt 6 (PyQt6 or PySide6) instead. --- Tests/test_image_fromqimage.py | 8 +++++++- Tests/test_imageqt.py | 7 +++++-- Tests/test_imageqt_deprecated.py | 18 ++++++++++++++++++ Tests/test_qt_image_qapplication.py | 6 +++++- Tests/test_qt_image_toqimage.py | 6 +++++- docs/deprecations.rst | 27 ++++++++++++++++++++------- docs/reference/ImageQt.rst | 8 ++++++++ docs/releasenotes/9.2.0.rst | 15 +++++++++++++++ src/PIL/ImageQt.py | 5 +++++ 9 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 Tests/test_imageqt_deprecated.py diff --git a/Tests/test_image_fromqimage.py b/Tests/test_image_fromqimage.py index 5ad5b5c3c..7fe992353 100644 --- a/Tests/test_image_fromqimage.py +++ b/Tests/test_image_fromqimage.py @@ -1,6 +1,12 @@ +import warnings + import pytest -from PIL import Image, ImageQt +from PIL import Image + +with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + from PIL import ImageQt from .helper import assert_image_equal, hopper diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index a42240d49..d6de69dd5 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -2,10 +2,13 @@ import warnings import pytest -from PIL import ImageQt - from .helper import assert_image_similar, hopper +with warnings.catch_warnings() as w: + warnings.simplefilter("always", category=DeprecationWarning) + from PIL import ImageQt + + pytestmark = pytest.mark.skipif( not ImageQt.qt_is_installed, reason="Qt bindings are not installed" ) diff --git a/Tests/test_imageqt_deprecated.py b/Tests/test_imageqt_deprecated.py new file mode 100644 index 000000000..2662995f7 --- /dev/null +++ b/Tests/test_imageqt_deprecated.py @@ -0,0 +1,18 @@ +import warnings + + +def test_deprecated(): + with warnings.catch_warnings(record=True) as w: + # Arrange: cause all warnings to always be triggered + warnings.simplefilter("always") + + # Act: trigger a warning with Qt5 + from PIL import ImageQt + + # Assert + if ImageQt.qt_version in ("5", "side2"): + assert len(w) == 1 + assert issubclass(w[0].category, DeprecationWarning) + assert "deprecated" in str(w[0].message) + else: + assert len(w) == 0 diff --git a/Tests/test_qt_image_qapplication.py b/Tests/test_qt_image_qapplication.py index dec790c50..1fc816146 100644 --- a/Tests/test_qt_image_qapplication.py +++ b/Tests/test_qt_image_qapplication.py @@ -1,6 +1,10 @@ +import warnings + import pytest -from PIL import ImageQt +with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + from PIL import ImageQt from .helper import assert_image_equal, assert_image_equal_tofile, hopper diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index 2a6b29abe..60bfaeb9b 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -1,6 +1,10 @@ +import warnings + import pytest -from PIL import ImageQt +with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + from PIL import ImageQt from .helper import assert_image_equal, assert_image_equal_tofile, hopper diff --git a/docs/deprecations.rst b/docs/deprecations.rst index 496a4df95..69044c8c8 100644 --- a/docs/deprecations.rst +++ b/docs/deprecations.rst @@ -142,13 +142,6 @@ The stub image plugin ``FitsStubImagePlugin`` has been deprecated and will be re Pillow 10.0.0 (2023-07-01). FITS images can be read without a handler through :mod:`~PIL.FitsImagePlugin` instead. -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). - FreeTypeFont.getmask2 fill parameter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -157,6 +150,26 @@ 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). + +PyQt5 and PySide2 +~~~~~~~~~~~~~~~~~ + +.. deprecated:: 9.2.0 + +`Qt 5 reached end-of-life `_ on 2020-12-08 for +open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). + +Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed +in Pillow 10 (2023-07-01). Upgrade to +`PyQt6 `_ or +`PySide6 `_ instead. + Removed features ---------------- diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst index 66f5880a3..15d052d1c 100644 --- a/docs/reference/ImageQt.rst +++ b/docs/reference/ImageQt.rst @@ -7,6 +7,14 @@ The :py:mod:`~PIL.ImageQt` module contains support for creating PyQt6, PySide6, PyQt5 or PySide2 QImage objects from PIL images. +`Qt 5 reached end-of-life `_ on 2020-12-08 for +open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). + +Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed +in Pillow 10 (2023-07-01). Upgrade to +`PyQt6 `_ or +`PySide6 `_ instead. + .. versionadded:: 1.1.6 .. py:class:: ImageQt(image) diff --git a/docs/releasenotes/9.2.0.rst b/docs/releasenotes/9.2.0.rst index 35e8ca2d5..c38944b10 100644 --- a/docs/releasenotes/9.2.0.rst +++ b/docs/releasenotes/9.2.0.rst @@ -10,9 +10,24 @@ TODO Deprecations ============ +PyQt5 and PySide2 +^^^^^^^^^^^^^^^^^ + +.. deprecated:: 9.2.0 + +`Qt 5 reached end-of-life `_ on 2020-12-08 for +open-source users (and will reach EOL on 2023-12-08 for commercial licence holders). + +Support for PyQt5 and PySide2 has been deprecated from ``ImageQt`` and will be removed +in Pillow 10 (2023-07-01). Upgrade to +`PyQt6 `_ or +`PySide6 `_ instead. + FreeTypeFont.getmask2 fill parameter ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. deprecated:: 9.2.0 + The undocumented ``fill`` parameter of :py:meth:`.FreeTypeFont.getmask2` has been deprecated and will be removed in Pillow 10 (2023-07-01). diff --git a/src/PIL/ImageQt.py b/src/PIL/ImageQt.py index c8143d394..a34678c78 100644 --- a/src/PIL/ImageQt.py +++ b/src/PIL/ImageQt.py @@ -20,6 +20,7 @@ import sys from io import BytesIO from . import Image +from ._deprecate import deprecate from ._util import is_path qt_versions = [ @@ -42,9 +43,13 @@ for qt_version, qt_module in qt_versions: elif qt_module == "PyQt5": from PyQt5.QtCore import QBuffer, QIODevice from PyQt5.QtGui import QImage, QPixmap, qRgba + + deprecate("Support for PyQt5", 10, "PyQt6 or PySide6") elif qt_module == "PySide2": from PySide2.QtCore import QBuffer, QIODevice from PySide2.QtGui import QImage, QPixmap, qRgba + + deprecate("Support for PySide2", 10, "PyQt6 or PySide6") except (ImportError, RuntimeError): continue qt_is_installed = True From 40711f9edb20f9f2034cade8bb2ae94bbe84b4ac Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Apr 2022 08:01:02 +1000 Subject: [PATCH 143/294] Run deprecated import check before imports from other tests --- Tests/test_deprecated_imageqt.py | 18 ++++++++++++++++++ Tests/test_imageqt_deprecated.py | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 Tests/test_deprecated_imageqt.py delete mode 100644 Tests/test_imageqt_deprecated.py diff --git a/Tests/test_deprecated_imageqt.py b/Tests/test_deprecated_imageqt.py new file mode 100644 index 000000000..2528ff3f7 --- /dev/null +++ b/Tests/test_deprecated_imageqt.py @@ -0,0 +1,18 @@ +import warnings + +with warnings.catch_warnings(record=True) as w: + # Arrange: cause all warnings to always be triggered + warnings.simplefilter("always") + + # Act: trigger a warning with Qt5 + from PIL import ImageQt + + +def test_deprecated(): + # Assert + if ImageQt.qt_version in ("5", "side2"): + assert len(w) == 1 + assert issubclass(w[0].category, DeprecationWarning) + assert "deprecated" in str(w[0].message) + else: + assert len(w) == 0 diff --git a/Tests/test_imageqt_deprecated.py b/Tests/test_imageqt_deprecated.py deleted file mode 100644 index 2662995f7..000000000 --- a/Tests/test_imageqt_deprecated.py +++ /dev/null @@ -1,18 +0,0 @@ -import warnings - - -def test_deprecated(): - with warnings.catch_warnings(record=True) as w: - # Arrange: cause all warnings to always be triggered - warnings.simplefilter("always") - - # Act: trigger a warning with Qt5 - from PIL import ImageQt - - # Assert - if ImageQt.qt_version in ("5", "side2"): - assert len(w) == 1 - assert issubclass(w[0].category, DeprecationWarning) - assert "deprecated" in str(w[0].message) - else: - assert len(w) == 0 From 47cf0296c3a1ad444bfda9a7f431887661b72b14 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Apr 2022 10:22:29 +1000 Subject: [PATCH 144/294] Updated to PyQt6 --- .ci/install.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index efc57a641..100424ee9 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -34,12 +34,10 @@ python3 -m pip install pyroma python3 -m pip install test-image-results python3 -m pip install numpy -# PyQt5 doesn't support PyPy3 +# PyQt6 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 + sudo apt-get -qq install libegl1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxkbcommon-x11-0 + python3 -m pip install pyqt6 fi # webp From 4deb85389fc561678afbb194896af26a4fc87ac6 Mon Sep 17 00:00:00 2001 From: Max Base Date: Sat, 23 Apr 2022 12:24:03 +0430 Subject: [PATCH 145/294] Update CHANGES.rst Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index bddc6a463..e6a9688c5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3138,6 +3138,7 @@ Changelog (Pillow) - Changed depends/install_*.sh urls to point to github pillow-depends repo #1983 [wiredfool] + - Allow ICC profile from ``encoderinfo`` while saving PNGs #1909 [homm] From a1972168ccbcbb7146851347477e15012d2fb122 Mon Sep 17 00:00:00 2001 From: Max Base Date: Sat, 23 Apr 2022 12:24:09 +0430 Subject: [PATCH 146/294] Update CHANGES.rst Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index e6a9688c5..156888dab 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1527,6 +1527,7 @@ Changelog (Pillow) - Deprecate PIL.*ImagePlugin.__version__ attributes #3628 [jdufresne] + - Docs: Add note about ImageDraw operations that exceed image bounds #3620 [radarhere] 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 147/294] 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 d621a5336..000000000 --- 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 8353714af..d05250a8d 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 148/294] 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 d05250a8d..a82c7446a 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 149/294] 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 a82c7446a..f4e98adeb 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 150/294] 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 f4e98adeb..b037d3343 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 53a8e747cd5b2241dae93774fffa31808c9fed29 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 24 Apr 2022 14:12:03 +1000 Subject: [PATCH 151/294] Updated harfbuzz to 4.2.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 b8c8f8dd3..ff36a61c9 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -280,9 +280,9 @@ deps = { "libs": [r"imagequant.lib"], }, "harfbuzz": { - "url": "https://github.com/harfbuzz/harfbuzz/archive/4.2.0.zip", - "filename": "harfbuzz-4.2.0.zip", - "dir": "harfbuzz-4.2.0", + "url": "https://github.com/harfbuzz/harfbuzz/archive/4.2.1.zip", + "filename": "harfbuzz-4.2.1.zip", + "dir": "harfbuzz-4.2.1", "build": [ cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"), cmd_nmake(target="clean"), From 889ceedfb98d9db07eb044c4b43f181815b6f9a7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 24 Apr 2022 08:28:21 +0300 Subject: [PATCH 152/294] Ignore warning Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- Tests/test_imageqt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index d6de69dd5..2f2b07918 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -5,7 +5,7 @@ import pytest from .helper import assert_image_similar, hopper with warnings.catch_warnings() as w: - warnings.simplefilter("always", category=DeprecationWarning) + warnings.simplefilter("ignore", category=DeprecationWarning) from PIL import ImageQt From 5867e0bbacb17a192b8c44a22aaca6be29dd2c39 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 24 Apr 2022 15:42:45 +1000 Subject: [PATCH 153/294] Decode bytes before passing to f-string --- Tests/test_file_ppm.py | 2 +- src/PIL/PpmImagePlugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 2c965318b..0f60d28ef 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -123,7 +123,7 @@ def test_token_too_long(tmp_path): with Image.open(path): pass - assert str(e.value) == "Token too long in file header: b'01234567890'" + assert str(e.value) == "Token too long in file header: 01234567890" def test_truncated_file(tmp_path): diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index b760e228d..14bf94d10 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -83,7 +83,7 @@ class PpmImageFile(ImageFile.ImageFile): # Token was not even 1 byte raise ValueError("Reached EOF while reading header") elif len(token) > 10: - raise ValueError(f"Token too long in file header: {token}") + raise ValueError(f"Token too long in file header: {token.decode()}") return token def _open(self): From f77aabf28134d93e35ca2d5622759c856333beb9 Mon Sep 17 00:00:00 2001 From: Sumanth Date: Sun, 24 Apr 2022 18:42:37 +0530 Subject: [PATCH 154/294] Update Image.py docstrings. Update Image.py file with a typo in effect_mandelbrot method. The Typo was in docstrings of the effect_mandelbrot method in Image module of PIL. --- 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 3c36178bd..eb190a0f6 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -3327,7 +3327,7 @@ def effect_mandelbrot(size, extent, quality): :param size: The requested size in pixels, as a 2-tuple: (width, height). :param extent: The extent to cover, as a 4-tuple: - (x0, y0, x1, y2). + (x0, y0, x1, y1). :param quality: Quality. """ return Image()._new(core.effect_mandelbrot(size, extent, quality)) From 139e1e81cc5be5911181171c28ad78bef4661aab Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 25 Apr 2022 11:18:31 +1000 Subject: [PATCH 155/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1f58dfe91..f13beb245 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Deprecate support for Qt 5 (PyQt5 and PySide2) #6237 + [hugovk, radarhere] + - Increase wait time of temporary file deletion on Windows #6224 [AlexTedeschi] From 5d4258e72bd3f43d062dd363a26eca4e6635e667 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 25 Apr 2022 22:50:08 +1000 Subject: [PATCH 156/294] Simplified index slicing --- Tests/test_color_lut.py | 12 ++++++------ Tests/test_imagesequence.py | 4 ++-- src/PIL/GribStubImagePlugin.py | 2 +- src/PIL/ImImagePlugin.py | 2 +- src/PIL/Image.py | 6 +++--- src/PIL/ImageColor.py | 2 +- src/PIL/ImageMorph.py | 4 ++-- src/PIL/JpegImagePlugin.py | 2 +- src/PIL/PaletteFile.py | 2 +- src/PIL/XpmImagePlugin.py | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 120ff777e..d2e2d5156 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -567,7 +567,7 @@ class TestTransformColorLut3D: assert tuple(lut.size) == tuple(source.size) assert len(lut.table) == len(source.table) assert lut.table != source.table - assert lut.table[0:10] == [0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0] + assert lut.table[:10] == [0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0] def test_3_to_4_channels(self): source = ImageFilter.Color3DLUT.generate((6, 5, 4), lambda r, g, b: (r, g, b)) @@ -576,7 +576,7 @@ class TestTransformColorLut3D: assert len(lut.table) != len(source.table) assert lut.table != source.table # fmt: off - assert lut.table[0:16] == [ + assert lut.table[:16] == [ 0.0, 0.0, 0.0, 1, 0.2**2, 0.0, 0.0, 1, 0.4**2, 0.0, 0.0, 1, 0.6**2, 0.0, 0.0, 1] # fmt: on @@ -592,7 +592,7 @@ class TestTransformColorLut3D: assert len(lut.table) != len(source.table) assert lut.table != source.table # fmt: off - assert lut.table[0:18] == [ + assert lut.table[:18] == [ 1.0, 1.0, 1.0, 0.75, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.96, 1.0, 0.75, 0.96, 1.0, 0.0, 0.96, 1.0] # fmt: on @@ -606,7 +606,7 @@ class TestTransformColorLut3D: assert len(lut.table) == len(source.table) assert lut.table != source.table # fmt: off - assert lut.table[0:16] == [ + assert lut.table[:16] == [ 0.0, 0.0, 0.0, 0.5, 0.2**2, 0.0, 0.0, 0.5, 0.4**2, 0.0, 0.0, 0.5, 0.6**2, 0.0, 0.0, 0.5] # fmt: on @@ -622,7 +622,7 @@ class TestTransformColorLut3D: assert len(lut.table) == len(source.table) assert lut.table != source.table # fmt: off - assert lut.table[0:18] == [ + assert lut.table[:18] == [ 0.0, 0.0, 0.0, 0.16, 0.0, 0.0, 0.24, 0.0, 0.0, 0.24, 0.0, 0.0, 0.8 - (0.8**2), 0, 0, 0, 0, 0] # fmt: on @@ -639,7 +639,7 @@ class TestTransformColorLut3D: assert len(lut.table) == len(source.table) assert lut.table != source.table # fmt: off - assert lut.table[0:16] == [ + assert lut.table[:16] == [ 0.0, 0.0, 0.0, 0.5, 0.25, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5] # fmt: on diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 7cf237b46..d79e8e525 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -77,9 +77,9 @@ def test_consecutive(): def test_palette_mmap(): # Using mmap in ImageFile can require to reload the palette. with Image.open("Tests/images/multipage-mmap.tiff") as im: - color1 = im.getpalette()[0:3] + color1 = im.getpalette()[:3] im.seek(0) - color2 = im.getpalette()[0:3] + color2 = im.getpalette()[:3] assert color1 == color2 diff --git a/src/PIL/GribStubImagePlugin.py b/src/PIL/GribStubImagePlugin.py index cc9bc2639..4575f8237 100644 --- a/src/PIL/GribStubImagePlugin.py +++ b/src/PIL/GribStubImagePlugin.py @@ -29,7 +29,7 @@ def register_handler(handler): def _accept(prefix): - return prefix[0:4] == b"GRIB" and prefix[7] == 1 + return prefix[:4] == b"GRIB" and prefix[7] == 1 class GribStubImageFile(ImageFile.StubImageFile): diff --git a/src/PIL/ImImagePlugin.py b/src/PIL/ImImagePlugin.py index f7e690b35..5563da4f5 100644 --- a/src/PIL/ImImagePlugin.py +++ b/src/PIL/ImImagePlugin.py @@ -210,7 +210,7 @@ class ImImageFile(ImageFile.ImageFile): self.mode = self.info[MODE] # Skip forward to start of image data - while s and s[0:1] != b"\x1A": + while s and s[:1] != b"\x1A": s = self.fp.read(1) if not s: raise SyntaxError("File truncated") diff --git a/src/PIL/Image.py b/src/PIL/Image.py index ca7f8308e..853ed15d5 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2582,7 +2582,7 @@ class Image: h = box[3] - box[1] if method == Transform.AFFINE: - data = data[0:6] + data = data[:6] elif method == Transform.EXTENT: # convert extent to an affine transform @@ -2593,12 +2593,12 @@ class Image: data = (xs, 0, x0, 0, ys, y0) elif method == Transform.PERSPECTIVE: - data = data[0:8] + data = data[:8] elif method == Transform.QUAD: # quadrilateral warp. data specifies the four corners # given as NW, SW, SE, and NE. - nw = data[0:2] + nw = data[:2] sw = data[2:4] se = data[4:6] ne = data[6:8] diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py index 958635bf9..3cf9ddafc 100644 --- a/src/PIL/ImageColor.py +++ b/src/PIL/ImageColor.py @@ -133,7 +133,7 @@ def getcolor(color, mode): # same as getrgb, but converts the result to the given mode color, alpha = getrgb(color), 255 if len(color) == 4: - color, alpha = color[0:3], color[3] + color, alpha = color[:3], color[3] if Image.getmodebase(mode) == "L": r, g, b = color diff --git a/src/PIL/ImageMorph.py b/src/PIL/ImageMorph.py index fe0083754..1e22c36a8 100644 --- a/src/PIL/ImageMorph.py +++ b/src/PIL/ImageMorph.py @@ -119,13 +119,13 @@ class LutBuilder: # mirror if "M" in options: n = len(patterns) - for pattern, res in patterns[0:n]: + for pattern, res in patterns[:n]: patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res)) # negate if "N" in options: n = len(patterns) - for pattern, res in patterns[0:n]: + for pattern, res in patterns[:n]: # Swap 0 and 1 pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1") res = 1 - int(res) diff --git a/src/PIL/JpegImagePlugin.py b/src/PIL/JpegImagePlugin.py index aae2a4591..92417eacd 100644 --- a/src/PIL/JpegImagePlugin.py +++ b/src/PIL/JpegImagePlugin.py @@ -330,7 +330,7 @@ MARKER = { def _accept(prefix): # Magic number was taken from https://en.wikipedia.org/wiki/JPEG - return prefix[0:3] == b"\xFF\xD8\xFF" + return prefix[:3] == b"\xFF\xD8\xFF" ## diff --git a/src/PIL/PaletteFile.py b/src/PIL/PaletteFile.py index 6ccaa1f53..ee9dca860 100644 --- a/src/PIL/PaletteFile.py +++ b/src/PIL/PaletteFile.py @@ -31,7 +31,7 @@ class PaletteFile: if not s: break - if s[0:1] == b"#": + if s[:1] == b"#": continue if len(s) > 100: raise SyntaxError("bad palette file") diff --git a/src/PIL/XpmImagePlugin.py b/src/PIL/XpmImagePlugin.py index 19c50cafc..aaed2039d 100644 --- a/src/PIL/XpmImagePlugin.py +++ b/src/PIL/XpmImagePlugin.py @@ -83,7 +83,7 @@ class XpmImageFile(ImageFile.ImageFile): rgb = s[i + 1] if rgb == b"None": self.info["transparency"] = c - elif rgb[0:1] == b"#": + elif rgb[:1] == b"#": # FIXME: handle colour names (see ImagePalette.py) rgb = int(rgb[1:], 16) palette[c] = ( From 79e8eba3f8baa8cd79294374ec18bda1ad7b04f8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 25 Apr 2022 18:13:50 +0300 Subject: [PATCH 157/294] Docs: spacing Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- src/PIL/ImageColor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageColor.py b/src/PIL/ImageColor.py index 958635bf9..476cd2f82 100644 --- a/src/PIL/ImageColor.py +++ b/src/PIL/ImageColor.py @@ -128,7 +128,7 @@ def getcolor(color, mode): :param color: A color string :param mode: Convert result to this mode - :return: ``(graylevel [, alpha]) or (red, green, blue[, alpha])`` + :return: ``(graylevel[, alpha]) or (red, green, blue[, alpha])`` """ # same as getrgb, but converts the result to the given mode color, alpha = getrgb(color), 255 From f64dd53897b1eb51cadf9b0c00f41427189042ef Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 Apr 2022 18:55:54 +1000 Subject: [PATCH 158/294] Test invalid WebP background colors --- Tests/test_file_webp.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index c69e13a89..f1bdc59cf 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -185,6 +185,17 @@ class TestFileWebp: Image.open(blob).load() Image.open(blob).load() + @pytest.mark.parametrize( + "background", + (0, (0,), (-1, 0, 1, 2), (253, 254, 255, 256)), + ) + @skip_unless_feature("webp_anim") + def test_invalid_background(self, background, tmp_path): + temp_file = str(tmp_path / "temp.webp") + im = hopper() + with pytest.raises(OSError): + im.save(temp_file, save_all=True, append_images=[im], background=background) + @skip_unless_feature("webp_anim") def test_background_from_gif(self, tmp_path): # Save L mode GIF with background From 9fb0087ec27e2b90ed59d2c698d5320d827db1c5 Mon Sep 17 00:00:00 2001 From: axt-one Date: Tue, 26 Apr 2022 18:04:08 +0900 Subject: [PATCH 159/294] modified screencapture option in ImageGrab.grab() --- 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 8bd14d331..eb21ac399 100644 --- a/src/PIL/ImageGrab.py +++ b/src/PIL/ImageGrab.py @@ -33,7 +33,7 @@ def grab(bbox=None, include_layered_windows=False, all_screens=False, xdisplay=N args = ["screencapture"] if bbox: left, top, right, bottom = bbox - args += ["-R", f"{left},{right},{right-left},{bottom-top}"] + args += ["-R", f"{left},{top},{right-left},{bottom-top}"] subprocess.call(args + ["-x", filepath]) im = Image.open(filepath) im.load() From 5da5cafb316d774d6c377d0d886d00e5e1d1f1b9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 Apr 2022 20:33:29 +1000 Subject: [PATCH 160/294] Added all_frames() to documentation --- docs/reference/ImageSequence.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/reference/ImageSequence.rst b/docs/reference/ImageSequence.rst index 1bfb554b6..f2e7d9edd 100644 --- a/docs/reference/ImageSequence.rst +++ b/docs/reference/ImageSequence.rst @@ -25,3 +25,8 @@ The :py:class:`~PIL.ImageSequence.Iterator` class .. autoclass:: PIL.ImageSequence.Iterator :members: + +Functions +--------- + +.. autofunction:: PIL.ImageSequence.all_frames From a2b74b04eb306fe56ed2ccc26949ec9d89b5b97a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 26 Apr 2022 20:54:08 +1000 Subject: [PATCH 161/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f13beb245..d75f9eab0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Corrected screencapture argument in ImageGrab.grab() #6244 + [axt-one] + - Deprecate support for Qt 5 (PyQt5 and PySide2) #6237 [hugovk, radarhere] From 9ea46247048f861f088f09541cd434aeb16e6f9c Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Tue, 15 Mar 2022 23:31:59 -0600 Subject: [PATCH 162/294] Search pkg-config system libs/cflags. We need to search the system paths as well from pkg-config for some packages to be found properly. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 9a05e5105..d41aedbd6 100755 --- a/setup.py +++ b/setup.py @@ -252,8 +252,8 @@ def _cmd_exists(cmd): def _pkg_config(name): try: command = os.environ.get("PKG_CONFIG", "pkg-config") - command_libs = [command, "--libs-only-L", name] - command_cflags = [command, "--cflags-only-I", name] + command_libs = [command, "--libs-only-L", "--keep-system-libs", name] + command_cflags = [command, "--cflags-only-I", "--keep-system-cflags", name] if not DEBUG: command_libs.append("--silence-errors") command_cflags.append("--silence-errors") From 6ec9dfb9c0bc04a5504231887a3fb86cb8863721 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 23 Apr 2022 21:58:30 +1000 Subject: [PATCH 163/294] If an exception is raised, try again without system paths --- setup.py | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/setup.py b/setup.py index d41aedbd6..37bb2ceb8 100755 --- a/setup.py +++ b/setup.py @@ -250,28 +250,32 @@ def _cmd_exists(cmd): def _pkg_config(name): - try: - command = os.environ.get("PKG_CONFIG", "pkg-config") - command_libs = [command, "--libs-only-L", "--keep-system-libs", name] - command_cflags = [command, "--cflags-only-I", "--keep-system-cflags", name] - if not DEBUG: - command_libs.append("--silence-errors") - command_cflags.append("--silence-errors") - libs = ( - subprocess.check_output(command_libs) - .decode("utf8") - .strip() - .replace("-L", "") - ) - cflags = ( - subprocess.check_output(command_cflags) - .decode("utf8") - .strip() - .replace("-I", "") - ) - return libs, cflags - except Exception: - pass + command = os.environ.get("PKG_CONFIG", "pkg-config") + for keep_system in (True, False): + try: + command_libs = [command, "--libs-only-L", name] + command_cflags = [command, "--cflags-only-I", name] + if keep_system: + command_libs.append("--keep-system-libs") + command_cflags.append("--keep-system-cflags") + if not DEBUG: + command_libs.append("--silence-errors") + command_cflags.append("--silence-errors") + libs = ( + subprocess.check_output(command_libs) + .decode("utf8") + .strip() + .replace("-L", "") + ) + cflags = ( + subprocess.check_output(command_cflags) + .decode("utf8") + .strip() + .replace("-I", "") + ) + return libs, cflags + except Exception: + pass class pil_build_ext(build_ext): From 0edfea0bbca1a155934adec3db1bff9b53c46248 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Apr 2022 21:36:35 +1000 Subject: [PATCH 164/294] Changed indents and kept line length --- CHANGES.rst | 1384 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 820 insertions(+), 564 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 156888dab..ca90ec018 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4822,21 +4822,19 @@ Pre-fork Ka-Ping Yee, and many others (if your name should be on this list, let me know.) - 1.1.6 to 1.1.7 -------------- This section may not be fully complete. For changes since this file was last updated, see the repository revision history: - - http://svn.effbot.org/public/pil/ +http://svn.effbot.org/public/pil/ 1.1.7 final ----------- - Set GIF loop info property to the number of iterations if a NETSCAPE - loop extension is present, instead of always setting it to 1 (from - Valentino Volonghi). + loop extension is present, instead of always setting it to 1 (from + Valentino Volonghi). 1.1.7c1 ------- @@ -4846,31 +4844,31 @@ was last updated, see the repository revision history: - Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) - Added various TGA improvements from Alexey Borzenkov, including - support for specifying image orientation. + support for specifying image orientation. - Bumped block threshold to 16 megabytes, made size estimation a bit - more accurate. This speeds up allocation of large images. + more accurate. This speeds up allocation of large images. - Fixed rounding error in ImagingDrawWideLine. - "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every - version I've seen, which leads to different width lines depending on - the order of the points in the line. This is especially bad at some - angles where a 'width=2' line can completely disappear. + "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every + version I've seen, which leads to different width lines depending on + the order of the points in the line. This is especially bad at some + angles where a 'width=2' line can completely disappear. - Added support for RGBA mode to the SGI module (based on code by - Karsten Hiddemann). + Karsten Hiddemann). - Handle repeated IPTC tags (adapted from a patch by Eric Bruning). - Eric writes: According to the specification, some IPTC tags can be - repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last - instance of that tag. Below is a patch to store all tags. If there are - multiple tag instances, they are stored in a (python) list. Single tag - instances remain as strings. + Eric writes: According to the specification, some IPTC tags can be + repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last + instance of that tag. Below is a patch to store all tags. If there are + multiple tag instances, they are stored in a (python) list. Single tag + instances remain as strings. - Fixed potential crash in ImageFilter for small target images - (reported by Zac Burns and Daniel Fetchinson). + (reported by Zac Burns and Daniel Fetchinson). - Use BMP instead of JPEG as temporary show format on Mac OS X. @@ -4881,39 +4879,39 @@ was last updated, see the repository revision history: - Added limited support for I;16L mode (explicit little endian). - Moved WMF support into Image.core; enable WMF rendering by default - if renderer is available. + if renderer is available. - Mark the ARG plugin as obsolete. - Added version query mechanism to ImageCms and ImageFont, for - debugging. + debugging. - Added (experimental) ImageCms function for fetching the ICC profile - for the current display (currently Windows only). + for the current display (currently Windows only). - Added HWND/HDC support to ImageCms.get_display_profile(). + Added HWND/HDC support to ImageCms.get_display_profile(). - Added WMF renderer (Windows only). - Added ImagePointHandler and ImageTransformHandler mixins; made - ImageCmsTransform work with im.point. + ImageCmsTransform work with im.point. - Fixed potential endless loop in the XVThumbnail reader (from Nikolai - Ugelvik). + Ugelvik). - Added Kevin Cazabon's pyCMS package. - The C code has been moved to _imagingcms.c, the Python interface - module is installed as PIL.ImageCMS. + The C code has been moved to _imagingcms.c, the Python interface + module is installed as PIL.ImageCMS. - Added support for in-memory ICC profiles. + Added support for in-memory ICC profiles. - Unified buildTransform and buildTransformFromOpenProfiles. + Unified buildTransform and buildTransformFromOpenProfiles. - The profile can now be either a filename, a profile object, or a - file-like object containing an in-memory profile. + The profile can now be either a filename, a profile object, or a + file-like object containing an in-memory profile. - Additional fixes from Florian Böch: + Additional fixes from Florian Böch: Very nice - it just needs LCMS flags support so we can use black point compensation and softproofing :) See attached patches. They @@ -4924,33 +4922,33 @@ was last updated, see the repository revision history: - Improved support for layer names in PSD files (from Sylvain Baubeau) - Sylvain writes: I needed to be able to retrieve the names of the - layers in a PSD files. But PsdImagePlugin.py didn't do the job so I - wrote this very small patch. + Sylvain writes: I needed to be able to retrieve the names of the + layers in a PSD files. But PsdImagePlugin.py didn't do the job so I + wrote this very small patch. - Improved RGBA support for ImageTk for 8.4 and newer (from Con - Radchenko). + Radchenko). - This replaces the slow run-length based encoding model with true - compositing at the Tk level. + This replaces the slow run-length based encoding model with true + compositing at the Tk level. - Added support for 16- and 32-bit images to McIdas loader. - Based on file samples and stand-alone reader code provided by Craig - Swank. + Based on file samples and stand-alone reader code provided by Craig + Swank. - Added ImagePalette support to putpalette. - Fixed problem with incremental parsing of PNG files. - Make selftest.py report non-zero status on failure (from Mark - Sienkiewicz) + Sienkiewicz) - Add big endian save support and multipage infrastructure to the TIFF - writer (from Sebastian Haase). + writer (from Sebastian Haase). - Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt - Schwehr). + Schwehr). - Added zTXT support (from Andrew Kuchling via Lowell Alleman). @@ -4962,36 +4960,38 @@ was last updated, see the repository revision history: - Added Chroma subsampling support for JPEG (from Justin Huff). - Justin writes: Attached is a patch (against PIL 1.1.6) to provide - control over the chroma subsampling done by the JPEG encoder. This - is often useful for reducing compression artifacts around edges of - clipart and text. + Justin writes: Attached is a patch (against PIL 1.1.6) to provide + control over the chroma subsampling done by the JPEG encoder. This + is often useful for reducing compression artifacts around edges of + clipart and text. - Added USM/Gaussian Blur code from Kevin Cazabon. - Fixed bug w. uninitialized image data when cropping outside the - source image. + source image. - Use ImageShow to implement the Image.show method. - Most notably, this picks the 'display' utility when available. It - also allows application code to register new display utilities via - the ImageShow registry. + Most notably, this picks the 'display' utility when available. It + also allows application code to register new display utilities via + the ImageShow registry. - Release the GIL in the PNG compressor (from Michael van Tellingen). - Revised JPEG CMYK handling. - Always assume Adobe behaviour, both when reading and writing (based on - a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and - additional debugging by Michael van Tellingen). + Always assume Adobe behaviour, both when reading and writing (based on + a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and + additional debugging by Michael van Tellingen). - Support for preserving ICC profiles (by Florian Böch via Tim Hatch). - Florian writes: + Florian writes: It's a beta, so still needs some testing, but should allow you to: - - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. Existing code doesn't need to be changed. + + - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. + Existing code doesn't need to be changed. - access embedded profiles in JPEG, PNG, PSD, TIFF. It also includes patches for TIFF to retain IPTC, Photoshop and XMP @@ -5004,37 +5004,37 @@ was last updated, see the repository revision history: - Added resolution save option for PDF files. - Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py - based on 1.1.6 as included in Ubuntu, that supports a "resolution" - save option. Not great, but it makes the PDF saving more useful by - allowing PDFs that are not exactly 72dpi. + Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py + based on 1.1.6 as included in Ubuntu, that supports a "resolution" + save option. Not great, but it makes the PDF saving more useful by + allowing PDFs that are not exactly 72dpi. - Look for Tcl/Tk include files in version-specific include directory - (from Encolpe Degoute). + (from Encolpe Degoute). - Fixed grayscale rounding error in ImageColor.getcolor (from Tim - Hatch). + Hatch). - Fixed calculation of mean value in ImageEnhance.Contrast (reported - by "roop" and Scott David Daniels). + by "roop" and Scott David Daniels). - Fixed truetype positioning when first character has a negative left - bearing (from Ned Batchelder): + bearing (from Ned Batchelder): - Ned writes: In PIL 1.1.6, ImageDraw.text will position the string - incorrectly if the first character has a negative left bearing. To - see the problem, show a string like "///" in an italic font. The - first slash will be clipped at the left, and the string will be - mis-positioned. + Ned writes: In PIL 1.1.6, ImageDraw.text will position the string + incorrectly if the first character has a negative left bearing. To + see the problem, show a string like "///" in an italic font. The + first slash will be clipped at the left, and the string will be + mis-positioned. - Fixed resolution unit bug in tiff reader/writer (based on code by - Florian Höch, Gary Bloom, and others). + Florian Höch, Gary Bloom, and others). - Added simple transparency support for RGB images (reported by - Sebastian Spaeth). + Sebastian Spaeth). - Added support for Unicode filenames in ImageFont.truetype (from Donn - Ingle). + Ingle). - Fixed potential crash in ImageFont.getname method (from Donn Ingle). @@ -5051,25 +5051,25 @@ was last updated, see the repository revision history: ------- - Added experimental "floodfill" function to the ImageDraw module - (based on code by Eric Raymond). + (based on code by Eric Raymond). - The default arguments for "frombuffer" doesn't match "fromstring" - and the documentation; this is a bug, and will most likely be fixed - in a future version. In this release, PIL prints a warning message - instead. To silence the warning, change any calls of the form - "frombuffer(mode, size, data)" to + and the documentation; this is a bug, and will most likely be fixed + in a future version. In this release, PIL prints a warning message + instead. To silence the warning, change any calls of the form + "frombuffer(mode, size, data)" to - frombuffer(mode, size, data, "raw", mode, 0, 1) + frombuffer(mode, size, data, "raw", mode, 0, 1) - Added "fromarray" function, which takes an object implementing the - NumPy array interface and creates a PIL Image from it. (from Travis - Oliphant). + NumPy array interface and creates a PIL Image from it. (from Travis + Oliphant). - Added NumPy array interface support (__array_interface__) to the - Image class (based on code by Travis Oliphant). + Image class (based on code by Travis Oliphant). - This allows you to easily convert between PIL image memories and - NumPy arrays: + This allows you to easily convert between PIL image memories and + NumPy arrays: import numpy, Image @@ -5080,76 +5080,76 @@ was last updated, see the repository revision history: im = Image.fromarray(a) - Fixed CMYK polarity for JPEG images, by treating all images as - "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon - for samples, debugging, and patches). + "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon + for samples, debugging, and patches). 1.1.6b1 ------- - Added 'expand' option to the Image 'rotate' method. If true, the - output image is made large enough to hold the entire rotated image. + output image is made large enough to hold the entire rotated image. - Changed the ImageDraw 'line' method to always draw the last pixel in - a polyline, independent of line angle. + a polyline, independent of line angle. - Fixed bearing calculation and clipping in the ImageFont truetype - renderer; this could lead to clipped text, or crashes in the low- - level _imagingft module. (based on input from Adam Twardoch and - others). + renderer; this could lead to clipped text, or crashes in the low- + level _imagingft module. (based on input from Adam Twardoch and + others). - Added ImageQt wrapper module, for converting PIL Image objects to - QImage objects in an efficient way. + QImage objects in an efficient way. - Fixed 'getmodebands' to return the number of bands also for "PA" - and "LA" modes. Added 'getmodebandnames' helper that return the - band names. + and "LA" modes. Added 'getmodebandnames' helper that return the + band names. 1.1.6a2 ------- - Added float/double support to the TIFF loader (from Russell - Nelson). + Nelson). - Fixed broken use of realloc() in path.c (from Jan Matejek) - Added save support for Spider images (from William Baxter). - Fixed broken 'paste' and 'resize' operations in pildriver - (from Bill Janssen). + (from Bill Janssen). - Added support for duplex scanning to the Sane interface (Abel - Deuring). + Deuring). 1.1.6a1 ------- - Fixed a memory leak in "convert(mode)", when converting from - L to P. + L to P. - Added pixel access object. The "load" method now returns a - access object that can be used to directly get and set pixel - values, using ordinary [x, y] notation: + access object that can be used to directly get and set pixel + values, using ordinary [x, y] notation: pixel = im.load() v = pixel[x, y] pixel[x, y] = v - If you're accessing more than a few pixels, this is a lot - faster than using getpixel/putpixel. + If you're accessing more than a few pixels, this is a lot + faster than using getpixel/putpixel. - Fixed building on Cygwin (from Miki Tebeka). - Fixed "point(callable)" on unloaded images (reported by Håkan - Karlsson). + Karlsson). - Fixed size bug in ImageWin.ImageWindow constructor (from Victor - Reijs) + Reijs) - Fixed ImageMath float() and int() operations for Python 2.4 - (reported by Don Rozenberg). + (reported by Don Rozenberg). - Fixed "RuntimeError: encoder error -8 in tostring" problem for - wide "RGB", "I", and "F" images. + wide "RGB", "I", and "F" images. - Fixed line width calculation. @@ -5159,16 +5159,16 @@ was last updated, see the repository revision history: - Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). - Fixed off-by-0.5 errors in the ANTIALIAS code (based on input - from Douglas Bagnall). + from Douglas Bagnall). - Added buffer interface support to the Path constructor. If - a buffer is provided, it is assumed to contain a flat array - of float coordinates (e.g. array.array('f', seq)). + a buffer is provided, it is assumed to contain a flat array + of float coordinates (e.g. array.array('f', seq)). - Added new ImageMath module. - Fixed ImageOps.equalize when used with a small number of distinct - values (reported by David Kirtley). + values (reported by David Kirtley). - Fixed potential integer division in PSDraw.image (from Eric Etheridge). @@ -5176,28 +5176,28 @@ was last updated, see the repository revision history: ----------------------- - Added experimental PERSPECTIVE transform method (from Jeff Breiden- - bach). + bach). 1.1.5c1 ------- - Make sure "thumbnail" never generates zero-wide or zero-high images - (reported by Gene Skonicki) + (reported by Gene Skonicki) - Fixed a "getcolors" bug that could result in a zero count for some - colors (reported by Richard Oudkerk). + colors (reported by Richard Oudkerk). - Changed default "convert" palette to avoid "rounding errors" when - round-tripping white source pixels (reported by Henryk Gerlach and - Jeff Epler). + round-tripping white source pixels (reported by Henryk Gerlach and + Jeff Epler). 1.1.5b3 ------- - Don't crash in "quantize" method if the number of colors requested - is larger than 256. This release raises a ValueError exception; - future versions may return a mode "RGB" image instead (reported - by Richard Oudkerk). + is larger than 256. This release raises a ValueError exception; + future versions may return a mode "RGB" image instead (reported + by Richard Oudkerk). - Added WBMP read/write support (based on code by Duncan Booth). @@ -5205,43 +5205,43 @@ was last updated, see the repository revision history: ------- - Added DPI read/write support to the PNG codec. The decoder sets - the info["dpi"] attribute for PNG files with appropriate resolution - settings. The encoder uses the "dpi" option (based on code by Niki - Spahiev). + the info["dpi"] attribute for PNG files with appropriate resolution + settings. The encoder uses the "dpi" option (based on code by Niki + Spahiev). - Added limited support for "point" mappings from mode "I" to mode "L". - Only 16-bit values are supported (other values are clipped), the lookup - table must contain exactly 65536 entries, and the mode argument must be - set to "L". + Only 16-bit values are supported (other values are clipped), the lookup + table must contain exactly 65536 entries, and the mode argument must be + set to "L". - Added support for Mac OS X icns files (based on code by Bob Ippolito). - Added "ModeFilter" support to the ImageFilter module. - Added support for Spider images (from William Baxter). See the - comments in PIL/SpiderImagePlugin.py for more information on this - format. + comments in PIL/SpiderImagePlugin.py for more information on this + format. 1.1.5b1 ------- - Added new Sane release (from Ralph Heinkel). See the Sane/README - and Sane/CHANGES files for more information. + and Sane/CHANGES files for more information. - Added experimental PngInfo chunk container to the PngImageFile - module. This can be used to add arbitrary chunks to a PNG file. - Create a PngInfo instance, use "add" or "add_text" to add chunks, - and pass the instance as the "pnginfo" option when saving the - file. + module. This can be used to add arbitrary chunks to a PNG file. + Create a PngInfo instance, use "add" or "add_text" to add chunks, + and pass the instance as the "pnginfo" option when saving the + file. - Added "getpalette" method. This returns the palette as a list, - or None if the image has no palette. To modify the palette, use - "getpalette" to fetch the current palette, modify the list, and - put it back using "putpalette". + or None if the image has no palette. To modify the palette, use + "getpalette" to fetch the current palette, modify the list, and + put it back using "putpalette". - Added optional flattening to the ImagePath "tolist" method. - tolist() or tolist(0) returns a list of 2-tuples, as before. - tolist(1) returns a flattened list instead. + tolist() or tolist(0) returns a list of 2-tuples, as before. + tolist(1) returns a flattened list instead. 1.1.5a5 ------- @@ -5249,99 +5249,98 @@ was last updated, see the repository revision history: - Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". - Added "getcolors()" method. This is similar to the existing histo- - gram method, but looks at color values instead of individual layers, - and returns an unsorted list of (count, color) tuples. + gram method, but looks at color values instead of individual layers, + and returns an unsorted list of (count, color) tuples. - By default, the method returns None if finds more than 256 colors. - If you need to look for more colors, you can pass in a limit (this - is used to allocate internal tables, so you probably don't want to - pass in too large values). + By default, the method returns None if finds more than 256 colors. + If you need to look for more colors, you can pass in a limit (this + is used to allocate internal tables, so you probably don't want to + pass in too large values). - Build improvements: Fixed building under AIX, improved detection of - FreeType2 and Mac OS X framework libraries, and more. Many thanks - to everyone who helped test the new "setup.py" script! + FreeType2 and Mac OS X framework libraries, and more. Many thanks + to everyone who helped test the new "setup.py" script! 1.1.5a4 ------- - The "save" method now looks for a file format driver before - creating the file. + creating the file. - Don't use antialiased truetype fonts when drawing in mode "P", "I", - and "F" images. + and "F" images. - Rewrote the "setup.py" file. The new version scans for available - support libraries, and configures both the libImaging core library - and the bindings in one step. + support libraries, and configures both the libImaging core library + and the bindings in one step. - To use specific versions of the libraries, edit the ROOT variables - in the setup.py file. + To use specific versions of the libraries, edit the ROOT variables + in the setup.py file. - Removed threaded "show" viewer; use the old "show" implementation - instead (Windows). + instead (Windows). - Added deprecation warnings to Image.offset, ImageDraw.setink, and - ImageDraw.setfill. + ImageDraw.setfill. - Added width option to ImageDraw.line(). The current implementation - works best for straight lines; it does not support line joins, so - polylines won't look good. + works best for straight lines; it does not support line joins, so + polylines won't look good. - ImageDraw.Draw is now a factory function instead of a class. If - you need to create custom draw classes, inherit from the ImageDraw - class. All other code should use the factory function. + you need to create custom draw classes, inherit from the ImageDraw + class. All other code should use the factory function. - Fixed loading of certain PCX files (problem reported by Greg - Hamilton, who also provided samples). + Hamilton, who also provided samples). - Changed _imagingft.c to require FreeType 2.1 or newer. The - module can still be built with earlier versions; see comments - in _imagingft.c for details. + module can still be built with earlier versions; see comments + in _imagingft.c for details. 1.1.5a3 ------- - Added 'getim' method, which returns a PyCObject wrapping an - Imaging pointer. The description string is set to IMAGING_MAGIC. - See Imaging.h for pointer and string declarations. + Imaging pointer. The description string is set to IMAGING_MAGIC. + See Imaging.h for pointer and string declarations. - Fixed reading of TIFF JPEG images (problem reported by Ulrik - Svensson). + Svensson). - Made ImageColor work under Python 1.5.2 - Fixed division by zero "equalize" on very small images (from - Douglas Bagnall). + Douglas Bagnall). 1.1.5a2 ------- - The "paste" method now supports the alternative "paste(im, mask)" - syntax (in this case, the box defaults to im's bounding box). + syntax (in this case, the box defaults to im's bounding box). - The "ImageFile.Parser" class now works also for PNG files with - more than one IDAT block. + more than one IDAT block. - Added DPI read/write to the TIFF codec, and fixed writing of - rational values. The decoder sets the info["dpi"] attribute - for TIFF files with appropriate resolution settings. The - encoder uses the "dpi" option. + rational values. The decoder sets the info["dpi"] attribute + for TIFF files with appropriate resolution settings. The + encoder uses the "dpi" option. - Disable interlacing for small (or narrow) GIF images, to - work around what appears to be a hard-to-find bug in PIL's - GIF encoder. + work around what appears to be a hard-to-find bug in PIL's + GIF encoder. - Fixed writing of mode "P" PDF images. Made mode "1" PDF - images smaller. + images smaller. - Made the XBM reader a bit more robust; the file may now start - with a few whitespace characters. + with a few whitespace characters. - Added support for enhanced metafiles to the WMF driver. The - separate PILWMF kit lets you render both placeable WMF files - and EMF files as raster images. See - - http://effbot.org/downloads#pilwmf + separate PILWMF kit lets you render both placeable WMF files + and EMF files as raster images. See + http://effbot.org/downloads#pilwmf 1.1.5a1 ------- @@ -5349,85 +5348,85 @@ was last updated, see the repository revision history: - Replaced broken WMF driver with a WMF stub plugin (see below). - Fixed writing of mode "1", "L", and "CMYK" PDF images (based on - input from Nicholas Riley and others). + input from Nicholas Riley and others). - Fixed adaptive palette conversion for zero-width or zero-height - images (from Chris Cogdon) + images (from Chris Cogdon) - Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) - Added support for StubImageFile plugins, including stub plugins - for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify - a given file format, but relies on application code to open and - save files in that format. + for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify + a given file format, but relies on application code to open and + save files in that format. - Added optional "encoding" argument to the ImageFont.truetype - factory. This argument can be used to specify non-Unicode character - maps for fonts that support that. For example, to draw text using - the Microsoft Symbol font, use: + factory. This argument can be used to specify non-Unicode character + maps for fonts that support that. For example, to draw text using + the Microsoft Symbol font, use: - font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") - draw.text((0, 0), unichr(0xF000 + 0xAA)) + font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") + draw.text((0, 0), unichr(0xF000 + 0xAA)) - (note that the symbol font uses characters in the 0xF000-0xF0FF - range) + (note that the symbol font uses characters in the 0xF000-0xF0FF + range) - Common encodings are "unic" (Unicode), "symb" (Microsoft Symbol), - "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple - Roman). See the FreeType documentation for more information. + Common encodings are "unic" (Unicode), "symb" (Microsoft Symbol), + "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple + Roman). See the FreeType documentation for more information. - Made "putalpha" a bit more robust; you can now attach an alpha - layer to a plain "L" or "RGB" image, and you can also specify - constant alphas instead of alpha layers (using integers or colour - names). + layer to a plain "L" or "RGB" image, and you can also specify + constant alphas instead of alpha layers (using integers or colour + names). - Added experimental "LA" mode support. - An "LA" image is an "L" image with an attached transparency layer. - Note that support for "LA" is not complete; some operations may - fail or produce unexpected results. + An "LA" image is an "L" image with an attached transparency layer. + Note that support for "LA" is not complete; some operations may + fail or produce unexpected results. - Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" - classes to the ImageFilter module. + classes to the ImageFilter module. - Improved support for applications using multiple threads; PIL - now releases the global interpreter lock for many CPU-intensive - operations (based on work by Kevin Cazabon). + now releases the global interpreter lock for many CPU-intensive + operations (based on work by Kevin Cazabon). - Ignore Unicode characters in the PCF loader (from Andres Polit) - Fixed typo in OleFileIO.loadfat, which could affect loading of - FlashPix and Image Composer images (Daniel Haertle) + FlashPix and Image Composer images (Daniel Haertle) - Fixed building on platforms that have Freetype but don't have - Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) + Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) - Added EXIF GPSInfo read support for JPEG files. To extract - GPSInfo information, open the file, extract the exif dictionary, - and check for the key 0x8825 (GPSInfo). If present, it contains - a dictionary mapping GPS keys to GPS values. For a list of keys, - see the EXIF specification. + GPSInfo information, open the file, extract the exif dictionary, + and check for the key 0x8825 (GPSInfo). If present, it contains + a dictionary mapping GPS keys to GPS values. For a list of keys, + see the EXIF specification. - The "ExifTags" module contains a GPSTAGS dictionary mapping GPS - tags to tag names. + The "ExifTags" module contains a GPSTAGS dictionary mapping GPS + tags to tag names. - Added DPI read support to the PCX and DCX codecs (info["dpi"]). - The "show" methods now uses a built-in image viewer on Windows. - This viewer creates an instance of the ImageWindow class (see - below) and keeps it running in a separate thread. NOTE: This - was disabled in 1.1.5a4. + This viewer creates an instance of the ImageWindow class (see + below) and keeps it running in a separate thread. NOTE: This + was disabled in 1.1.5a4. - Added experimental "Window" and "ImageWindow" classes to the - ImageWin module. These classes allow you to create a WCK-style - toplevel window, and use it to display raster data. + ImageWin module. These classes allow you to create a WCK-style + toplevel window, and use it to display raster data. - Fixed some Python 1.5.2 issues (to build under 1.5.2, use the - Makefile.pre.in/Setup.in approach) + Makefile.pre.in/Setup.in approach) - Added support for the TIFF FillOrder tag. PIL can read mode "1", - "L", "P" and "RGB" images with non-standard FillOrder (based on - input from Jeff Breidenbach). + "L", "P" and "RGB" images with non-standard FillOrder (based on + input from Jeff Breidenbach). 1.1.4 final ----------- @@ -5442,36 +5441,36 @@ was last updated, see the repository revision history: - Improved building on Windows with MinGW (from Klamer Shutte). - If no font is specified, ImageDraw now uses the embedded default - font. Use the "load" or "truetype" methods to load a real font. + font. Use the "load" or "truetype" methods to load a real font. - Added embedded default font to the ImageFont module (currently - an 8-pixel Courier font, taken from the X window distribution). + an 8-pixel Courier font, taken from the X window distribution). 1.1.4b1 ------- - Added experimental EXIF support for JPEG files. To extract EXIF - information from a JPEG file, open the file as usual, and call the - "_getexif" method. If successful, this method returns a dictionary - mapping EXIF TIFF tags to values. If the file does not contain EXIF - data, the "_getexif" method returns None. + information from a JPEG file, open the file as usual, and call the + "_getexif" method. If successful, this method returns a dictionary + mapping EXIF TIFF tags to values. If the file does not contain EXIF + data, the "_getexif" method returns None. - The "ExifTags" module contains a dictionary mapping tags to tag - names. + The "ExifTags" module contains a dictionary mapping tags to tag + names. - This interface will most likely change in future versions. + This interface will most likely change in future versions. - Fixed a bug when using the "transparency" option with the GIF - writer. + writer. - Added limited support for "bitfield compression" in BMP files - and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This - also fixes a problem with ImageGrab module when copying screen- - dumps from the clipboard on 15/16/32-bit displays. + and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This + also fixes a problem with ImageGrab module when copying screen- + dumps from the clipboard on 15/16/32-bit displays. - Added experimental WAL (Quake 2 textures) loader. To use this - loader, import WalImageFile and call the "open" method in that - module. + loader, import WalImageFile and call the "open" method in that + module. 1.1.4a4 ------- @@ -5479,99 +5478,99 @@ was last updated, see the repository revision history: - Added updated SANE driver (Andrew Kuchling, Abel Deuring) - Use Python's "mmap" module on non-Windows platforms to read some - uncompressed formats using memory mapping. Also added a "frombuffer" - function that allows you to access the contents of an existing string - or buffer object as if it were an image object. + uncompressed formats using memory mapping. Also added a "frombuffer" + function that allows you to access the contents of an existing string + or buffer object as if it were an image object. - Fixed a memory leak that could appear when processing mode "P" - images (from Pier Paolo Glave) + images (from Pier Paolo Glave) - Ignore Unicode characters in the BDF loader (from Graham Dumpleton) -1.1.4a3 released; windows only +1.1.4a3 released; Windows only ------------------------------ - Added experimental RGBA-on-RGB drawing support. To use RGBA - colours on an RGB image, pass "RGBA" as the second string to - the ImageDraw.Draw constructor. + colours on an RGB image, pass "RGBA" as the second string to + the ImageDraw.Draw constructor. - Added support for non-ASCII strings (Latin-1) and Unicode - to the truetype font renderer. + to the truetype font renderer. - The ImageWin "Dib" object can now be constructed directly from - an image object. + an image object. - The ImageWin module now allows you use window handles as well - as device contexts. To use a window handle, wrap the handle in - an ImageWin.HWND object, and pass in this object instead of the - device context. + as device contexts. To use a window handle, wrap the handle in + an ImageWin.HWND object, and pass in this object instead of the + device context. 1.1.4a2 ------- - Improved support for 16-bit unsigned integer images (mode "I;16"). - This includes TIFF reader support, and support for "getextrema" - and "point" (from Klamer Shutte). + This includes TIFF reader support, and support for "getextrema" + and "point" (from Klamer Shutte). - Made the BdfFontFile reader a bit more robust (from Kevin Cazabon - and Dmitry Vasiliev) + and Dmitry Vasiliev) - Changed TIFF writer to always write Compression tag, even when - using the default compression (from Greg Couch). + using the default compression (from Greg Couch). - Added "show" support for Mac OS X (from Dan Wolfe). - Added clipboard support to the "ImageGrab" module (Windows only). - The "grabclipboard" function returns an Image object, a list of - filenames (not in 1.1.4), or None if neither was found. + The "grabclipboard" function returns an Image object, a list of + filenames (not in 1.1.4), or None if neither was found. 1.1.4a1 ------- - Improved support for drawing RGB data in palette images. You can - now use RGB tuples or colour names (see below) when drawing in a - mode "P" image. The drawing layer automatically assigns color - indexes, as long as you don't use more than 256 unique colours. + now use RGB tuples or colour names (see below) when drawing in a + mode "P" image. The drawing layer automatically assigns color + indexes, as long as you don't use more than 256 unique colours. - Moved self test from MiniTest/test.py to ./selftest.py. - Added support for CSS3-style color strings to most places that - accept colour codes/tuples. This includes the "ImageDraw" module, - the Image "new" function, and the Image "paste" method. + accept colour codes/tuples. This includes the "ImageDraw" module, + the Image "new" function, and the Image "paste" method. - Colour strings can use one of the following formats: "#f00", - "#ff0000", "rgb(255,0,0)", "rgb(100%,0%,0%)", "hsl(0, 100%, 50%)", - or "red" (most X11-style colour names are supported). See the - documentation for the "ImageColor" module for more information. + Colour strings can use one of the following formats: "#f00", + "#ff0000", "rgb(255,0,0)", "rgb(100%,0%,0%)", "hsl(0, 100%, 50%)", + or "red" (most X11-style colour names are supported). See the + documentation for the "ImageColor" module for more information. - Fixed DCX decoder (based on input from Larry Bates) - Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA - newsphoto properties from JPEG, TIFF, or IPTC files. + newsphoto properties from JPEG, TIFF, or IPTC files. - Support for TrueType/OpenType fonts has been added to - the standard distribution. You need the freetype 2.0 - library. + the standard distribution. You need the freetype 2.0 + library. - Made the PCX reader a bit more robust when reading 2-bit - and 4-bit PCX images with odd image sizes. + and 4-bit PCX images with odd image sizes. - Added "Kernel" class to the ImageFilter module. This class - allows you to filter images with user-defined 3x3 and 5x5 - convolution kernels. + allows you to filter images with user-defined 3x3 and 5x5 + convolution kernels. - Added "putdata" support for mode "I", "F" and "RGB". - The GIF writer now supports the transparency option (from - Denis Benoit). + Denis Benoit). - A HTML version of the module documentation is now shipped - with the source code distribution. You'll find the files in - the Doc subdirectory. + with the source code distribution. You'll find the files in + the Doc subdirectory. - Added support for Palm pixmaps (from Bill Janssen). This - change was listed for 1.1.3, but the "PalmImagePlugin" driver - didn't make it into the distribution. + change was listed for 1.1.3, but the "PalmImagePlugin" driver + didn't make it into the distribution. - Improved decoder error messages. @@ -5579,119 +5578,119 @@ was last updated, see the repository revision history: ----------- - Made setup.py look for old versions of zlib. For some back- - ground, see: https://zlib.net/advisory-2002-03-11.txt + ground, see: https://zlib.net/advisory-2002-03-11.txt 1.1.3c2 ------- - Added setup.py file (tested on Unix and Windows). You still - need to build libImaging/imaging.lib in the traditional way, - but the setup.py script takes care of the rest. + need to build libImaging/imaging.lib in the traditional way, + but the setup.py script takes care of the rest. - The old Setup.in/Makefile.pre.in build method is still - supported. + The old Setup.in/Makefile.pre.in build method is still + supported. - Fixed segmentation violation in ANTIALIAS filter (an internal - buffer wasn't properly allocated). + buffer wasn't properly allocated). 1.1.3c1 ------- - Added ANTIALIAS downsampling filter for high-quality "resize" - and "thumbnail" operations. Also added filter option to the - "thumbnail" operation; the default value is NEAREST, but this - will most likely change in future versions. + and "thumbnail" operations. Also added filter option to the + "thumbnail" operation; the default value is NEAREST, but this + will most likely change in future versions. - Fixed plugin loader to be more robust if the __file__ - variable isn't set. + variable isn't set. - Added seek/tell support (for layers) to the PhotoShop - loader. Layer 0 is the main image. + loader. Layer 0 is the main image. - Added new (but experimental) "ImageOps" module, which provides - shortcuts for commonly used operations on entire images. + shortcuts for commonly used operations on entire images. - Don't mess up when loading PNG images if the decoder leaves - data in the output buffer. This could cause internal errors - on some PNG images, with some versions of ZLIB. (Bug report - and patch provided by Bernhard Herzog.) + data in the output buffer. This could cause internal errors + on some PNG images, with some versions of ZLIB. (Bug report + and patch provided by Bernhard Herzog.) - Don't mess up on Unicode filenames. - Don't mess up when drawing on big endian platforms. - Made the TIFF loader a bit more robust; it can now read some - more slightly broken TIFF files (based on input from Ted Wright, - Bob Klimek, and D. Alan Stewart) + more slightly broken TIFF files (based on input from Ted Wright, + Bob Klimek, and D. Alan Stewart) - Added OS/2 EMX build files (from Andrew MacIntyre) - Change "ImageFont" to reject image files if they don't have the - right mode. Older versions could leak memory for "P" images. - (Bug reported by Markus Gritsch). + right mode. Older versions could leak memory for "P" images. + (Bug reported by Markus Gritsch). - Renamed some internal functions to avoid potential build - problem on Mac OS X. + problem on Mac OS X. - Added DL_EXPORT where relevant (for Cygwin, based on input - from Robert Yodlowski) + from Robert Yodlowski) - (re)moved bogus __init__ call in BdfFontFile (bug spotted - by Fred Clare) + by Fred Clare) - Added "ImageGrab" support (Windows only) - Added support for XBM hotspots (based on code contributed by - Bernhard Herzog). + Bernhard Herzog). - Added write support for more TIFF tags, namely the Artist, - Copyright, DateTime, ResolutionUnit, Software, XResolution and - YResolution tags (from Greg Couch) + Copyright, DateTime, ResolutionUnit, Software, XResolution and + YResolution tags (from Greg Couch) - Added TransposedFont wrapper to ImageFont module - Added "optimize" flag to GIF encoder. If optimize is present - and non-zero, PIL will work harder to create a small file. + and non-zero, PIL will work harder to create a small file. - Raise "EOFError" (not IndexError) when reading beyond the - end of a TIFF sequence. + end of a TIFF sequence. - Support rewind ("seek(0)") for GIF and TIFF sequences. - Load grayscale GIF images as mode "L" - Added DPI read/write support to the JPEG codec. The decoder - sets the info["dpi"] attribute for JPEG files with JFIF dpi - settings. The encoder uses the "dpi" option: + sets the info["dpi"] attribute for JPEG files with JFIF dpi + settings. The encoder uses the "dpi" option: - im = Image.open("file.jpg") - dpi = im.info["dpi"] # raises KeyError if DPI not known - im.save("out.jpg", dpi=dpi) + im = Image.open("file.jpg") + dpi = im.info["dpi"] # raises KeyError if DPI not known + im.save("out.jpg", dpi=dpi) - Note that PIL doesn't always preserve the "info" attribute - for normal image operations. + Note that PIL doesn't always preserve the "info" attribute + for normal image operations. 1.1.2c1 and 1.1.2 final ----------------------- - Adapted to Python 2.1. Among other things, all uses of the - "regex" module have been replaced with "re". + "regex" module have been replaced with "re". - Fixed attribute error when reading large PNG files (this bug - was introduced in maintenance code released after the 1.1.1 - release) + was introduced in maintenance code released after the 1.1.1 + release) - Ignore non-string objects in sys.path - Fixed Image.transform(EXTENT) for negative xoffsets - Fixed loading of image plugins if PIL is installed as a package. - (The plugin loader now always looks in the directory where the - Image.py module itself is found, even if that directory isn't on - the standard search path) + (The plugin loader now always looks in the directory where the + Image.py module itself is found, even if that directory isn't on + the standard search path) - The Png plugin has been added to the list of preloaded standard - formats + formats - Fixed bitmap/text drawing in fill mode. @@ -5700,24 +5699,28 @@ was last updated, see the repository revision history: - Added transparency support for L and P images to the PNG codec. - Improved support for read-only images. The "load" method now - sets the "readonly" attribute for memory-mapped images. Operations - that modifies an image in place (such as "paste" and drawing operations) - creates an in-memory copy of the image, if necessary. (before this - change, any attempt to modify a memory-mapped image resulted in a - core dump...) + sets the "readonly" attribute for memory-mapped images. Operations + that modifies an image in place (such as "paste" and drawing operations) + creates an in-memory copy of the image, if necessary. (before this + change, any attempt to modify a memory-mapped image resulted in a + core dump...) -- Added special cases for lists everywhere PIL expects a sequence. This should speed up things like "putdata" and drawing operations. +- Added special cases for lists everywhere PIL expects a sequence. + This should speed up things like "putdata" and drawing operations. -- The Image.offset method is deprecated. Use the ImageChops.offset function instead. +- The Image.offset method is deprecated. Use the ImageChops.offset + function instead. -- Changed ImageChops operators to copy palette and info dictionary from the first image argument. +- Changed ImageChops operators to copy palette and info dictionary + from the first image argument. 1.1.1 ----- - Additional fixes for Python 1.6/2.0, including TIFF "save" bug. -- Changed "init" to properly load plugins when PIL is used as a package. +- Changed "init" to properly load plugins when PIL is used as a + package. - Fixed broken "show" method (on Unix) @@ -5726,224 +5729,317 @@ was last updated, see the repository revision history: - Adapted to Python 1.6 ("append" and other method changes) -- Fixed Image.paste when pasting with solid colour and matte layers ("L" or "RGBA" masks) (bug reported by Robert Kern) +- Fixed Image.paste when pasting with solid colour and matte + layers ("L" or "RGBA" masks) (bug reported by Robert Kern) -- To make it easier to distribute prebuilt versions of PIL, the tkinit binding stuff has been moved to a separate extension module, named "_imagingtk". +- To make it easier to distribute prebuilt versions of PIL, + the tkinit binding stuff has been moved to a separate + extension module, named "_imagingtk". 0.3b2 to 1.0 final ------------------ -- If there's no 16-bit integer (like on a Cray T3E), set INT16 to the smallest integer available. Most of the library works just fine anyway (from Bill Crutchfield) +- If there's no 16-bit integer (like on a Cray T3E), set + INT16 to the smallest integer available. Most of the + library works just fine anyway (from Bill Crutchfield) - Tweaks to make drawing work on big-endian platforms. 1.0c2 ----- -- If PIL is built with the WITH_TKINTER flag, ImageTk can automatically hook into a standard Tkinter build. You no longer need to build your own Tkinter to use the ImageTk module. +- If PIL is built with the WITH_TKINTER flag, ImageTk can + automatically hook into a standard Tkinter build. You + no longer need to build your own Tkinter to use the + ImageTk module. - The old way still works, though. For more information, see Tk/install.txt. + The old way still works, though. For more information, + see Tk/install.txt. -- Some tweaks to ImageTk to support multiple Tk interpreters (from Greg Couch). +- Some tweaks to ImageTk to support multiple Tk interpreters + (from Greg Couch). -- ImageFont "load_path" now scans directory mentioned in .pth files (from Richard Jones). +- ImageFont "load_path" now scans directory mentioned in .pth + files (from Richard Jones). 1.0c1 ----- - The TIFF plugin has been rewritten. The new plugin fully - supports all major PIL image modes (including F and I). + supports all major PIL image modes (including F and I). - The ImageFile module now includes a Parser class, which can - be used to incrementally decode an image file (while down- - loading it from the net, for example). See the handbook for - details. + be used to incrementally decode an image file (while down- + loading it from the net, for example). See the handbook for + details. - "show" now converts non-standard modes to "L" or "RGB" (as - appropriate), rather than writing weird things to disk for - "xv" to choke upon. (bug reported by Les Schaffer). + appropriate), rather than writing weird things to disk for + "xv" to choke upon. (bug reported by Les Schaffer). 1.0b2 ----- -- Major speedups for rotate, transform(EXTENT), and transform(AFFINE) when using nearest neighbour resampling. +- Major speedups for rotate, transform(EXTENT), and transform(AFFINE) + when using nearest neighbour resampling. -- Modified ImageDraw to be compatible with the Arrow graphics interface. See the handbook for details. +- Modified ImageDraw to be compatible with the Arrow graphics + interface. See the handbook for details. -- PIL now automatically loads file codecs when used as a package (from The Dragon De Monsyne). Also included an __init__.py file in the standard distribution. +- PIL now automatically loads file codecs when used as a package + (from The Dragon De Monsyne). Also included an __init__.py file + in the standard distribution. - The GIF encoder has been modified to produce much smaller files. - PIL now uses a run-length encoding method to encode GIF files. - On a random selection of GIF images grabbed from the web, this - version makes the images about twice as large as the original - LZW files, where the earlier version made them over 5 times - larger. YMMV, of course. + PIL now uses a run-length encoding method to encode GIF files. + On a random selection of GIF images grabbed from the web, this + version makes the images about twice as large as the original + LZW files, where the earlier version made them over 5 times + larger. YMMV, of course. - Added PCX write support (works with "1", "P", "L", and "RGB") - Added "bitmap" and "textsize" methods to ImageDraw. -- Improved font rendering code. Fixed a bug or two, and moved most of the time critical stuff to C. +- Improved font rendering code. Fixed a bug or two, and moved + most of the time critical stuff to C. - Removed "bdf2pil.py". Use "pilfont.py" instead! - Improved 16-bit support (still experimental, though). - The following methods now support "I;16" and "I;16B" images: - "getpixel", "copy", "convert" (to and from mode "I"), "resize", - "rotate", and "transform" with nearest neighbour filters, and - "save" using the IM format. The "new" and "open" functions - also work as expected. On Windows, 16-bit files are memory - mapped. + The following methods now support "I;16" and "I;16B" images: + "getpixel", "copy", "convert" (to and from mode "I"), "resize", + "rotate", and "transform" with nearest neighbour filters, and + "save" using the IM format. The "new" and "open" functions + also work as expected. On Windows, 16-bit files are memory + mapped. - NOTE: ALL other operations are still UNDEFINED on 16-bit images. + NOTE: ALL other operations are still UNDEFINED on 16-bit images. -- The "paste" method now supports constant sources. Just pass a colour value (a number or a tuple, depending on the target image mode) instead of the source image. This was in fact implemented in an inefficient way in earlier versions (the "paste" method generated a temporary source image if you passed it a colour instead of an image). In this version, this is handled on the C level instead. +- The "paste" method now supports constant sources. + + Just pass a colour value (a number or a tuple, depending on + the target image mode) instead of the source image. + + This was in fact implemented in an inefficient way in + earlier versions (the "paste" method generated a temporary + source image if you passed it a colour instead of an image). + In this version, this is handled on the C level instead. - Added experimental "RGBa" mode support. - An "RGBa" image is an RGBA image where the colour components have have been premultiplied with the alpha value. PIL allows you to convert an RGBA image to an RGBa image, and to paste RGBa images on top of RGB images. Since this saves a bunch of multiplications and shifts, it is typically about twice as fast an ordinary RGBA paste. + An "RGBa" image is an RGBA image where the colour components + have have been premultiplied with the alpha value. PIL allows + you to convert an RGBA image to an RGBa image, and to paste + RGBa images on top of RGB images. Since this saves a bunch + of multiplications and shifts, it is typically about twice + as fast an ordinary RGBA paste. -- Eliminated extra conversion step when pasting "RGBA" or "RGBa" images on top of "RGB" images. +- Eliminated extra conversion step when pasting "RGBA" or "RGBa" + images on top of "RGB" images. - Fixed Image.BICUBIC resampling for "RGB" images. -- Fixed PCX image file handler to properly read 8-bit PCX files (bug introduced in 1.0b1, reported by Bernhard Herzog) +- Fixed PCX image file handler to properly read 8-bit PCX + files (bug introduced in 1.0b1, reported by Bernhard + Herzog) -- Fixed PSDraw "image" method to restore the coordinate system. +- Fixed PSDraw "image" method to restore the coordinate + system. -- Fixed "blend" problem when applied to images that was not already loaded (reported by Edward C. Jones) +- Fixed "blend" problem when applied to images that was + not already loaded (reported by Edward C. Jones) - Fixed -f option to "pilconvert.py" (from Anthony Baxter) 1.0b1 ----- -- Added Toby J. Sargeant's quantization package. To enable quantization, use the "palette" option to "convert": +- Added Toby J. Sargeant's quantization package. To enable + quantization, use the "palette" option to "convert": imOut = im.convert("P", palette=Image.ADAPTIVE) - This can be used with "L", "P", and "RGB" images. In this version, dithering cannot be used with adaptive palettes. + This can be used with "L", "P", and "RGB" images. In this + version, dithering cannot be used with adaptive palettes. - Note: ADAPTIVE currently maps to median cut quantization with 256 colours. The quantization package also contains a maximum coverage quantizer, which will be supported by future versions of PIL. + Note: ADAPTIVE currently maps to median cut quantization + with 256 colours. The quantization package also contains + a maximum coverage quantizer, which will be supported by + future versions of PIL. -- Added Eric S. Raymond's "pildriver" image calculator to the distribution. See the docstring for more information. +- Added Eric S. Raymond's "pildriver" image calculator to the + distribution. See the docstring for more information. -- The "offset" method no longer dumps core if given positive offsets (from Charles Waldman). +- The "offset" method no longer dumps core if given positive + offsets (from Charles Waldman). -- Fixed a resource leak that could cause ImageWin to run out of GDI resources (from Roger Burnham). +- Fixed a resource leak that could cause ImageWin to run out of + GDI resources (from Roger Burnham). -- Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired by code contributed by Richard Jones). +- Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired + by code contributed by Richard Jones). -- Added experimental 16-bit support, via modes "I;16" (little endian data) and "I;16B" (big endian). Only a few methods properly support such images (see above). +- Added experimental 16-bit support, via modes "I;16" (little endian + data) and "I;16B" (big endian). Only a few methods properly support + such images (see above). - Added XV thumbnail file handler (from Gene Cash). -- Fixed BMP image file handler to handle palette images with small palettes (from Rob Hooft). +- Fixed BMP image file handler to handle palette images with small + palettes (from Rob Hooft). -- Fixed Sun raster file handler for palette images (from Charles Waldman). +- Fixed Sun raster file handler for palette images (from Charles + Waldman). - Improved various internal error messages. -- Fixed Path constructor to handle arbitrary sequence objects. This also affects the ImageDraw class (from Richard Jones). +- Fixed Path constructor to handle arbitrary sequence objects. This + also affects the ImageDraw class (from Richard Jones). -- Fixed a bug in JpegDecode that caused PIL to report "decoder error -2" for some progressive JPEG files (reported by Magnus Källström, who also provided samples). +- Fixed a bug in JpegDecode that caused PIL to report "decoder error + -2" for some progressive JPEG files (reported by Magnus Källström, + who also provided samples). -- Fixed a bug in JpegImagePlugin that caused PIL to hang when loading JPEG files using 16-bit quantization tables. +- Fixed a bug in JpegImagePlugin that caused PIL to hang when loading + JPEG files using 16-bit quantization tables. -- The Image "transform" method now supports Image.QUAD transforms. The data argument is an 8-tuple giving the upper left, lower left, lower right, and upper right corner of the source quadri-lateral. Also added Image.MESH transform which takes a list of quadrilaterals. +- The Image "transform" method now supports Image.QUAD transforms. + The data argument is an 8-tuple giving the upper left, lower + left, lower right, and upper right corner of the source quadri- + lateral. Also added Image.MESH transform which takes a list + of quadrilaterals. -- The Image "resize", "rotate", and "transform" methods now support Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. Filters can be used with all transform methods. +- The Image "resize", "rotate", and "transform" methods now support + Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. + Filters can be used with all transform methods. -- The ImageDraw "rectangle" method now includes both the right and the bottom edges when drawing filled rectangles. +- The ImageDraw "rectangle" method now includes both the right + and the bottom edges when drawing filled rectangles. -- The TGA decoder now works properly for runlength encoded images which have more than one byte per pixel. +- The TGA decoder now works properly for runlength encoded images + which have more than one byte per pixel. - "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") -- Some file drivers didn't handle the optional "modify" argument to the load method. This resulted in exceptions when you used "paste" (and other methods that modify an image in place) on a newly opened file. +- Some file drivers didn't handle the optional "modify" argument + to the load method. This resulted in exceptions when you used + "paste" (and other methods that modify an image in place) on a + newly opened file. 0.3b2 ----- The test suite includes 825 individual tests. -- An Image "getbands" method has been added. It returns a tuple containing the individual band names for this image. To figure out how many bands an image has, use "len(im.getbands())". +- An Image "getbands" method has been added. It returns a tuple + containing the individual band names for this image. To figure + out how many bands an image has, use "len(im.getbands())". - An Image "putpixel" method has been added. -- The Image "point" method can now be used to convert "L" images to any other format, via a lookup table. That table should contain 256 values for each band in the output image. +- The Image "point" method can now be used to convert "L" images + to any other format, via a lookup table. That table should + contain 256 values for each band in the output image. -- Some file drivers (including FLI/FLC, GIF, and IM) accidentally overwrote the offset method with an internal attribute. All drivers have been updated to use private attributes where possible. +- Some file drivers (including FLI/FLC, GIF, and IM) accidentally + overwrote the offset method with an internal attribute. All + drivers have been updated to use private attributes where + possible. -- The Image "histogram" method now works for "I" and "F" images. For these modes, PIL divides the range between the min and max values used in the image into 256 bins. - - You can also pass in your own min and max values via the "extrema" option: +- The Image "histogram" method now works for "I" and "F" images. + For these modes, PIL divides the range between the min and + max values used in the image into 256 bins. You can also + pass in your own min and max values via the "extrema" option: h = im.histogram(extrema=(0, 255)) -- An Image "getextrema" method has been added. It returns the min and max values used in the image. In this release, this works for single band images only. +- An Image "getextrema" method has been added. It returns the + min and max values used in the image. In this release, this + works for single band images only. -- Changed the PNG driver to load and save mode "I" images as 16-bit images. When saving, values outside the range 0..65535 are clipped. +- Changed the PNG driver to load and save mode "I" images as + 16-bit images. When saving, values outside the range 0..65535 + are clipped. - Fixed ImageFont.py to work with the new "pilfont" compiler. - Added JPEG "save" and "draft" support for mode "YCbCr" images. - - Note that if you save an "YCbCr" image as a JPEG file and read it back, it is read as an RGB file. To get around this, you can use the "draft" method: + Note that if you save an "YCbCr" image as a JPEG file and read + it back, it is read as an RGB file. To get around this, you + can use the "draft" method: im = Image.open("color.jpg") im.draft("YCbCr", im.size) -- Read "RGBA" TGA images. Also fixed the orientation bug; all images should now come out the right way. +- Read "RGBA" TGA images. Also fixed the orientation bug; all + images should now come out the right way. -- Changed mode name (and internal representation) from "YCrCb" to "YCbCr" (!) - - *** WARNING: MAY BREAK EXISTING CODE *** +- Changed mode name (and internal representation) from "YCrCb" + to "YCbCr" (!) + *** WARNING: MAY BREAK EXISTING CODE *** 0.3b1 ----- The test suite includes 750 individual tests. -- The "pilfont" package is now included in the standard PIL distribution. The pilfont utility can be used to convert X BDF and PCF raster font files to a format understood by the ImageFont module. +- The "pilfont" package is now included in the standard PIL + distribution. The pilfont utility can be used to convert + X BDF and PCF raster font files to a format understood by + the ImageFont module. -- GIF files are now interlaced by default. To write a non-interlaced file, pass interlace=0 to the "save" method. +- GIF files are now interlaced by default. To write a + non-interlaced file, pass interlace=0 to the "save" + method. -- The default string format has changed for the "fromstring" and "tostring" methods. +- The default string format has changed for the "fromstring" + and "tostring" methods. + *** WARNING: MAY BREAK EXISTING CODE *** - *** WARNING: MAY BREAK EXISTING CODE *** + NOTE: If no extra arguments are given, the first line in + the string buffer is the top line of the image, instead of + the bottom line. For RGB images, the string now contains + 3 bytes per pixel instead of 4. These changes were made + to make the methods compatible with the "fromstring" + factory function. - NOTE: If no extra arguments are given, the first line in - the string buffer is the top line of the image, instead of - the bottom line. For RGB images, the string now contains - 3 bytes per pixel instead of 4. These changes were made - to make the methods compatible with the "fromstring" - factory function. - - To get the old behaviour, use the following syntax: + To get the old behaviour, use the following syntax: data = im.tostring("raw", "RGBX", 0, -1) im.fromstring(data, "raw", "RGBX", 0, -1) -- "new" no longer gives a MemoryError if the width or height is zero (this only happened on platforms where malloc(0) or calloc(0) returns NULL). +- "new" no longer gives a MemoryError if the width or height + is zero (this only happened on platforms where malloc(0) + or calloc(0) returns NULL). - "new" now adds a default palette object to "P" images. -- You can now convert directly between all modes supported by PIL. When converting colour images to "P", PIL defaults to a "web" palette and dithering. When converting greyscale images to "1", PIL uses a thresholding and dithering. +- You can now convert directly between all modes supported by + PIL. When converting colour images to "P", PIL defaults to + a "web" palette and dithering. When converting greyscale + images to "1", PIL uses a thresholding and dithering. -- Added a "dither" option to "convert". By default, "convert" uses floyd-steinberg error diffusion for "P" and "1" targets, so this option is only used to *disable* dithering. Allowed values are NONE (no dithering) or FLOYDSTEINBERG (default). +- Added a "dither" option to "convert". By default, "convert" + uses floyd-steinberg error diffusion for "P" and "1" targets, + so this option is only used to *disable* dithering. Allowed + values are NONE (no dithering) or FLOYDSTEINBERG (default). imOut = im.convert("P", dither=Image.NONE) -- Added a full set of "I" decoders. You can use "fromstring" (and file decoders) to read any standard integer type as an "I" image. +- Added a full set of "I" decoders. You can use "fromstring" + (and file decoders) to read any standard integer type as an + "I" image. -- Added some support for "YCbCr" images (creation, conversion from/to "L" and "RGB", IM YCC load/save) +- Added some support for "YCbCr" images (creation, conversion + from/to "L" and "RGB", IM YCC load/save) - "getpixel" now works properly with fractional coordinates. -- ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", "RGBX", "CMYK", and "YCbCr" images. +- ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", + "RGBX", "CMYK", and "YCbCr" images. - ImImagePlugin no longer attaches palettes to "RGB" images. @@ -5954,15 +6050,19 @@ The test suite includes 750 individual tests. - Added experimental IPTC/NAA support. -- Eliminated AttributeError exceptions after "crop" (from Skip Montanaro) +- Eliminated AttributeError exceptions after "crop" (from + Skip Montanaro) -- Reads some uncompressed formats via memory mapping (this is currently supported on Win32 only) +- Reads some uncompressed formats via memory mapping (this + is currently supported on Win32 only) -- Fixed some last minute glitches in the last alpha release (Types instead of types in Image.py, version numbers, etc.) +- Fixed some last minute glitches in the last alpha release + (Types instead of types in Image.py, version numbers, etc.) - Eliminated some more bogus compiler warnings. -- Various fixes to make PIL compile and run smoother on Macs (from Jack Jansen). +- Various fixes to make PIL compile and run smoother on Macs + (from Jack Jansen). - Fixed "fromstring" and "tostring" for mode "I" images. @@ -5971,29 +6071,39 @@ The test suite includes 750 individual tests. The test suite includes 530 individual tests. -- Eliminated unexpected side-effect in "paste" with matte. "paste" now works properly also if compiled with "gcc". +- Eliminated unexpected side-effect in "paste" with matte. "paste" + now works properly also if compiled with "gcc". - Adapted to Python 1.5 (build issues only) -- Fixed the ImageDraw "point" method to draw also the last point (!). +- Fixed the ImageDraw "point" method to draw also the last + point (!). - Added "I" and "RGBX" support to Image.new. -- The plugin path is now properly prepended to the module search path when a plugin module is imported. +- The plugin path is now properly prepended to the module search + path when a plugin module is imported. -- Added "draw" method to the ImageWin.Dib class. This is used by Topaz to print images on Windows printers. +- Added "draw" method to the ImageWin.Dib class. This is used by + Topaz to print images on Windows printers. - "convert" now supports conversions from "P" to "1" and "F". -- "paste" can now take a colour instead of an image as the first argument. The colour must match the colour argument given to the new function, and match the mode of the target image. +- "paste" can now take a colour instead of an image as the first argument. + The colour must match the colour argument given to the new function, and + match the mode of the target image. - Fixed "paste" to allow a mask also for mode "F" images. -- The BMP driver now saves mode "1" images. When loading images, the mode is set to "L" for 8-bit files with greyscale palettes, and to "P" for other 8-bit files. +- The BMP driver now saves mode "1" images. When loading images, the mode + is set to "L" for 8-bit files with greyscale palettes, and to "P" for + other 8-bit files. - The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). -- The JPEG and GIF drivers now saves "1" images. For JPEG, the image is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the image will be loaded as a "P" image. +- The JPEG and GIF drivers now saves "1" images. For JPEG, the image + is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the + image will be loaded as a "P" image. - Fixed a potential buffer overrun in the GIF encoder. @@ -6003,28 +6113,28 @@ The test suite includes 530 individual tests. The test suite includes 400 individual tests. - Improvements to the test suite revealed a number of minor bugs, which - are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont - are still weak spots in this release. + are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont + are still weak spots in this release. - Added "putpalette" method to the Image class. You can use this - to add or modify the palette for "P" and "L" images. If a palette - is added to an "L" image, it is automatically converted to a "P" - image. + to add or modify the palette for "P" and "L" images. If a palette + is added to an "L" image, it is automatically converted to a "P" + image. - Fixed ImageDraw to properly handle 32-bit image memories - ("RGB", "RGBA", "CMYK", "F") + ("RGB", "RGBA", "CMYK", "F") - Fixed "fromstring" and "tostring" not to mess up the mode attribute - in default mode. + in default mode. - Changed ImPlatform.h to work on CRAY's (don't have one at home, so I - haven't tried it). The previous version assumed that either "short" - or "int" were 16-bit wide. PIL still won't compile on platforms where - neither "short", "int" nor "long" are 32-bit wide. + haven't tried it). The previous version assumed that either "short" + or "int" were 16-bit wide. PIL still won't compile on platforms where + neither "short", "int" nor "long" are 32-bit wide. - Added file= and data= keyword arguments to PhotoImage and BitmapImage. - This allows you to use them as drop-in replacements for the corre- - sponding Tkinter classes. + This allows you to use them as drop-in replacements for the corre- + sponding Tkinter classes. - Removed bogus references to the crack coder (ImagingCrack). @@ -6038,264 +6148,390 @@ The test suite includes 400 individual tests. 0.1b1 to 0.2 (b5) ----------------- -- Modified "fromstring" and "tostring" methods to use file codecs. Also added "fromstring" factory method to create an image directly from data in a string. +- Modified "fromstring" and "tostring" methods to use file codecs. + Also added "fromstring" factory method to create an image directly + from data in a string. -- Added support for 32-bit floating point images (mode "F"). You can convert between "L" and "F" images, and apply a subset of the available image processing methods on the "F" image. You can also read virtually any data format into a floating point image memory; see the section on "Decoding Floating Point Data" in the handbook for more information. +- Added support for 32-bit floating point images (mode "F"). You + can convert between "L" and "F" images, and apply a subset of the + available image processing methods on the "F" image. You can also + read virtually any data format into a floating point image memory; + see the section on "Decoding Floating Point Data" in the handbook + for more information. 0.2b5 released; on windows only ------------------------------- - Fixed the tobitmap() method to work properly for small bitmaps. -- Added RMS and standard deviation to the ImageStat.Stat class. Also modified the constructor to take an optional feature mask, and also to accept either an image or a list containing the histogram data. +- Added RMS and standard deviation to the ImageStat.Stat class. Also + modified the constructor to take an optional feature mask, and also + to accept either an image or a list containing the histogram data. -- The BitmapImage code in ImageTk can now use a special bitmap decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" file for details. If not installed, bitmaps are transferred to Tk as XBM strings. +- The BitmapImage code in ImageTk can now use a special bitmap + decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" + file for details. If not installed, bitmaps are transferred to Tk as + XBM strings. -- The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") instead of a special image type. This gives somewhat better performance, and also allows PIL to support transparency. +- The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") + instead of a special image type. This gives somewhat better performance, + and also allows PIL to support transparency. + *** WARNING: TKAPPINIT MUST BE MODIFIED *** -*** WARNING: TKAPPINIT MUST BE MODIFIED *** - -- ImageTk now honours the alpha layer in RGBA images. Only fully transparent pixels are made transparent (that is, the alpha layer is treated as a mask). To treat the alpha laters as a matte, you must paste the image on the background before handing it over to ImageTk. +- ImageTk now honours the alpha layer in RGBA images. Only fully + transparent pixels are made transparent (that is, the alpha layer + is treated as a mask). To treat the alpha laters as a matte, you + must paste the image on the background before handing it over to + ImageTk. - Added McIdas reader (supports 8-bit images only). -- PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As long as you only load and save these formats, you don't have to wait for a full scan for drivers. To force scanning, call the Image.init() function. +- PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As + long as you only load and save these formats, you don't have to + wait for a full scan for drivers. To force scanning, call the + Image.init() function. -- The "seek" and "tell" methods are now always available, also for single-frame images. +- The "seek" and "tell" methods are now always available, also for + single-frame images. -- Added optional mask argument to histogram method. The mask may be an "1" or "L" image with the same size as the original image. Only pixels where the mask is non-zero are included in the histogram. +- Added optional mask argument to histogram method. The mask may + be an "1" or "L" image with the same size as the original image. + Only pixels where the mask is non-zero are included in the + histogram. -- The "paste" method now allows you to specify only the lower left corner (a 2-tuple), instead of the full region (a 4-tuple). +- The "paste" method now allows you to specify only the lower left + corner (a 2-tuple), instead of the full region (a 4-tuple). -- Reverted to old plugin scanning model; now scans all directory names in the path when looking for plugins. +- Reverted to old plugin scanning model; now scans all directory + names in the path when looking for plugins. -- Added PIXAR raster support. Only uncompressed ("dumped") RGB images can currently be read (based on information provided by Greg Coats). +- Added PIXAR raster support. Only uncompressed ("dumped") RGB + images can currently be read (based on information provided + by Greg Coats). -- Added FlashPix (FPX) read support. Reads all pixel formats, but only the highest resolution is read, and the viewing transform is currently ignored. +- Added FlashPix (FPX) read support. Reads all pixel formats, but + only the highest resolution is read, and the viewing transform is + currently ignored. -- Made PNG encoding somewhat more efficient in "optimize" mode; a bug in 0.2b4 didn't enable all predictor filters when optimized storage were requested. +- Made PNG encoding somewhat more efficient in "optimize" mode; a + bug in 0.2b4 didn't enable all predictor filters when optimized + storage were requested. -- Added Microsoft Image Composer (MIC) read support. When opened, the first sprite in the file is loaded. You can use the seek method to load additional sprites from the file. +- Added Microsoft Image Composer (MIC) read support. When opened, + the first sprite in the file is loaded. You can use the seek method + to load additional sprites from the file. - Properly reads "P" and "CMYK" PSD images. -- "pilconvert" no longer optimizes by default; use the -o option to make the file as small as possible (at the expense of speed); use the -q option to set the quality when compressing to JPEG. +- "pilconvert" no longer optimizes by default; use the -o option to + make the file as small as possible (at the expense of speed); use + the -q option to set the quality when compressing to JPEG. - Fixed "crop" not to drop the palette for "P" images. - Added and verified FLC support. -- Paste with "L" or "RGBA" alpha is now several times faster on most platforms. +- Paste with "L" or "RGBA" alpha is now several times faster on most + platforms. -- Changed Image.new() to initialize the image to black, as described in the handbook. To get an uninitialized image, use None as the colour. +- Changed Image.new() to initialize the image to black, as described + in the handbook. To get an uninitialized image, use None as the + colour. -- Fixed the PDF encoder to produce a valid header; Acrobat no longer complains when you load PDF images created by PIL. +- Fixed the PDF encoder to produce a valid header; Acrobat no longer + complains when you load PDF images created by PIL. -- PIL only scans fully-qualified directory names in the path when looking for plugins. +- PIL only scans fully-qualified directory names in the path when + looking for plugins. + *** WARNING: MAY BREAK EXISTING CODE *** -*** WARNING: MAY BREAK EXISTING CODE *** - -- Faster implementation of "save" used when filename is given, or when file object has "fileno" and "flush" methods. +- Faster implementation of "save" used when filename is given, + or when file object has "fileno" and "flush" methods. - Don't crash in "crop" if region extends outside the source image. - Eliminated a massive memory leak in the "save" function. -- The GIF decoder doesn't crash if the code size is set to an illegal value. This could happen since another bug didn't handle local palettes properly if they didn't have the same size as the global palette (not very common). +- The GIF decoder doesn't crash if the code size is set to an illegal + value. This could happen since another bug didn't handle local + palettes properly if they didn't have the same size as the + global palette (not very common). - Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. -- Fixed palette and padding problems in BMP driver. Now properly writes "1", "L", "P" and "RGB" images. +- Fixed palette and padding problems in BMP driver. Now properly + writes "1", "L", "P" and "RGB" images. - Fixed getpixel()/getdata() to return correct pixel values. -- Added PSD (PhotoShop) read support. Reads both uncompressed and compressed images of most types. +- Added PSD (PhotoShop) read support. Reads both uncompressed + and compressed images of most types. -- Added GIF write support (writes "uncompressed" GIF files only, due to unresolvable licensing issues). The "gifmaker.py" script can be used to create GIF animations. +- Added GIF write support (writes "uncompressed" GIF files only, + due to unresolvable licensing issues). The "gifmaker.py" script + can be used to create GIF animations. -- Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" images. +- Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" + images. -- Added FLI read support. This driver has only been tested on a few FLI samples. +- Added FLI read support. This driver has only been tested + on a few FLI samples. - Reads 2-bit and 4-bit PCX images. -- Added MSP read and write support. Both version 1 and 2 can be read, but only version 1 (uncompressed) files are written. +- Added MSP read and write support. Both version 1 and 2 can be + read, but only version 1 (uncompressed) files are written. -- Fixed a bug in the FLI/FLC identification code that caused the driver to raise an exception when parsing valid FLI/FLC files. +- Fixed a bug in the FLI/FLC identification code that caused the + driver to raise an exception when parsing valid FLI/FLC files. -- Improved performance when loading file format plugins, and when opening files. +- Improved performance when loading file format plugins, and when + opening files. -- Added GIF animation support, via the "seek" and "tell" methods. You can use "player.py" to play an animated GIF file. +- Added GIF animation support, via the "seek" and "tell" methods. + You can use "player.py" to play an animated GIF file. -- Removed MNG support, since the spec is changing faster than I can change the code. I've added support for the experimental ARG format instead. Contact me for more information on this format. +- Removed MNG support, since the spec is changing faster than I + can change the code. I've added support for the experimental + ARG format instead. Contact me for more information on this + format. -- Added keyword options to the "save" method. The following options are currently supported: +- Added keyword options to the "save" method. The following options + are currently supported: - .. list-table:: - :widths: 25 25 50 - :header-rows: 1 + .. list-table:: + :widths: 25 25 50 + :header-rows: 1 - * - Format - - Option - - Description - * - JPEG - - optimize - - Minimize output file at the expense of compression speed. - * - JPEG - - progressive - - Enable progressive output. The option value is ignored. - * - JPEG - - quality - - Set compression quality (1-100). The default value is 75. - * - JPEG - - smooth - - Smooth dithered images. Value is strength (1-100). Default is off (0). - * - PNG - - optimize - - Minimize output file at the expense of compression speed. + * - Format + - Option + - Description + * - JPEG + - optimize + - Minimize output file at the expense of compression speed. + * - JPEG + - progressive + - Enable progressive output. The option value is ignored. + * - JPEG + - quality + - Set compression quality (1-100). The default value is 75. + * - JPEG + - smooth + - Smooth dithered images. Value is strength (1-100). Default is off (0). + * - PNG + - optimize + - Minimize output file at the expense of compression speed. -Expect more options in future releases. Also note that file writers silently ignore unknown options. + Expect more options in future releases. Also note that + file writers silently ignore unknown options. - Plugged memory leaks in the PNG and TIFF decoders. - Added PNG write support. -- (internal) RGB unpackers and converters now set the pad byte to 255 (full opacity). +- (internal) RGB unpackers and converters now set the pad byte + to 255 (full opacity). -- Properly handles the "transparency" property for GIF, PNG and XPM files. +- Properly handles the "transparency" property for GIF, PNG + and XPM files. -- Added a "putalpha" method, allowing you to attach a "1" or "L" image as the alpha layer to an "RGBA" image. +- Added a "putalpha" method, allowing you to attach a "1" or "L" + image as the alpha layer to an "RGBA" image. - Various improvements to the sample scripts: - "pilconvert" Carries out some extra tricks in order to make - the resulting file as small as possible. + "pilconvert" Carries out some extra tricks in order to make + the resulting file as small as possible. - "explode" (NEW) Split an image sequence into individual frames. + "explode" (NEW) Split an image sequence into individual frames. - "gifmaker" (NEW) Convert a sequence file into a GIF animation. - Note that the GIF encoder create "uncompressed" GIF - files, so animations created by this script are - rather large (typically 2-5 times the compressed - sizes). + "gifmaker" (NEW) Convert a sequence file into a GIF animation. + Note that the GIF encoder create "uncompressed" GIF + files, so animations created by this script are + rather large (typically 2-5 times the compressed + sizes). - "image2py" (NEW) Convert a single image to a python module. See - comments in this script for details. + "image2py" (NEW) Convert a single image to a python module. See + comments in this script for details. - "player" If multiple images are given on the command line, - they are interpreted as frames in a sequence. The - script assumes that they all have the same size. - Also note that this script now can play FLI/FLC - and GIF animations. + "player" If multiple images are given on the command line, + they are interpreted as frames in a sequence. The + script assumes that they all have the same size. + Also note that this script now can play FLI/FLC + and GIF animations. - This player can also execute embedded Python - animation applets (ARG format only). + This player can also execute embedded Python + animation applets (ARG format only). - "viewer" Transparent images ("P" with transparency property, - and "RGBA") are superimposed on the standard Tk back- - ground. + "viewer" Transparent images ("P" with transparency property, + and "RGBA") are superimposed on the standard Tk back- + ground. -- Fixed colour argument to "new". For multilayer images, pass a tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, Magenta, Yellow, Black). +- Fixed colour argument to "new". For multilayer images, pass a + tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, + Magenta, Yellow, Black). - Added XPM (X pixmap) read support. 0.2b3 ----- -- Added MNG (multi-image network graphics) read support. "Ming" is a proposed animation standard, based on the PNG file format. +- Added MNG (multi-image network graphics) read support. "Ming" + is a proposed animation standard, based on the PNG file format. - You can use the "player" sample script to display some flavours of this format. The MNG standard is still under development, as is this driver. More information, including sample files, can be found at + You can use the "player" sample script to display some flavours + of this format. The MNG standard is still under development, + as is this driver. More information, including sample files, + can be found at -- Added a "verify" method to images loaded from file. This method scans the file for errors, without actually decoding the image data, and raises a suitable exception if it finds any problems. Currently implemented for PNG and MNG files only. +- Added a "verify" method to images loaded from file. This method + scans the file for errors, without actually decoding the image + data, and raises a suitable exception if it finds any problems. + Currently implemented for PNG and MNG files only. - Added support for interlaced GIF images. -- Added PNG read support -- if linked with the ZLIB compression library, PIL reads all kinds of PNG images, except interlaced files. +- Added PNG read support -- if linked with the ZLIB compression library, + PIL reads all kinds of PNG images, except interlaced files. -- Improved PNG identification support -- doesn't mess up on unknown chunks, identifies all possible PNG modes, and verifies checksum on PNG header chunks. +- Improved PNG identification support -- doesn't mess up on unknown + chunks, identifies all possible PNG modes, and verifies checksum + on PNG header chunks. -- Added an experimental reader for placable Windows Meta Files (WMF). This reader is still very incomplete, but it illustrates how PIL's drawing capabilities can be used to render vector and metafile formats. +- Added an experimental reader for placable Windows Meta Files (WMF). + This reader is still very incomplete, but it illustrates how PIL's + drawing capabilities can be used to render vector and metafile + formats. -- Added restricted drivers for images from Image Tools (greyscale only) and LabEye/IFUNC (common interchange modes only). +- Added restricted drivers for images from Image Tools (greyscale + only) and LabEye/IFUNC (common interchange modes only). -- Some minor improvements to the sample scripts provided in the "Scripts" directory. +- Some minor improvements to the sample scripts provided in the + "Scripts" directory. - The test images have been moved to the "Images" directory. 0.2b2, 0.2b1 released; Windows only ----------------------------------- -- Fixed filling of complex polygons. The ImageDraw "line" and "polygon" methods also accept Path objects. +- Fixed filling of complex polygons. The ImageDraw "line" and + "polygon" methods also accept Path objects. - The ImageTk "PhotoImage" object can now be constructed directly - from an image. You can also pass the object itself to Tkinter, - instead of using the "image" attribute. Finally, using "paste" - on a displayed image automatically updates the display. + from an image. You can also pass the object itself to Tkinter, + instead of using the "image" attribute. Finally, using "paste" + on a displayed image automatically updates the display. - The ImageTk "BitmapImage" object allows you to create transparent - overlays from 1-bit images. You can pass the object itself to - Tkinter. The constructor takes the same arguments as the Tkinter - BitmapImage class; use the "foreground" option to set the colour - of the overlay. + overlays from 1-bit images. You can pass the object itself to + Tkinter. The constructor takes the same arguments as the Tkinter + BitmapImage class; use the "foreground" option to set the colour + of the overlay. -- Added a "putdata" method to the Image class. This can be used to load a 1-layer image with data from a sequence object or a string. An optional floating point scale and offset can be used to adjust the data to fit into the 8-bit pixel range. Also see the "getdata" method. +- Added a "putdata" method to the Image class. This can be used to + load a 1-layer image with data from a sequence object or a string. + An optional floating point scale and offset can be used to adjust + the data to fit into the 8-bit pixel range. Also see the "getdata" + method. -- Added the EXTENT method to the Image "transform" method. This can be used to quickly crop, stretch, shrink, or mirror a subregion from another image. +- Added the EXTENT method to the Image "transform" method. This can + be used to quickly crop, stretch, shrink, or mirror a subregion + from another image. - Adapted to Python 1.4. -- Added a project makefile for Visual C++ 4.x. This allows you to easily build a dynamically linked version of PIL for Windows 95 and NT. +- Added a project makefile for Visual C++ 4.x. This allows you to + easily build a dynamically linked version of PIL for Windows 95 + and NT. -- A Tk "booster" patch for Windows is available. It gives dramatic performance improvements for some displays. Has been tested with Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk subdirectory for details. +- A Tk "booster" patch for Windows is available. It gives dramatic + performance improvements for some displays. Has been tested with + Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk + subdirectory for details. -- You can now save 1-bit images in the XBM format. In addition, the Image class now provides a "tobitmap" method which returns a string containing an XBM representation of the image. Quite handy to use with Tk. +- You can now save 1-bit images in the XBM format. In addition, the + Image class now provides a "tobitmap" method which returns a string + containing an XBM representation of the image. Quite handy to use + with Tk. - More conversions, including "RGB" to "1" and more. 0.2a1 ----- -- Where earlier versions accepted lists, this version accepts arbitrary Python sequences (including strings, in some cases). A few resource leaks were plugged in the process. +- Where earlier versions accepted lists, this version accepts arbitrary + Python sequences (including strings, in some cases). A few resource + leaks were plugged in the process. -- The Image "paste" method now allows the box to extend outside the target image. The size of the box, the image to be pasted, and the optional mask must still match. +- The Image "paste" method now allows the box to extend outside + the target image. The size of the box, the image to be pasted, + and the optional mask must still match. -- The ImageDraw module now supports filled polygons, outlined and filled ellipses, and text. Font support is rudimentary, though. +- The ImageDraw module now supports filled polygons, outlined and + filled ellipses, and text. Font support is rudimentary, though. -- The Image "point" method now takes an optional mode argument, allowing you to convert the image while translating it. Currently, this can only be used to convert "L" or "P" images to "1" images (creating thresholded images or "matte" masks). +- The Image "point" method now takes an optional mode argument, + allowing you to convert the image while translating it. Currently, + this can only be used to convert "L" or "P" images to "1" images + (creating thresholded images or "matte" masks). -- An Image "getpixel" method has been added. For single band images, it returns the pixel value at a given position as an integer. For n-band images, it returns an n-tuple of integers. +- An Image "getpixel" method has been added. For single band images, + it returns the pixel value at a given position as an integer. + For n-band images, it returns an n-tuple of integers. -- An Image "getdata" method has been added. It returns a sequence object representing the image as a 1-dimensional array. Only len() and [] can be used with this sequence. This method returns a reference to the existing image data, so changes in the image will be immediately reflected in the sequence object. +- An Image "getdata" method has been added. It returns a sequence + object representing the image as a 1-dimensional array. Only len() + and [] can be used with this sequence. This method returns a + reference to the existing image data, so changes in the image + will be immediately reflected in the sequence object. - Fixed alignment problems in the Windows BMP writer. -- If converting an "RGB" image to "RGB" or "L", you can give a second argument containing a colour conversion matrix. +- If converting an "RGB" image to "RGB" or "L", you can give a second + argument containing a colour conversion matrix. -- An Image "getbbox" method has been added. It returns the bounding box of data in an image, considering the value 0 as background. +- An Image "getbbox" method has been added. It returns the bounding + box of data in an image, considering the value 0 as background. -- An Image "offset" method has been added. It returns a new image where the contents of the image have been offset the given distance in X and/or Y direction. Data wraps between edges. +- An Image "offset" method has been added. It returns a new image + where the contents of the image have been offset the given distance + in X and/or Y direction. Data wraps between edges. -- Saves PDF images. The driver creates a binary PDF 1.1 files, using JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding (same as for PostScript) for other formats. +- Saves PDF images. The driver creates a binary PDF 1.1 files, using + JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding + (same as for PostScript) for other formats. -- The "paste" method now accepts "1" masks. Zero means transparent, any other pixel value means opaque. This is faster than using an "L" transparency mask. +- The "paste" method now accepts "1" masks. Zero means transparent, + any other pixel value means opaque. This is faster than using an + "L" transparency mask. -- Properly writes EPS files (and properly prints images to PostScript printers as well). +- Properly writes EPS files (and properly prints images to PostScript + printers as well). -- Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR files. Cursor animations are not supported. +- Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR + files. Cursor animations are not supported. - Fixed alignment problems in the Sun raster loader. -- Added "draft" and "thumbnail" methods. The draft method is used to optimize loading of JPEG and PCD files, the thumbnail method is used to create a thumbnail representation of an image. +- Added "draft" and "thumbnail" methods. The draft method is used + to optimize loading of JPEG and PCD files, the thumbnail method is + used to create a thumbnail representation of an image. -- Added Windows display support, via the ImageWin class (see the handbook for details). +- Added Windows display support, via the ImageWin class (see the + handbook for details). -- Added raster conversion for EPS files. This requires GNU or Aladdin Ghostscript, and probably works on UNIX only. +- Added raster conversion for EPS files. This requires GNU or Aladdin + Ghostscript, and probably works on UNIX only. -- Reads PhotoCD (PCD) images. The base resolution (768x512) can be read from a PhotoCD file. +- Reads PhotoCD (PCD) images. The base resolution (768x512) can be + read from a PhotoCD file. -- Eliminated some compiler warnings. Bindings now compile cleanly in C++ mode. Note that the Imaging library itself must be compiled in C mode. +- Eliminated some compiler warnings. Bindings now compile cleanly in C++ + mode. Note that the Imaging library itself must be compiled in C mode. -- Added "bdf2pil.py", which converts BDF fonts into images with associated metrics. This is definitely work in progress. For info, see description in script for details. +- Added "bdf2pil.py", which converts BDF fonts into images with associated + metrics. This is definitely work in progress. For info, see description + in script for details. - Fixed a bug in the "ImageEnhance.py" module. @@ -6307,45 +6543,65 @@ Expect more options in future releases. Also note that file writers silently ig - Reads plane separated RGB and CMYK TIFF images. -- Added driver debug mode. This is enabled by setting Image.DEBUG to a non-zero value. Try the -D option to "pilfile.py" and see what happens. +- Added driver debug mode. This is enabled by setting Image.DEBUG + to a non-zero value. Try the -D option to "pilfile.py" and see what + happens. - Don't crash on "atend" constructs in PostScript files. -- Only the Image module imports _imaging directly. Other modules should refer to the binding module as "Image.core". +- Only the Image module imports _imaging directly. Other modules + should refer to the binding module as "Image.core". 0.0 to 0.1 (b1) --------------- - A handbook is available (distributed separately). -- The coordinate system is changed so that (0,0) is now located in the upper left corner. This is in compliancy with ISO 12087 and 90% of all other image processing and graphics libraries. +- The coordinate system is changed so that (0,0) is now located + in the upper left corner. This is in compliancy with ISO 12087 + and 90% of all other image processing and graphics libraries. -- Modes "1" (bilevel) and "P" (palette) have been introduced. Note that bilevel images are stored with one byte per pixel. +- Modes "1" (bilevel) and "P" (palette) have been introduced. Note + that bilevel images are stored with one byte per pixel. -- The Image "crop" and "paste" methods now accepts None as the box argument, to refer to the full image (self, that is). +- The Image "crop" and "paste" methods now accepts None as the + box argument, to refer to the full image (self, that is). - The Image "crop" method now works properly. -- The Image "point" method is now available. You can use either a lookup table or a function taking one argument. +- The Image "point" method is now available. You can use either a + lookup table or a function taking one argument. - The Image join function has been renamed to "merge". -- An Image "composite" function has been added. It is identical to copy() followed by paste(mask). +- An Image "composite" function has been added. It is identical + to copy() followed by paste(mask). -- An Image "eval" function has been added. It is currently identical to point(function); that is, only a single image can be processed. +- An Image "eval" function has been added. It is currently identical + to point(function); that is, only a single image can be processed. -- A set of channel operations has been added. See the "ImageChops" module, test_chops.py, and the handbook for details. +- A set of channel operations has been added. See the "ImageChops" + module, test_chops.py, and the handbook for details. -- Added the "pilconvert" utility, which converts image files. Note that the number of output formats are still quite restricted. +- Added the "pilconvert" utility, which converts image files. Note + that the number of output formats are still quite restricted. -- Added the "pilfile" utility, which quickly identifies image files (without loading them, in most cases). +- Added the "pilfile" utility, which quickly identifies image files + (without loading them, in most cases). -- Added the "pilprint" utility, which prints image files to PostScript printers. +- Added the "pilprint" utility, which prints image files to PostScript + printers. -- Added a rudimentary version of the "pilview" utility, which is simple image viewer based on Tk. Only File/Exit and Image/Next works properly. +- Added a rudimentary version of the "pilview" utility, which is + simple image viewer based on Tk. Only File/Exit and Image/Next + works properly. -- An interface to Tk has been added. See "Lib/ImageTk.py" and README for details. +- An interface to Tk has been added. See "Lib/ImageTk.py" and README + for details. -- An interface to Jack Jansen's Img library has been added (thanks to Jack). This allows you to read images through the Img extensions file format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. +- An interface to Jack Jansen's Img library has been added (thanks to + Jack). This allows you to read images through the Img extensions file + format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. -- PostScript printing is provided through the PSDraw module. See the handbook for details. +- PostScript printing is provided through the PSDraw module. See the + handbook for details. From 09b27753e52da5e4987b922c98db386d6e2e3424 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Apr 2022 20:58:50 +1000 Subject: [PATCH 165/294] Clarify that 0.2b1 was released for Windows only, not 0.2b2 --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index ca90ec018..3a6a31423 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6411,8 +6411,8 @@ The test suite includes 400 individual tests. - The test images have been moved to the "Images" directory. -0.2b2, 0.2b1 released; Windows only ------------------------------------ +0.2b2 released. 0.2b1 released for Windows only +----------------------------------------------- - Fixed filling of complex polygons. The ImageDraw "line" and "polygon" methods also accept Path objects. From 9830abd19e96eec7da74f3a51919f54555a143a9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Apr 2022 21:35:40 +1000 Subject: [PATCH 166/294] Formatted script improvements as table --- CHANGES.rst | 91 +++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3a6a31423..f69f74f20 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6300,27 +6300,27 @@ The test suite includes 400 individual tests. are currently supported: .. list-table:: - :widths: 25 25 50 - :header-rows: 1 + :widths: 25 25 50 + :header-rows: 1 - * - Format - - Option - - Description - * - JPEG - - optimize - - Minimize output file at the expense of compression speed. - * - JPEG - - progressive - - Enable progressive output. The option value is ignored. - * - JPEG - - quality - - Set compression quality (1-100). The default value is 75. - * - JPEG - - smooth - - Smooth dithered images. Value is strength (1-100). Default is off (0). - * - PNG - - optimize - - Minimize output file at the expense of compression speed. + * - Format + - Option + - Description + * - JPEG + - optimize + - Minimize output file at the expense of compression speed. + * - JPEG + - progressive + - Enable progressive output. The option value is ignored. + * - JPEG + - quality + - Set compression quality (1-100). The default value is 75. + * - JPEG + - smooth + - Smooth dithered images. Value is strength (1-100). Default is off (0). + * - PNG + - optimize + - Minimize output file at the expense of compression speed. Expect more options in future releases. Also note that file writers silently ignore unknown options. @@ -6340,32 +6340,35 @@ The test suite includes 400 individual tests. - Various improvements to the sample scripts: - "pilconvert" Carries out some extra tricks in order to make - the resulting file as small as possible. + .. list-table:: + :widths: 25 75 - "explode" (NEW) Split an image sequence into individual frames. + * - pilconvert + - Carries out some extra tricks in order to make + the resulting file as small as possible. + * - explode + - (NEW) Split an image sequence into individual frames. + * - gifmaker + - (NEW) Convert a sequence file into a GIF animation. + Note that the GIF encoder create "uncompressed" GIF + files, so animations created by this script are + rather large (typically 2-5 times the compressed + sizes). + * - image2py + - (NEW) Convert a single image to a python module. See + comments in this script for details. + * - player + - If multiple images are given on the command line, + they are interpreted as frames in a sequence. The + script assumes that they all have the same size. + Also note that this script now can play FLI/FLC + and GIF animations. - "gifmaker" (NEW) Convert a sequence file into a GIF animation. - Note that the GIF encoder create "uncompressed" GIF - files, so animations created by this script are - rather large (typically 2-5 times the compressed - sizes). - - "image2py" (NEW) Convert a single image to a python module. See - comments in this script for details. - - "player" If multiple images are given on the command line, - they are interpreted as frames in a sequence. The - script assumes that they all have the same size. - Also note that this script now can play FLI/FLC - and GIF animations. - - This player can also execute embedded Python - animation applets (ARG format only). - - "viewer" Transparent images ("P" with transparency property, - and "RGBA") are superimposed on the standard Tk back- - ground. + This player can also execute embedded Python + animation applets (ARG format only). + * - viewer + - Transparent images ("P" with transparency property, + and "RGBA") are superimposed on the standard Tk background. - Fixed colour argument to "new". For multilayer images, pass a tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, From 5a8b8b95f635c2163fea9e69f52271a6a7b59d9a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Apr 2022 22:01:35 +1000 Subject: [PATCH 167/294] Use literal code blocks --- CHANGES.rst | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f69f74f20..5287f0a2e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5057,7 +5057,7 @@ http://svn.effbot.org/public/pil/ and the documentation; this is a bug, and will most likely be fixed in a future version. In this release, PIL prints a warning message instead. To silence the warning, change any calls of the form - "frombuffer(mode, size, data)" to + "frombuffer(mode, size, data)" to:: frombuffer(mode, size, data, "raw", mode, 0, 1) @@ -5069,14 +5069,11 @@ http://svn.effbot.org/public/pil/ Image class (based on code by Travis Oliphant). This allows you to easily convert between PIL image memories and - NumPy arrays: + NumPy arrays:: import numpy, Image - im = Image.open('hopper.jpg') - a = numpy.asarray(im) # a is readonly - im = Image.fromarray(a) - Fixed CMYK polarity for JPEG images, by treating all images as @@ -5128,7 +5125,7 @@ http://svn.effbot.org/public/pil/ - Added pixel access object. The "load" method now returns a access object that can be used to directly get and set pixel - values, using ordinary [x, y] notation: + values, using ordinary [x, y] notation:: pixel = im.load() v = pixel[x, y] @@ -5363,7 +5360,7 @@ http://svn.effbot.org/public/pil/ - Added optional "encoding" argument to the ImageFont.truetype factory. This argument can be used to specify non-Unicode character maps for fonts that support that. For example, to draw text using - the Microsoft Symbol font, use: + the Microsoft Symbol font, use:: font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") draw.text((0, 0), unichr(0xF000 + 0xAA)) @@ -5661,7 +5658,7 @@ http://svn.effbot.org/public/pil/ - Added DPI read/write support to the JPEG codec. The decoder sets the info["dpi"] attribute for JPEG files with JFIF dpi - settings. The encoder uses the "dpi" option: + settings. The encoder uses the "dpi" option:: im = Image.open("file.jpg") dpi = im.info["dpi"] # raises KeyError if DPI not known @@ -5858,7 +5855,7 @@ http://svn.effbot.org/public/pil/ ----- - Added Toby J. Sargeant's quantization package. To enable - quantization, use the "palette" option to "convert": + quantization, use the "palette" option to "convert":: imOut = im.convert("P", palette=Image.ADAPTIVE) @@ -5952,7 +5949,7 @@ The test suite includes 825 individual tests. - The Image "histogram" method now works for "I" and "F" images. For these modes, PIL divides the range between the min and max values used in the image into 256 bins. You can also - pass in your own min and max values via the "extrema" option: + pass in your own min and max values via the "extrema" option:: h = im.histogram(extrema=(0, 255)) @@ -5969,7 +5966,7 @@ The test suite includes 825 individual tests. - Added JPEG "save" and "draft" support for mode "YCbCr" images. Note that if you save an "YCbCr" image as a JPEG file and read it back, it is read as an RGB file. To get around this, you - can use the "draft" method: + can use the "draft" method:: im = Image.open("color.jpg") im.draft("YCbCr", im.size) @@ -6006,7 +6003,7 @@ The test suite includes 750 individual tests. to make the methods compatible with the "fromstring" factory function. - To get the old behaviour, use the following syntax: + To get the old behaviour, use the following syntax:: data = im.tostring("raw", "RGBX", 0, -1) im.fromstring(data, "raw", "RGBX", 0, -1) @@ -6026,6 +6023,7 @@ The test suite includes 750 individual tests. uses floyd-steinberg error diffusion for "P" and "1" targets, so this option is only used to *disable* dithering. Allowed values are NONE (no dithering) or FLOYDSTEINBERG (default). + :: imOut = im.convert("P", dither=Image.NONE) From ea24341c5f2b07af8a94117a9ba02baaa1a36aa0 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Apr 2022 22:03:10 +1000 Subject: [PATCH 168/294] Use bold syntax --- CHANGES.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5287f0a2e..57dbdbe3c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5976,7 +5976,7 @@ The test suite includes 825 individual tests. - Changed mode name (and internal representation) from "YCrCb" to "YCbCr" (!) - *** WARNING: MAY BREAK EXISTING CODE *** + **WARNING: MAY BREAK EXISTING CODE** 0.3b1 ----- @@ -5994,7 +5994,7 @@ The test suite includes 750 individual tests. - The default string format has changed for the "fromstring" and "tostring" methods. - *** WARNING: MAY BREAK EXISTING CODE *** + **WARNING: MAY BREAK EXISTING CODE** NOTE: If no extra arguments are given, the first line in the string buffer is the top line of the image, instead of @@ -6174,7 +6174,7 @@ The test suite includes 400 individual tests. - The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") instead of a special image type. This gives somewhat better performance, and also allows PIL to support transparency. - *** WARNING: TKAPPINIT MUST BE MODIFIED *** + **WARNING: TKAPPINIT MUST BE MODIFIED** - ImageTk now honours the alpha layer in RGBA images. Only fully transparent pixels are made transparent (that is, the alpha layer @@ -6241,7 +6241,7 @@ The test suite includes 400 individual tests. - PIL only scans fully-qualified directory names in the path when looking for plugins. - *** WARNING: MAY BREAK EXISTING CODE *** + **WARNING: MAY BREAK EXISTING CODE** - Faster implementation of "save" used when filename is given, or when file object has "fileno" and "flush" methods. From a50c3957df3c6929ab9f872a833036212634da1e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 27 Apr 2022 22:28:10 +1000 Subject: [PATCH 169/294] Updated URL --- src/PIL/JpegPresets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/JpegPresets.py b/src/PIL/JpegPresets.py index e5a5d178a..a678e248e 100644 --- a/src/PIL/JpegPresets.py +++ b/src/PIL/JpegPresets.py @@ -37,7 +37,7 @@ You can get the subsampling of a JPEG with the :func:`.JpegImagePlugin.get_sampling` function. In JPEG compressed data a JPEG marker is used instead of an EXIF tag. -(ref.: https://www.exiv2.org/tags.html) +(ref.: https://exiv2.org/tags.html) Quantization tables From 87e4ffcc59089f217007cd27778578266417a16d Mon Sep 17 00:00:00 2001 From: Max Base Date: Wed, 27 Apr 2022 18:19:04 +0430 Subject: [PATCH 170/294] Update CHANGES.rst Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 156888dab..35ba42b14 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3149,7 +3149,7 @@ Changelog (Pillow) - Change function declaration to match Tcl_CmdProc type #1966 [homm] -- Integer overflow checks on all calls to ``*alloc`` #1781 +- Integer overflow checks on all calls to \*alloc #1781 [wiredfool] - Change equals method on Image so it short circuits #1967 From d42efd7ec0450bc355b4cbbccef3b364f67513d4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 28 Apr 2022 01:26:57 +0300 Subject: [PATCH 171/294] Update URL (#16) --- Tests/test_file_jpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 203065802..12edd7582 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -743,7 +743,7 @@ class TestFileJpeg: # Act / Assert # "When the image resolution is unknown, 72 [dpi] is designated." - # http://www.exiv2.org/tags.html + # https://exiv2.org/tags.html assert im.info.get("dpi") == (72, 72) def test_invalid_exif(self): From b4dab3aca301f49dd937fd29a06f1766cf138493 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 28 Apr 2022 21:28:46 +0300 Subject: [PATCH 172/294] Test Python 3.11-dev --- .ci/install.sh | 3 ++- .github/workflows/macos-install.sh | 3 ++- .github/workflows/test-windows.yml | 2 +- .github/workflows/test.yml | 3 +++ docs/installation.rst | 9 ++++++--- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.ci/install.sh b/.ci/install.sh index 100424ee9..028d68795 100755 --- a/.ci/install.sh +++ b/.ci/install.sh @@ -32,7 +32,8 @@ 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 +# 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 diff --git a/.github/workflows/macos-install.sh b/.github/workflows/macos-install.sh index 8260cf8d8..06b829645 100755 --- a/.github/workflows/macos-install.sh +++ b/.github/workflows/macos-install.sh @@ -15,7 +15,8 @@ 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 -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 # extra test images pushd depends && ./install_extra_test_images.sh && popd diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 6ed8bb0c5..71b54021c 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"] architecture: ["x86", "x64"] include: # PyPy 7.3.4+ only ships 64-bit binaries for Windows diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b13addfd..d41f4b571 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,7 @@ jobs: python-version: [ "pypy-3.8", "pypy-3.7", + "3.11-dev", "3.10", "3.9", "3.8", @@ -59,6 +60,8 @@ jobs: if: startsWith(matrix.os, 'macOS') run: | .github/workflows/macos-install.sh + env: + GHA_PYTHON_VERSION: ${{ matrix.python-version }} - name: Build run: | diff --git a/docs/installation.rst b/docs/installation.rst index 41e99797c..05a12188f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -465,11 +465,13 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| macOS 10.15 Catalina | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 | +| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11 | x86-64 | +| | PyPy3 | | +----------------------------------+----------------------------+---------------------+ | Ubuntu Linux 18.04 LTS (Bionic) | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86-64 | +| Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, 3.11 | x86-64 | +| | PyPy3 | | | +----------------------------+---------------------+ | | 3.8 | arm64v8, ppc64le, | | | | s390x | @@ -478,7 +480,8 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Windows Server 2016 | 3.7 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Windows Server 2019 | 3.7, 3.8, 3.9, 3.10, PyPy3 | x86, x86-64 | +| Windows Server 2022 | 3.7, 3.8, 3.9, 3.10, 3.11 | x86, x86-64 | +| | PyPy3 | | | +----------------------------+---------------------+ | | 3.9/MinGW | x86, x86-64 | +----------------------------------+----------------------------+---------------------+ From f4d2082a27c97efdc068247289196fe37f514fa7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 29 Apr 2022 10:33:49 +0300 Subject: [PATCH 173/294] Fix for RST rendering Co-authored-by: Andrew Murray <3112309+radarhere@users.noreply.github.com> --- docs/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 05a12188f..75c1cb9a2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -465,12 +465,12 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Gentoo | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11 | x86-64 | +| macOS 11 Big Sur | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 | | | PyPy3 | | +----------------------------------+----------------------------+---------------------+ | Ubuntu Linux 18.04 LTS (Bionic) | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, 3.11 | x86-64 | +| Ubuntu Linux 20.04 LTS (Focal) | 3.7, 3.8, 3.9, 3.10, 3.11, | x86-64 | | | PyPy3 | | | +----------------------------+---------------------+ | | 3.8 | arm64v8, ppc64le, | @@ -480,7 +480,7 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Windows Server 2016 | 3.7 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Windows Server 2022 | 3.7, 3.8, 3.9, 3.10, 3.11 | x86, x86-64 | +| Windows Server 2022 | 3.7, 3.8, 3.9, 3.10, 3.11, | x86, x86-64 | | | PyPy3 | | | +----------------------------+---------------------+ | | 3.9/MinGW | x86, x86-64 | From 9c9aa9964e6344c0598c01badde0cb0f095fa95c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 29 Apr 2022 19:17:03 +1000 Subject: [PATCH 174/294] Skip tests unless JPEG 2000 is available --- Tests/test_file_icns.py | 11 ++++------- Tests/test_image.py | 4 +++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index 7d8f89184..55632909c 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -4,15 +4,13 @@ import warnings import pytest -from PIL import IcnsImagePlugin, Image, _binary, features +from PIL import IcnsImagePlugin, Image, _binary -from .helper import assert_image_equal, assert_image_similar_tofile +from .helper import assert_image_equal, assert_image_similar_tofile, skip_unless_feature # sample icon file TEST_FILE = "Tests/images/pillow.icns" -ENABLE_JPEG2K = features.check_codec("jpg_2000") - def test_sanity(): # Loading this icon by default should result in the largest size @@ -111,14 +109,12 @@ def test_older_icon(): assert im2.size == (wr, hr) +@skip_unless_feature("jpg_2000") def test_jp2_icon(): # This icon uses JPEG 2000 images instead of the PNG images. # The advantage of doing this is that OS X 10.5 supports JPEG 2000 # but not PNG; some commercial software therefore does just this. - if not ENABLE_JPEG2K: - return - with Image.open("Tests/images/pillow3.icns") as im: for w, h, r in im.info["sizes"]: wr = w * r @@ -149,6 +145,7 @@ def test_not_an_icns_file(): IcnsImagePlugin.IcnsFile(fp) +@skip_unless_feature("jpg_2000") def test_icns_decompression_bomb(): with Image.open( "Tests/images/oom-8ed3316a4109213ca96fb8a256a0bfefdece1461.icns" diff --git a/Tests/test_image.py b/Tests/test_image.py index d42fb9f1d..5544ae43c 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -7,7 +7,7 @@ import warnings import pytest -from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError +from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError, features from .helper import ( assert_image_equal, @@ -161,6 +161,8 @@ class TestImage: assert im.size == (128, 128) for ext in (".jpg", ".jp2"): + if ext == ".jp2" and not features.check_codec("jpg_2000"): + pytest.skip("jpg_2000 not available") temp_file = str(tmp_path / ("temp." + ext)) if os.path.exists(temp_file): os.remove(temp_file) 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 175/294] 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 b037d3343..f4e98adeb 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 176/294] 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 f4e98adeb..4d1065b48 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 177/294] 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 4d1065b48..8f5dbe252 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 178/294] 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 8f5dbe252..3205f2f68 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 179/294] 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 3205f2f68..fa99e04b6 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 180/294] 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 fa99e04b6..65cdb2039 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 181/294] 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 65cdb2039..499d1f75a 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 182/294] 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 fc7d9fc18..856cbe5f9 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 81b473f9d2325540e858aba5cfd688ad1df25806 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 30 Apr 2022 10:37:50 +1000 Subject: [PATCH 183/294] Raise ValueError for invalid maxval --- Tests/test_file_ppm.py | 13 +++++++++++++ src/PIL/PpmImagePlugin.py | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index 2c965318b..702250b39 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -145,6 +145,19 @@ def test_truncated_file(tmp_path): im.load() +@pytest.mark.parametrize("maxval", (0, 65536)) +def test_invalid_maxval(maxval, tmp_path): + path = str(tmp_path / "temp.ppm") + with open(path, "w") as f: + f.write("P6\n3 1 " + str(maxval)) + + with pytest.raises(ValueError) as e: + with Image.open(path): + pass + + assert str(e.value) == "maxval must be greater than 0 and less than 65536" + + def test_neg_ppm(): # Storage.c accepted negative values for xsize, ysize. the # internal open_ppm function didn't check for sanity but it diff --git a/src/PIL/PpmImagePlugin.py b/src/PIL/PpmImagePlugin.py index b760e228d..011fadc6b 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -116,6 +116,10 @@ class PpmImageFile(ImageFile.ImageFile): break elif ix == 2: # token is maxval maxval = token + if not 0 < maxval < 65536: + raise ValueError( + "maxval must be greater than 0 and less than 65536" + ) if maxval > 255 and mode == "L": self.mode = "I" 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 184/294] 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 499d1f75a..aec937aa7 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 51bdc99b96ba50f398a96dbcb7749565739c0cb9 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 May 2022 13:45:58 +1000 Subject: [PATCH 185/294] Raise ValueError if PNG chunks are truncated --- Tests/test_file_png.py | 11 +++++++++++ src/PIL/PngImagePlugin.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index bb2b0d119..2a40ab7be 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -635,6 +635,17 @@ class TestFilePng: assert_image_equal_tofile(im, "Tests/images/bw_gradient.png") + @pytest.mark.parametrize("cid", (b"IHDR", b"pHYs", b"acTL", b"fcTL", b"fdAT")) + def test_truncated_chunks(self, cid): + fp = BytesIO() + with PngImagePlugin.PngStream(fp) as png: + with pytest.raises(ValueError): + png.call(cid, 0, 0) + + ImageFile.LOAD_TRUNCATED_IMAGES = True + png.call(cid, 0, 0) + ImageFile.LOAD_TRUNCATED_IMAGES = False + def test_specify_bits(self, tmp_path): im = hopper("P") diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index c939b86e7..01b4fd9ce 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -424,6 +424,10 @@ class PngStream(ChunkStream): # image header s = ImageFile._safe_read(self.fp, length) + if length < 13: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("Truncated IHDR chunk") self.im_size = i32(s, 0), i32(s, 4) try: self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] @@ -512,6 +516,10 @@ class PngStream(ChunkStream): # pixels per unit s = ImageFile._safe_read(self.fp, length) + if length < 9: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("Truncated pHYs chunk") px, py = i32(s, 0), i32(s, 4) unit = s[8] if unit == 1: # meter @@ -624,6 +632,10 @@ class PngStream(ChunkStream): # APNG chunks def chunk_acTL(self, pos, length): s = ImageFile._safe_read(self.fp, length) + if length < 8: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("APNG contains truncated acTL chunk") if self.im_n_frames is not None: self.im_n_frames = None warnings.warn("Invalid APNG, will use default PNG image if possible") @@ -639,6 +651,10 @@ class PngStream(ChunkStream): def chunk_fcTL(self, pos, length): s = ImageFile._safe_read(self.fp, length) + if length < 26: + if ImageFile.LOAD_TRUNCATED_IMAGES: + return s + raise ValueError("APNG contains truncated fcTL chunk") seq = i32(s) if (self._seq_num is None and seq != 0) or ( self._seq_num is not None and self._seq_num != seq - 1 @@ -660,6 +676,11 @@ class PngStream(ChunkStream): return s def chunk_fdAT(self, pos, length): + if length < 4: + if ImageFile.LOAD_TRUNCATED_IMAGES: + s = ImageFile._safe_read(self.fp, length) + return s + raise ValueError("APNG contains truncated fDAT chunk") s = ImageFile._safe_read(self.fp, 4) seq = i32(s) if self._seq_num != seq - 1: From 47756b9bcdecde90f02c039eadd464f60549f007 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 May 2022 21:57:59 +1000 Subject: [PATCH 186/294] Updated freetype to 2.12.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 ff36a61c9..c5fcd62ff 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -186,9 +186,9 @@ deps = { "libs": [r"libpng16.lib"], }, "freetype": { - "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.12.0.tar.gz", # noqa: E501 - "filename": "freetype-2.12.0.tar.gz", - "dir": "freetype-2.12.0", + "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.gz", # noqa: E501 + "filename": "freetype-2.12.1.tar.gz", + "dir": "freetype-2.12.1", "patch": { r"builds\windows\vc2010\freetype.vcxproj": { # freetype setting is /MD for .dll and /MT for .lib, we need /MD From b299d7cfc27a3a3365e8a01098ff830c696dc385 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 1 May 2022 22:33:49 +1000 Subject: [PATCH 187/294] 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 1c6b9d6a2..69cbcd086 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -1519,7 +1519,7 @@ error_0: 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 188/294] 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 69cbcd086..dfa6d842d 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 44bb3e62df5ea5dc7a401018ec852e7253879ccd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 2 May 2022 08:23:19 +1000 Subject: [PATCH 189/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d75f9eab0..62cb58085 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Raise ValueError for invalid PPM maxval #6242 + [radarhere] + - Corrected screencapture argument in ImageGrab.grab() #6244 [axt-one] From 9d988dab6aeb4346960a24b2ec423d8f16f8cddc Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 2 May 2022 21:11:01 +1000 Subject: [PATCH 190/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 0757752cf..618e815a1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Search pkgconf system libs/cflags #6138 + [jameshilliard, radarhere] + - Raise ValueError for invalid PPM maxval #6242 [radarhere] 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 191/294] 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 856cbe5f9..02e1474bd 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 aec937aa7..0c175a6d4 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 0c1720d3588ac5f95740f7fa34b1bad1b3fac7ad Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 3 May 2022 09:19:09 +1000 Subject: [PATCH 192/294] Hide stderr from --keep-system-libs --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 37bb2ceb8..2701aa1ce 100755 --- a/setup.py +++ b/setup.py @@ -255,14 +255,16 @@ def _pkg_config(name): try: command_libs = [command, "--libs-only-L", name] command_cflags = [command, "--cflags-only-I", name] + stderr = None if keep_system: command_libs.append("--keep-system-libs") command_cflags.append("--keep-system-cflags") + stderr = subprocess.DEVNULL if not DEBUG: command_libs.append("--silence-errors") command_cflags.append("--silence-errors") libs = ( - subprocess.check_output(command_libs) + subprocess.check_output(command_libs, stderr=stderr) .decode("utf8") .strip() .replace("-L", "") 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 193/294] 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 02e1474bd..7364e4a19 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 0c175a6d4..c2b4159b4 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 a90d9666fb6d00c2a9f15f525e06981658bc23ff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 3 May 2022 19:20:58 +1000 Subject: [PATCH 194/294] libjpeg-turbo may result in different pixels --- docs/releasenotes/9.0.0.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/releasenotes/9.0.0.rst b/docs/releasenotes/9.0.0.rst index 947ccd849..dd993d39e 100644 --- a/docs/releasenotes/9.0.0.rst +++ b/docs/releasenotes/9.0.0.rst @@ -149,6 +149,9 @@ Switched to libjpeg-turbo in macOS and Linux wheels The Pillow wheels from PyPI for macOS and Linux have switched from libjpeg to libjpeg-turbo. It is a fork of libjpeg, popular for its speed. +Because different JPEG decoders load images differently, JPEG pixels may be +altered slightly with this change. + Added support for pickling TrueType fonts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 7e084c7ede0e6a6c3400ad7c877f1424560e66ea Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 3 May 2022 20:07:47 +1000 Subject: [PATCH 195/294] Use durations from each frame by default when saving --- Tests/test_file_gif.py | 19 ++++++++++++++++++- src/PIL/GifImagePlugin.py | 4 +++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index fd30cded0..3c2fab722 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -3,7 +3,7 @@ from io import BytesIO import pytest -from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features +from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, ImageSequence, features from .helper import ( assert_image_equal, @@ -691,6 +691,23 @@ def test_multiple_duration(tmp_path): pass +def test_roundtrip_info_duration(tmp_path): + duration_list = [100, 500, 500] + + out = str(tmp_path / "temp.gif") + with Image.open("Tests/images/transparent_dispose.gif") as im: + assert [ + frame.info["duration"] for frame in ImageSequence.Iterator(im) + ] == duration_list + + im.save(out, save_all=True) + + with Image.open(out) as reloaded: + assert [ + frame.info["duration"] for frame in ImageSequence.Iterator(reloaded) + ] == duration_list + + def test_identical_frames(tmp_path): duration_list = [1000, 1500, 2000, 4000] diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 80c1d63c5..9b34a3b0e 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -561,7 +561,7 @@ def _write_single_frame(im, fp, palette): def _write_multiple_frames(im, fp, palette): - duration = im.encoderinfo.get("duration", im.info.get("duration")) + duration = im.encoderinfo.get("duration") disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) im_frames = [] @@ -579,6 +579,8 @@ def _write_multiple_frames(im, fp, palette): encoderinfo = im.encoderinfo.copy() if isinstance(duration, (list, tuple)): encoderinfo["duration"] = duration[frame_count] + elif duration is None and "duration" in im_frame.info: + encoderinfo["duration"] = im_frame.info["duration"] if isinstance(disposal, (list, tuple)): encoderinfo["disposal"] = disposal[frame_count] frame_count += 1 From 4e12ccc63e40a9b567af3b2e1ac821f5157cddc6 Mon Sep 17 00:00:00 2001 From: Ben Rudiak-Gould Date: Sat, 30 Apr 2022 22:58:44 -0700 Subject: [PATCH 196/294] 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 428ad116b..2a4218bf8 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 99c7ba0d1..e3a1eac70 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 197/294] 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 2a4218bf8..140b7a3c9 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 e3a1eac70..114f4adb3 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -29,7 +29,6 @@ import builtins 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 @@ class _Affine: 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 198/294] 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 114f4adb3..09214e2f9 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 199/294] 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 140b7a3c9..157ecb120 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 886fcbe3d6b71c890e115318ec1829d630aae5ed Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 4 May 2022 21:25:40 +1000 Subject: [PATCH 200/294] Do not open images with zero or negative height --- Tests/images/zero_height.j2k | Bin 0 -> 3886 bytes Tests/test_imagefile.py | 14 +++++++++++++- src/PIL/ImageFile.py | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 Tests/images/zero_height.j2k diff --git a/Tests/images/zero_height.j2k b/Tests/images/zero_height.j2k new file mode 100644 index 0000000000000000000000000000000000000000..21bdd8f7667842621074f53e9c5eb6d9128fc460 GIT binary patch literal 3886 zcmV+}57F@dPybN>DF6Tf002M$002M$00000002M$002M$002M$00000000000S^HI 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 literal 0 HcmV?d00001 diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 0b3aecd08..fc0fbfb9b 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -2,7 +2,15 @@ from io import BytesIO import pytest -from PIL import BmpImagePlugin, EpsImagePlugin, Image, ImageFile, _binary, features +from PIL import ( + BmpImagePlugin, + EpsImagePlugin, + Image, + ImageFile, + UnidentifiedImageError, + _binary, + features, +) from .helper import ( assert_image, @@ -377,3 +385,7 @@ class TestPyEncoder(CodecsTest): with pytest.raises(NotImplementedError): encoder.encode_to_file(None, None) + + def test_zero_height(self): + with pytest.raises(UnidentifiedImageError): + Image.open("Tests/images/zero_height.j2k") diff --git a/src/PIL/ImageFile.py b/src/PIL/ImageFile.py index 5e1dbbf68..99b77a37f 100644 --- a/src/PIL/ImageFile.py +++ b/src/PIL/ImageFile.py @@ -123,7 +123,7 @@ class ImageFile(Image.Image): ) as v: raise SyntaxError(v) from v - if not self.mode or self.size[0] <= 0: + if not self.mode or self.size[0] <= 0 or self.size[1] <= 0: raise SyntaxError("not identified by this driver") except BaseException: # close the file only if we have opened it this constructor From 4dc1a55f658ead6c4e425b2c5f3a39bc485c974d Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 4 May 2022 22:11:10 +1000 Subject: [PATCH 201/294] Adjust BITSPERSAMPLE to match SAMPLESPERPIXEL --- src/PIL/TiffImagePlugin.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index 0b4cce397..99d15e649 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1355,19 +1355,19 @@ class TiffImageFile(ImageFile.ImageFile): bps_count = 1 bps_count += len(extra_tuple) bps_actual_count = len(bps_tuple) - if bps_count < bps_actual_count: - # If a file has more values in bps_tuple than expected, - # remove the excess. - bps_tuple = bps_tuple[:bps_count] - elif bps_count > bps_actual_count and bps_actual_count == 1: - # If a file has only one value in bps_tuple, when it should have more, - # presume it is the same number of bits for all of the samples. - bps_tuple = bps_tuple * bps_count - samples_per_pixel = self.tag_v2.get( SAMPLESPERPIXEL, 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, ) + if samples_per_pixel < bps_actual_count: + # If a file has more values in bps_tuple than expected, + # remove the excess. + bps_tuple = bps_tuple[:samples_per_pixel] + elif samples_per_pixel > bps_actual_count and bps_actual_count == 1: + # If a file has only one value in bps_tuple, when it should have more, + # presume it is the same number of bits for all of the samples. + bps_tuple = bps_tuple * samples_per_pixel + if len(bps_tuple) != samples_per_pixel: raise SyntaxError("unknown data organization") 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 202/294] 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 c2b4159b4..103fb10f4 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 26e68ed321cdd73916b531fb2130acea8c72d100 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 4 May 2022 22:30:44 +1000 Subject: [PATCH 203/294] Skip test_imagetk if tk raises a RuntimeError --- Tests/test_imagetk.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 9df66df2d..a929910b3 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -26,6 +26,8 @@ def setup_module(): # setup tk tk.Frame() # root = tk.Tk() + except RuntimeError as v: + pytest.skip(f"RuntimeError: {v}") except tk.TclError as v: pytest.skip(f"TCL Error: {v}") From 3ad3d2e0efc4e1d038b16bf106b5cf553c6cccaf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 5 May 2022 09:57:39 +1000 Subject: [PATCH 204/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 618e815a1..7b94b0e91 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Do not open images with zero or negative height #6269 + [radarhere] + - Search pkgconf system libs/cflags #6138 [jameshilliard, radarhere] From 4e52d06e6cf0515207e0fd82ed7e88e205d7e08a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 5 May 2022 17:57:08 +1000 Subject: [PATCH 205/294] Added test --- .../images/tiff_wrong_bits_per_sample_3.tiff | Bin 0 -> 49053 bytes Tests/test_file_tiff.py | 26 ++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 Tests/images/tiff_wrong_bits_per_sample_3.tiff diff --git a/Tests/images/tiff_wrong_bits_per_sample_3.tiff b/Tests/images/tiff_wrong_bits_per_sample_3.tiff new file mode 100644 index 0000000000000000000000000000000000000000..43526b157a5c9ea3c584ea593393a2b97e744e78 GIT binary patch literal 49053 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 ugut1At$#0 literal 0 HcmV?d00001 diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 16c43b00f..87e0c2d25 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -92,17 +92,33 @@ class TestFileTiff: assert_image_equal_tofile(im, "Tests/images/hopper.tif") @pytest.mark.parametrize( - "file_name,mode,size,offset", + "file_name,mode,size,tile", [ - ("tiff_wrong_bits_per_sample.tiff", "RGBA", (52, 53), 160), - ("tiff_wrong_bits_per_sample_2.tiff", "RGB", (16, 16), 8), + ( + "tiff_wrong_bits_per_sample.tiff", + "RGBA", + (52, 53), + [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))], + ), + ( + "tiff_wrong_bits_per_sample_2.tiff", + "RGB", + (16, 16), + [("raw", (0, 0, 16, 16), 8, ("RGB", 0, 1))], + ), + ( + "tiff_wrong_bits_per_sample_3.tiff", + "RGBA", + (512, 256), + [("libtiff", (0, 0, 512, 256), 0, ("RGBA", "tiff_lzw", False, 48782))], + ), ], ) - def test_wrong_bits_per_sample(self, file_name, mode, size, offset): + def test_wrong_bits_per_sample(self, file_name, mode, size, tile): with Image.open("Tests/images/" + file_name) as im: assert im.mode == mode assert im.size == size - assert im.tile == [("raw", (0, 0) + size, offset, (mode, 0, 1))] + assert im.tile == tile im.load() def test_set_legacy_api(self): From 45f862e7dc675e9eacbd1573c96ce4b5547158bf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 5 May 2022 21:32:27 +1000 Subject: [PATCH 206/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7b94b0e91..92bdb99e7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Adjust BITSPERSAMPLE to match SAMPLESPERPIXEL when opening TIFFs #6270 + [radarhere] + - Do not open images with zero or negative height #6269 [radarhere] 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 207/294] 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 7364e4a19..6d6894566 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 103fb10f4..61e745ea5 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 48e5b94c2..8cc03f28e 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 208/294] 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 6755d94b8..7f4a18c47 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 86cd6c3a0..543651f7f 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 1fd5f54450df12252994162a397dddeb68638ae1 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 7 May 2022 08:30:14 +1000 Subject: [PATCH 209/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 92bdb99e7..de5bd8b8d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,12 @@ Changelog (Pillow) 9.2.0 (unreleased) ------------------ +- Raise ValueError if PNG chunks are truncated #6253 + [radarhere] + +- Use durations from each frame by default when saving GIFs #6265 + [radarhere] + - Adjust BITSPERSAMPLE to match SAMPLESPERPIXEL when opening TIFFs #6270 [radarhere] 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 210/294] 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 61e745ea5..42f5f2975 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 211/294] 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 42f5f2975..2aa03b88e 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 212/294] 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 2aa03b88e..ae1c0fb8b 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 213/294] 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 42f5f2975..3ff3a15af 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 214/294] 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 75c1cb9a2..ad2414c84 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 215/294] 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 80729efa1..a656d64c5 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 216/294] Install cffi --- .ci/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/install.sh b/.ci/install.sh index 6d6894566..c588af42f 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 217/294] 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 a2e3041bd..e678f68ec 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 a656d64c5..a2795b088 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 218/294] Added Cygwin badge --- README.md | 3 +++ docs/index.rst | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 7bff737a2..5e9adaf7e 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 f1a721c6a..5e886c2e8 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 219/294] 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 5c9cdd7e0..7168c4265 100644 --- a/Tests/test_image_array.py +++ b/Tests/test_image_array.py @@ -80,3 +80,15 @@ def test_fromarray(): 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 99c7ba0d1..c141da09f 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 220/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index de5bd8b8d..c5bf6b5f8 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 221/294] 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 1572328de..d85d1f3c2 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -51,7 +51,6 @@ class TestDecompressionBomb: 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 222/294] 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 497b994db..b3cfb99bb 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 ad2414c84..59d828405 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 223/294] 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 a2795b088..2e8fc9c09 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 224/294] 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 3c2fab722..07b5592e8 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 9b34a3b0e..3376cccae 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 225/294] 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 3c2fab722..9fb171411 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 9b34a3b0e..6c2b1dedf 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -228,12 +228,17 @@ class GifImageFile(ImageFile.ImageFile): # # 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 226/294] 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 3c2fab722..1bd772dc6 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 9b34a3b0e..4d785d834 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 227/294] 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 914e71e53..4f4b81a62 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 1807ecf9f..199a3e272 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 c5fcd62ff..1c09b6002 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -246,15 +246,15 @@ deps = { "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 228/294] 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 2ff9b3799..fe2658047 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 c141da09f..fead48b29 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1324,7 +1324,7 @@ class Image: 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 1a72f5c04..d5d6f7b18 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 0be4771cd..92b2607b4 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 229/294] [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 1bd772dc6..eee432116 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 230/294] 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 eee432116..762dab8df 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 231/294] 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 554645787..2d014e5a4 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 232/294] 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 6755d94b8..5db4fbf61 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 86cd6c3a0..2fcd38048 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 233/294] 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 ad030acd0..8c5b8a748 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 c38944b10..db051d188 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 234/294] 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 235/294] 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 c5bf6b5f8..b7b6fbfc6 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 000000000..f8b155f3d --- /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 db578bdb7..597c804f8 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 236/294] 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 d83c584b5..588b9b703 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -18,6 +18,7 @@ from .helper import ( hopper, mark_if_feature_version, skip_unless_feature, + skip_unless_feature_version, ) @@ -991,6 +992,7 @@ class TestFileLibTiff(LibTiffTestCase): 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 237/294] 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 437050ed4..ff929fee7 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 a6049b685..aa7511c8a 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 238/294] 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 ff929fee7..219dda1de 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 239/294] 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 b3cfb99bb..2b4dc6b52 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 199a3e272..efde6e931 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 240/294] 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 3376cccae..e8551ca33 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 241/294] 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 07b5592e8..a404387cb 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 242/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b7b6fbfc6..eef1a75d0 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 243/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index eef1a75d0..311404f12 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 244/294] 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 c02965a05..6e87db561 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 245/294] 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 6e87db561..bb2f854bc 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 246/294] 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 bb2f854bc..b38efaccd 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 247/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 311404f12..b22d29368 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 248/294] 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 1c09b6002..3d9391321 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -280,9 +280,9 @@ deps = { "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 249/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b22d29368..9127d1934 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 250/294] 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 efde6e931..04903071f 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 251/294] 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 9caea84a8..49dc3d841 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 fad6cdf1b..cead48c19 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 813ac52aa..086b2b196 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1934,6 +1934,9 @@ class Image: 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 252/294] 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 07cf6eb92..0a951af8a 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -607,6 +607,20 @@ class TestImage: 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 086b2b196..e226f1400 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1935,7 +1935,11 @@ class Image: 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 253/294] 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 9fb171411..96fbfd2a1 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 6c2b1dedf..d914a0a21 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -228,15 +228,16 @@ class GifImageFile(ImageFile.ImageFile): # # 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 254/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9127d1934..4bcd9d5ff 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 255/294] 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 3f7b7aeb9..268a2349d 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 256/294] 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 268a2349d..c4f634fae 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 f5ec610cb..c91c1fbff 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -163,6 +163,8 @@ class GifImageFile(ImageFile.ImageFile): 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 @@ class GifImageFile(ImageFile.ImageFile): # comment = b"" - # Collect one comment block + # Read this comment block while block: comment += block block = self.data() @@ -395,7 +397,9 @@ class GifImageFile(ImageFile.ImageFile): ) ] - 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 257/294] 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 b38efaccd..8591ab2a6 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 258/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4bcd9d5ff..b28b0fc6a 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 259/294] 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 c91c1fbff..1b317d233 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 260/294] 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 c4f634fae..dcdf2179a 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 1b317d233..33b968a8f 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -244,7 +244,7 @@ class GifImageFile(ImageFile.ImageFile): info["comment"] = comment s = None continue - elif s[0] == 255: + elif s[0] == 255 and frame == 0: # # application extension # @@ -252,7 +252,7 @@ class GifImageFile(ImageFile.ImageFile): 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 @@ class GifImageFile(ImageFile.ImageFile): 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 261/294] 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 bc9587744..ba2b7d8ed 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 262/294] 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 d9b59321b..d093f26cc 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 d03f7c736..8706cb950 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -497,6 +497,26 @@ class TestFileTiff: 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 5b7a50e18..6b7a4dc3b 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1383,6 +1383,10 @@ class Image: 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 @@ class Image: 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 fc3f8556f..27c30958c 100644 --- a/src/PIL/MpoImagePlugin.py +++ b/src/PIL/MpoImagePlugin.py @@ -82,6 +82,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): 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 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): 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 7cfd76af0..78a7fd14d 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1136,6 +1136,7 @@ class TiffImageFile(ImageFile.ImageFile): 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 263/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index b28b0fc6a..6ef953eda 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 264/294] 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 ba57deca1..5dc17db60 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 265/294] 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 266/294] 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 8591ab2a6..1826d965f 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 267/294] 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 6b877592a..e5639e105 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 268/294] 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 5db4fbf61..59af64bba 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 269/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6ef953eda..cc8996377 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 270/294] 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 efde6e931..0b04b134e 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 3d9391321..a963205c1 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -143,9 +143,9 @@ deps = { "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 271/294] 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 f818f19d5..3bb444c80 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 272/294] 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 353dd0c19..d975ccf04 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 273/294] 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 d975ccf04..1837f5736 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 274/294] 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 661c5a5ec..e8dc62771 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 275/294] Remove sentence fragment --- winbuild/build.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/winbuild/build.rst b/winbuild/build.rst index e8dc62771..af7c558cf 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 276/294] 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 f9782bc50..3824626bd 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 @@ class ImageDraw: 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 277/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cc8996377..30e1058e3 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 278/294] 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 a963205c1..09310a904 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 @@ header = [ # 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 @@ deps = { "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 @@ deps = { # "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 279/294] [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 09310a904..d7e88ca40 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -107,7 +107,8 @@ header = [ # 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 280/294] 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 db051d188..eddbd07a2 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 281/294] 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 2ed236b18..bc67d9368 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,7 +68,7 @@ release = PIL.__version__ # # 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 282/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 30e1058e3..a3accd4e3 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 283/294] 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 792250518..0f7379536 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -604,6 +604,15 @@ class TestImage: 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 0ba2808f8..eb9239bec 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1867,10 +1867,15 @@ class Image: 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 @@ class Image: # 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 @@ class Image: 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 284/294] 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 792250518..24289bf51 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -840,6 +840,35 @@ class TestImage: 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 0ba2808f8..7edb9a4c2 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -1449,6 +1449,28 @@ class Image: 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 285/294] [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 9153202fe..1806db54c 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 286/294] 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 a26cd9b46..a43548ae0 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -18,7 +18,6 @@ from .helper import ( hopper, mark_if_feature_version, skip_unless_feature, - skip_unless_feature_version, ) @@ -992,7 +991,6 @@ class TestFileLibTiff(LibTiffTestCase): 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 287/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a3accd4e3..098e0abf5 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 288/294] 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 2613b6585..ed37521fd 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 eddbd07a2..424fd487a 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 289/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 098e0abf5..7f998cea1 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 c4d51fb2681c2434fd324098d116a66013549de7 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 13 Jun 2022 19:55:25 +1000 Subject: [PATCH 290/294] 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 627ed9d0f..4e5729598 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 b512701d3..7df75fb38 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -105,6 +105,8 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmImageFile(ImageFile.ImageFile): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmDecoder(ImageFile.PyDecoder): 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 291/294] 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 4e5729598..40495bf10 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") +@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_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): +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): +@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"P1\n2 2\n\n1010") - f2.write(b"P1\n2 2\n" + comment + b"\n1010" + 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_pbm_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"P1\n128 128\n") + f.write(data) with Image.open(path) as im: with pytest.raises(ValueError): im.load() -def test_plain_pbm_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"P1\n128 128\n1009") + f.write(data) with Image.open(path) as im: with pytest.raises(ValueError): im.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 im: - assert_image_equal_tofile(im, path2) - - -def test_plain_ppm_truncated_data(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\n") - - with Image.open(path) as im: - with pytest.raises(ValueError): - im.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 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): - 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 292/294] 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 40495bf10..5c6376caf 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 293/294] 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 7df75fb38..392771d3e 100644 --- a/src/PIL/PpmImagePlugin.py +++ b/src/PIL/PpmImagePlugin.py @@ -156,24 +156,38 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): # 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 @@ class PpmPlainDecoder(ImageFile.PyDecoder): 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 294/294] Update CHANGES.rst [ci skip] --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7f998cea1..658a950dd 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]