mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 01:46:18 +03:00
Merge branch 'master' into winbuild-cache
This commit is contained in:
commit
e7a29da517
|
@ -14,7 +14,7 @@ environment:
|
||||||
matrix:
|
matrix:
|
||||||
- PYTHON: C:/Python38
|
- PYTHON: C:/Python38
|
||||||
ARCHITECTURE: x86
|
ARCHITECTURE: x86
|
||||||
- PYTHON: C:/Python35-x64
|
- PYTHON: C:/Python36-x64
|
||||||
ARCHITECTURE: x64
|
ARCHITECTURE: x64
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,11 @@ pip install -U pytest-cov
|
||||||
pip install pyroma
|
pip install pyroma
|
||||||
pip install test-image-results
|
pip install test-image-results
|
||||||
pip install numpy
|
pip install numpy
|
||||||
|
|
||||||
|
# TODO Remove when 3.9-dev includes setuptools 49.3.2+:
|
||||||
|
if [ "$TRAVIS_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi
|
||||||
|
if [ "$GHA_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi
|
||||||
|
|
||||||
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
||||||
# arm64, ppc64le, s390x CPUs:
|
# arm64, ppc64le, s390x CPUs:
|
||||||
# "ERROR: Could not find a version that satisfies the requirement pyqt5"
|
# "ERROR: Could not find a version that satisfies the requirement pyqt5"
|
||||||
|
|
21
.github/workflows/lint.yml
vendored
21
.github/workflows/lint.yml
vendored
|
@ -6,17 +6,14 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: ["3.8"]
|
|
||||||
|
|
||||||
name: Python ${{ matrix.python-version }}
|
name: Lint
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: pip cache
|
- name: pip cache
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: ~/.cache/pip
|
||||||
key: lint-pip-${{ hashFiles('**/setup.py') }}
|
key: lint-pip-${{ hashFiles('**/setup.py') }}
|
||||||
|
@ -24,26 +21,28 @@ jobs:
|
||||||
lint-pip-
|
lint-pip-
|
||||||
|
|
||||||
- name: pre-commit cache
|
- name: pre-commit cache
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pre-commit
|
path: ~/.cache/pre-commit
|
||||||
key: lint-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }}
|
key: lint-pre-commit-${{ hashFiles('**/.pre-commit-config.yaml') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
lint-pre-commit-
|
lint-pre-commit-
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: 3.8
|
||||||
|
|
||||||
- name: Build system information
|
- name: Build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python .github/workflows/system-info.py
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install -U pip
|
||||||
python -m pip install --upgrade tox
|
python -m pip install -U tox
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: tox -e lint
|
run: tox -e lint
|
||||||
|
env:
|
||||||
|
PRE_COMMIT_COLOR: always
|
||||||
|
|
||||||
|
|
7
.github/workflows/macos-install.sh
vendored
7
.github/workflows/macos-install.sh
vendored
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype
|
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype openblas
|
||||||
|
|
||||||
PYTHONOPTIMIZE=0 pip install cffi
|
PYTHONOPTIMIZE=0 pip install cffi
|
||||||
pip install coverage
|
pip install coverage
|
||||||
|
@ -11,7 +11,12 @@ pip install -U pytest
|
||||||
pip install -U pytest-cov
|
pip install -U pytest-cov
|
||||||
pip install pyroma
|
pip install pyroma
|
||||||
pip install test-image-results
|
pip install test-image-results
|
||||||
|
|
||||||
|
echo -e "[openblas]\nlibraries = openblas\nlibrary_dirs = /usr/local/opt/openblas/lib" >> ~/.numpy-site.cfg
|
||||||
pip install numpy
|
pip install numpy
|
||||||
|
|
||||||
|
# TODO Remove when 3.9-dev includes setuptools 49.3.2+:
|
||||||
|
if [ "$GHA_PYTHON_VERSION" == "3.9-dev" ]; then pip install -U "setuptools>=49.3.2" ; fi
|
||||||
|
|
||||||
# extra test images
|
# extra test images
|
||||||
pushd depends && ./install_extra_test_images.sh && popd
|
pushd depends && ./install_extra_test_images.sh && popd
|
||||||
|
|
2
.github/workflows/test-docker.yml
vendored
2
.github/workflows/test-docker.yml
vendored
|
@ -12,10 +12,8 @@ jobs:
|
||||||
docker: [
|
docker: [
|
||||||
alpine,
|
alpine,
|
||||||
arch,
|
arch,
|
||||||
ubuntu-16.04-xenial-amd64,
|
|
||||||
ubuntu-18.04-bionic-amd64,
|
ubuntu-18.04-bionic-amd64,
|
||||||
ubuntu-20.04-focal-amd64,
|
ubuntu-20.04-focal-amd64,
|
||||||
debian-9-stretch-x86,
|
|
||||||
debian-10-buster-x86,
|
debian-10-buster-x86,
|
||||||
centos-6-amd64,
|
centos-6-amd64,
|
||||||
centos-7-amd64,
|
centos-7-amd64,
|
||||||
|
|
66
.github/workflows/test-windows.yml
vendored
66
.github/workflows/test-windows.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.5", "3.6", "3.7", "3.8", "pypy3"]
|
python-version: ["3.6", "3.7", "3.8", "3.9-dev", "pypy3"]
|
||||||
architecture: ["x86", "x64"]
|
architecture: ["x86", "x64"]
|
||||||
include:
|
include:
|
||||||
- architecture: "x86"
|
- architecture: "x86"
|
||||||
|
@ -47,17 +47,27 @@ jobs:
|
||||||
|
|
||||||
# sets env: pythonLocation
|
# sets env: pythonLocation
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
architecture: ${{ matrix.architecture }}
|
architecture: ${{ matrix.architecture }}
|
||||||
|
|
||||||
|
- name: Set up TCL
|
||||||
|
if: "contains(matrix.python-version, 'pypy')"
|
||||||
|
run: Write-Host "::set-env name=TCL_LIBRARY::$env:pythonLocation\tcl\tcl8.5"
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
- name: Print build system information
|
- name: Print build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python .github/workflows/system-info.py
|
||||||
|
|
||||||
- name: pip install wheel pytest pytest-cov
|
- name: pip install wheel pytest pytest-cov
|
||||||
run: python -m pip install wheel pytest pytest-cov
|
run: python -m pip install wheel pytest pytest-cov
|
||||||
|
|
||||||
|
# TODO Remove when 3.9-dev includes setuptools 49.3.2+:
|
||||||
|
- name: Upgrade setuptools
|
||||||
|
if: "contains(matrix.python-version, '3.9-dev')"
|
||||||
|
run: python -m pip install -U "setuptools>=49.3.2"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
||||||
|
@ -153,7 +163,7 @@ jobs:
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload errors
|
- name: Upload errors
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: errors
|
name: errors
|
||||||
|
@ -188,25 +198,15 @@ jobs:
|
||||||
msys:
|
msys:
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
mingw: ["MINGW32", "MINGW64"]
|
|
||||||
include:
|
|
||||||
- mingw: "MINGW32"
|
|
||||||
package: "mingw-w64-i686"
|
|
||||||
- mingw: "MINGW64"
|
|
||||||
package: "mingw-w64-x86_64"
|
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash.exe --login -eo pipefail "{0}"
|
shell: bash.exe --login -eo pipefail "{0}"
|
||||||
env:
|
env:
|
||||||
MSYSTEM: ${{ matrix.mingw }}
|
MSYSTEM: MINGW64
|
||||||
CHERE_INVOKING: 1
|
CHERE_INVOKING: 1
|
||||||
|
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
name: MSYS2 ${{ matrix.mingw }}
|
name: MSYS2 MinGW 64-bit
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
@ -218,23 +218,23 @@ jobs:
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
pacman -S --noconfirm \
|
pacman -S --noconfirm \
|
||||||
${{ matrix.package }}-python3-pip \
|
mingw-w64-x86_64-python3-pip \
|
||||||
${{ matrix.package }}-python3-setuptools \
|
mingw-w64-x86_64-python3-setuptools \
|
||||||
${{ matrix.package }}-python3-pytest \
|
mingw-w64-x86_64-python3-pytest \
|
||||||
${{ matrix.package }}-python3-pytest-cov \
|
mingw-w64-x86_64-python3-pytest-cov \
|
||||||
${{ matrix.package }}-python3-cffi \
|
mingw-w64-x86_64-python3-cffi \
|
||||||
${{ matrix.package }}-python3-olefile \
|
mingw-w64-x86_64-python3-olefile \
|
||||||
${{ matrix.package }}-python3-numpy \
|
mingw-w64-x86_64-python3-numpy \
|
||||||
${{ matrix.package }}-python3-pyqt5 \
|
mingw-w64-x86_64-python3-pyqt5 \
|
||||||
${{ matrix.package }}-python3-numpy \
|
mingw-w64-x86_64-python3-numpy \
|
||||||
${{ matrix.package }}-freetype \
|
mingw-w64-x86_64-freetype \
|
||||||
${{ matrix.package }}-lcms2 \
|
mingw-w64-x86_64-lcms2 \
|
||||||
${{ matrix.package }}-libwebp \
|
mingw-w64-x86_64-libwebp \
|
||||||
${{ matrix.package }}-libjpeg-turbo \
|
mingw-w64-x86_64-libjpeg-turbo \
|
||||||
${{ matrix.package }}-openjpeg2 \
|
mingw-w64-x86_64-openjpeg2 \
|
||||||
${{ matrix.package }}-libimagequant \
|
mingw-w64-x86_64-libimagequant \
|
||||||
${{ matrix.package }}-libraqm \
|
mingw-w64-x86_64-libraqm \
|
||||||
${{ matrix.package }}-ghostscript \
|
mingw-w64-x86_64-ghostscript \
|
||||||
subversion
|
subversion
|
||||||
|
|
||||||
python3 -m pip install pyroma
|
python3 -m pip install pyroma
|
||||||
|
@ -256,4 +256,4 @@ jobs:
|
||||||
python3 -m pip install codecov
|
python3 -m pip install codecov
|
||||||
bash <(curl -s https://codecov.io/bash) -F GHA_Windows
|
bash <(curl -s https://codecov.io/bash) -F GHA_Windows
|
||||||
env:
|
env:
|
||||||
CODECOV_NAME: MSYS2 ${{ matrix.mingw }}
|
CODECOV_NAME: MSYS2 MinGW 64-bit
|
||||||
|
|
50
.github/workflows/test.yml
vendored
50
.github/workflows/test.yml
vendored
|
@ -14,16 +14,16 @@ jobs:
|
||||||
]
|
]
|
||||||
python-version: [
|
python-version: [
|
||||||
"pypy3",
|
"pypy3",
|
||||||
|
"3.9-dev",
|
||||||
"3.8",
|
"3.8",
|
||||||
"3.7",
|
"3.7",
|
||||||
"3.6",
|
"3.6",
|
||||||
"3.5",
|
|
||||||
]
|
]
|
||||||
include:
|
include:
|
||||||
- python-version: "3.5"
|
|
||||||
env: PYTHONOPTIMIZE=2
|
|
||||||
- python-version: "3.6"
|
- python-version: "3.6"
|
||||||
env: PYTHONOPTIMIZE=1
|
env: PYTHONOPTIMIZE=1
|
||||||
|
- python-version: "3.7"
|
||||||
|
env: PYTHONOPTIMIZE=2
|
||||||
# Include new variables for Codecov
|
# Include new variables for Codecov
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
codecov-flag: GHA_Ubuntu
|
codecov-flag: GHA_Ubuntu
|
||||||
|
@ -36,31 +36,25 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Ubuntu cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
|
||||||
with:
|
|
||||||
path: ~/.cache/pip
|
|
||||||
key:
|
|
||||||
${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/.ci/*.sh') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ matrix.os }}-${{ matrix.python-version }}-
|
|
||||||
|
|
||||||
- name: macOS cache
|
|
||||||
uses: actions/cache@v1
|
|
||||||
if: startsWith(matrix.os, 'macOS')
|
|
||||||
with:
|
|
||||||
path: ~/Library/Caches/pip
|
|
||||||
key:
|
|
||||||
${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/.ci/*.sh') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ matrix.os }}-${{ matrix.python-version }}-
|
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Get pip cache dir
|
||||||
|
id: pip-cache
|
||||||
|
run: |
|
||||||
|
echo "::set-output name=dir::$(pip cache dir)"
|
||||||
|
|
||||||
|
- name: pip cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pip-cache.outputs.dir }}
|
||||||
|
key:
|
||||||
|
${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/.ci/*.sh') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ matrix.os }}-${{ matrix.python-version }}-
|
||||||
|
|
||||||
- name: Build system information
|
- name: Build system information
|
||||||
run: python .github/workflows/system-info.py
|
run: python .github/workflows/system-info.py
|
||||||
|
|
||||||
|
@ -68,11 +62,15 @@ jobs:
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
run: |
|
run: |
|
||||||
.ci/install.sh
|
.ci/install.sh
|
||||||
|
env:
|
||||||
|
GHA_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install macOS dependencies
|
- name: Install macOS dependencies
|
||||||
if: startsWith(matrix.os, 'macOS')
|
if: startsWith(matrix.os, 'macOS')
|
||||||
run: |
|
run: |
|
||||||
.github/workflows/macos-install.sh
|
.github/workflows/macos-install.sh
|
||||||
|
env:
|
||||||
|
GHA_PYTHON_VERSION: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
@ -89,7 +87,7 @@ jobs:
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Upload errors
|
- name: Upload errors
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: errors
|
name: errors
|
||||||
|
@ -98,7 +96,7 @@ jobs:
|
||||||
- name: Docs
|
- name: Docs
|
||||||
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.8
|
if: startsWith(matrix.os, 'ubuntu') && matrix.python-version == 3.8
|
||||||
run: |
|
run: |
|
||||||
pip install sphinx-rtd-theme
|
pip install sphinx-removed-in sphinx-rtd-theme
|
||||||
make doccheck
|
make doccheck
|
||||||
|
|
||||||
- name: After success
|
- name: After success
|
||||||
|
|
|
@ -9,35 +9,35 @@ repos:
|
||||||
types: []
|
types: []
|
||||||
|
|
||||||
- repo: https://github.com/timothycrosley/isort
|
- repo: https://github.com/timothycrosley/isort
|
||||||
rev: 7c29dd9d55161704cfc45998c6f5c2c43d39264b # frozen: 4.3.21
|
rev: 9ae09866e278fbc6ec0383ccb16b5c84e78e6e4d # frozen: 5.3.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: isort
|
||||||
|
|
||||||
- repo: https://github.com/asottile/yesqa
|
- repo: https://github.com/asottile/yesqa
|
||||||
rev: b13a51aa54142c59219c764e9f9362c049b439ed # frozen: v1.2.0
|
rev: 7a009f3ee493c796827ee334f9058b110a0e0db8 # frozen: v1.2.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: yesqa
|
- id: yesqa
|
||||||
|
|
||||||
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
rev: ffbd448645bad2e7ca13f96fca5830058d27ccd5 # frozen: v1.1.7
|
rev: f30f4974a08a6b2f6a1eeaf30a4d501cf909163a # frozen: v1.1.9
|
||||||
hooks:
|
hooks:
|
||||||
- id: remove-tabs
|
- id: remove-tabs
|
||||||
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
|
exclude: (Makefile$|\.bat$|\.cmake$|\.eps$|\.fits$|\.opt$)
|
||||||
|
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
rev: 735cfe7e1c57a8e05f660ba75de72313005af54a # frozen: 3.8.2
|
rev: 05f6544aef321e2fee03a1277ce2eef8880fb927 # frozen: 3.8.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [flake8-2020, flake8-implicit-str-concat]
|
additional_dependencies: [flake8-2020, flake8-implicit-str-concat]
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||||
rev: 0d7d077d6ed5624854f93ac601739c1804ebeb98 # frozen: v1.5.1
|
rev: 20b9ac745c5adaab12b845b3564c773dcc051d0e # frozen: v1.5.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: python-check-blanket-noqa
|
- id: python-check-blanket-noqa
|
||||||
- id: rst-backticks
|
- id: rst-backticks
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: ebc15addedad713c86ef18ae9632c88e187dd0af # frozen: v3.1.0
|
rev: e1668fe86af3810fbca72b8653fe478e66a0afdc # frozen: v3.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
|
|
12
.travis.yml
12
.travis.yml
|
@ -23,7 +23,7 @@ matrix:
|
||||||
arch: arm64
|
arch: arm64
|
||||||
- python: "3.7"
|
- python: "3.7"
|
||||||
arch: ppc64le
|
arch: ppc64le
|
||||||
- python: "3.5"
|
- python: "3.8"
|
||||||
arch: s390x
|
arch: s390x
|
||||||
|
|
||||||
- python: "pypy3"
|
- python: "pypy3"
|
||||||
|
@ -35,16 +35,16 @@ matrix:
|
||||||
name: "3.8 Xenial"
|
name: "3.8 Xenial"
|
||||||
services: xvfb
|
services: xvfb
|
||||||
- python: '3.7'
|
- python: '3.7'
|
||||||
name: "3.7 Xenial"
|
name: "3.7 Xenial PYTHONOPTIMIZE=2"
|
||||||
|
env: PYTHONOPTIMIZE=2
|
||||||
services: xvfb
|
services: xvfb
|
||||||
- python: '3.6'
|
- python: '3.6'
|
||||||
name: "3.6 Xenial PYTHONOPTIMIZE=1"
|
name: "3.6 Xenial PYTHONOPTIMIZE=1"
|
||||||
env: PYTHONOPTIMIZE=1
|
env: PYTHONOPTIMIZE=1
|
||||||
services: xvfb
|
services: xvfb
|
||||||
- python: '3.5'
|
|
||||||
name: "3.5 Xenial PYTHONOPTIMIZE=2"
|
allow_failures:
|
||||||
env: PYTHONOPTIMIZE=2
|
- python: "3.9-dev"
|
||||||
services: xvfb
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- |
|
- |
|
||||||
|
|
68
CHANGES.rst
68
CHANGES.rst
|
@ -2,9 +2,66 @@
|
||||||
Changelog (Pillow)
|
Changelog (Pillow)
|
||||||
==================
|
==================
|
||||||
|
|
||||||
7.2.0 (unreleased)
|
8.0.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Read EXIF data tEXt chunk into info as bytes instead of string #4828
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Remove long-deprecated Image.py functions #4798
|
||||||
|
[hugovk, nulano, radarhere]
|
||||||
|
|
||||||
|
- Add MIME type to PsdImagePlugin #4788
|
||||||
|
[samamorgan]
|
||||||
|
|
||||||
|
- Drop support for EOL Python 3.5 #4746
|
||||||
|
[hugovk, radarhere, nulano]
|
||||||
|
|
||||||
|
- Remove ImageCms.CmsProfile attributes deprecated since 3.2.0 #4768
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
- Allow ImageOps.autocontrast to specify low and high cutoffs separately #4749
|
||||||
|
[millionhz, radarhere]
|
||||||
|
|
||||||
|
7.2.0 (2020-07-01)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Do not convert I;16 images when showing PNGs #4744
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fixed ICNS file pointer saving #4741
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fixed loading non-RGBA mode APNGs with dispose background #4742
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Deprecated _showxv #4714
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Deprecate Image.show(command="...") #4646
|
||||||
|
[nulano, hugovk, radarhere]
|
||||||
|
|
||||||
|
- Updated JPEG magic number #4707
|
||||||
|
[Cykooz, radarhere]
|
||||||
|
|
||||||
|
- Change STRIPBYTECOUNTS to LONG if necessary when saving #4626
|
||||||
|
[radarhere, hugovk]
|
||||||
|
|
||||||
|
- Write JFIF header when saving JPEG #4639
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Replaced tiff_jpeg with jpeg compression when saving TIFF images #4627
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Writing TIFF tags: improved BYTE, added UNDEFINED #4605
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Consider transparency when pasting text on an RGBA image #4566
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Added method argument to single frame WebP saving #4547
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Use ImageFileDirectory_v2 in Image.Exif #4637
|
- Use ImageFileDirectory_v2 in Image.Exif #4637
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
@ -44,6 +101,9 @@ Changelog (Pillow)
|
||||||
- Fix pickling WebP #4561
|
- Fix pickling WebP #4561
|
||||||
[hugovk, radarhere]
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
- Replace IOError and WindowsError aliases with OSError #4536
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
7.1.2 (2020-04-25)
|
7.1.2 (2020-04-25)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -5542,7 +5602,7 @@ Pre-fork
|
||||||
any other pixel value means opaque. This is faster than using an
|
any other pixel value means opaque. This is faster than using an
|
||||||
"L" transparency mask.
|
"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).
|
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
|
||||||
|
@ -5625,7 +5685,7 @@ Pre-fork
|
||||||
+ Added the "pilfile" utility, which quickly identifies image files
|
+ Added the "pilfile" utility, which quickly identifies image files
|
||||||
(without loading them, in most cases).
|
(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.
|
printers.
|
||||||
|
|
||||||
+ Added a rudimentary version of the "pilview" utility, which is
|
+ Added a rudimentary version of the "pilview" utility, which is
|
||||||
|
@ -5639,5 +5699,5 @@ Pre-fork
|
||||||
Jack). This allows you to read images through the Img extensions file
|
Jack). This allows you to read images through the Img extensions file
|
||||||
format handlers. See the file "Lib/ImgExtImagePlugin.py" for details.
|
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.
|
handbook for details.
|
||||||
|
|
30
Makefile
30
Makefile
|
@ -1,7 +1,6 @@
|
||||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
|
||||||
.PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test
|
|
||||||
.DEFAULT_GOAL := release-test
|
.DEFAULT_GOAL := release-test
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
python3 setup.py clean
|
python3 setup.py clean
|
||||||
rm src/PIL/*.so || true
|
rm src/PIL/*.so || true
|
||||||
|
@ -9,28 +8,34 @@ clean:
|
||||||
find . -name __pycache__ | xargs rm -r || true
|
find . -name __pycache__ | xargs rm -r || true
|
||||||
|
|
||||||
BRANCHES=`git branch -a | grep -v HEAD | grep -v master | grep remote`
|
BRANCHES=`git branch -a | grep -v HEAD | grep -v master | grep remote`
|
||||||
|
.PHONY: co
|
||||||
co:
|
co:
|
||||||
-for i in $(BRANCHES) ; do \
|
-for i in $(BRANCHES) ; do \
|
||||||
git checkout -t $$i ; \
|
git checkout -t $$i ; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
pytest -qq
|
pytest -qq
|
||||||
rm -r htmlcov || true
|
rm -r htmlcov || true
|
||||||
coverage report
|
coverage report
|
||||||
|
|
||||||
|
.PHONY: doc
|
||||||
doc:
|
doc:
|
||||||
$(MAKE) -C docs html
|
$(MAKE) -C docs html
|
||||||
|
|
||||||
|
.PHONY: doccheck
|
||||||
doccheck:
|
doccheck:
|
||||||
$(MAKE) -C docs html
|
$(MAKE) -C docs html
|
||||||
# Don't make our tests rely on the links in the docs being up every single build.
|
# Don't make our tests rely on the links in the docs being up every single build.
|
||||||
# We don't control them. But do check, and update them to the target of their redirects.
|
# We don't control them. But do check, and update them to the target of their redirects.
|
||||||
$(MAKE) -C docs linkcheck || true
|
$(MAKE) -C docs linkcheck || true
|
||||||
|
|
||||||
|
.PHONY: docserve
|
||||||
docserve:
|
docserve:
|
||||||
cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
|
cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
|
||||||
|
|
||||||
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@echo "Welcome to Pillow development. Please use \`make <target>\` where <target> is one of"
|
@echo "Welcome to Pillow development. Please use \`make <target>\` where <target> is one of"
|
||||||
@echo " clean remove build products"
|
@echo " clean remove build products"
|
||||||
|
@ -48,17 +53,21 @@ help:
|
||||||
@echo " upload build and upload sdists to PyPI"
|
@echo " upload build and upload sdists to PyPI"
|
||||||
@echo " upload-test build and upload sdists to test.pythonpackages.com"
|
@echo " upload-test build and upload sdists to test.pythonpackages.com"
|
||||||
|
|
||||||
|
.PHONY: inplace
|
||||||
inplace: clean
|
inplace: clean
|
||||||
python3 setup.py develop build_ext --inplace
|
python3 setup.py develop build_ext --inplace
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
install:
|
install:
|
||||||
python3 setup.py install
|
python3 setup.py install
|
||||||
python3 selftest.py
|
python3 selftest.py
|
||||||
|
|
||||||
|
.PHONY: install-coverage
|
||||||
install-coverage:
|
install-coverage:
|
||||||
CFLAGS="-coverage" python3 setup.py build_ext install
|
CFLAGS="-coverage" python3 setup.py build_ext install
|
||||||
python3 selftest.py
|
python3 selftest.py
|
||||||
|
|
||||||
|
.PHONY: debug
|
||||||
debug:
|
debug:
|
||||||
# make a debug version if we don't have a -dbg python. Leaves in symbols
|
# make a debug version if we don't have a -dbg python. Leaves in symbols
|
||||||
# for our stuff, kills optimization, and redirects to dev null so we
|
# for our stuff, kills optimization, and redirects to dev null so we
|
||||||
|
@ -66,13 +75,16 @@ debug:
|
||||||
make clean > /dev/null
|
make clean > /dev/null
|
||||||
CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null
|
CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null
|
||||||
|
|
||||||
|
.PHONY: install-req
|
||||||
install-req:
|
install-req:
|
||||||
python3 -m pip install -r requirements.txt
|
python3 -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
.PHONY: install-venv
|
||||||
install-venv:
|
install-venv:
|
||||||
virtualenv .
|
virtualenv .
|
||||||
bin/pip install -r requirements.txt
|
bin/pip install -r requirements.txt
|
||||||
|
|
||||||
|
.PHONY: release-test
|
||||||
release-test:
|
release-test:
|
||||||
$(MAKE) install-req
|
$(MAKE) install-req
|
||||||
python3 setup.py develop
|
python3 setup.py develop
|
||||||
|
@ -84,22 +96,14 @@ release-test:
|
||||||
pyroma .
|
pyroma .
|
||||||
viewdoc
|
viewdoc
|
||||||
|
|
||||||
|
.PHONY: sdist
|
||||||
sdist:
|
sdist:
|
||||||
python3 setup.py sdist --format=gztar
|
python3 setup.py sdist --format=gztar
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
pytest -qq
|
pytest -qq
|
||||||
|
|
||||||
# https://docs.python.org/3/distutils/packageindex.html#the-pypirc-file
|
.PHONY: readme
|
||||||
upload-test:
|
|
||||||
# [test]
|
|
||||||
# username:
|
|
||||||
# password:
|
|
||||||
# repository = http://test.pythonpackages.com
|
|
||||||
python3 setup.py sdist --format=gztar upload -r test
|
|
||||||
|
|
||||||
upload:
|
|
||||||
python3 setup.py sdist --format=gztar upload
|
|
||||||
|
|
||||||
readme:
|
readme:
|
||||||
viewdoc
|
viewdoc
|
||||||
|
|
|
@ -114,3 +114,12 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
* [ ] Make sure the [default version for Read the Docs](https://pillow.readthedocs.io/en/stable/) is up-to-date with the release changes
|
* [ ] Make sure the [default version for Read the Docs](https://pillow.readthedocs.io/en/stable/) is up-to-date with the release changes
|
||||||
|
|
||||||
|
## Docker Images
|
||||||
|
|
||||||
|
* [ ] Update Pillow in the Docker Images repository
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/python-pillow/docker-images
|
||||||
|
cd docker-images
|
||||||
|
./update-pillow-tag.sh [[release tag]]
|
||||||
|
```
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_win32
|
from .helper import is_win32
|
||||||
|
@ -11,7 +12,7 @@ pytestmark = pytest.mark.skipif(is_win32(), reason="requires Unix or macOS")
|
||||||
|
|
||||||
|
|
||||||
def _get_mem_usage():
|
def _get_mem_usage():
|
||||||
from resource import getpagesize, getrusage, RUSAGE_SELF
|
from resource import RUSAGE_SELF, getpagesize, getrusage
|
||||||
|
|
||||||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||||
return mem * getpagesize() / 1024 / 1024
|
return mem * getpagesize() / 1024 / 1024
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_win32, skip_unless_feature
|
from .helper import is_win32, skip_unless_feature
|
||||||
|
@ -18,7 +19,7 @@ pytestmark = [
|
||||||
|
|
||||||
|
|
||||||
def test_leak_load():
|
def test_leak_load():
|
||||||
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
|
from resource import RLIMIT_AS, RLIMIT_STACK, setrlimit
|
||||||
|
|
||||||
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
||||||
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
||||||
|
@ -28,7 +29,7 @@ def test_leak_load():
|
||||||
|
|
||||||
|
|
||||||
def test_leak_save():
|
def test_leak_save():
|
||||||
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
|
from resource import RLIMIT_AS, RLIMIT_STACK, setrlimit
|
||||||
|
|
||||||
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
|
||||||
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# This test is not run automatically.
|
# This test is not run automatically.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# This test is not run automatically.
|
# This test is not run automatically.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
||||||
|
|
|
@ -11,6 +11,7 @@ import tempfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageMath, features
|
from PIL import Image, ImageMath, features
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -165,12 +166,6 @@ def assert_tuple_approx_equal(actuals, targets, threshold, msg):
|
||||||
assert value, msg + ": " + repr(actuals) + " != " + repr(targets)
|
assert value, msg + ": " + repr(actuals) + " != " + repr(targets)
|
||||||
|
|
||||||
|
|
||||||
def skip_known_bad_test(msg=None):
|
|
||||||
# Skip if PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
|
||||||
if not os.environ.get("PILLOW_RUN_KNOWN_BAD", False):
|
|
||||||
pytest.skip(msg or "Known bad test")
|
|
||||||
|
|
||||||
|
|
||||||
def skip_unless_feature(feature):
|
def skip_unless_feature(feature):
|
||||||
reason = "%s not available" % feature
|
reason = "%s not available" % feature
|
||||||
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
return pytest.mark.skipif(not features.check(feature), reason=reason)
|
||||||
|
@ -190,7 +185,7 @@ class PillowLeakTestCase:
|
||||||
:returns: memory usage in kilobytes
|
:returns: memory usage in kilobytes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from resource import getrusage, RUSAGE_SELF
|
from resource import RUSAGE_SELF, getrusage
|
||||||
|
|
||||||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
|
|
BIN
Tests/images/apng/dispose_op_background_p_mode.png
Normal file
BIN
Tests/images/apng/dispose_op_background_p_mode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
Tests/images/exif_text.png
Normal file
BIN
Tests/images/exif_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 174 KiB |
BIN
Tests/images/imagedraw_wide_line_larger_than_int.png
Normal file
BIN
Tests/images/imagedraw_wide_line_larger_than_int.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 557 B |
BIN
Tests/images/text_mono.gif
Normal file
BIN
Tests/images/text_mono.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
Tests/images/transparent_background_text.png
Normal file
BIN
Tests/images/transparent_background_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_similar
|
from .helper import assert_image_similar
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
sample = Image.new("L", (7, 5))
|
sample = Image.new("L", (7, 5))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from array import array
|
from array import array
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_pypy
|
from .helper import is_pypy
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import io
|
import io
|
||||||
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import features
|
from PIL import features
|
||||||
|
|
||||||
from .helper import skip_unless_feature
|
from .helper import skip_unless_feature
|
||||||
|
@ -21,6 +23,27 @@ def test_check():
|
||||||
assert features.check_feature(feature) == features.check(feature)
|
assert features.check_feature(feature) == features.check(feature)
|
||||||
|
|
||||||
|
|
||||||
|
def test_version():
|
||||||
|
# Check the correctness of the convenience function
|
||||||
|
# and the format of version numbers
|
||||||
|
|
||||||
|
def test(name, function):
|
||||||
|
version = features.version(name)
|
||||||
|
if not features.check(name):
|
||||||
|
assert version is None
|
||||||
|
else:
|
||||||
|
assert function(name) == version
|
||||||
|
if name != "PIL":
|
||||||
|
assert version is None or re.search(r"\d+(\.\d+)*$", version)
|
||||||
|
|
||||||
|
for module in features.modules:
|
||||||
|
test(module, features.version_module)
|
||||||
|
for codec in features.codecs:
|
||||||
|
test(codec, features.version_codec)
|
||||||
|
for feature in features.features:
|
||||||
|
test(feature, features.version_feature)
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("webp")
|
@skip_unless_feature("webp")
|
||||||
def test_webp_transparency():
|
def test_webp_transparency():
|
||||||
assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha()
|
assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha()
|
||||||
|
@ -37,9 +60,22 @@ def test_webp_anim():
|
||||||
assert features.check("webp_anim") == _webp.HAVE_WEBPANIM
|
assert features.check("webp_anim") == _webp.HAVE_WEBPANIM
|
||||||
|
|
||||||
|
|
||||||
|
@skip_unless_feature("libjpeg_turbo")
|
||||||
|
def test_libjpeg_turbo_version():
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version("libjpeg_turbo"))
|
||||||
|
|
||||||
|
|
||||||
|
@skip_unless_feature("libimagequant")
|
||||||
|
def test_libimagequant_version():
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version("libimagequant"))
|
||||||
|
|
||||||
|
|
||||||
def test_check_modules():
|
def test_check_modules():
|
||||||
for feature in features.modules:
|
for feature in features.modules:
|
||||||
assert features.check_module(feature) in [True, False]
|
assert features.check_module(feature) in [True, False]
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_codecs():
|
||||||
for feature in features.codecs:
|
for feature in features.codecs:
|
||||||
assert features.check_codec(feature) in [True, False]
|
assert features.check_codec(feature) in [True, False]
|
||||||
|
|
||||||
|
@ -64,6 +100,8 @@ def test_unsupported_codec():
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
features.check_codec(codec)
|
features.check_codec(codec)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
features.version_codec(codec)
|
||||||
|
|
||||||
|
|
||||||
def test_unsupported_module():
|
def test_unsupported_module():
|
||||||
|
@ -72,6 +110,8 @@ def test_unsupported_module():
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
features.check_module(module)
|
features.check_module(module)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
features.version_module(module)
|
||||||
|
|
||||||
|
|
||||||
def test_pilinfo():
|
def test_pilinfo():
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, PngImagePlugin
|
from PIL import Image, ImageSequence, PngImagePlugin
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,6 +105,13 @@ def test_apng_dispose_region():
|
||||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
|
def test_apng_dispose_op_background_p_mode():
|
||||||
|
with Image.open("Tests/images/apng/dispose_op_background_p_mode.png") as im:
|
||||||
|
im.seek(1)
|
||||||
|
im.load()
|
||||||
|
assert im.size == (128, 64)
|
||||||
|
|
||||||
|
|
||||||
def test_apng_blend():
|
def test_apng_blend():
|
||||||
with Image.open("Tests/images/apng/blend_op_source_solid.png") as im:
|
with Image.open("Tests/images/apng/blend_op_source_solid.png") as im:
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
|
@ -494,6 +502,26 @@ def test_apng_save_disposal(tmp_path):
|
||||||
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
|
def test_apng_save_disposal_previous(tmp_path):
|
||||||
|
test_file = str(tmp_path / "temp.png")
|
||||||
|
size = (128, 64)
|
||||||
|
transparent = Image.new("RGBA", size, (0, 0, 0, 0))
|
||||||
|
red = Image.new("RGBA", size, (255, 0, 0, 255))
|
||||||
|
green = Image.new("RGBA", size, (0, 255, 0, 255))
|
||||||
|
|
||||||
|
# test APNG_DISPOSE_OP_NONE
|
||||||
|
transparent.save(
|
||||||
|
test_file,
|
||||||
|
save_all=True,
|
||||||
|
append_images=[red, green],
|
||||||
|
disposal=PngImagePlugin.APNG_DISPOSE_OP_PREVIOUS,
|
||||||
|
)
|
||||||
|
with Image.open(test_file) as im:
|
||||||
|
im.seek(2)
|
||||||
|
assert im.getpixel((0, 0)) == (0, 255, 0, 255)
|
||||||
|
assert im.getpixel((64, 32)) == (0, 255, 0, 255)
|
||||||
|
|
||||||
|
|
||||||
def test_apng_save_blend(tmp_path):
|
def test_apng_save_blend(tmp_path):
|
||||||
test_file = str(tmp_path / "temp.png")
|
test_file = str(tmp_path / "temp.png")
|
||||||
size = (128, 64)
|
size = (128, 64)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BmpImagePlugin, Image
|
from PIL import BmpImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BufrStubImagePlugin, Image
|
from PIL import BufrStubImagePlugin, Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import CurImagePlugin, Image
|
from PIL import CurImagePlugin, Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/deerstalker.cur"
|
TEST_FILE = "Tests/images/deerstalker.cur"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import DcxImagePlugin, Image
|
from PIL import DcxImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import DdsImagePlugin, Image
|
from PIL import DdsImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import EpsImagePlugin, Image, features
|
from PIL import EpsImagePlugin, Image, features
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FitsStubImagePlugin, Image
|
from PIL import FitsStubImagePlugin, Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.fits"
|
TEST_FILE = "Tests/images/hopper.fits"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FliImagePlugin, Image
|
from PIL import FliImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, is_pypy
|
from .helper import assert_image_equal, is_pypy
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
FpxImagePlugin = pytest.importorskip(
|
FpxImagePlugin = pytest.importorskip(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GbrImagePlugin, Image
|
from PIL import GbrImagePlugin, Image
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GdImageFile, UnidentifiedImageError
|
from PIL import GdImageFile, UnidentifiedImageError
|
||||||
|
|
||||||
TEST_GD_FILE = "Tests/images/hopper.gd"
|
TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features
|
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL.GimpPaletteFile import GimpPaletteFile
|
from PIL.GimpPaletteFile import GimpPaletteFile
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import GribStubImagePlugin, Image
|
from PIL import GribStubImagePlugin, Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Hdf5StubImagePlugin, Image
|
from PIL import Hdf5StubImagePlugin, Image
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hdf5.h5"
|
TEST_FILE = "Tests/images/hdf5.h5"
|
||||||
|
|
|
@ -2,14 +2,15 @@ import io
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import IcnsImagePlugin, Image
|
|
||||||
|
from PIL import IcnsImagePlugin, Image, features
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar
|
from .helper import assert_image_equal, assert_image_similar
|
||||||
|
|
||||||
# sample icon file
|
# sample icon file
|
||||||
TEST_FILE = "Tests/images/pillow.icns"
|
TEST_FILE = "Tests/images/pillow.icns"
|
||||||
|
|
||||||
ENABLE_JPEG2K = hasattr(Image.core, "jp2klib_version")
|
ENABLE_JPEG2K = features.check_codec("jpg_2000")
|
||||||
|
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
|
@ -55,6 +56,19 @@ def test_save_append_images(tmp_path):
|
||||||
assert_image_equal(reread, provided_im)
|
assert_image_equal(reread, provided_im)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.platform != "darwin", reason="Requires macOS")
|
||||||
|
def test_save_fp():
|
||||||
|
fp = io.BytesIO()
|
||||||
|
|
||||||
|
with Image.open(TEST_FILE) as im:
|
||||||
|
im.save(fp, format="ICNS")
|
||||||
|
|
||||||
|
with Image.open(fp) as reread:
|
||||||
|
assert reread.mode == "RGBA"
|
||||||
|
assert reread.size == (1024, 1024)
|
||||||
|
assert reread.format == "ICNS"
|
||||||
|
|
||||||
|
|
||||||
def test_sizes():
|
def test_sizes():
|
||||||
# Check that we can load all of the sizes, and that the final pixel
|
# Check that we can load all of the sizes, and that the final pixel
|
||||||
# dimensions are as expected
|
# dimensions are as expected
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import IcoImagePlugin, Image, ImageDraw
|
from PIL import IcoImagePlugin, Image, ImageDraw
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import filecmp
|
import filecmp
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImImagePlugin
|
from PIL import Image, ImImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
|
@ -3,7 +3,15 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin
|
|
||||||
|
from PIL import (
|
||||||
|
ExifTags,
|
||||||
|
Image,
|
||||||
|
ImageFile,
|
||||||
|
JpegImagePlugin,
|
||||||
|
UnidentifiedImageError,
|
||||||
|
features,
|
||||||
|
)
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image,
|
assert_image,
|
||||||
|
@ -41,7 +49,7 @@ class TestFileJpeg:
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
# internal version number
|
# internal version number
|
||||||
assert re.search(r"\d+\.\d+$", Image.core.jpeglib_version)
|
assert re.search(r"\d+\.\d+$", features.version_codec("jpg"))
|
||||||
|
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
@ -90,9 +98,12 @@ class TestFileJpeg:
|
||||||
]
|
]
|
||||||
assert k > 0.9
|
assert k > 0.9
|
||||||
|
|
||||||
def test_dpi(self):
|
@pytest.mark.parametrize(
|
||||||
|
"test_image_path", [TEST_FILE, "Tests/images/pil_sample_cmyk.jpg"],
|
||||||
|
)
|
||||||
|
def test_dpi(self, test_image_path):
|
||||||
def test(xdpi, ydpi=None):
|
def test(xdpi, ydpi=None):
|
||||||
with Image.open(TEST_FILE) as im:
|
with Image.open(test_image_path) as im:
|
||||||
im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))
|
im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi))
|
||||||
return im.info.get("dpi")
|
return im.info.get("dpi")
|
||||||
|
|
||||||
|
@ -706,6 +717,24 @@ class TestFileJpeg:
|
||||||
with Image.open("Tests/images/icc-after-SOF.jpg") as im:
|
with Image.open("Tests/images/icc-after-SOF.jpg") as im:
|
||||||
assert im.info["icc_profile"] == b"profile"
|
assert im.info["icc_profile"] == b"profile"
|
||||||
|
|
||||||
|
def test_jpeg_magic_number(self):
|
||||||
|
size = 4097
|
||||||
|
buffer = BytesIO(b"\xFF" * size) # Many xFF bytes
|
||||||
|
buffer.max_pos = 0
|
||||||
|
orig_read = buffer.read
|
||||||
|
|
||||||
|
def read(n=-1):
|
||||||
|
res = orig_read(n)
|
||||||
|
buffer.max_pos = max(buffer.max_pos, buffer.tell())
|
||||||
|
return res
|
||||||
|
|
||||||
|
buffer.read = read
|
||||||
|
with pytest.raises(UnidentifiedImageError):
|
||||||
|
Image.open(buffer)
|
||||||
|
|
||||||
|
# Assert the entire file has not been read
|
||||||
|
assert 0 < buffer.max_pos < size
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
@pytest.mark.skipif(not is_win32(), reason="Windows only")
|
||||||
@skip_unless_feature("jpg")
|
@skip_unless_feature("jpg")
|
||||||
|
|
|
@ -2,13 +2,13 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageFile, Jpeg2KImagePlugin
|
|
||||||
|
from PIL import Image, ImageFile, Jpeg2KImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
is_big_endian,
|
is_big_endian,
|
||||||
on_ci,
|
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ def roundtrip(im, **options):
|
||||||
|
|
||||||
def test_sanity():
|
def test_sanity():
|
||||||
# Internal version number
|
# Internal version number
|
||||||
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("jpg_2000"))
|
||||||
|
|
||||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
px = im.load()
|
px = im.load()
|
||||||
|
@ -190,14 +190,14 @@ def test_16bit_monochrome_has_correct_mode():
|
||||||
assert jp2.mode == "I;16"
|
assert jp2.mode == "I;16"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||||
def test_16bit_monochrome_jp2_like_tiff():
|
def test_16bit_monochrome_jp2_like_tiff():
|
||||||
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
||||||
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
|
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
|
||||||
assert_image_similar(jp2, tiff_16bit, 1e-3)
|
assert_image_similar(jp2, tiff_16bit, 1e-3)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||||
def test_16bit_monochrome_j2k_like_tiff():
|
def test_16bit_monochrome_j2k_like_tiff():
|
||||||
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
||||||
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
|
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import base64
|
import base64
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from ctypes import c_float
|
from ctypes import c_float
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags
|
|
||||||
|
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -18,8 +19,6 @@ from .helper import (
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("libtiff")
|
@skip_unless_feature("libtiff")
|
||||||
class LibTiffTestCase:
|
class LibTiffTestCase:
|
||||||
|
@ -47,6 +46,9 @@ class LibTiffTestCase:
|
||||||
|
|
||||||
|
|
||||||
class TestFileLibTiff(LibTiffTestCase):
|
class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
def test_version(self):
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("libtiff"))
|
||||||
|
|
||||||
def test_g4_tiff(self, tmp_path):
|
def test_g4_tiff(self, tmp_path):
|
||||||
"""Test the ordinary file path load path"""
|
"""Test the ordinary file path load path"""
|
||||||
|
|
||||||
|
@ -203,6 +205,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
del core_items[tag]
|
del core_items[tag]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
del core_items[320] # colormap is special, tested below
|
||||||
|
|
||||||
# Type codes:
|
# Type codes:
|
||||||
# 2: "ascii",
|
# 2: "ascii",
|
||||||
|
@ -299,9 +302,6 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if libtiff and isinstance(value, bytes):
|
|
||||||
value = value.decode()
|
|
||||||
|
|
||||||
assert reloaded_value == value
|
assert reloaded_value == value
|
||||||
|
|
||||||
# Test with types
|
# Test with types
|
||||||
|
@ -322,6 +322,17 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
)
|
)
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
def test_xmlpacket_tag(self, tmp_path):
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
|
|
||||||
|
out = str(tmp_path / "temp.tif")
|
||||||
|
hopper().save(out, tiffinfo={700: b"xmlpacket tag"})
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
if 700 in reloaded.tag_v2:
|
||||||
|
assert reloaded.tag_v2[700] == b"xmlpacket tag"
|
||||||
|
|
||||||
def test_int_dpi(self, tmp_path):
|
def test_int_dpi(self, tmp_path):
|
||||||
# issue #1765
|
# issue #1765
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
|
@ -448,6 +459,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
assert size_compressed > size_jpeg
|
assert size_compressed > size_jpeg
|
||||||
assert size_jpeg > size_jpeg_30
|
assert size_jpeg > size_jpeg_30
|
||||||
|
|
||||||
|
def test_tiff_jpeg_compression(self, tmp_path):
|
||||||
|
im = hopper("RGB")
|
||||||
|
out = str(tmp_path / "temp.tif")
|
||||||
|
im.save(out, compression="tiff_jpeg")
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
assert reloaded.info["compression"] == "jpeg"
|
||||||
|
|
||||||
def test_quality(self, tmp_path):
|
def test_quality(self, tmp_path):
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
out = str(tmp_path / "temp.tif")
|
out = str(tmp_path / "temp.tif")
|
||||||
|
@ -471,6 +490,18 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
with Image.open(out) as im2:
|
with Image.open(out) as im2:
|
||||||
assert_image_equal(im, im2)
|
assert_image_equal(im, im2)
|
||||||
|
|
||||||
|
def test_palette_save(self, tmp_path):
|
||||||
|
im = hopper("P")
|
||||||
|
out = str(tmp_path / "temp.tif")
|
||||||
|
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
|
im.save(out)
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
# colormap/palette tag
|
||||||
|
assert len(reloaded.tag_v2[320]) == 768
|
||||||
|
|
||||||
def xtest_bw_compression_w_rgb(self, tmp_path):
|
def xtest_bw_compression_w_rgb(self, tmp_path):
|
||||||
""" This test passes, but when running all tests causes a failure due
|
""" This test passes, but when running all tests causes a failure due
|
||||||
to output on stderr from the error thrown by libtiff. We need to
|
to output on stderr from the error thrown by libtiff. We need to
|
||||||
|
@ -667,6 +698,26 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
assert icc == icc_libtiff
|
assert icc == icc_libtiff
|
||||||
|
|
||||||
|
def test_write_icc(self, tmp_path):
|
||||||
|
def check_write(libtiff):
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = libtiff
|
||||||
|
|
||||||
|
with Image.open("Tests/images/hopper.iccprofile.tif") as img:
|
||||||
|
icc_profile = img.info["icc_profile"]
|
||||||
|
|
||||||
|
out = str(tmp_path / "temp.tif")
|
||||||
|
img.save(out, icc_profile=icc_profile)
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
assert icc_profile == reloaded.info["icc_profile"]
|
||||||
|
|
||||||
|
libtiffs = []
|
||||||
|
if Image.core.libtiff_support_custom_tags:
|
||||||
|
libtiffs.append(True)
|
||||||
|
libtiffs.append(False)
|
||||||
|
|
||||||
|
for libtiff in libtiffs:
|
||||||
|
check_write(libtiff)
|
||||||
|
|
||||||
def test_multipage_compression(self):
|
def test_multipage_compression(self):
|
||||||
with Image.open("Tests/images/compression.tif") as im:
|
with Image.open("Tests/images/compression.tif") as im:
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, McIdasImagePlugin
|
from PIL import Image, McIdasImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal
|
from .helper import assert_image_equal
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImagePalette
|
from PIL import Image, ImagePalette
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, skip_unless_feature
|
from .helper import assert_image_similar, hopper, skip_unless_feature
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_similar, is_pypy, skip_unless_feature
|
from .helper import assert_image_similar, is_pypy, skip_unless_feature
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, MspImagePlugin
|
from PIL import Image, MspImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -2,15 +2,10 @@ import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import IMCONVERT, assert_image_equal, hopper, imagemagick_available
|
||||||
IMCONVERT,
|
|
||||||
assert_image_equal,
|
|
||||||
hopper,
|
|
||||||
imagemagick_available,
|
|
||||||
skip_known_bad_test,
|
|
||||||
)
|
|
||||||
|
|
||||||
_roundtrip = imagemagick_available()
|
_roundtrip = imagemagick_available()
|
||||||
|
|
||||||
|
@ -62,13 +57,13 @@ def test_monochrome(tmp_path):
|
||||||
roundtrip(tmp_path, mode)
|
roundtrip(tmp_path, mode)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(reason="Palm P image is wrong")
|
||||||
def test_p_mode(tmp_path):
|
def test_p_mode(tmp_path):
|
||||||
# Arrange
|
# Arrange
|
||||||
mode = "P"
|
mode = "P"
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
helper_save_as_palm(tmp_path, mode)
|
helper_save_as_palm(tmp_path, mode)
|
||||||
skip_known_bad_test("Palm P image is wrong")
|
|
||||||
roundtrip(tmp_path, mode)
|
roundtrip(tmp_path, mode)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PcxImagePlugin
|
from PIL import Image, ImageFile, PcxImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -5,6 +5,7 @@ import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, PdfParser
|
from PIL import Image, PdfParser
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, PixarImagePlugin
|
from PIL import Image, PixarImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -3,7 +3,8 @@ import zlib
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageFile, PngImagePlugin
|
|
||||||
|
from PIL import Image, ImageFile, PngImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
PillowLeakTestCase,
|
PillowLeakTestCase,
|
||||||
|
@ -12,7 +13,6 @@ from .helper import (
|
||||||
hopper,
|
hopper,
|
||||||
is_big_endian,
|
is_big_endian,
|
||||||
is_win32,
|
is_win32,
|
||||||
on_ci,
|
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,11 +69,11 @@ class TestFilePng:
|
||||||
png.crc(cid, s)
|
png.crc(cid, s)
|
||||||
return chunks
|
return chunks
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||||
def test_sanity(self, tmp_path):
|
def test_sanity(self, tmp_path):
|
||||||
|
|
||||||
# internal version number
|
# internal version number
|
||||||
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version)
|
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib"))
|
||||||
|
|
||||||
test_file = str(tmp_path / "temp.png")
|
test_file = str(tmp_path / "temp.png")
|
||||||
|
|
||||||
|
@ -607,6 +607,11 @@ class TestFilePng:
|
||||||
exif = im.copy().getexif()
|
exif = im.copy().getexif()
|
||||||
assert exif[274] == 1
|
assert exif[274] == 1
|
||||||
|
|
||||||
|
# With a tEXt chunk
|
||||||
|
with Image.open("Tests/images/exif_text.png") as im:
|
||||||
|
exif = im._getexif()
|
||||||
|
assert exif[274] == 1
|
||||||
|
|
||||||
# With XMP tags
|
# With XMP tags
|
||||||
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
with Image.open("Tests/images/xmp_tags_orientation.png") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, PsdImagePlugin
|
from PIL import Image, PsdImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper, is_pypy
|
from .helper import assert_image_similar, hopper, is_pypy
|
||||||
|
@ -12,6 +13,7 @@ def test_sanity():
|
||||||
assert im.mode == "RGB"
|
assert im.mode == "RGB"
|
||||||
assert im.size == (128, 128)
|
assert im.size == (128, 128)
|
||||||
assert im.format == "PSD"
|
assert im.format == "PSD"
|
||||||
|
assert im.get_format_mimetype() == "image/vnd.adobe.photoshop"
|
||||||
|
|
||||||
im2 = hopper()
|
im2 = hopper()
|
||||||
assert_image_similar(im, im2, 4.8)
|
assert_image_similar(im, im2, 4.8)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, SgiImagePlugin
|
from PIL import Image, SgiImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -2,6 +2,7 @@ import tempfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_pypy
|
from .helper import assert_image_equal, hopper, is_pypy
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, SunImagePlugin
|
from PIL import Image, SunImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, TarIO, features
|
from PIL import Image, TarIO, features
|
||||||
|
|
||||||
from .helper import is_pypy
|
from .helper import is_pypy
|
||||||
|
|
|
@ -3,6 +3,7 @@ from glob import glob
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin
|
from PIL import Image, TiffImagePlugin
|
||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||||
|
|
||||||
|
@ -16,8 +16,6 @@ from .helper import (
|
||||||
is_win32,
|
is_win32,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class TestFileTiff:
|
class TestFileTiff:
|
||||||
def test_sanity(self, tmp_path):
|
def test_sanity(self, tmp_path):
|
||||||
|
|
|
@ -2,6 +2,7 @@ import io
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin, TiffTags
|
from PIL import Image, TiffImagePlugin, TiffTags
|
||||||
from PIL.TiffImagePlugin import IFDRational
|
from PIL.TiffImagePlugin import IFDRational
|
||||||
|
|
||||||
|
@ -156,6 +157,23 @@ def test_write_metadata(tmp_path):
|
||||||
assert value == reloaded[tag], "%s didn't roundtrip" % tag
|
assert value == reloaded[tag], "%s didn't roundtrip" % tag
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_stripbytecounts_tag_type(tmp_path):
|
||||||
|
out = str(tmp_path / "temp.tiff")
|
||||||
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
|
info = im.tag_v2
|
||||||
|
|
||||||
|
# Resize the image so that STRIPBYTECOUNTS will be larger than a SHORT
|
||||||
|
im = im.resize((500, 500))
|
||||||
|
|
||||||
|
# STRIPBYTECOUNTS can be a SHORT or a LONG
|
||||||
|
info.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] = TiffTags.SHORT
|
||||||
|
|
||||||
|
im.save(out, tiffinfo=info)
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
assert reloaded.tag_v2.tagtype[TiffImagePlugin.STRIPBYTECOUNTS] == TiffTags.LONG
|
||||||
|
|
||||||
|
|
||||||
def test_no_duplicate_50741_tag():
|
def test_no_duplicate_50741_tag():
|
||||||
assert TAG_IDS["MakerNoteSafety"] == 50741
|
assert TAG_IDS["MakerNoteSafety"] == 50741
|
||||||
assert TAG_IDS["BestQualityScale"] == 50780
|
assert TAG_IDS["BestQualityScale"] == 50780
|
||||||
|
@ -319,13 +337,13 @@ def test_empty_values():
|
||||||
|
|
||||||
def test_PhotoshopInfo(tmp_path):
|
def test_PhotoshopInfo(tmp_path):
|
||||||
with Image.open("Tests/images/issue_2278.tif") as im:
|
with Image.open("Tests/images/issue_2278.tif") as im:
|
||||||
assert len(im.tag_v2[34377]) == 1
|
assert len(im.tag_v2[34377]) == 70
|
||||||
assert isinstance(im.tag_v2[34377][0], bytes)
|
assert isinstance(im.tag_v2[34377], bytes)
|
||||||
out = str(tmp_path / "temp.tiff")
|
out = str(tmp_path / "temp.tiff")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
with Image.open(out) as reloaded:
|
with Image.open(out) as reloaded:
|
||||||
assert len(reloaded.tag_v2[34377]) == 1
|
assert len(reloaded.tag_v2[34377]) == 70
|
||||||
assert isinstance(reloaded.tag_v2[34377][0], bytes)
|
assert isinstance(reloaded.tag_v2[34377], bytes)
|
||||||
|
|
||||||
|
|
||||||
def test_too_many_entries():
|
def test_too_many_entries():
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
import io
|
||||||
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, WebPImagePlugin
|
|
||||||
|
from PIL import Image, WebPImagePlugin, features
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
|
@ -36,6 +40,7 @@ class TestFileWebp:
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
_webp.WebPDecoderVersion()
|
_webp.WebPDecoderVersion()
|
||||||
_webp.WebPDecoderBuggyAlpha()
|
_webp.WebPDecoderBuggyAlpha()
|
||||||
|
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("webp"))
|
||||||
|
|
||||||
def test_read_rgb(self):
|
def test_read_rgb(self):
|
||||||
"""
|
"""
|
||||||
|
@ -54,15 +59,10 @@ class TestFileWebp:
|
||||||
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
|
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
|
||||||
assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0)
|
assert_image_similar_tofile(image, "Tests/images/hopper_webp_bits.ppm", 1.0)
|
||||||
|
|
||||||
def test_write_rgb(self, tmp_path):
|
def _roundtrip(self, tmp_path, mode, epsilon, args={}):
|
||||||
"""
|
|
||||||
Can we write a RGB mode file to webp without error.
|
|
||||||
Does it have the bits we expect?
|
|
||||||
"""
|
|
||||||
|
|
||||||
temp_file = str(tmp_path / "temp.webp")
|
temp_file = str(tmp_path / "temp.webp")
|
||||||
|
|
||||||
hopper(self.rgb_mode).save(temp_file)
|
hopper(mode).save(temp_file, **args)
|
||||||
with Image.open(temp_file) as image:
|
with Image.open(temp_file) as image:
|
||||||
assert image.mode == self.rgb_mode
|
assert image.mode == self.rgb_mode
|
||||||
assert image.size == (128, 128)
|
assert image.size == (128, 128)
|
||||||
|
@ -70,6 +70,7 @@ class TestFileWebp:
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
image.getdata()
|
||||||
|
|
||||||
|
if mode == self.rgb_mode:
|
||||||
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
|
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
|
||||||
assert_image_similar_tofile(
|
assert_image_similar_tofile(
|
||||||
image, "Tests/images/hopper_webp_write.ppm", 12.0
|
image, "Tests/images/hopper_webp_write.ppm", 12.0
|
||||||
|
@ -78,10 +79,29 @@ class TestFileWebp:
|
||||||
# This test asserts that the images are similar. If the average pixel
|
# This test asserts that the images are similar. If the average pixel
|
||||||
# difference between the two images is less than the epsilon value,
|
# difference between the two images is less than the epsilon value,
|
||||||
# then we're going to accept that it's a reasonable lossy version of
|
# then we're going to accept that it's a reasonable lossy version of
|
||||||
# the image. The old lena images for WebP are showing ~16 on
|
# the image.
|
||||||
# Ubuntu, the jpegs are showing ~18.
|
target = hopper(mode)
|
||||||
target = hopper(self.rgb_mode)
|
if mode != self.rgb_mode:
|
||||||
assert_image_similar(image, target, 12.0)
|
target = target.convert(self.rgb_mode)
|
||||||
|
assert_image_similar(image, target, epsilon)
|
||||||
|
|
||||||
|
def test_write_rgb(self, tmp_path):
|
||||||
|
"""
|
||||||
|
Can we write a RGB mode file to webp without error?
|
||||||
|
Does it have the bits we expect?
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._roundtrip(tmp_path, self.rgb_mode, 12.5)
|
||||||
|
|
||||||
|
def test_write_method(self, tmp_path):
|
||||||
|
self._roundtrip(tmp_path, self.rgb_mode, 12.0, {"method": 6})
|
||||||
|
|
||||||
|
buffer_no_args = io.BytesIO()
|
||||||
|
hopper().save(buffer_no_args, format="WEBP")
|
||||||
|
|
||||||
|
buffer_method = io.BytesIO()
|
||||||
|
hopper().save(buffer_method, format="WEBP", method=6)
|
||||||
|
assert buffer_no_args.getbuffer() != buffer_method.getbuffer()
|
||||||
|
|
||||||
def test_write_unsupported_mode_L(self, tmp_path):
|
def test_write_unsupported_mode_L(self, tmp_path):
|
||||||
"""
|
"""
|
||||||
|
@ -89,18 +109,7 @@ class TestFileWebp:
|
||||||
similar to the original file.
|
similar to the original file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
temp_file = str(tmp_path / "temp.webp")
|
self._roundtrip(tmp_path, "L", 10.0)
|
||||||
hopper("L").save(temp_file)
|
|
||||||
with Image.open(temp_file) as image:
|
|
||||||
assert image.mode == self.rgb_mode
|
|
||||||
assert image.size == (128, 128)
|
|
||||||
assert image.format == "WEBP"
|
|
||||||
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
target = hopper("L").convert(self.rgb_mode)
|
|
||||||
|
|
||||||
assert_image_similar(image, target, 10.0)
|
|
||||||
|
|
||||||
def test_write_unsupported_mode_P(self, tmp_path):
|
def test_write_unsupported_mode_P(self, tmp_path):
|
||||||
"""
|
"""
|
||||||
|
@ -108,18 +117,7 @@ class TestFileWebp:
|
||||||
similar to the original file.
|
similar to the original file.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
temp_file = str(tmp_path / "temp.webp")
|
self._roundtrip(tmp_path, "P", 50.0)
|
||||||
hopper("P").save(temp_file)
|
|
||||||
with Image.open(temp_file) as image:
|
|
||||||
assert image.mode == self.rgb_mode
|
|
||||||
assert image.size == (128, 128)
|
|
||||||
assert image.format == "WEBP"
|
|
||||||
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
target = hopper("P").convert(self.rgb_mode)
|
|
||||||
|
|
||||||
assert_image_similar(image, target, 50.0)
|
|
||||||
|
|
||||||
def test_WebPEncode_with_invalid_args(self):
|
def test_WebPEncode_with_invalid_args(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
assert_image_similar,
|
assert_image_similar,
|
||||||
is_big_endian,
|
is_big_endian,
|
||||||
on_ci,
|
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def test_n_frames():
|
||||||
assert im.is_animated
|
assert im.is_animated
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||||
def test_write_animation_L(tmp_path):
|
def test_write_animation_L(tmp_path):
|
||||||
"""
|
"""
|
||||||
Convert an animated GIF to animated WebP, then compare the frame count, and first
|
Convert an animated GIF to animated WebP, then compare the frame count, and first
|
||||||
|
@ -53,7 +53,7 @@ def test_write_animation_L(tmp_path):
|
||||||
assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(is_big_endian() and on_ci(), reason="Fails on big-endian")
|
@pytest.mark.xfail(is_big_endian(), reason="Fails on big-endian")
|
||||||
def test_write_animation_RGB(tmp_path):
|
def test_write_animation_RGB(tmp_path):
|
||||||
"""
|
"""
|
||||||
Write an animated WebP from RGB frames, and ensure the frames
|
Write an animated WebP from RGB frames, and ensure the frames
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, WmfImagePlugin
|
from PIL import Image, WmfImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, XpmImagePlugin
|
from PIL import Image, XpmImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, XVThumbImagePlugin
|
from PIL import Image, XVThumbImagePlugin
|
||||||
|
|
||||||
from .helper import assert_image_similar, hopper
|
from .helper import assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import BdfFontFile, FontFile
|
from PIL import BdfFontFile, FontFile
|
||||||
|
|
||||||
filename = "Tests/images/courB08.bdf"
|
filename = "Tests/images/courB08.bdf"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
|
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, skip_unless_feature
|
from .helper import assert_image_equal, assert_image_similar, skip_unless_feature
|
||||||
|
|
|
@ -3,9 +3,10 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import PIL
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageDraw, ImagePalette, UnidentifiedImageError
|
|
||||||
|
import PIL
|
||||||
|
from PIL import Image, ImageDraw, ImagePalette, ImageShow, UnidentifiedImageError
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
|
@ -13,6 +14,7 @@ from .helper import (
|
||||||
assert_not_all_same,
|
assert_not_all_same,
|
||||||
hopper,
|
hopper,
|
||||||
is_win32,
|
is_win32,
|
||||||
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -465,18 +467,6 @@ class TestImage:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
Image.core.fill("RGB", (2, -2), (0, 0, 0))
|
Image.core.fill("RGB", (2, -2), (0, 0, 0))
|
||||||
|
|
||||||
def test_offset_not_implemented(self):
|
|
||||||
# Arrange
|
|
||||||
with hopper() as im:
|
|
||||||
|
|
||||||
# Act / Assert
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
im.offset(None)
|
|
||||||
|
|
||||||
def test_fromstring(self):
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
Image.fromstring()
|
|
||||||
|
|
||||||
def test_linear_gradient_wrong_mode(self):
|
def test_linear_gradient_wrong_mode(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
wrong_mode = "RGB"
|
wrong_mode = "RGB"
|
||||||
|
@ -585,6 +575,22 @@ class TestImage:
|
||||||
expected = Image.new(mode, (100, 100), color)
|
expected = Image.new(mode, (100, 100), color)
|
||||||
assert_image_equal(im.convert(mode), expected)
|
assert_image_equal(im.convert(mode), expected)
|
||||||
|
|
||||||
|
def test_showxv_deprecation(self):
|
||||||
|
class TestViewer(ImageShow.Viewer):
|
||||||
|
def show_image(self, image, **options):
|
||||||
|
return True
|
||||||
|
|
||||||
|
viewer = TestViewer()
|
||||||
|
ImageShow.register(viewer, -1)
|
||||||
|
|
||||||
|
im = Image.new("RGB", (50, 50), "white")
|
||||||
|
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
Image._showxv(im)
|
||||||
|
|
||||||
|
# Restore original state
|
||||||
|
ImageShow._viewers.pop(0)
|
||||||
|
|
||||||
def test_no_resource_warning_on_save(self, tmp_path):
|
def test_no_resource_warning_on_save(self, tmp_path):
|
||||||
# https://github.com/python-pillow/Pillow/issues/835
|
# https://github.com/python-pillow/Pillow/issues/835
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -609,6 +615,97 @@ class TestImage:
|
||||||
|
|
||||||
assert not fp.closed
|
assert not fp.closed
|
||||||
|
|
||||||
|
def test_exif_jpeg(self, tmp_path):
|
||||||
|
with Image.open("Tests/images/exif-72dpi-int.jpg") as im: # Little endian
|
||||||
|
exif = im.getexif()
|
||||||
|
assert 258 not in exif
|
||||||
|
assert 40960 in exif
|
||||||
|
assert exif[40963] == 450
|
||||||
|
assert exif[11] == "gThumb 3.0.1"
|
||||||
|
|
||||||
|
out = str(tmp_path / "temp.jpg")
|
||||||
|
exif[258] = 8
|
||||||
|
del exif[40960]
|
||||||
|
exif[40963] = 455
|
||||||
|
exif[11] = "Pillow test"
|
||||||
|
im.save(out, exif=exif)
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
reloaded_exif = reloaded.getexif()
|
||||||
|
assert reloaded_exif[258] == 8
|
||||||
|
assert 40960 not in reloaded_exif
|
||||||
|
assert reloaded_exif[40963] == 455
|
||||||
|
assert reloaded_exif[11] == "Pillow test"
|
||||||
|
|
||||||
|
with Image.open("Tests/images/no-dpi-in-exif.jpg") as im: # Big endian
|
||||||
|
exif = im.getexif()
|
||||||
|
assert 258 not in exif
|
||||||
|
assert 40962 in exif
|
||||||
|
assert exif[40963] == 200
|
||||||
|
assert exif[305] == "Adobe Photoshop CC 2017 (Macintosh)"
|
||||||
|
|
||||||
|
out = str(tmp_path / "temp.jpg")
|
||||||
|
exif[258] = 8
|
||||||
|
del exif[34665]
|
||||||
|
exif[40963] = 455
|
||||||
|
exif[305] = "Pillow test"
|
||||||
|
im.save(out, exif=exif)
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
reloaded_exif = reloaded.getexif()
|
||||||
|
assert reloaded_exif[258] == 8
|
||||||
|
assert 34665 not in reloaded_exif
|
||||||
|
assert reloaded_exif[40963] == 455
|
||||||
|
assert reloaded_exif[305] == "Pillow test"
|
||||||
|
|
||||||
|
@skip_unless_feature("webp")
|
||||||
|
@skip_unless_feature("webp_anim")
|
||||||
|
def test_exif_webp(self, tmp_path):
|
||||||
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
|
exif = im.getexif()
|
||||||
|
assert exif == {}
|
||||||
|
|
||||||
|
out = str(tmp_path / "temp.webp")
|
||||||
|
exif[258] = 8
|
||||||
|
exif[40963] = 455
|
||||||
|
exif[305] = "Pillow test"
|
||||||
|
|
||||||
|
def check_exif():
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
reloaded_exif = reloaded.getexif()
|
||||||
|
assert reloaded_exif[258] == 8
|
||||||
|
assert reloaded_exif[40963] == 455
|
||||||
|
assert reloaded_exif[305] == "Pillow test"
|
||||||
|
|
||||||
|
im.save(out, exif=exif)
|
||||||
|
check_exif()
|
||||||
|
im.save(out, exif=exif, save_all=True)
|
||||||
|
check_exif()
|
||||||
|
|
||||||
|
def test_exif_png(self, tmp_path):
|
||||||
|
with Image.open("Tests/images/exif.png") as im:
|
||||||
|
exif = im.getexif()
|
||||||
|
assert exif == {274: 1}
|
||||||
|
|
||||||
|
out = str(tmp_path / "temp.png")
|
||||||
|
exif[258] = 8
|
||||||
|
del exif[274]
|
||||||
|
exif[40963] = 455
|
||||||
|
exif[305] = "Pillow test"
|
||||||
|
im.save(out, exif=exif)
|
||||||
|
|
||||||
|
with Image.open(out) as reloaded:
|
||||||
|
reloaded_exif = reloaded.getexif()
|
||||||
|
assert reloaded_exif == {258: 8, 40963: 455, 305: "Pillow test"}
|
||||||
|
|
||||||
|
def test_exif_interop(self):
|
||||||
|
with Image.open("Tests/images/flower.jpg") as im:
|
||||||
|
exif = im.getexif()
|
||||||
|
assert exif.get_ifd(0xA005) == {
|
||||||
|
1: "R98",
|
||||||
|
2: b"0100",
|
||||||
|
4097: 2272,
|
||||||
|
4098: 1704,
|
||||||
|
}
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"test_module", [PIL, Image],
|
"test_module", [PIL, Image],
|
||||||
)
|
)
|
||||||
|
@ -664,6 +761,18 @@ class TestImage:
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
assert str(e) == "buffer overrun when reading image file"
|
assert str(e) == "buffer overrun when reading image file"
|
||||||
|
|
||||||
|
def test_show_deprecation(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(Image, "_show", lambda *args, **kwargs: None)
|
||||||
|
|
||||||
|
im = Image.new("RGB", (50, 50), "white")
|
||||||
|
|
||||||
|
with pytest.warns(None) as raised:
|
||||||
|
im.show()
|
||||||
|
assert not raised
|
||||||
|
|
||||||
|
with pytest.warns(DeprecationWarning):
|
||||||
|
im.show(command="mock")
|
||||||
|
|
||||||
|
|
||||||
class MockEncoder:
|
class MockEncoder:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,9 +2,11 @@ import ctypes
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from distutils import ccompiler, sysconfig
|
import sysconfig
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from setuptools.command.build_ext import new_compiler
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper, is_win32, on_ci
|
from .helper import assert_image_equal, hopper, is_win32, on_ci
|
||||||
|
@ -15,8 +17,9 @@ if os.environ.get("PYTHONOPTIMIZE") == "2":
|
||||||
cffi = None
|
cffi = None
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
from PIL import PyAccess
|
|
||||||
import cffi
|
import cffi
|
||||||
|
|
||||||
|
from PIL import PyAccess
|
||||||
except ImportError:
|
except ImportError:
|
||||||
cffi = None
|
cffi = None
|
||||||
|
|
||||||
|
@ -359,13 +362,12 @@ int main(int argc, char* argv[])
|
||||||
% sys.prefix.replace("\\", "\\\\")
|
% sys.prefix.replace("\\", "\\\\")
|
||||||
)
|
)
|
||||||
|
|
||||||
compiler = ccompiler.new_compiler()
|
compiler = new_compiler()
|
||||||
compiler.add_include_dir(sysconfig.get_python_inc())
|
compiler.add_include_dir(sysconfig.get_config_var("INCLUDEPY"))
|
||||||
|
|
||||||
libdir = sysconfig.get_config_var(
|
libdir = sysconfig.get_config_var("LIBDIR") or sysconfig.get_config_var(
|
||||||
"LIBDIR"
|
"INCLUDEPY"
|
||||||
) or sysconfig.get_python_inc().replace("include", "libs")
|
).replace("include", "libs")
|
||||||
print(libdir)
|
|
||||||
compiler.add_library_dir(libdir)
|
compiler.add_library_dir(libdir)
|
||||||
objects = compiler.compile(["embed_pil.c"])
|
objects = compiler.compile(["embed_pil.c"])
|
||||||
compiler.link_executable(objects, "embed_pil")
|
compiler.link_executable(objects, "embed_pil")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import pytest
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
@ -9,8 +8,3 @@ def test_sanity():
|
||||||
im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes())
|
im2 = Image.frombytes(im1.mode, im1.size, im1.tobytes())
|
||||||
|
|
||||||
assert_image_equal(im1, im2)
|
assert_image_equal(im1, im2)
|
||||||
|
|
||||||
|
|
||||||
def test_not_implemented():
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
Image.fromstring()
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageQt
|
from PIL import Image, ImageQt
|
||||||
|
|
||||||
from .helper import assert_image_equal, hopper
|
from .helper import assert_image_equal, hopper
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -236,7 +236,7 @@ class TestImagingPaste:
|
||||||
[
|
[
|
||||||
(127, 191, 254, 191),
|
(127, 191, 254, 191),
|
||||||
(111, 207, 206, 110),
|
(111, 207, 206, 110),
|
||||||
(127, 254, 127, 0),
|
(255, 255, 255, 0) if mode == "RGBA" else (127, 254, 127, 0),
|
||||||
(207, 207, 239, 239),
|
(207, 207, 239, 239),
|
||||||
(191, 191, 190, 191),
|
(191, 191, 190, 191),
|
||||||
(207, 206, 111, 112),
|
(207, 206, 111, 112),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import ImagePalette
|
from PIL import ImagePalette
|
||||||
|
|
||||||
from .helper import hopper
|
from .helper import hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageMath, ImageMode
|
from PIL import Image, ImageMath, ImageMode
|
||||||
|
|
||||||
from .helper import convert_to_comparable
|
from .helper import convert_to_comparable, skip_unless_feature
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
|
@ -254,9 +255,7 @@ def test_mode_F():
|
||||||
compare_reduce_with_box(im, factor)
|
compare_reduce_with_box(im, factor)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@skip_unless_feature("jpg_2000")
|
||||||
"jpeg2k_decoder" not in codecs, reason="JPEG 2000 support not available"
|
|
||||||
)
|
|
||||||
def test_jpeg2k():
|
def test_jpeg2k():
|
||||||
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
assert im.reduce(2).size == (320, 240)
|
assert im.reduce(2).size == (320, 240)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
@ -218,7 +219,7 @@ class TestImagingCoreResampleAccuracy:
|
||||||
assert_image_equal(im, ref)
|
assert_image_equal(im, ref)
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleConsistencyTest:
|
class TestCoreResampleConsistency:
|
||||||
def make_case(self, mode, fill):
|
def make_case(self, mode, fill):
|
||||||
im = Image.new(mode, (512, 9), fill)
|
im = Image.new(mode, (512, 9), fill)
|
||||||
return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0]
|
return im.resize((9, 512), Image.LANCZOS), im.load()[0, 0]
|
||||||
|
@ -253,7 +254,7 @@ class CoreResampleConsistencyTest:
|
||||||
self.run_case(self.make_case("F", 1.192093e-07))
|
self.run_case(self.make_case("F", 1.192093e-07))
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleAlphaCorrectTest:
|
class TestCoreResampleAlphaCorrect:
|
||||||
def make_levels_case(self, mode):
|
def make_levels_case(self, mode):
|
||||||
i = Image.new(mode, (256, 16))
|
i = Image.new(mode, (256, 16))
|
||||||
px = i.load()
|
px = i.load()
|
||||||
|
@ -274,7 +275,7 @@ class CoreResampleAlphaCorrectTest:
|
||||||
len(used_colors), y
|
len(used_colors), y
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||||
def test_levels_rgba(self):
|
def test_levels_rgba(self):
|
||||||
case = self.make_levels_case("RGBA")
|
case = self.make_levels_case("RGBA")
|
||||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||||
|
@ -283,7 +284,7 @@ class CoreResampleAlphaCorrectTest:
|
||||||
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
|
self.run_levels_case(case.resize((512, 32), Image.BICUBIC))
|
||||||
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
|
self.run_levels_case(case.resize((512, 32), Image.LANCZOS))
|
||||||
|
|
||||||
@pytest.mark.skip("Current implementation isn't precise enough")
|
@pytest.mark.xfail(reason="Current implementation isn't precise enough")
|
||||||
def test_levels_la(self):
|
def test_levels_la(self):
|
||||||
case = self.make_levels_case("LA")
|
case = self.make_levels_case("LA")
|
||||||
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
self.run_levels_case(case.resize((512, 32), Image.BOX))
|
||||||
|
@ -329,7 +330,7 @@ class CoreResampleAlphaCorrectTest:
|
||||||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,))
|
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255,))
|
||||||
|
|
||||||
|
|
||||||
class CoreResamplePassesTest:
|
class TestCoreResamplePasses:
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def count(self, diff):
|
def count(self, diff):
|
||||||
count = Image.core.get_stats()["new_count"]
|
count = Image.core.get_stats()["new_count"]
|
||||||
|
@ -372,7 +373,7 @@ class CoreResamplePassesTest:
|
||||||
assert_image_similar(with_box, cropped, 0.1)
|
assert_image_similar(with_box, cropped, 0.1)
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleCoefficientsTest:
|
class TestCoreResampleCoefficients:
|
||||||
def test_reduce(self):
|
def test_reduce(self):
|
||||||
test_color = 254
|
test_color = 254
|
||||||
|
|
||||||
|
@ -401,7 +402,7 @@ class CoreResampleCoefficientsTest:
|
||||||
assert histogram[0x100 * 3 + 0xFF] == 0x10000
|
assert histogram[0x100 * 3 + 0xFF] == 0x10000
|
||||||
|
|
||||||
|
|
||||||
class CoreResampleBoxTest:
|
class TestCoreResampleBox:
|
||||||
def test_wrong_arguments(self):
|
def test_wrong_arguments(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
for resample in (
|
for resample in (
|
||||||
|
|
|
@ -4,6 +4,7 @@ Tests for resize functionality.
|
||||||
from itertools import permutations
|
from itertools import permutations
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import math
|
import math
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageTransform
|
from PIL import Image, ImageTransform
|
||||||
|
|
||||||
from .helper import assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
|
@ -4,7 +4,8 @@ import re
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from PIL import Image, ImageMode
|
|
||||||
|
from PIL import Image, ImageMode, features
|
||||||
|
|
||||||
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ def test_sanity():
|
||||||
assert list(map(type, v)) == [str, str, str, str]
|
assert list(map(type, v)) == [str, str, str, str]
|
||||||
|
|
||||||
# internal version number
|
# internal version number
|
||||||
assert re.search(r"\d+\.\d+$", ImageCms.core.littlecms_version)
|
assert re.search(r"\d+\.\d+$", features.version_module("littlecms2"))
|
||||||
|
|
||||||
skip_missing()
|
skip_missing()
|
||||||
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
|
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
|
||||||
|
@ -435,39 +436,6 @@ def test_extended_information():
|
||||||
assert p.xcolor_space == "RGB "
|
assert p.xcolor_space == "RGB "
|
||||||
|
|
||||||
|
|
||||||
def test_deprecations():
|
|
||||||
skip_missing()
|
|
||||||
o = ImageCms.getOpenProfile(SRGB)
|
|
||||||
p = o.profile
|
|
||||||
|
|
||||||
def helper_deprecated(attr, expected):
|
|
||||||
result = pytest.warns(DeprecationWarning, getattr, p, attr)
|
|
||||||
assert result == expected
|
|
||||||
|
|
||||||
# p.color_space
|
|
||||||
helper_deprecated("color_space", "RGB")
|
|
||||||
|
|
||||||
# p.pcs
|
|
||||||
helper_deprecated("pcs", "XYZ")
|
|
||||||
|
|
||||||
# p.product_copyright
|
|
||||||
helper_deprecated(
|
|
||||||
"product_copyright", "Copyright International Color Consortium, 2009"
|
|
||||||
)
|
|
||||||
|
|
||||||
# p.product_desc
|
|
||||||
helper_deprecated("product_desc", "sRGB IEC61966-2-1 black scaled")
|
|
||||||
|
|
||||||
# p.product_description
|
|
||||||
helper_deprecated("product_description", "sRGB IEC61966-2-1 black scaled")
|
|
||||||
|
|
||||||
# p.product_manufacturer
|
|
||||||
helper_deprecated("product_manufacturer", "")
|
|
||||||
|
|
||||||
# p.product_model
|
|
||||||
helper_deprecated("product_model", "IEC 61966-2-1 Default RGB Colour Space - sRGB")
|
|
||||||
|
|
||||||
|
|
||||||
def test_profile_typesafety():
|
def test_profile_typesafety():
|
||||||
""" Profile init type safety
|
""" Profile init type safety
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageColor
|
from PIL import Image, ImageColor
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from PIL import Image, ImageColor, ImageDraw, ImageFont
|
from PIL import Image, ImageColor, ImageDraw, ImageFont
|
||||||
|
|
||||||
from .helper import (
|
from .helper import (
|
||||||
assert_image_equal,
|
assert_image_equal,
|
||||||
assert_image_similar,
|
assert_image_similar_tofile,
|
||||||
hopper,
|
hopper,
|
||||||
skip_unless_feature,
|
skip_unless_feature,
|
||||||
)
|
)
|
||||||
|
@ -71,7 +72,7 @@ def helper_arc(bbox, start, end):
|
||||||
draw.arc(bbox, start, end)
|
draw.arc(bbox, start, end)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc1():
|
def test_arc1():
|
||||||
|
@ -110,20 +111,19 @@ def test_arc_no_loops():
|
||||||
draw.arc(BBOX1, start=start, end=end)
|
draw.arc(BBOX1, start=start, end=end)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_no_loops.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width():
|
def test_arc_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_arc_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, 10, 260, width=5)
|
draw.arc(BBOX1, 10, 260, width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width_pieslice_large():
|
def test_arc_width_pieslice_large():
|
||||||
|
@ -131,26 +131,24 @@ def test_arc_width_pieslice_large():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_arc_width_pieslice.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
|
draw.arc(BBOX1, 10, 260, fill="yellow", width=100)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_pieslice.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width_fill():
|
def test_arc_width_fill():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_arc_width_fill.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
|
draw.arc(BBOX1, 10, 260, fill="yellow", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_arc_width_fill.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_width_non_whole_angle():
|
def test_arc_width_non_whole_angle():
|
||||||
|
@ -163,7 +161,7 @@ def test_arc_width_non_whole_angle():
|
||||||
draw.arc(BBOX1, 10, 259.5, width=5)
|
draw.arc(BBOX1, 10, 259.5, width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_bitmap():
|
def test_bitmap():
|
||||||
|
@ -190,7 +188,7 @@ def helper_chord(mode, bbox, start, end):
|
||||||
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_chord1():
|
def test_chord1():
|
||||||
|
@ -209,26 +207,24 @@ def test_chord_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_chord_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
|
draw.chord(BBOX1, 10, 260, outline="yellow", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_chord_width_fill():
|
def test_chord_width_fill():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_chord_width_fill.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
|
draw.chord(BBOX1, 10, 260, fill="red", outline="yellow", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_chord_width_fill.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_chord_zero_width():
|
def test_chord_zero_width():
|
||||||
|
@ -254,7 +250,7 @@ def helper_ellipse(mode, bbox):
|
||||||
draw.ellipse(bbox, fill="green", outline="blue")
|
draw.ellipse(bbox, fill="green", outline="blue")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse1():
|
def test_ellipse1():
|
||||||
|
@ -276,8 +272,8 @@ def test_ellipse_translucent():
|
||||||
draw.ellipse(BBOX1, fill=(0, 255, 0, 127))
|
draw.ellipse(BBOX1, fill=(0, 255, 0, 127))
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
expected = Image.open("Tests/images/imagedraw_ellipse_translucent.png")
|
expected = "Tests/images/imagedraw_ellipse_translucent.png"
|
||||||
assert_image_similar(im, expected, 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_edge():
|
def test_ellipse_edge():
|
||||||
|
@ -289,7 +285,7 @@ def test_ellipse_edge():
|
||||||
draw.ellipse(((0, 0), (W - 1, H)), fill="white")
|
draw.ellipse(((0, 0), (W - 1, H)), fill="white")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_edge.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_symmetric():
|
def test_ellipse_symmetric():
|
||||||
|
@ -304,39 +300,36 @@ def test_ellipse_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_ellipse_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(BBOX1, outline="blue", width=5)
|
draw.ellipse(BBOX1, outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_width_large():
|
def test_ellipse_width_large():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (500, 500))
|
im = Image.new("RGB", (500, 500))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_ellipse_width_large.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
|
draw.ellipse((25, 25, 475, 475), outline="blue", width=75)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_large.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_width_fill():
|
def test_ellipse_width_fill():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_ellipse_width_fill.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
|
draw.ellipse(BBOX1, fill="green", outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_ellipse_width_fill.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_ellipse_zero_width():
|
def test_ellipse_zero_width():
|
||||||
|
@ -423,7 +416,7 @@ def helper_pieslice(bbox, start, end):
|
||||||
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open("Tests/images/imagedraw_pieslice.png"), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_pieslice1():
|
def test_pieslice1():
|
||||||
|
@ -440,13 +433,12 @@ def test_pieslice_width():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_pieslice_width.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
|
draw.pieslice(BBOX1, 10, 260, outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_pieslice_width.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_pieslice_width_fill():
|
def test_pieslice_width_fill():
|
||||||
|
@ -459,7 +451,7 @@ def test_pieslice_width_fill():
|
||||||
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
|
draw.pieslice(BBOX1, 10, 260, fill="white", outline="blue", width=5)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
def test_pieslice_zero_width():
|
def test_pieslice_zero_width():
|
||||||
|
@ -571,13 +563,12 @@ def test_big_rectangle():
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
bbox = [(-1, -1), (W + 1, H + 1)]
|
bbox = [(-1, -1), (W + 1, H + 1)]
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_big_rectangle.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.rectangle(bbox, fill="orange")
|
draw.rectangle(bbox, fill="orange")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_big_rectangle.png", 1)
|
||||||
|
|
||||||
|
|
||||||
def test_rectangle_width():
|
def test_rectangle_width():
|
||||||
|
@ -878,13 +869,25 @@ def test_wide_line_dot():
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (W, H))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_wide_line_dot.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line([(50, 50), (50, 50)], width=3)
|
draw.line([(50, 50), (50, 50)], width=3)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_wide_line_dot.png", 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wide_line_larger_than_int():
|
||||||
|
# Arrange
|
||||||
|
im = Image.new("RGB", (W, H))
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
expected = "Tests/images/imagedraw_wide_line_larger_than_int.png"
|
||||||
|
|
||||||
|
# Act
|
||||||
|
draw.line([(0, 0), (32768, 32768)], width=3)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -971,13 +974,12 @@ def test_wide_line_dot():
|
||||||
def test_line_joint(xy):
|
def test_line_joint(xy):
|
||||||
im = Image.new("RGB", (500, 325))
|
im = Image.new("RGB", (500, 325))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
expected = "Tests/images/imagedraw_line_joint_curve.png"
|
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line(xy, GRAY, 50, "curve")
|
draw.line(xy, GRAY, 50, "curve")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(im, Image.open(expected), 3)
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_line_joint_curve.png", 3)
|
||||||
|
|
||||||
|
|
||||||
def test_textsize_empty_string():
|
def test_textsize_empty_string():
|
||||||
|
@ -1018,8 +1020,8 @@ def test_stroke():
|
||||||
draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill)
|
draw.text((10, 10), "A", "#f00", font, stroke_width=2, stroke_fill=stroke_fill)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(
|
assert_image_similar_tofile(
|
||||||
im, Image.open("Tests/images/imagedraw_stroke_" + suffix + ".png"), 3.1
|
im, "Tests/images/imagedraw_stroke_" + suffix + ".png", 3.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1034,9 +1036,7 @@ def test_stroke_descender():
|
||||||
draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0")
|
draw.text((10, 0), "y", "#f00", font, stroke_width=2, stroke_fill="#0f0")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_descender.png", 6.76)
|
||||||
im, Image.open("Tests/images/imagedraw_stroke_descender.png"), 6.76
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@skip_unless_feature("freetype2")
|
@skip_unless_feature("freetype2")
|
||||||
|
@ -1052,9 +1052,7 @@ def test_stroke_multiline():
|
||||||
)
|
)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert_image_similar(
|
assert_image_similar_tofile(im, "Tests/images/imagedraw_stroke_multiline.png", 3.3)
|
||||||
im, Image.open("Tests/images/imagedraw_stroke_multiline.png"), 3.3
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_same_color_outline():
|
def test_same_color_outline():
|
||||||
|
@ -1093,4 +1091,4 @@ def test_same_color_outline():
|
||||||
expected = "Tests/images/imagedraw_outline_{}_{}.png".format(
|
expected = "Tests/images/imagedraw_outline_{}_{}.png".format(
|
||||||
operation, mode
|
operation, mode
|
||||||
)
|
)
|
||||||
assert_image_similar(im, Image.open(expected), 1)
|
assert_image_similar_tofile(im, expected, 1)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user