mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 09:14:27 +03:00
Merge branch 'master' into reduce
This commit is contained in:
commit
5283141417
|
@ -13,16 +13,8 @@ environment:
|
||||||
TEST_OPTIONS:
|
TEST_OPTIONS:
|
||||||
DEPLOY: YES
|
DEPLOY: YES
|
||||||
matrix:
|
matrix:
|
||||||
- PYTHON: C:/vp/pypy2
|
- PYTHON: C:/Python38
|
||||||
EXECUTABLE: bin/pypy.exe
|
- PYTHON: C:/Python38-x64
|
||||||
PIP_DIR: bin
|
|
||||||
VENV: YES
|
|
||||||
- PYTHON: C:/Python27-x64
|
|
||||||
- PYTHON: C:/Python37
|
|
||||||
- PYTHON: C:/Python27
|
|
||||||
- PYTHON: C:/Python37-x64
|
|
||||||
- PYTHON: C:/Python36
|
|
||||||
- PYTHON: C:/Python36-x64
|
|
||||||
- PYTHON: C:/Python35
|
- PYTHON: C:/Python35
|
||||||
- PYTHON: C:/Python35-x64
|
- PYTHON: C:/Python35-x64
|
||||||
- PYTHON: C:/msys64/mingw32
|
- PYTHON: C:/msys64/mingw32
|
||||||
|
@ -44,11 +36,6 @@ install:
|
||||||
- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\
|
- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\
|
||||||
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
|
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
|
||||||
- cd c:\pillow\winbuild\
|
- cd c:\pillow\winbuild\
|
||||||
- ps: |
|
|
||||||
if ($env:PYTHON -eq "c:/vp/pypy2")
|
|
||||||
{
|
|
||||||
c:\pillow\winbuild\appveyor_install_pypy2.cmd
|
|
||||||
}
|
|
||||||
- ps: |
|
- ps: |
|
||||||
if ($env:PYTHON -eq "c:/vp/pypy3")
|
if ($env:PYTHON -eq "c:/vp/pypy3")
|
||||||
{
|
{
|
||||||
|
@ -65,6 +52,9 @@ install:
|
||||||
c:\pillow\winbuild\build_deps.cmd
|
c:\pillow\winbuild\build_deps.cmd
|
||||||
$host.SetShouldExit(0)
|
$host.SetShouldExit(0)
|
||||||
}
|
}
|
||||||
|
- curl -fsSL -o gs950.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs950/gs950w32.exe
|
||||||
|
- gs950.exe /S
|
||||||
|
- path %path%;C:\Program Files (x86)\gs\gs9.50\bin
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- ps: |
|
- ps: |
|
||||||
|
@ -85,7 +75,8 @@ build_script:
|
||||||
test_script:
|
test_script:
|
||||||
- cd c:\pillow
|
- cd c:\pillow
|
||||||
- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov'
|
- '%PYTHON%\%PIP_DIR%\pip.exe install pytest pytest-cov'
|
||||||
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov-report term --cov-report xml Tests'
|
- c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\%EXECUTABLE%
|
||||||
|
- '%PYTHON%\%EXECUTABLE% -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests'
|
||||||
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
|
#- '%PYTHON%\%EXECUTABLE% test-installed.py -v -s %TEST_OPTIONS%' TODO TEST_OPTIONS with pytest?
|
||||||
|
|
||||||
after_test:
|
after_test:
|
||||||
|
|
|
@ -6,4 +6,13 @@ codecov:
|
||||||
# https://docs.codecov.io/v4.3.6/docs/comparing-commits
|
# https://docs.codecov.io/v4.3.6/docs/comparing-commits
|
||||||
allow_coverage_offsets: true
|
allow_coverage_offsets: true
|
||||||
|
|
||||||
|
token: 6dafc396-e7f5-4221-a38a-8b07a49fbdae
|
||||||
|
|
||||||
comment: off
|
comment: off
|
||||||
|
|
||||||
|
# Matches 'omit:' in .coveragerc
|
||||||
|
ignore:
|
||||||
|
- "Tests/32bit_segfault_check.py"
|
||||||
|
- "Tests/bench_cffi_access.py"
|
||||||
|
- "Tests/check_*.py"
|
||||||
|
- "Tests/createfontdatachunk.py"
|
||||||
|
|
|
@ -12,3 +12,10 @@ exclude_lines =
|
||||||
# Don't complain about debug code
|
# Don't complain about debug code
|
||||||
if Image.DEBUG:
|
if Image.DEBUG:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
|
|
||||||
|
[run]
|
||||||
|
omit =
|
||||||
|
Tests/32bit_segfault_check.py
|
||||||
|
Tests/bench_cffi_access.py
|
||||||
|
Tests/check_*.py
|
||||||
|
Tests/createfontdatachunk.py
|
||||||
|
|
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
|
@ -9,14 +9,14 @@ Please send a pull request to the master branch. Please include [documentation](
|
||||||
- Fork the Pillow repository.
|
- Fork the Pillow repository.
|
||||||
- Create a branch from master.
|
- Create a branch from master.
|
||||||
- Develop bug fixes, features, tests, etc.
|
- Develop bug fixes, features, tests, etc.
|
||||||
- Run the test suite on Python 2.7 and 3.x. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
|
- Run the test suite. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
|
||||||
- Create a pull request to pull the changes from your branch to the Pillow master.
|
- Create a pull request to pull the changes from your branch to the Pillow master.
|
||||||
|
|
||||||
### Guidelines
|
### Guidelines
|
||||||
|
|
||||||
- Separate code commits from reformatting commits.
|
- Separate code commits from reformatting commits.
|
||||||
- Provide tests for any newly added code.
|
- Provide tests for any newly added code.
|
||||||
- Follow PEP8.
|
- Follow PEP 8.
|
||||||
- When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor.
|
- When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor.
|
||||||
|
|
||||||
## Reporting Issues
|
## Reporting Issues
|
||||||
|
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -1 +1 @@
|
||||||
tidelift: pypi/pillow
|
tidelift: "pypi/Pillow"
|
||||||
|
|
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
|
@ -8,17 +8,17 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python: [3.7]
|
python-version: ["3.8"]
|
||||||
|
|
||||||
name: Python ${{ matrix.python }}
|
name: Python ${{ matrix.python-version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|
17
.github/workflows/macos-install.sh
vendored
Executable file
17
.github/workflows/macos-install.sh
vendored
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype
|
||||||
|
|
||||||
|
PYTHONOPTIMIZE=0 pip install cffi
|
||||||
|
pip install coverage
|
||||||
|
pip install olefile
|
||||||
|
pip install -U pytest
|
||||||
|
pip install -U pytest-cov
|
||||||
|
pip install pyroma
|
||||||
|
pip install test-image-results
|
||||||
|
pip install numpy
|
||||||
|
|
||||||
|
# extra test images
|
||||||
|
pushd depends && ./install_extra_test_images.sh && popd
|
40
.github/workflows/test-docker.yml
vendored
Normal file
40
.github/workflows/test-docker.yml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
name: Test Docker
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
docker: [
|
||||||
|
alpine,
|
||||||
|
arch,
|
||||||
|
ubuntu-16.04-xenial-amd64,
|
||||||
|
ubuntu-18.04-bionic-amd64,
|
||||||
|
debian-9-stretch-x86,
|
||||||
|
debian-10-buster-x86,
|
||||||
|
centos-6-amd64,
|
||||||
|
centos-7-amd64,
|
||||||
|
amazon-1-amd64,
|
||||||
|
amazon-2-amd64,
|
||||||
|
fedora-30-amd64,
|
||||||
|
fedora-31-amd64,
|
||||||
|
]
|
||||||
|
dockerTag: [master]
|
||||||
|
|
||||||
|
name: ${{ matrix.docker }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Docker pull
|
||||||
|
run: |
|
||||||
|
docker pull pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
||||||
|
|
||||||
|
- name: Docker build
|
||||||
|
run: |
|
||||||
|
# The Pillow user in the docker container is UID 1000
|
||||||
|
sudo chown -R 1000 $GITHUB_WORKSPACE
|
||||||
|
docker run -v $GITHUB_WORKSPACE:/Pillow pythonpillow/${{ matrix.docker }}:${{ matrix.dockerTag }}
|
392
.github/workflows/test-windows.yml
vendored
Normal file
392
.github/workflows/test-windows.yml
vendored
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
name: Test Windows
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: windows-2019
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.5", "3.6", "3.7", "3.8", "pypy3.6"]
|
||||||
|
architecture: ["x86", "x64"]
|
||||||
|
include:
|
||||||
|
- architecture: "x86"
|
||||||
|
platform-vcvars: "x86"
|
||||||
|
platform-msbuild: "Win32"
|
||||||
|
- architecture: "x64"
|
||||||
|
platform-vcvars: "x86_amd64"
|
||||||
|
platform-msbuild: "x64"
|
||||||
|
- python-version: "pypy3.6"
|
||||||
|
pypy-version: "pypy3.6-v7.2.0-win32"
|
||||||
|
pypy-url: "https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.2.0-win32.zip"
|
||||||
|
exclude:
|
||||||
|
- python-version: "pypy3.6"
|
||||||
|
architecture: "x64"
|
||||||
|
timeout-minutes: 30
|
||||||
|
|
||||||
|
name: Python ${{ matrix.python-version }} ${{ matrix.architecture }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
repository: python-pillow/pillow-depends
|
||||||
|
ref: master
|
||||||
|
|
||||||
|
- name: Cache
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~\AppData\Local\pip\Cache
|
||||||
|
key:
|
||||||
|
${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.architecture }}-${{ hashFiles('**/.github/workflows/test-windows.yml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.architecture }}-
|
||||||
|
${{ runner.os }}-${{ matrix.python-version }}-
|
||||||
|
|
||||||
|
- name: Install PyPy
|
||||||
|
if: "contains(matrix.python-version, 'pypy')"
|
||||||
|
run: |
|
||||||
|
curl -fsSL -o pypy3.zip "${{ matrix.pypy-url }}"
|
||||||
|
7z x pypy3.zip "-o$env:RUNNER_WORKSPACE\"
|
||||||
|
mv "$env:RUNNER_WORKSPACE\${{ matrix.pypy-version }}" "$env:RUNNER_WORKSPACE\${{ matrix.python-version }}"
|
||||||
|
$env:PYTHON="$env:RUNNER_WORKSPACE\${{ matrix.python-version }}"
|
||||||
|
# set env: pythonLocation
|
||||||
|
Write-Host "`#`#[set-env name=pythonLocation;]$env:PYTHON" # old syntax https://github.com/actions/toolkit/issues/61
|
||||||
|
Write-Host "::set-env name=pythonLocation::$env:PYTHON" # new syntax https://github.com/actions/toolkit/blob/5bb77ec03fea98332e41f9347c8fbb1ce1e48f4a/docs/commands.md
|
||||||
|
New-Item -ItemType SymbolicLink -Path "$env:PYTHON\python.exe" -Target "$env:PYTHON\pypy3.exe"
|
||||||
|
curl -fsSL -o get-pip.py https://bootstrap.pypa.io/get-pip.py
|
||||||
|
$env:PATH = "$env:PYTHON\bin;$env:PATH"
|
||||||
|
& $env:PYTHON\python.exe get-pip.py
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
# sets env: pythonLocation
|
||||||
|
- name: Set up Python
|
||||||
|
if: "!contains(matrix.python-version, 'pypy')"
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
architecture: ${{ matrix.architecture }}
|
||||||
|
|
||||||
|
- name: pip install wheel pytest pytest-cov
|
||||||
|
run: |
|
||||||
|
"%pythonLocation%\python.exe" -m pip install wheel pytest pytest-cov
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Fetch dependencies
|
||||||
|
run: |
|
||||||
|
7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
|
||||||
|
Write-Host "`#`#[add-path]$env:RUNNER_WORKSPACE\nasm-2.14.02"
|
||||||
|
Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02"
|
||||||
|
|
||||||
|
..\pillow-depends\gs950w32.exe /S
|
||||||
|
Write-Host "`#`#[add-path]C:\Program Files (x86)\gs\gs9.50\bin"
|
||||||
|
Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin"
|
||||||
|
|
||||||
|
$env:PYTHON=$env:pythonLocation
|
||||||
|
xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\
|
||||||
|
xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\
|
||||||
|
xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\
|
||||||
|
cd $env:GITHUB_WORKSPACE/winbuild/
|
||||||
|
python.exe $env:GITHUB_WORKSPACE\winbuild\build_dep.py
|
||||||
|
env:
|
||||||
|
EXECUTABLE: bin\python.exe
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Build dependencies / libjpeg
|
||||||
|
if: false
|
||||||
|
run: |
|
||||||
|
REM FIXME uses /MT not /MD, see makefile.vc and win32.mak for more info
|
||||||
|
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\jpeg-9c
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
nmake -nologo -f makefile.vc setup-vc6
|
||||||
|
nmake -nologo -f makefile.vc clean
|
||||||
|
nmake -nologo -f makefile.vc nodebug=1 libjpeg.lib cjpeg.exe djpeg.exe
|
||||||
|
copy /Y /B j*.h %INCLIB%
|
||||||
|
copy /Y /B *.lib %INCLIB%
|
||||||
|
copy /Y /B *.exe %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / libjpeg-turbo
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\libjpeg-turbo-2.0.3
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DENABLE_SHARED:BOOL=OFF -DWITH_JPEG8:BOOL=TRUE -DWITH_CRT_DLL:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
|
||||||
|
%CMAKE% -G "NMake Makefiles" .
|
||||||
|
nmake -nologo -f Makefile clean
|
||||||
|
nmake -nologo -f Makefile jpeg-static cjpeg-static djpeg-static
|
||||||
|
copy /Y /B j*.h %INCLIB%
|
||||||
|
copy /Y /B jpeg-static.lib %INCLIB%\libjpeg.lib
|
||||||
|
copy /Y /B cjpeg-static.exe %INCLIB%\cjpeg.exe
|
||||||
|
copy /Y /B djpeg-static.exe %INCLIB%\djpeg.exe
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / zlib
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\zlib-1.2.11
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
nmake -nologo -f win32\Makefile.msc clean
|
||||||
|
nmake -nologo -f win32\Makefile.msc zlib.lib
|
||||||
|
copy /Y /B z*.h %INCLIB%
|
||||||
|
copy /Y /B *.lib %INCLIB%
|
||||||
|
copy /Y /B zlib.lib %INCLIB%\z.lib
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / LibTIFF
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\tiff-4.1.0
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
copy %GITHUB_WORKSPACE%\winbuild\tiff.opt nmake.opt
|
||||||
|
nmake -nologo -f makefile.vc clean
|
||||||
|
nmake -nologo -f makefile.vc lib
|
||||||
|
copy /Y /B libtiff\tiff*.h %INCLIB%
|
||||||
|
copy /Y /B libtiff\*.dll %INCLIB%
|
||||||
|
copy /Y /B libtiff\*.lib %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / WebP
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\libwebp-1.0.3
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
rmdir /S /Q output\release-static
|
||||||
|
nmake -nologo -f Makefile.vc CFG=release-static OBJDIR=output ARCH=${{ matrix.architecture }} all
|
||||||
|
mkdir %INCLIB%\webp
|
||||||
|
copy /Y /B src\webp\*.h %INCLIB%\webp
|
||||||
|
copy /Y /B output\release-static\${{ matrix.architecture }}\lib\* %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / FreeType
|
||||||
|
run: |
|
||||||
|
REM Toolkit v100 not available; missing VCTargetsPath; Clean fails
|
||||||
|
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\freetype-2.10.1
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
rmdir /S /Q objs
|
||||||
|
set DefaultPlatformToolset=v142
|
||||||
|
set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\
|
||||||
|
set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
|
||||||
|
powershell -Command "(gc builds\windows\vc2010\freetype.vcxproj) -replace 'MultiThreaded<', 'MultiThreadedDLL<' | Out-File -encoding ASCII builds\windows\vc2010\freetype.vcxproj"
|
||||||
|
%MSBUILD% builds\windows\vc2010\freetype.sln /t:Build /p:Configuration="Release Static" /p:Platform=${{ matrix.platform-msbuild }} /m
|
||||||
|
xcopy /Y /E /Q include %INCLIB%
|
||||||
|
copy /Y /B "objs\${{ matrix.platform-msbuild }}\Release Static\freetype.lib" %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / LCMS2
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\lcms2-2.8
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
rmdir /S /Q Lib
|
||||||
|
rmdir /S /Q Projects\VC2015\Release
|
||||||
|
set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\
|
||||||
|
set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
|
||||||
|
powershell %GITHUB_WORKSPACE%\winbuild\lcms2_patch.ps1
|
||||||
|
%MSBUILD% Projects\VC2015\lcms2.sln /t:Clean;lcms2_static /p:Configuration="Release" /p:Platform=${{ matrix.platform-msbuild }} /m
|
||||||
|
xcopy /Y /E /Q include %INCLIB%
|
||||||
|
copy /Y /B Lib\MS\*.lib %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / OpenJPEG
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\openjpeg-2.3.1msvcr10-x32
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DBUILD_THIRDPARTY:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
|
||||||
|
%CMAKE% -G "NMake Makefiles" .
|
||||||
|
nmake -nologo -f Makefile clean
|
||||||
|
nmake -nologo -f Makefile
|
||||||
|
mkdir %INCLIB%\openjpeg-2.3.1
|
||||||
|
copy /Y /B src\lib\openjp2\*.h %INCLIB%\openjpeg-2.3.1
|
||||||
|
copy /Y /B bin\*.lib %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
# GPL licensed; skip if building wheels
|
||||||
|
- name: Build dependencies / libimagequant
|
||||||
|
if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')"
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
rem ba653c8: Merge tag '2.12.5' into msvc
|
||||||
|
cd /D %BUILD%\libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
echo (gc CMakeLists.txt) -replace 'add_library', "add_compile_options(-openmp-)`r`nadd_library" ^| Out-File -encoding ASCII CMakeLists.txt > patch.ps1
|
||||||
|
echo (gc CMakeLists.txt) -replace ' SHARED', ' STATIC' ^| Out-File -encoding ASCII CMakeLists.txt >> patch.ps1
|
||||||
|
powershell .\patch.ps1
|
||||||
|
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
|
||||||
|
%CMAKE% -G "NMake Makefiles" .
|
||||||
|
nmake -nologo -f Makefile clean
|
||||||
|
nmake -nologo -f Makefile
|
||||||
|
copy /Y /B *.h %INCLIB%
|
||||||
|
copy /Y /B *.lib %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
# for Raqm
|
||||||
|
- name: Build dependencies / HarfBuzz
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
set INCLUDE=%INCLUDE%;%INCLIB%
|
||||||
|
set LIB=%LIB%;%INCLIB%
|
||||||
|
cd /D %BUILD%\harfbuzz-2.6.1
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DHB_HAVE_FREETYPE:BOOL=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
%CMAKE% -G "NMake Makefiles" .
|
||||||
|
nmake -nologo -f Makefile clean
|
||||||
|
nmake -nologo -f Makefile harfbuzz
|
||||||
|
copy /Y /B src\*.h %INCLIB%
|
||||||
|
copy /Y /B *.lib %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
# for Raqm
|
||||||
|
- name: Build dependencies / FriBidi
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
cd /D %BUILD%\fribidi-1.0.7
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
copy /Y /B %GITHUB_WORKSPACE%\winbuild\fribidi.cmake CMakeLists.txt
|
||||||
|
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
|
||||||
|
%CMAKE% -G "NMake Makefiles" .
|
||||||
|
nmake -nologo -f Makefile clean
|
||||||
|
nmake -nologo -f Makefile fribidi
|
||||||
|
copy /Y /B lib\*.h %INCLIB%
|
||||||
|
copy /Y /B *.lib %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build dependencies / Raqm
|
||||||
|
run: |
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
|
||||||
|
set INCLUDE=%INCLUDE%;%INCLIB%
|
||||||
|
set LIB=%LIB%;%INCLIB%
|
||||||
|
cd /D %BUILD%\libraqm-0.7.0
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
echo on
|
||||||
|
copy /Y /B %GITHUB_WORKSPACE%\winbuild\raqm.cmake CMakeLists.txt
|
||||||
|
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
|
||||||
|
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
|
||||||
|
%CMAKE% -G "NMake Makefiles" .
|
||||||
|
nmake -nologo -f Makefile clean
|
||||||
|
nmake -nologo -f Makefile libraqm
|
||||||
|
copy /Y /B src\*.h %INCLIB%
|
||||||
|
copy /Y /B libraqm.dll %INCLIB%
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Build Pillow
|
||||||
|
run: |
|
||||||
|
set PYTHON=%pythonLocation%
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set MPLSRC=%GITHUB_WORKSPACE%
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
cd /D %GITHUB_WORKSPACE%
|
||||||
|
set LIB=%INCLIB%;%PYTHON%\tcl
|
||||||
|
set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE%
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
set MSSdk=1
|
||||||
|
set DISTUTILS_USE_SDK=1
|
||||||
|
set py_vcruntime_redist=true
|
||||||
|
%PYTHON%\python.exe setup.py build_ext install
|
||||||
|
rem Add libraqm.dll (copied to INCLIB) to PATH.
|
||||||
|
path %INCLIB%;%PATH%
|
||||||
|
%PYTHON%\python.exe selftest.py --installed
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
# failing with PyPy3
|
||||||
|
- name: Enable heap verification
|
||||||
|
if: "!contains(matrix.python-version, 'pypy')"
|
||||||
|
run: |
|
||||||
|
c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\python.exe
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Test Pillow
|
||||||
|
run: |
|
||||||
|
set PYTHON=%pythonLocation%
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
rem Add libraqm.dll (copied to INCLIB) to PATH.
|
||||||
|
path %INCLIB%;%PATH%
|
||||||
|
cd /D %GITHUB_WORKSPACE%
|
||||||
|
%PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- name: Upload errors
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: errors
|
||||||
|
path: Tests/errors
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
if: success()
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
name: ${{ runner.os }} Python ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Build wheel
|
||||||
|
id: wheel
|
||||||
|
if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')"
|
||||||
|
run: |
|
||||||
|
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ##[set-output name=dist;]dist-%%a
|
||||||
|
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a
|
||||||
|
set PYTHON=%pythonLocation%
|
||||||
|
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
|
||||||
|
set MPLSRC=%GITHUB_WORKSPACE%
|
||||||
|
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
|
||||||
|
cd /D %GITHUB_WORKSPACE%
|
||||||
|
set LIB=%INCLIB%;%PYTHON%\tcl
|
||||||
|
set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE%
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
|
||||||
|
%PYTHON%\python.exe setup.py bdist_wheel
|
||||||
|
shell: cmd
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v1
|
||||||
|
if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')"
|
||||||
|
with:
|
||||||
|
name: ${{ steps.wheel.outputs.dist }}
|
||||||
|
path: dist
|
103
.github/workflows/test.yml
vendored
Normal file
103
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [
|
||||||
|
"ubuntu-latest",
|
||||||
|
"macOS-latest",
|
||||||
|
]
|
||||||
|
python-version: [
|
||||||
|
"pypy3",
|
||||||
|
"3.8",
|
||||||
|
"3.7",
|
||||||
|
"3.6",
|
||||||
|
"3.5",
|
||||||
|
]
|
||||||
|
include:
|
||||||
|
- python-version: "3.5"
|
||||||
|
env: PYTHONOPTIMIZE=2
|
||||||
|
- python-version: "3.6"
|
||||||
|
env: PYTHONOPTIMIZE=1
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Ubuntu cache
|
||||||
|
uses: actions/cache@v1
|
||||||
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key:
|
||||||
|
${{ matrix.os }}-${{ matrix.python-version }}-${{ hashFiles('**/.travis/*.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('**/.travis/*.sh') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ matrix.os }}-${{ matrix.python-version }}-
|
||||||
|
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
|
- name: Install Linux dependencies
|
||||||
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
|
run: |
|
||||||
|
.travis/install.sh
|
||||||
|
|
||||||
|
- name: Install macOS dependencies
|
||||||
|
if: startsWith(matrix.os, 'macOS')
|
||||||
|
run: |
|
||||||
|
.github/workflows/macos-install.sh
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
.travis/build.sh
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
.travis/test.sh
|
||||||
|
|
||||||
|
- name: Upload errors
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: errors
|
||||||
|
path: Tests/errors
|
||||||
|
|
||||||
|
- name: Docs
|
||||||
|
if: matrix.python-version == 3.8
|
||||||
|
run: |
|
||||||
|
pip install sphinx-rtd-theme
|
||||||
|
make doccheck
|
||||||
|
|
||||||
|
- name: After success
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
.travis/after_success.sh
|
||||||
|
env:
|
||||||
|
MATRIX_OS: ${{ matrix.os }}
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
if: success()
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
|
22
.travis.yml
22
.travis.yml
|
@ -6,8 +6,8 @@ notifications:
|
||||||
irc: "chat.freenode.net#pil"
|
irc: "chat.freenode.net#pil"
|
||||||
|
|
||||||
# Run fast lint first to get fast feedback.
|
# Run fast lint first to get fast feedback.
|
||||||
# Run slow PyPy* next, to give them a headstart and reduce waiting time.
|
# Run slow PyPy next, to give it a headstart and reduce waiting time.
|
||||||
# Run latest 3.x and 2.x next, to get quick compatibility results.
|
# Run latest 3.x next, to get quick compatibility results.
|
||||||
# Then run the remainder, with fastest Docker jobs last.
|
# Then run the remainder, with fastest Docker jobs last.
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -16,25 +16,22 @@ matrix:
|
||||||
- python: "3.6"
|
- python: "3.6"
|
||||||
name: "Lint"
|
name: "Lint"
|
||||||
env: LINT="true"
|
env: LINT="true"
|
||||||
- python: "pypy"
|
|
||||||
name: "PyPy2 Xenial"
|
|
||||||
- python: "pypy3"
|
- python: "pypy3"
|
||||||
name: "PyPy3 Xenial"
|
name: "PyPy3 Xenial"
|
||||||
|
- python: "3.8"
|
||||||
|
name: "3.8 Xenial"
|
||||||
|
services: xvfb
|
||||||
- python: '3.7'
|
- python: '3.7'
|
||||||
name: "3.7 Xenial"
|
name: "3.7 Xenial"
|
||||||
- python: '2.7'
|
|
||||||
name: "2.7 Xenial"
|
|
||||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
|
||||||
name: "2.7_with_system_site_packages Xenial"
|
|
||||||
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
|
||||||
- python: '3.5'
|
- python: '3.5'
|
||||||
name: "3.5 Xenial PYTHONOPTIMIZE=2"
|
name: "3.5 Xenial PYTHONOPTIMIZE=2"
|
||||||
env: PYTHONOPTIMIZE=2
|
env: PYTHONOPTIMIZE=2
|
||||||
- python: "3.8-dev"
|
services: xvfb
|
||||||
name: "3.8-dev Xenial"
|
|
||||||
- env: DOCKER="alpine" DOCKER_TAG="master"
|
- env: DOCKER="alpine" DOCKER_TAG="master"
|
||||||
- env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5
|
- env: DOCKER="arch" DOCKER_TAG="master" # contains PyQt5
|
||||||
- env: DOCKER="ubuntu-16.04-xenial-amd64" DOCKER_TAG="master"
|
- env: DOCKER="ubuntu-16.04-xenial-amd64" DOCKER_TAG="master"
|
||||||
|
@ -45,8 +42,8 @@ matrix:
|
||||||
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
|
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
|
||||||
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
|
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
|
||||||
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
|
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
|
||||||
- env: DOCKER="fedora-29-amd64" DOCKER_TAG="master"
|
|
||||||
- env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
|
- env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
|
||||||
|
- env: DOCKER="fedora-31-amd64" DOCKER_TAG="master"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
@ -67,7 +64,8 @@ script:
|
||||||
if [ "$LINT" == "true" ]; then
|
if [ "$LINT" == "true" ]; then
|
||||||
tox -e lint
|
tox -e lint
|
||||||
elif [ "$DOCKER" == "" ]; then
|
elif [ "$DOCKER" == "" ]; then
|
||||||
.travis/script.sh
|
.travis/build.sh
|
||||||
|
.travis/test.sh
|
||||||
elif [ "$DOCKER" ]; then
|
elif [ "$DOCKER" ]; then
|
||||||
# the Pillow user in the docker container is UID 1000
|
# the Pillow user in the docker container is UID 1000
|
||||||
sudo chown -R 1000 $TRAVIS_BUILD_DIR
|
sudo chown -R 1000 $TRAVIS_BUILD_DIR
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# gather the coverage data
|
# gather the coverage data
|
||||||
sudo apt-get -qq install lcov
|
if [[ "$MATRIX_OS" == "macOS-latest" ]]; then
|
||||||
|
brew install lcov
|
||||||
|
else
|
||||||
|
sudo apt-get -qq install lcov
|
||||||
|
fi
|
||||||
|
|
||||||
lcov --capture --directory . -b . --output-file coverage.info
|
lcov --capture --directory . -b . --output-file coverage.info
|
||||||
# filter to remove system headers
|
# filter to remove system headers
|
||||||
lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
|
lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
|
||||||
|
@ -11,16 +16,14 @@ coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
|
||||||
|
|
||||||
coverage report
|
coverage report
|
||||||
pip install codecov
|
pip install codecov
|
||||||
if [[ $TRAVIS_PYTHON_VERSION != "2.7_with_system_site_packages" ]]; then
|
pip install coveralls-merge
|
||||||
# Not working here. Just skip it, it's being removed soon.
|
coveralls-merge coverage.c.json
|
||||||
pip install coveralls-merge
|
if [[ $TRAVIS ]]; then
|
||||||
coveralls-merge coverage.c.json
|
codecov
|
||||||
fi
|
fi
|
||||||
codecov
|
|
||||||
|
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then
|
if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ] && [ "$DOCKER" == "" ]; then
|
||||||
# Coverage and quality reports on just the latest diff.
|
# Coverage and quality reports on just the latest diff.
|
||||||
# (Installation is very slow on Py3, so just do it for Py2.)
|
|
||||||
depends/diffcover-install.sh
|
depends/diffcover-install.sh
|
||||||
depends/diffcover-run.sh
|
depends/diffcover-run.sh
|
||||||
fi
|
fi
|
||||||
|
|
10
.travis/build.sh
Executable file
10
.travis/build.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
coverage erase
|
||||||
|
if [ $(uname) == "Darwin" ]; then
|
||||||
|
export CPPFLAGS="-I/usr/local/miniconda/include";
|
||||||
|
fi
|
||||||
|
make clean
|
||||||
|
make install-coverage
|
|
@ -3,7 +3,7 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk python-qt4\
|
sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk\
|
||||||
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
|
ghostscript libffi-dev libjpeg-turbo-progs libopenjp2-7-dev\
|
||||||
cmake imagemagick libharfbuzz-dev libfribidi-dev
|
cmake imagemagick libharfbuzz-dev libfribidi-dev
|
||||||
|
|
||||||
|
@ -15,9 +15,13 @@ 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
|
||||||
|
if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
|
||||||
|
sudo apt-get -qq install pyqt5-dev-tools
|
||||||
|
pip install pyqt5
|
||||||
|
fi
|
||||||
|
|
||||||
# docs only on Python 2.7
|
# docs only on Python 3.7
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi
|
if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then pip install -r requirements.txt ; fi
|
||||||
|
|
||||||
# webp
|
# webp
|
||||||
pushd depends && ./install_webp.sh && popd
|
pushd depends && ./install_webp.sh && popd
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
coverage erase
|
|
||||||
make clean
|
|
||||||
make install-coverage
|
|
||||||
|
|
||||||
python -m pytest -v -x --cov PIL --cov-report term Tests
|
|
||||||
|
|
||||||
# Docs
|
|
||||||
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make doccheck; fi
|
|
8
.travis/test.sh
Executable file
8
.travis/test.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
python -m pytest -v -x --cov PIL --cov Tests --cov-report term Tests
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then make doccheck; fi
|
62
CHANGES.rst
62
CHANGES.rst
|
@ -2,11 +2,71 @@
|
||||||
Changelog (Pillow)
|
Changelog (Pillow)
|
||||||
==================
|
==================
|
||||||
|
|
||||||
6.2.0 (2019-10-01)
|
7.0.0 (unreleased)
|
||||||
|
------------------
|
||||||
|
|
||||||
|
- Handle broken Photoshop data #4239
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Raise a specific exception if no data is found for an MPO frame #4240
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Fix Unicode support for PyPy #4145
|
||||||
|
[nulano]
|
||||||
|
|
||||||
|
- Drop support for EOL Python 2.7 #4109
|
||||||
|
[hugovk, radarhere, jdufresne]
|
||||||
|
|
||||||
|
- Added UnidentifiedImageError #4182
|
||||||
|
[radarhere, hugovk]
|
||||||
|
|
||||||
|
- Remove deprecated __version__ from plugins #4197
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
- Fixed freeing unallocated pointer when resizing with height too large #4116
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Copy info in Image.transform #4128
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Corrected DdsImagePlugin setting info gamma #4171
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Depends: Update libtiff to 4.1.0 #4195, Tk Tcl to 8.6.10 #4229
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Improve handling of file resources #3577
|
||||||
|
[jdufresne]
|
||||||
|
|
||||||
|
- Removed CI testing of Fedora 29 #4165
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Added pypy3 to tox envlist #4137
|
||||||
|
[jdufresne]
|
||||||
|
|
||||||
|
- Drop support for EOL PyQt4 and PySide #4108
|
||||||
|
[hugovk, radarhere]
|
||||||
|
|
||||||
|
- Removed deprecated setting of TIFF image sizes #4114
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Removed deprecated PILLOW_VERSION #4107
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Changed default frombuffer raw decoder args #1730
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
6.2.1 (2019-10-21)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
- This is the last Pillow release to support Python 2.7 #3642
|
- This is the last Pillow release to support Python 2.7 #3642
|
||||||
|
|
||||||
|
- Add support for Python 3.8 #4141
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
6.2.0 (2019-10-01)
|
||||||
|
------------------
|
||||||
|
|
||||||
- Catch buffer overruns #4104
|
- Catch buffer overruns #4104
|
||||||
[radarhere]
|
[radarhere]
|
||||||
|
|
||||||
|
|
36
Makefile
36
Makefile
|
@ -3,7 +3,7 @@
|
||||||
.DEFAULT_GOAL := release-test
|
.DEFAULT_GOAL := release-test
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
python setup.py clean
|
python3 setup.py clean
|
||||||
rm src/PIL/*.so || true
|
rm src/PIL/*.so || true
|
||||||
rm -r build || true
|
rm -r build || true
|
||||||
find . -name __pycache__ | xargs rm -r || true
|
find . -name __pycache__ | xargs rm -r || true
|
||||||
|
@ -15,8 +15,8 @@ co:
|
||||||
done
|
done
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
python selftest.py
|
python3 selftest.py
|
||||||
python setup.py test
|
python3 setup.py test
|
||||||
rm -r htmlcov || true
|
rm -r htmlcov || true
|
||||||
coverage report
|
coverage report
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ doccheck:
|
||||||
$(MAKE) -C docs linkcheck || true
|
$(MAKE) -C docs linkcheck || true
|
||||||
|
|
||||||
docserve:
|
docserve:
|
||||||
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null&
|
cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
|
||||||
|
|
||||||
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"
|
||||||
|
@ -50,22 +50,22 @@ help:
|
||||||
@echo " upload-test build and upload sdists to test.pythonpackages.com"
|
@echo " upload-test build and upload sdists to test.pythonpackages.com"
|
||||||
|
|
||||||
inplace: clean
|
inplace: clean
|
||||||
python setup.py develop build_ext --inplace
|
python3 setup.py develop build_ext --inplace
|
||||||
|
|
||||||
install:
|
install:
|
||||||
python setup.py install
|
python3 setup.py install
|
||||||
python selftest.py
|
python3 selftest.py
|
||||||
|
|
||||||
install-coverage:
|
install-coverage:
|
||||||
CFLAGS="-coverage" python setup.py build_ext install
|
CFLAGS="-coverage" python3 setup.py build_ext install
|
||||||
python selftest.py
|
python3 selftest.py
|
||||||
|
|
||||||
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
|
||||||
# see any build failures.
|
# see any build failures.
|
||||||
make clean > /dev/null
|
make clean > /dev/null
|
||||||
CFLAGS='-g -O0' python setup.py build_ext install > /dev/null
|
CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null
|
||||||
|
|
||||||
install-req:
|
install-req:
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
@ -76,17 +76,17 @@ install-venv:
|
||||||
|
|
||||||
release-test:
|
release-test:
|
||||||
$(MAKE) install-req
|
$(MAKE) install-req
|
||||||
python setup.py develop
|
python3 setup.py develop
|
||||||
python selftest.py
|
python3 selftest.py
|
||||||
python -m pytest Tests
|
python3 -m pytest Tests
|
||||||
python setup.py install
|
python3 setup.py install
|
||||||
python -m pytest -qq
|
python3 -m pytest -qq
|
||||||
check-manifest
|
check-manifest
|
||||||
pyroma .
|
pyroma .
|
||||||
viewdoc
|
viewdoc
|
||||||
|
|
||||||
sdist:
|
sdist:
|
||||||
python setup.py sdist --format=gztar
|
python3 setup.py sdist --format=gztar
|
||||||
|
|
||||||
test:
|
test:
|
||||||
pytest -qq
|
pytest -qq
|
||||||
|
@ -97,10 +97,10 @@ upload-test:
|
||||||
# username:
|
# username:
|
||||||
# password:
|
# password:
|
||||||
# repository = http://test.pythonpackages.com
|
# repository = http://test.pythonpackages.com
|
||||||
python setup.py sdist --format=gztar upload -r test
|
python3 setup.py sdist --format=gztar upload -r test
|
||||||
|
|
||||||
upload:
|
upload:
|
||||||
python setup.py sdist --format=gztar upload
|
python3 setup.py sdist --format=gztar upload
|
||||||
|
|
||||||
readme:
|
readme:
|
||||||
viewdoc
|
viewdoc
|
||||||
|
|
14
README.rst
14
README.rst
|
@ -14,7 +14,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
||||||
* - docs
|
* - docs
|
||||||
- |docs|
|
- |docs|
|
||||||
* - tests
|
* - tests
|
||||||
- |linux| |macos| |windows| |coverage|
|
- |linux| |macos| |windows| |gha_lint| |gha| |gha_windows| |gha_docker| |coverage|
|
||||||
* - package
|
* - package
|
||||||
- |zenodo| |tidelift| |version| |downloads|
|
- |zenodo| |tidelift| |version| |downloads|
|
||||||
* - social
|
* - social
|
||||||
|
@ -60,6 +60,18 @@ To report a security vulnerability, please follow the procedure described in the
|
||||||
:target: https://ci.appveyor.com/project/python-pillow/Pillow
|
:target: https://ci.appveyor.com/project/python-pillow/Pillow
|
||||||
:alt: AppVeyor CI build status (Windows)
|
:alt: AppVeyor CI build status (Windows)
|
||||||
|
|
||||||
|
.. |gha_lint| image:: https://github.com/python-pillow/Pillow/workflows/Lint/badge.svg
|
||||||
|
:alt: GitHub Actions build status (Lint)
|
||||||
|
|
||||||
|
.. |gha_docker| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Docker/badge.svg
|
||||||
|
:alt: GitHub Actions build status (Test Docker)
|
||||||
|
|
||||||
|
.. |gha| image:: https://github.com/python-pillow/Pillow/workflows/Test/badge.svg
|
||||||
|
:alt: GitHub Actions build status (Test Linux and macOS)
|
||||||
|
|
||||||
|
.. |gha_windows| image:: https://github.com/python-pillow/Pillow/workflows/Test%20Windows/badge.svg
|
||||||
|
:alt: GitHub Actions build status (Test Windows)
|
||||||
|
|
||||||
.. |coverage| image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
|
.. |coverage| image:: https://codecov.io/gh/python-pillow/Pillow/branch/master/graph/badge.svg
|
||||||
:target: https://codecov.io/gh/python-pillow/Pillow
|
:target: https://codecov.io/gh/python-pillow/Pillow
|
||||||
:alt: Code coverage
|
:alt: Code coverage
|
||||||
|
|
|
@ -51,6 +51,7 @@ Released as needed for security, installation or critical bug fixes.
|
||||||
make sdist
|
make sdist
|
||||||
```
|
```
|
||||||
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
* [ ] Create [binary distributions](https://github.com/python-pillow/Pillow/blob/master/RELEASING.md#binary-distributions)
|
||||||
|
* [ ] Upload all binaries and source distributions e.g. `twine upload dist/Pillow-5.2.1*`
|
||||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
||||||
|
|
||||||
## Embargoed Release
|
## Embargoed Release
|
||||||
|
@ -92,7 +93,7 @@ Released as needed privately to individual vendors for critical security-related
|
||||||
```
|
```
|
||||||
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
||||||
```bash
|
```bash
|
||||||
wget -m -A 'Pillow-<VERSION>*' \
|
wget -m -A 'Pillow-<VERSION>-*' \
|
||||||
http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com
|
http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,6 @@ Run all the tests from the root of the Pillow source distribution::
|
||||||
|
|
||||||
Or with coverage::
|
Or with coverage::
|
||||||
|
|
||||||
pytest --cov PIL --cov-report term
|
pytest --cov PIL --cov Tests --cov-report term
|
||||||
coverage html
|
coverage html
|
||||||
open htmlcov/index.html
|
open htmlcov/index.html
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import time
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import PyAccess
|
from PIL import PyAccess
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
# Not running this test by default. No DOS against Travis CI.
|
# Not running this test by default. No DOS against Travis CI.
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import sys
|
|
||||||
import timeit
|
|
||||||
|
|
||||||
from . import helper
|
|
||||||
|
|
||||||
sys.path.insert(0, ".")
|
|
||||||
|
|
||||||
|
|
||||||
def bench(mode):
|
|
||||||
im = helper.hopper(mode)
|
|
||||||
get = im.im.getpixel
|
|
||||||
xy = 50, 50 # position shouldn't really matter
|
|
||||||
t0 = timeit.default_timer()
|
|
||||||
for _ in range(1000000):
|
|
||||||
get(xy)
|
|
||||||
print(mode, timeit.default_timer() - t0, "us")
|
|
||||||
|
|
||||||
|
|
||||||
bench("L")
|
|
||||||
bench("I")
|
|
||||||
bench("I;16")
|
|
||||||
bench("F")
|
|
||||||
bench("RGB")
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/fli_overflow.fli"
|
TEST_FILE = "Tests/images/fli_overflow.fli"
|
||||||
|
|
||||||
|
@ -9,8 +11,8 @@ class TestFliOverflow(PillowTestCase):
|
||||||
def test_fli_overflow(self):
|
def test_fli_overflow(self):
|
||||||
|
|
||||||
# this should not crash with a malloc error or access violation
|
# this should not crash with a malloc error or access violation
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -4,9 +4,5 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL._util import py3
|
|
||||||
|
|
||||||
if py3:
|
Image.open(BytesIO(b"icns\x00\x00\x00\x10hang\x00\x00\x00\x00"))
|
||||||
Image.open(BytesIO(bytes("icns\x00\x00\x00\x10hang\x00\x00\x00\x00", "latin-1")))
|
|
||||||
else:
|
|
||||||
Image.open(BytesIO(bytes("icns\x00\x00\x00\x10hang\x00\x00\x00\x00")))
|
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
import unittest
|
||||||
from __future__ import division
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase, is_win32
|
||||||
|
|
||||||
min_iterations = 100
|
min_iterations = 100
|
||||||
max_iterations = 10000
|
max_iterations = 10000
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
|
@unittest.skipIf(is_win32(), "requires Unix or macOS")
|
||||||
class TestImagingLeaks(PillowTestCase):
|
class TestImagingLeaks(PillowTestCase):
|
||||||
def _get_mem_usage(self):
|
def _get_mem_usage(self):
|
||||||
from resource import getpagesize, getrusage, RUSAGE_SELF
|
from resource import getpagesize, getrusage, RUSAGE_SELF
|
||||||
|
|
|
@ -4,19 +4,5 @@
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL._util import py3
|
|
||||||
|
|
||||||
if py3:
|
Image.open(BytesIO(b"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
|
||||||
Image.open(
|
|
||||||
BytesIO(
|
|
||||||
bytes(
|
|
||||||
"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang",
|
|
||||||
"latin-1",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
|
||||||
Image.open(
|
|
||||||
BytesIO(bytes("\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import sys
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase, is_win32
|
||||||
|
|
||||||
# Limits for testing the leak
|
# Limits for testing the leak
|
||||||
mem_limit = 1024 * 1048576
|
mem_limit = 1024 * 1048576
|
||||||
|
@ -13,7 +13,7 @@ codecs = dir(Image.core)
|
||||||
test_file = "Tests/images/rgb_trns_ycbc.jp2"
|
test_file = "Tests/images/rgb_trns_ycbc.jp2"
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
|
@unittest.skipIf(is_win32(), "requires Unix or macOS")
|
||||||
class TestJpegLeaks(PillowTestCase):
|
class TestJpegLeaks(PillowTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs:
|
if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs:
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestJ2kEncodeOverflow(PillowTestCase):
|
class TestJ2kEncodeOverflow(PillowTestCase):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import sys
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper, is_win32
|
||||||
|
|
||||||
iterations = 5000
|
iterations = 5000
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ valgrind --tool=massif python test-installed.py -s -v Tests/check_jpeg_leaks.py
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
|
@unittest.skipIf(is_win32(), "requires Unix or macOS")
|
||||||
class TestJpegLeaks(PillowTestCase):
|
class TestJpegLeaks(PillowTestCase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
# This test is not run automatically.
|
# This test is not run automatically.
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
# This test is not run automatically.
|
# This test is not run automatically.
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
TEST_FILE = "Tests/images/libtiff_segfault.tif"
|
||||||
|
|
||||||
|
@ -12,8 +14,8 @@ class TestLibtiffSegfault(PillowTestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
import unittest
|
||||||
import zlib
|
import zlib
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PngImagePlugin
|
from PIL import Image, ImageFile, PngImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/png_decompression_dos.png"
|
TEST_FILE = "Tests/images/png_decompression_dos.png"
|
||||||
|
|
||||||
|
|
11
Tests/conftest.py
Normal file
11
Tests/conftest.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
def pytest_report_header(config):
|
||||||
|
import io
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import features
|
||||||
|
|
||||||
|
with io.StringIO() as out:
|
||||||
|
features.pilinfo(out=out, supported_formats=False)
|
||||||
|
return out.getvalue()
|
||||||
|
except Exception as e:
|
||||||
|
return "pytest_report_header failed: %s" % str(e)
|
|
@ -1,6 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
129
Tests/helper.py
129
Tests/helper.py
|
@ -1,16 +1,16 @@
|
||||||
"""
|
"""
|
||||||
Helper functions.
|
Helper functions.
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageMath
|
from PIL import Image, ImageMath
|
||||||
from PIL._util import py3
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -22,12 +22,26 @@ if os.environ.get("SHOW_ERRORS", None):
|
||||||
HAS_UPLOADER = True
|
HAS_UPLOADER = True
|
||||||
|
|
||||||
class test_image_results:
|
class test_image_results:
|
||||||
@classmethod
|
@staticmethod
|
||||||
def upload(self, a, b):
|
def upload(a, b):
|
||||||
a.show()
|
a.show()
|
||||||
b.show()
|
b.show()
|
||||||
|
|
||||||
|
|
||||||
|
elif "GITHUB_ACTIONS" in os.environ:
|
||||||
|
HAS_UPLOADER = True
|
||||||
|
|
||||||
|
class test_image_results:
|
||||||
|
@staticmethod
|
||||||
|
def upload(a, b):
|
||||||
|
dir_errors = os.path.join(os.path.dirname(__file__), "errors")
|
||||||
|
os.makedirs(dir_errors, exist_ok=True)
|
||||||
|
tmpdir = tempfile.mkdtemp(dir=dir_errors)
|
||||||
|
a.save(os.path.join(tmpdir, "a.png"))
|
||||||
|
b.save(os.path.join(tmpdir, "b.png"))
|
||||||
|
return tmpdir
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
import test_image_results
|
import test_image_results
|
||||||
|
@ -51,37 +65,22 @@ def convert_to_comparable(a, b):
|
||||||
|
|
||||||
|
|
||||||
class PillowTestCase(unittest.TestCase):
|
class PillowTestCase(unittest.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
unittest.TestCase.__init__(self, *args, **kwargs)
|
|
||||||
# holds last result object passed to run method:
|
|
||||||
self.currentResult = None
|
|
||||||
|
|
||||||
def run(self, result=None):
|
|
||||||
self.currentResult = result # remember result for use later
|
|
||||||
unittest.TestCase.run(self, result) # call superclass run method
|
|
||||||
|
|
||||||
def delete_tempfile(self, path):
|
def delete_tempfile(self, path):
|
||||||
try:
|
try:
|
||||||
ok = self.currentResult.wasSuccessful()
|
os.remove(path)
|
||||||
except AttributeError: # for pytest
|
except OSError:
|
||||||
ok = True
|
pass # report?
|
||||||
|
|
||||||
if ok:
|
|
||||||
# only clean out tempfiles if test passed
|
|
||||||
try:
|
|
||||||
os.remove(path)
|
|
||||||
except OSError:
|
|
||||||
pass # report?
|
|
||||||
else:
|
|
||||||
print("=== orphaned temp file: %s" % path)
|
|
||||||
|
|
||||||
def assert_deep_equal(self, a, b, msg=None):
|
def assert_deep_equal(self, a, b, msg=None):
|
||||||
try:
|
try:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(a), len(b), msg or "got length %s, expected %s" % (len(a), len(b))
|
len(a),
|
||||||
|
len(b),
|
||||||
|
msg or "got length {}, expected {}".format(len(a), len(b)),
|
||||||
)
|
)
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
all(x == y for x, y in zip(a, b)), msg or "got %s, expected %s" % (a, b)
|
all(x == y for x, y in zip(a, b)),
|
||||||
|
msg or "got {}, expected {}".format(a, b),
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.assertEqual(a, b, msg)
|
self.assertEqual(a, b, msg)
|
||||||
|
@ -89,20 +88,24 @@ class PillowTestCase(unittest.TestCase):
|
||||||
def assert_image(self, im, mode, size, msg=None):
|
def assert_image(self, im, mode, size, msg=None):
|
||||||
if mode is not None:
|
if mode is not None:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.mode, mode, msg or "got mode %r, expected %r" % (im.mode, mode)
|
im.mode,
|
||||||
|
mode,
|
||||||
|
msg or "got mode {!r}, expected {!r}".format(im.mode, mode),
|
||||||
)
|
)
|
||||||
|
|
||||||
if size is not None:
|
if size is not None:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.size, size, msg or "got size %r, expected %r" % (im.size, size)
|
im.size,
|
||||||
|
size,
|
||||||
|
msg or "got size {!r}, expected {!r}".format(im.size, size),
|
||||||
)
|
)
|
||||||
|
|
||||||
def assert_image_equal(self, a, b, msg=None):
|
def assert_image_equal(self, a, b, msg=None):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode)
|
a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode)
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size)
|
a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size)
|
||||||
)
|
)
|
||||||
if a.tobytes() != b.tobytes():
|
if a.tobytes() != b.tobytes():
|
||||||
if HAS_UPLOADER:
|
if HAS_UPLOADER:
|
||||||
|
@ -123,10 +126,10 @@ class PillowTestCase(unittest.TestCase):
|
||||||
def assert_image_similar(self, a, b, epsilon, msg=None):
|
def assert_image_similar(self, a, b, epsilon, msg=None):
|
||||||
epsilon = float(epsilon)
|
epsilon = float(epsilon)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode)
|
a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode)
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size)
|
a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size)
|
||||||
)
|
)
|
||||||
|
|
||||||
a, b = convert_to_comparable(a, b)
|
a, b = convert_to_comparable(a, b)
|
||||||
|
@ -200,24 +203,13 @@ class PillowTestCase(unittest.TestCase):
|
||||||
|
|
||||||
self.assertTrue(value, msg + ": " + repr(actuals) + " != " + repr(targets))
|
self.assertTrue(value, msg + ": " + repr(actuals) + " != " + repr(targets))
|
||||||
|
|
||||||
def skipKnownBadTest(self, msg=None, platform=None, travis=None, interpreter=None):
|
def skipKnownBadTest(self, msg=None):
|
||||||
# Skip if platform/travis matches, and
|
# Skip if PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
||||||
# PILLOW_RUN_KNOWN_BAD is not true in the environment.
|
|
||||||
if os.environ.get("PILLOW_RUN_KNOWN_BAD", False):
|
if os.environ.get("PILLOW_RUN_KNOWN_BAD", False):
|
||||||
print(os.environ.get("PILLOW_RUN_KNOWN_BAD", False))
|
print(os.environ.get("PILLOW_RUN_KNOWN_BAD", False))
|
||||||
return
|
return
|
||||||
|
|
||||||
skip = True
|
self.skipTest(msg or "Known Bad Test")
|
||||||
if platform is not None:
|
|
||||||
skip = sys.platform.startswith(platform)
|
|
||||||
if travis is not None:
|
|
||||||
skip = skip and (travis == bool(os.environ.get("TRAVIS", False)))
|
|
||||||
if interpreter is not None:
|
|
||||||
skip = skip and (
|
|
||||||
interpreter == "pypy" and hasattr(sys, "pypy_version_info")
|
|
||||||
)
|
|
||||||
if skip:
|
|
||||||
self.skipTest(msg or "Known Bad Test")
|
|
||||||
|
|
||||||
def tempfile(self, template):
|
def tempfile(self, template):
|
||||||
assert template[:5] in ("temp.", "temp_")
|
assert template[:5] in ("temp.", "temp_")
|
||||||
|
@ -229,12 +221,12 @@ class PillowTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def open_withImagemagick(self, f):
|
def open_withImagemagick(self, f):
|
||||||
if not imagemagick_available():
|
if not imagemagick_available():
|
||||||
raise IOError()
|
raise OSError()
|
||||||
|
|
||||||
outfile = self.tempfile("temp.png")
|
outfile = self.tempfile("temp.png")
|
||||||
if command_succeeds([IMCONVERT, f, outfile]):
|
if command_succeeds([IMCONVERT, f, outfile]):
|
||||||
return Image.open(outfile)
|
return Image.open(outfile)
|
||||||
raise IOError()
|
raise OSError()
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
|
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
|
||||||
|
@ -277,21 +269,12 @@ class PillowLeakTestCase(PillowTestCase):
|
||||||
|
|
||||||
# helpers
|
# helpers
|
||||||
|
|
||||||
if not py3:
|
|
||||||
# Remove DeprecationWarning in Python 3
|
|
||||||
PillowTestCase.assertRaisesRegex = PillowTestCase.assertRaisesRegexp
|
|
||||||
PillowTestCase.assertRegex = PillowTestCase.assertRegexpMatches
|
|
||||||
|
|
||||||
|
|
||||||
def fromstring(data):
|
def fromstring(data):
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
return Image.open(BytesIO(data))
|
return Image.open(BytesIO(data))
|
||||||
|
|
||||||
|
|
||||||
def tostring(im, string_format, **options):
|
def tostring(im, string_format, **options):
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
im.save(out, string_format, **options)
|
im.save(out, string_format, **options)
|
||||||
return out.getvalue()
|
return out.getvalue()
|
||||||
|
@ -323,13 +306,10 @@ def command_succeeds(cmd):
|
||||||
Runs the command, which must be a list of strings. Returns True if the
|
Runs the command, which must be a list of strings. Returns True if the
|
||||||
command succeeds, or False if an OSError was raised by subprocess.Popen.
|
command succeeds, or False if an OSError was raised by subprocess.Popen.
|
||||||
"""
|
"""
|
||||||
import subprocess
|
try:
|
||||||
|
subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||||
with open(os.devnull, "wb") as f:
|
except OSError:
|
||||||
try:
|
return False
|
||||||
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT)
|
|
||||||
except OSError:
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -355,10 +335,25 @@ def on_appveyor():
|
||||||
return "APPVEYOR" in os.environ
|
return "APPVEYOR" in os.environ
|
||||||
|
|
||||||
|
|
||||||
|
def on_github_actions():
|
||||||
|
return "GITHUB_ACTIONS" in os.environ
|
||||||
|
|
||||||
|
|
||||||
def on_ci():
|
def on_ci():
|
||||||
# Travis and AppVeyor have "CI"
|
# Travis and AppVeyor have "CI"
|
||||||
# Azure Pipelines has "TF_BUILD"
|
# Azure Pipelines has "TF_BUILD"
|
||||||
return "CI" in os.environ or "TF_BUILD" in os.environ
|
# GitHub Actions has "GITHUB_ACTIONS"
|
||||||
|
return (
|
||||||
|
"CI" in os.environ or "TF_BUILD" in os.environ or "GITHUB_ACTIONS" in os.environ
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_win32():
|
||||||
|
return sys.platform.startswith("win32")
|
||||||
|
|
||||||
|
|
||||||
|
def is_pypy():
|
||||||
|
return hasattr(sys, "pypy_translation_info")
|
||||||
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
|
@ -377,7 +372,7 @@ def distro():
|
||||||
return line.strip().split("=")[1]
|
return line.strip().split("=")[1]
|
||||||
|
|
||||||
|
|
||||||
class cached_property(object):
|
class cached_property:
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
self.func = func
|
self.func = func
|
||||||
|
|
||||||
|
|
BIN
Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds
Normal file
BIN
Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds
Normal file
Binary file not shown.
BIN
Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.png
Normal file
BIN
Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 106 B |
BIN
Tests/images/photoshop-200dpi-broken.jpg
Normal file
BIN
Tests/images/photoshop-200dpi-broken.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
Tests/images/sugarshack_no_data.mpo
Normal file
BIN
Tests/images/sugarshack_no_data.mpo
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
|
@ -1,16 +0,0 @@
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
sys.path.insert(0, ".")
|
|
||||||
|
|
||||||
for file in glob.glob("src/PIL/*.py"):
|
|
||||||
module = os.path.basename(file)[:-3]
|
|
||||||
try:
|
|
||||||
exec("from PIL import " + module)
|
|
||||||
except (ImportError, SyntaxError):
|
|
||||||
print("===", "failed to import", module)
|
|
||||||
traceback.print_exc()
|
|
|
@ -1,68 +0,0 @@
|
||||||
# brute-force search for access descriptor hash table
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
modes = [
|
|
||||||
"1",
|
|
||||||
"L",
|
|
||||||
"LA",
|
|
||||||
"La",
|
|
||||||
"I",
|
|
||||||
"I;16",
|
|
||||||
"I;16L",
|
|
||||||
"I;16B",
|
|
||||||
"I;32L",
|
|
||||||
"I;32B",
|
|
||||||
"F",
|
|
||||||
"P",
|
|
||||||
"PA",
|
|
||||||
"RGB",
|
|
||||||
"RGBA",
|
|
||||||
"RGBa",
|
|
||||||
"RGBX",
|
|
||||||
"CMYK",
|
|
||||||
"YCbCr",
|
|
||||||
"LAB",
|
|
||||||
"HSV",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def hash(s, i):
|
|
||||||
# djb2 hash: multiply by 33 and xor character
|
|
||||||
for c in s:
|
|
||||||
i = (((i << 5) + i) ^ ord(c)) & 0xFFFFFFFF
|
|
||||||
return i
|
|
||||||
|
|
||||||
|
|
||||||
def check(size, i0):
|
|
||||||
h = [None] * size
|
|
||||||
for m in modes:
|
|
||||||
i = hash(m, i0)
|
|
||||||
i = i % size
|
|
||||||
if h[i]:
|
|
||||||
return 0
|
|
||||||
h[i] = m
|
|
||||||
return h
|
|
||||||
|
|
||||||
|
|
||||||
min_start = 0
|
|
||||||
|
|
||||||
# 1) find the smallest table size with no collisions
|
|
||||||
for min_size in range(len(modes), 16384):
|
|
||||||
if check(min_size, 0):
|
|
||||||
print(len(modes), "modes fit in", min_size, "slots")
|
|
||||||
break
|
|
||||||
|
|
||||||
# 2) see if we can do better with a different initial value
|
|
||||||
for i0 in range(65556):
|
|
||||||
for size in range(1, min_size):
|
|
||||||
if check(size, i0):
|
|
||||||
if size < min_size:
|
|
||||||
print(len(modes), "modes fit in", size, "slots with start", i0)
|
|
||||||
min_size = size
|
|
||||||
min_start = i0
|
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
print("#define ACCESS_TABLE_SIZE", min_size)
|
|
||||||
print("#define ACCESS_TABLE_HASH", min_start)
|
|
|
@ -1,5 +1,3 @@
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
@ -24,8 +22,8 @@ class TestBmpReference(PillowTestCase):
|
||||||
|
|
||||||
def open(f):
|
def open(f):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -48,8 +46,8 @@ class TestBmpReference(PillowTestCase):
|
||||||
]
|
]
|
||||||
for f in self.get_files("q"):
|
for f in self.get_files("q"):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
if os.path.basename(f) not in supported:
|
if os.path.basename(f) not in supported:
|
||||||
print("Please add %s to the partially supported bmp specs." % f)
|
print("Please add %s to the partially supported bmp specs." % f)
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
|
@ -89,17 +87,17 @@ class TestBmpReference(PillowTestCase):
|
||||||
|
|
||||||
for f in self.get_files("g"):
|
for f in self.get_files("g"):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
compare = Image.open(get_compare(f))
|
with Image.open(get_compare(f)) as compare:
|
||||||
compare.load()
|
compare.load()
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
# assert image similar doesn't really work
|
# assert image similar doesn't really work
|
||||||
# with paletized image, since the palette might
|
# with paletized image, since the palette might
|
||||||
# be differently ordered for an equivalent image.
|
# be differently ordered for an equivalent image.
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
compare = im.convert("RGBA")
|
compare = im.convert("RGBA")
|
||||||
self.assert_image_similar(im, compare, 5)
|
self.assert_image_similar(im, compare, 5)
|
||||||
|
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
# there are three here that are unsupported:
|
# there are three here that are unsupported:
|
||||||
|
@ -109,4 +107,4 @@ class TestBmpReference(PillowTestCase):
|
||||||
os.path.join(base, "g", "pal4rle.bmp"),
|
os.path.join(base, "g", "pal4rle.bmp"),
|
||||||
)
|
)
|
||||||
if f not in unsupported:
|
if f not in unsupported:
|
||||||
self.fail("Unsupported Image %s: %s" % (f, msg))
|
self.fail("Unsupported Image {}: {}".format(f, msg))
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from __future__ import division
|
import unittest
|
||||||
|
|
||||||
from array import array
|
from array import array
|
||||||
|
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import numpy
|
import numpy
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
from __future__ import division, print_function
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
is_pypy = hasattr(sys, "pypy_version_info")
|
|
||||||
|
|
||||||
|
|
||||||
class TestCoreStats(PillowTestCase):
|
class TestCoreStats(PillowTestCase):
|
||||||
|
@ -87,7 +84,7 @@ class TestCoreMemory(PillowTestCase):
|
||||||
stats = Image.core.get_stats()
|
stats = Image.core.get_stats()
|
||||||
self.assertGreaterEqual(stats["new_count"], 1)
|
self.assertGreaterEqual(stats["new_count"], 1)
|
||||||
self.assertGreaterEqual(stats["allocated_blocks"], 64)
|
self.assertGreaterEqual(stats["allocated_blocks"], 64)
|
||||||
if not is_pypy:
|
if not is_pypy():
|
||||||
self.assertGreaterEqual(stats["freed_blocks"], 64)
|
self.assertGreaterEqual(stats["freed_blocks"], 64)
|
||||||
|
|
||||||
def test_get_blocks_max(self):
|
def test_get_blocks_max(self):
|
||||||
|
@ -108,7 +105,7 @@ class TestCoreMemory(PillowTestCase):
|
||||||
if sys.maxsize < 2 ** 32:
|
if sys.maxsize < 2 ** 32:
|
||||||
self.assertRaises(ValueError, Image.core.set_blocks_max, 2 ** 29)
|
self.assertRaises(ValueError, Image.core.set_blocks_max, 2 ** 29)
|
||||||
|
|
||||||
@unittest.skipIf(is_pypy, "images are not collected")
|
@unittest.skipIf(is_pypy(), "images are not collected")
|
||||||
def test_set_blocks_max_stats(self):
|
def test_set_blocks_max_stats(self):
|
||||||
Image.core.reset_stats()
|
Image.core.reset_stats()
|
||||||
Image.core.set_blocks_max(128)
|
Image.core.set_blocks_max(128)
|
||||||
|
@ -123,7 +120,7 @@ class TestCoreMemory(PillowTestCase):
|
||||||
self.assertEqual(stats["freed_blocks"], 0)
|
self.assertEqual(stats["freed_blocks"], 0)
|
||||||
self.assertEqual(stats["blocks_cached"], 64)
|
self.assertEqual(stats["blocks_cached"], 64)
|
||||||
|
|
||||||
@unittest.skipIf(is_pypy, "images are not collected")
|
@unittest.skipIf(is_pypy(), "images are not collected")
|
||||||
def test_clear_cache_stats(self):
|
def test_clear_cache_stats(self):
|
||||||
Image.core.reset_stats()
|
Image.core.reset_stats()
|
||||||
Image.core.clear_cache()
|
Image.core.clear_cache()
|
||||||
|
@ -153,7 +150,7 @@ class TestCoreMemory(PillowTestCase):
|
||||||
self.assertGreaterEqual(stats["allocated_blocks"], 16)
|
self.assertGreaterEqual(stats["allocated_blocks"], 16)
|
||||||
self.assertGreaterEqual(stats["reused_blocks"], 0)
|
self.assertGreaterEqual(stats["reused_blocks"], 0)
|
||||||
self.assertEqual(stats["blocks_cached"], 0)
|
self.assertEqual(stats["blocks_cached"], 0)
|
||||||
if not is_pypy:
|
if not is_pypy():
|
||||||
self.assertGreaterEqual(stats["freed_blocks"], 16)
|
self.assertGreaterEqual(stats["freed_blocks"], 16)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
def test_no_warning_small_file(self):
|
def test_no_warning_small_file(self):
|
||||||
# Implicit assert: no warning.
|
# Implicit assert: no warning.
|
||||||
# A warning would cause a failure.
|
# A warning would cause a failure.
|
||||||
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
|
with Image.open(TEST_FILE):
|
||||||
Image.open(TEST_FILE)
|
pass
|
||||||
|
|
||||||
def test_no_warning_no_limit(self):
|
def test_no_warning_no_limit(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -26,21 +26,28 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# Implicit assert: no warning.
|
# Implicit assert: no warning.
|
||||||
# A warning would cause a failure.
|
# A warning would cause a failure.
|
||||||
Image.open(TEST_FILE)
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_warning(self):
|
def test_warning(self):
|
||||||
# Set limit to trigger warning on the test file
|
# Set limit to trigger warning on the test file
|
||||||
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
|
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
|
||||||
self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1)
|
self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1)
|
||||||
|
|
||||||
self.assert_warning(Image.DecompressionBombWarning, Image.open, TEST_FILE)
|
def open():
|
||||||
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assert_warning(Image.DecompressionBombWarning, open)
|
||||||
|
|
||||||
def test_exception(self):
|
def test_exception(self):
|
||||||
# Set limit to trigger exception on the test file
|
# Set limit to trigger exception on the test file
|
||||||
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
|
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
|
||||||
self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1)
|
self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1)
|
||||||
|
|
||||||
self.assertRaises(Image.DecompressionBombError, lambda: Image.open(TEST_FILE))
|
with self.assertRaises(Image.DecompressionBombError):
|
||||||
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_exception_ico(self):
|
def test_exception_ico(self):
|
||||||
with self.assertRaises(Image.DecompressionBombError):
|
with self.assertRaises(Image.DecompressionBombError):
|
||||||
|
@ -54,6 +61,7 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
class TestDecompressionCrop(PillowTestCase):
|
class TestDecompressionCrop(PillowTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.src = hopper()
|
self.src = hopper()
|
||||||
|
self.addCleanup(self.src.close)
|
||||||
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
|
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import features
|
from PIL import features
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -70,11 +69,14 @@ class TestFeatures(PillowTestCase):
|
||||||
lines = out.splitlines()
|
lines = out.splitlines()
|
||||||
self.assertEqual(lines[0], "-" * 68)
|
self.assertEqual(lines[0], "-" * 68)
|
||||||
self.assertTrue(lines[1].startswith("Pillow "))
|
self.assertTrue(lines[1].startswith("Pillow "))
|
||||||
self.assertEqual(lines[2], "-" * 68)
|
self.assertTrue(lines[2].startswith("Python "))
|
||||||
self.assertTrue(lines[3].startswith("Python modules loaded from "))
|
lines = lines[3:]
|
||||||
self.assertTrue(lines[4].startswith("Binary modules loaded from "))
|
while lines[0].startswith(" "):
|
||||||
self.assertEqual(lines[5], "-" * 68)
|
lines = lines[1:]
|
||||||
self.assertTrue(lines[6].startswith("Python "))
|
self.assertEqual(lines[0], "-" * 68)
|
||||||
|
self.assertTrue(lines[1].startswith("Python modules loaded from "))
|
||||||
|
self.assertTrue(lines[2].startswith("Binary modules loaded from "))
|
||||||
|
self.assertEqual(lines[3], "-" * 68)
|
||||||
jpeg = (
|
jpeg = (
|
||||||
"\n"
|
"\n"
|
||||||
+ "-" * 68
|
+ "-" * 68
|
||||||
|
|
|
@ -5,16 +5,16 @@ from .helper import PillowTestCase
|
||||||
|
|
||||||
class TestFileBlp(PillowTestCase):
|
class TestFileBlp(PillowTestCase):
|
||||||
def test_load_blp2_raw(self):
|
def test_load_blp2_raw(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_raw.blp")
|
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_raw.png")
|
with Image.open("Tests/images/blp/blp2_raw.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_load_blp2_dxt1(self):
|
def test_load_blp2_dxt1(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_dxt1.blp")
|
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_dxt1.png")
|
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_load_blp2_dxt1a(self):
|
def test_load_blp2_dxt1a(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_dxt1a.blp")
|
with Image.open("Tests/images/blp/blp2_dxt1a.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_dxt1a.png")
|
with Image.open("Tests/images/blp/blp2_dxt1a.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
|
@ -11,12 +11,12 @@ class TestFileBmp(PillowTestCase):
|
||||||
|
|
||||||
im.save(outfile, "BMP")
|
im.save(outfile, "BMP")
|
||||||
|
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
reloaded.load()
|
reloaded.load()
|
||||||
self.assertEqual(im.mode, reloaded.mode)
|
self.assertEqual(im.mode, reloaded.mode)
|
||||||
self.assertEqual(im.size, reloaded.size)
|
self.assertEqual(im.size, reloaded.size)
|
||||||
self.assertEqual(reloaded.format, "BMP")
|
self.assertEqual(reloaded.format, "BMP")
|
||||||
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
|
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
self.roundtrip(hopper())
|
self.roundtrip(hopper())
|
||||||
|
@ -36,89 +36,86 @@ class TestFileBmp(PillowTestCase):
|
||||||
im.save(output, "BMP")
|
im.save(output, "BMP")
|
||||||
|
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
reloaded = Image.open(output)
|
with Image.open(output) as reloaded:
|
||||||
|
self.assertEqual(im.mode, reloaded.mode)
|
||||||
self.assertEqual(im.mode, reloaded.mode)
|
self.assertEqual(im.size, reloaded.size)
|
||||||
self.assertEqual(im.size, reloaded.size)
|
self.assertEqual(reloaded.format, "BMP")
|
||||||
self.assertEqual(reloaded.format, "BMP")
|
|
||||||
|
|
||||||
def test_dpi(self):
|
def test_dpi(self):
|
||||||
dpi = (72, 72)
|
dpi = (72, 72)
|
||||||
|
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
im.save(output, "BMP", dpi=dpi)
|
im.save(output, "BMP", dpi=dpi)
|
||||||
|
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
reloaded = Image.open(output)
|
with Image.open(output) as reloaded:
|
||||||
|
self.assertEqual(reloaded.info["dpi"], dpi)
|
||||||
self.assertEqual(reloaded.info["dpi"], dpi)
|
|
||||||
|
|
||||||
def test_save_bmp_with_dpi(self):
|
def test_save_bmp_with_dpi(self):
|
||||||
# Test for #1301
|
# Test for #1301
|
||||||
# Arrange
|
# Arrange
|
||||||
outfile = self.tempfile("temp.jpg")
|
outfile = self.tempfile("temp.jpg")
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
with Image.open("Tests/images/hopper.bmp") as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im.save(outfile, "JPEG", dpi=im.info["dpi"])
|
im.save(outfile, "JPEG", dpi=im.info["dpi"])
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
reloaded.load()
|
reloaded.load()
|
||||||
self.assertEqual(im.info["dpi"], reloaded.info["dpi"])
|
self.assertEqual(im.info["dpi"], reloaded.info["dpi"])
|
||||||
self.assertEqual(im.size, reloaded.size)
|
self.assertEqual(im.size, reloaded.size)
|
||||||
self.assertEqual(reloaded.format, "JPEG")
|
self.assertEqual(reloaded.format, "JPEG")
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
with Image.open("Tests/images/hopper.bmp") as im:
|
||||||
self.assertEqual(im.info["dpi"], (96, 96))
|
self.assertEqual(im.info["dpi"], (96, 96))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/hopper_roundDown.bmp")
|
with Image.open("Tests/images/hopper_roundDown.bmp") as im:
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.bmp")
|
outfile = self.tempfile("temp.bmp")
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
with Image.open("Tests/images/hopper.bmp") as im:
|
||||||
|
im.save(outfile, dpi=(72.2, 72.2))
|
||||||
|
with Image.open(outfile) as reloaded:
|
||||||
|
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.2, 72.2))
|
im.save(outfile, dpi=(72.8, 72.8))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.8, 72.8))
|
|
||||||
reloaded = Image.open(outfile)
|
|
||||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
|
||||||
|
|
||||||
def test_load_dib(self):
|
def test_load_dib(self):
|
||||||
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
||||||
im = Image.open("Tests/images/clipboard.dib")
|
with Image.open("Tests/images/clipboard.dib") as im:
|
||||||
self.assertEqual(im.format, "DIB")
|
self.assertEqual(im.format, "DIB")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/bmp")
|
self.assertEqual(im.get_format_mimetype(), "image/bmp")
|
||||||
|
|
||||||
target = Image.open("Tests/images/clipboard_target.png")
|
with Image.open("Tests/images/clipboard_target.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_save_dib(self):
|
def test_save_dib(self):
|
||||||
outfile = self.tempfile("temp.dib")
|
outfile = self.tempfile("temp.dib")
|
||||||
|
|
||||||
im = Image.open("Tests/images/clipboard.dib")
|
with Image.open("Tests/images/clipboard.dib") as im:
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.format, "DIB")
|
self.assertEqual(reloaded.format, "DIB")
|
||||||
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
|
self.assertEqual(reloaded.get_format_mimetype(), "image/bmp")
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
def test_rgba_bitfields(self):
|
def test_rgba_bitfields(self):
|
||||||
# This test image has been manually hexedited
|
# This test image has been manually hexedited
|
||||||
# to change the bitfield compression in the header from XBGR to RGBA
|
# to change the bitfield compression in the header from XBGR to RGBA
|
||||||
im = Image.open("Tests/images/rgb32bf-rgba.bmp")
|
with Image.open("Tests/images/rgb32bf-rgba.bmp") as im:
|
||||||
|
|
||||||
# So before the comparing the image, swap the channels
|
# So before the comparing the image, swap the channels
|
||||||
b, g, r = im.split()[1:]
|
b, g, r = im.split()[1:]
|
||||||
im = Image.merge("RGB", (r, g, b))
|
im = Image.merge("RGB", (r, g, b))
|
||||||
|
|
||||||
target = Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp")
|
with Image.open("Tests/images/bmp/q/rgb32bf-xbgr.bmp") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/gfs.t06z.rassda.tm00.bufr_d"
|
||||||
class TestFileBufrStub(PillowTestCase):
|
class TestFileBufrStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "BUFR")
|
self.assertEqual(im.format, "BUFR")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,10 +28,10 @@ class TestFileBufrStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -11,8 +11,8 @@ class TestFileContainer(PillowTestCase):
|
||||||
dir(ContainerIO)
|
dir(ContainerIO)
|
||||||
|
|
||||||
def test_isatty(self):
|
def test_isatty(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
container = ContainerIO.ContainerIO(im, 0, 0)
|
container = ContainerIO.ContainerIO(im, 0, 0)
|
||||||
|
|
||||||
self.assertFalse(container.isatty())
|
self.assertFalse(container.isatty())
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,13 @@ TEST_FILE = "Tests/images/deerstalker.cur"
|
||||||
|
|
||||||
class TestFileCur(PillowTestCase):
|
class TestFileCur(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
self.assertEqual(im.size, (32, 32))
|
||||||
self.assertEqual(im.size, (32, 32))
|
self.assertIsInstance(im, CurImagePlugin.CurImageFile)
|
||||||
self.assertIsInstance(im, CurImagePlugin.CurImageFile)
|
# Check some pixel colors to ensure image is loaded properly
|
||||||
# Check some pixel colors to ensure image is loaded properly
|
self.assertEqual(im.getpixel((10, 1)), (0, 0, 0, 0))
|
||||||
self.assertEqual(im.getpixel((10, 1)), (0, 0, 0, 0))
|
self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1))
|
||||||
self.assertEqual(im.getpixel((11, 1)), (253, 254, 254, 1))
|
self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255))
|
||||||
self.assertEqual(im.getpixel((16, 16)), (84, 87, 86, 255))
|
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import DcxImagePlugin, Image
|
from PIL import DcxImagePlugin, Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
# Created with ImageMagick: convert hopper.ppm hopper.dcx
|
# Created with ImageMagick: convert hopper.ppm hopper.dcx
|
||||||
TEST_FILE = "Tests/images/hopper.dcx"
|
TEST_FILE = "Tests/images/hopper.dcx"
|
||||||
|
@ -11,19 +13,35 @@ class TestFileDcx(PillowTestCase):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
|
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
|
||||||
orig = hopper()
|
orig = hopper()
|
||||||
self.assert_image_equal(im, orig)
|
self.assert_image_equal(im, orig)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_FILE)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_FILE) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -32,34 +50,34 @@ class TestFileDcx(PillowTestCase):
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_too_far(self):
|
def test_seek_too_far(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
frame = 999 # too big on purpose
|
frame = 999 # too big on purpose
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(EOFError, im.seek, frame)
|
self.assertRaises(EOFError, im.seek, frame)
|
||||||
|
|
|
@ -8,6 +8,7 @@ TEST_FILE_DXT1 = "Tests/images/dxt1-rgb-4bbp-noalpha_MipMaps-1.dds"
|
||||||
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
TEST_FILE_DXT3 = "Tests/images/dxt3-argb-8bbp-explicitalpha_MipMaps-1.dds"
|
||||||
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
|
TEST_FILE_DXT5 = "Tests/images/dxt5-argb-8bbp-interpolatedalpha_MipMaps-1.dds"
|
||||||
TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds"
|
TEST_FILE_DX10_BC7 = "Tests/images/bc7-argb-8bpp_MipMaps-1.dds"
|
||||||
|
TEST_FILE_DX10_BC7_UNORM_SRGB = "Tests/images/DXGI_FORMAT_BC7_UNORM_SRGB.dds"
|
||||||
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/uncompressed_rgb.dds"
|
TEST_FILE_UNCOMPRESSED_RGB = "Tests/images/uncompressed_rgb.dds"
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,58 +17,71 @@ class TestFileDds(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity_dxt1(self):
|
def test_sanity_dxt1(self):
|
||||||
"""Check DXT1 images can be opened"""
|
"""Check DXT1 images can be opened"""
|
||||||
target = Image.open(TEST_FILE_DXT1.replace(".dds", ".png"))
|
with Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) as target:
|
||||||
|
target = target.convert("RGBA")
|
||||||
|
with Image.open(TEST_FILE_DXT1) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
im = Image.open(TEST_FILE_DXT1)
|
self.assertEqual(im.format, "DDS")
|
||||||
im.load()
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (256, 256))
|
||||||
|
|
||||||
self.assertEqual(im.format, "DDS")
|
self.assert_image_equal(im, target)
|
||||||
self.assertEqual(im.mode, "RGBA")
|
|
||||||
self.assertEqual(im.size, (256, 256))
|
|
||||||
|
|
||||||
self.assert_image_equal(target.convert("RGBA"), im)
|
|
||||||
|
|
||||||
def test_sanity_dxt5(self):
|
def test_sanity_dxt5(self):
|
||||||
"""Check DXT5 images can be opened"""
|
"""Check DXT5 images can be opened"""
|
||||||
|
|
||||||
target = Image.open(TEST_FILE_DXT5.replace(".dds", ".png"))
|
with Image.open(TEST_FILE_DXT5) as im:
|
||||||
|
im.load()
|
||||||
im = Image.open(TEST_FILE_DXT5)
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assertEqual(im.format, "DDS")
|
self.assertEqual(im.format, "DDS")
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (256, 256))
|
self.assertEqual(im.size, (256, 256))
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
with Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) as target:
|
||||||
|
self.assert_image_equal(target, im)
|
||||||
|
|
||||||
def test_sanity_dxt3(self):
|
def test_sanity_dxt3(self):
|
||||||
"""Check DXT3 images can be opened"""
|
"""Check DXT3 images can be opened"""
|
||||||
|
|
||||||
target = Image.open(TEST_FILE_DXT3.replace(".dds", ".png"))
|
with Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) as target:
|
||||||
|
with Image.open(TEST_FILE_DXT3) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
im = Image.open(TEST_FILE_DXT3)
|
self.assertEqual(im.format, "DDS")
|
||||||
im.load()
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (256, 256))
|
||||||
|
|
||||||
self.assertEqual(im.format, "DDS")
|
self.assert_image_equal(target, im)
|
||||||
self.assertEqual(im.mode, "RGBA")
|
|
||||||
self.assertEqual(im.size, (256, 256))
|
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
|
||||||
|
|
||||||
def test_dx10_bc7(self):
|
def test_dx10_bc7(self):
|
||||||
"""Check DX10 images can be opened"""
|
"""Check DX10 images can be opened"""
|
||||||
|
|
||||||
target = Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png"))
|
with Image.open(TEST_FILE_DX10_BC7) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
im = Image.open(TEST_FILE_DX10_BC7)
|
self.assertEqual(im.format, "DDS")
|
||||||
im.load()
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (256, 256))
|
||||||
|
|
||||||
self.assertEqual(im.format, "DDS")
|
with Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) as target:
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assert_image_equal(target, im)
|
||||||
self.assertEqual(im.size, (256, 256))
|
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
def test_dx10_bc7_unorm_srgb(self):
|
||||||
|
"""Check DX10 unsigned normalized integer images can be opened"""
|
||||||
|
|
||||||
|
with Image.open(TEST_FILE_DX10_BC7_UNORM_SRGB) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
|
self.assertEqual(im.format, "DDS")
|
||||||
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (16, 16))
|
||||||
|
self.assertEqual(im.info["gamma"], 1 / 2.2)
|
||||||
|
|
||||||
|
with Image.open(
|
||||||
|
TEST_FILE_DX10_BC7_UNORM_SRGB.replace(".dds", ".png")
|
||||||
|
) as target:
|
||||||
|
self.assert_image_equal(target, im)
|
||||||
|
|
||||||
def test_unimplemented_dxgi_format(self):
|
def test_unimplemented_dxgi_format(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
|
@ -79,16 +93,17 @@ class TestFileDds(PillowTestCase):
|
||||||
def test_uncompressed_rgb(self):
|
def test_uncompressed_rgb(self):
|
||||||
"""Check uncompressed RGB images can be opened"""
|
"""Check uncompressed RGB images can be opened"""
|
||||||
|
|
||||||
target = Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png"))
|
with Image.open(TEST_FILE_UNCOMPRESSED_RGB) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
im = Image.open(TEST_FILE_UNCOMPRESSED_RGB)
|
self.assertEqual(im.format, "DDS")
|
||||||
im.load()
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (800, 600))
|
||||||
|
|
||||||
self.assertEqual(im.format, "DDS")
|
with Image.open(
|
||||||
self.assertEqual(im.mode, "RGBA")
|
TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")
|
||||||
self.assertEqual(im.size, (800, 600))
|
) as target:
|
||||||
|
self.assert_image_equal(target, im)
|
||||||
self.assert_image_equal(target, im)
|
|
||||||
|
|
||||||
def test__validate_true(self):
|
def test__validate_true(self):
|
||||||
"""Check valid prefix"""
|
"""Check valid prefix"""
|
||||||
|
@ -129,8 +144,8 @@ class TestFileDds(PillowTestCase):
|
||||||
img_file = f.read()
|
img_file = f.read()
|
||||||
|
|
||||||
def short_file():
|
def short_file():
|
||||||
im = Image.open(BytesIO(img_file[:-100]))
|
with Image.open(BytesIO(img_file[:-100])) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
self.assertRaises(IOError, short_file)
|
self.assertRaises(IOError, short_file)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import io
|
import io
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import EpsImagePlugin, Image
|
from PIL import EpsImagePlugin, Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript()
|
||||||
|
|
||||||
|
@ -25,30 +26,30 @@ class TestFileEps(PillowTestCase):
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Regular scale
|
# Regular scale
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
image1.load()
|
image1.load()
|
||||||
self.assertEqual(image1.mode, "RGB")
|
self.assertEqual(image1.mode, "RGB")
|
||||||
self.assertEqual(image1.size, (460, 352))
|
self.assertEqual(image1.size, (460, 352))
|
||||||
self.assertEqual(image1.format, "EPS")
|
self.assertEqual(image1.format, "EPS")
|
||||||
|
|
||||||
image2 = Image.open(file2)
|
with Image.open(file2) as image2:
|
||||||
image2.load()
|
image2.load()
|
||||||
self.assertEqual(image2.mode, "RGB")
|
self.assertEqual(image2.mode, "RGB")
|
||||||
self.assertEqual(image2.size, (360, 252))
|
self.assertEqual(image2.size, (360, 252))
|
||||||
self.assertEqual(image2.format, "EPS")
|
self.assertEqual(image2.format, "EPS")
|
||||||
|
|
||||||
# Double scale
|
# Double scale
|
||||||
image1_scale2 = Image.open(file1)
|
with Image.open(file1) as image1_scale2:
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
self.assertEqual(image1_scale2.mode, "RGB")
|
self.assertEqual(image1_scale2.mode, "RGB")
|
||||||
self.assertEqual(image1_scale2.size, (920, 704))
|
self.assertEqual(image1_scale2.size, (920, 704))
|
||||||
self.assertEqual(image1_scale2.format, "EPS")
|
self.assertEqual(image1_scale2.format, "EPS")
|
||||||
|
|
||||||
image2_scale2 = Image.open(file2)
|
with Image.open(file2) as image2_scale2:
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
self.assertEqual(image2_scale2.mode, "RGB")
|
self.assertEqual(image2_scale2.mode, "RGB")
|
||||||
self.assertEqual(image2_scale2.size, (720, 504))
|
self.assertEqual(image2_scale2.size, (720, 504))
|
||||||
self.assertEqual(image2_scale2.format, "EPS")
|
self.assertEqual(image2_scale2.format, "EPS")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -57,55 +58,55 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_cmyk(self):
|
def test_cmyk(self):
|
||||||
cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps")
|
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
|
||||||
|
|
||||||
self.assertEqual(cmyk_image.mode, "CMYK")
|
self.assertEqual(cmyk_image.mode, "CMYK")
|
||||||
self.assertEqual(cmyk_image.size, (100, 100))
|
self.assertEqual(cmyk_image.size, (100, 100))
|
||||||
self.assertEqual(cmyk_image.format, "EPS")
|
self.assertEqual(cmyk_image.format, "EPS")
|
||||||
|
|
||||||
cmyk_image.load()
|
cmyk_image.load()
|
||||||
self.assertEqual(cmyk_image.mode, "RGB")
|
self.assertEqual(cmyk_image.mode, "RGB")
|
||||||
|
|
||||||
if "jpeg_decoder" in dir(Image.core):
|
if "jpeg_decoder" in dir(Image.core):
|
||||||
target = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as target:
|
||||||
self.assert_image_similar(cmyk_image, target, 10)
|
self.assert_image_similar(cmyk_image, target, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_showpage(self):
|
def test_showpage(self):
|
||||||
# See https://github.com/python-pillow/Pillow/issues/2615
|
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||||
plot_image = Image.open("Tests/images/reqd_showpage.eps")
|
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
||||||
target = Image.open("Tests/images/reqd_showpage.png")
|
with Image.open("Tests/images/reqd_showpage.png") as target:
|
||||||
|
# should not crash/hang
|
||||||
# should not crash/hang
|
plot_image.load()
|
||||||
plot_image.load()
|
# fonts could be slightly different
|
||||||
# fonts could be slightly different
|
self.assert_image_similar(plot_image, target, 6)
|
||||||
self.assert_image_similar(plot_image, target, 6)
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_file_object(self):
|
def test_file_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
||||||
image1.save(fh, "EPS")
|
image1.save(fh, "EPS")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_iobase_object(self):
|
def test_iobase_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
with open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
||||||
image1.save(fh, "EPS")
|
image1.save(fh, "EPS")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_bytesio_object(self):
|
def test_bytesio_object(self):
|
||||||
with open(file1, "rb") as f:
|
with open(file1, "rb") as f:
|
||||||
img_bytes = io.BytesIO(f.read())
|
img_bytes = io.BytesIO(f.read())
|
||||||
|
|
||||||
img = Image.open(img_bytes)
|
with Image.open(img_bytes) as img:
|
||||||
img.load()
|
img.load()
|
||||||
|
|
||||||
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
with Image.open(file1_compare) as image1_scale1_compare:
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||||
self.assert_image_similar(img, image1_scale1_compare, 5)
|
image1_scale1_compare.load()
|
||||||
|
self.assert_image_similar(img, image1_scale1_compare, 5)
|
||||||
|
|
||||||
def test_image_mode_not_supported(self):
|
def test_image_mode_not_supported(self):
|
||||||
im = hopper("RGBA")
|
im = hopper("RGBA")
|
||||||
|
@ -120,18 +121,20 @@ class TestFileEps(PillowTestCase):
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale1 = Image.open(file1)
|
with Image.open(file1) as image1_scale1:
|
||||||
image1_scale1.load()
|
image1_scale1.load()
|
||||||
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
with Image.open(file1_compare) as image1_scale1_compare:
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare = image1_scale1_compare.convert("RGB")
|
||||||
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
image1_scale1_compare.load()
|
||||||
|
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||||
|
|
||||||
# Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale1 = Image.open(file2)
|
with Image.open(file2) as image2_scale1:
|
||||||
image2_scale1.load()
|
image2_scale1.load()
|
||||||
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
with Image.open(file2_compare) as image2_scale1_compare:
|
||||||
image2_scale1_compare.load()
|
image2_scale1_compare = image2_scale1_compare.convert("RGB")
|
||||||
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
image2_scale1_compare.load()
|
||||||
|
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_render_scale2(self):
|
def test_render_scale2(self):
|
||||||
|
@ -141,57 +144,46 @@ class TestFileEps(PillowTestCase):
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale2 = Image.open(file1)
|
with Image.open(file1) as image1_scale2:
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
with Image.open(file1_compare_scale2) as image1_scale2_compare:
|
||||||
image1_scale2_compare.load()
|
image1_scale2_compare = image1_scale2_compare.convert("RGB")
|
||||||
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
image1_scale2_compare.load()
|
||||||
|
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||||
|
|
||||||
# Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale2 = Image.open(file2)
|
with Image.open(file2) as image2_scale2:
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
with Image.open(file2_compare_scale2) as image2_scale2_compare:
|
||||||
image2_scale2_compare.load()
|
image2_scale2_compare = image2_scale2_compare.convert("RGB")
|
||||||
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
image2_scale2_compare.load()
|
||||||
|
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
# Arrange
|
files = [file1, file2, "Tests/images/illu10_preview.eps"]
|
||||||
image1 = Image.open(file1)
|
for fn in files:
|
||||||
image2 = Image.open(file2)
|
with Image.open(fn) as im:
|
||||||
image3 = Image.open("Tests/images/illu10_preview.eps")
|
new_size = (100, 100)
|
||||||
new_size = (100, 100)
|
im = im.resize(new_size)
|
||||||
|
self.assertEqual(im.size, new_size)
|
||||||
# Act
|
|
||||||
image1 = image1.resize(new_size)
|
|
||||||
image2 = image2.resize(new_size)
|
|
||||||
image3 = image3.resize(new_size)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertEqual(image1.size, new_size)
|
|
||||||
self.assertEqual(image2.size, new_size)
|
|
||||||
self.assertEqual(image3.size, new_size)
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_thumbnail(self):
|
def test_thumbnail(self):
|
||||||
# Issue #619
|
# Issue #619
|
||||||
# Arrange
|
# Arrange
|
||||||
image1 = Image.open(file1)
|
files = [file1, file2]
|
||||||
image2 = Image.open(file2)
|
for fn in files:
|
||||||
new_size = (100, 100)
|
with Image.open(file1) as im:
|
||||||
|
new_size = (100, 100)
|
||||||
# Act
|
im.thumbnail(new_size)
|
||||||
image1.thumbnail(new_size)
|
self.assertEqual(max(im.size), max(new_size))
|
||||||
image2.thumbnail(new_size)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertEqual(max(image1.size), max(new_size))
|
|
||||||
self.assertEqual(max(image2.size), max(new_size))
|
|
||||||
|
|
||||||
def test_read_binary_preview(self):
|
def test_read_binary_preview(self):
|
||||||
# Issue 302
|
# Issue 302
|
||||||
# open image with binary preview
|
# open image with binary preview
|
||||||
Image.open(file3)
|
with Image.open(file3):
|
||||||
|
pass
|
||||||
|
|
||||||
def _test_readline(self, t, ending):
|
def _test_readline(self, t, ending):
|
||||||
ending = "Failure with line ending: %s" % (
|
ending = "Failure with line ending: %s" % (
|
||||||
|
@ -239,16 +231,16 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
for filename in FILES:
|
for filename in FILES:
|
||||||
img = Image.open(filename)
|
with Image.open(filename) as img:
|
||||||
self.assertEqual(img.mode, "RGB")
|
self.assertEqual(img.mode, "RGB")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_emptyline(self):
|
def test_emptyline(self):
|
||||||
# Test file includes an empty line in the header data
|
# Test file includes an empty line in the header data
|
||||||
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
||||||
|
|
||||||
image = Image.open(emptyline_file)
|
with Image.open(emptyline_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
self.assertEqual(image.mode, "RGB")
|
self.assertEqual(image.mode, "RGB")
|
||||||
self.assertEqual(image.size, (460, 352))
|
self.assertEqual(image.size, (460, 352))
|
||||||
self.assertEqual(image.format, "EPS")
|
self.assertEqual(image.format, "EPS")
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hopper.fits"
|
||||||
class TestFileFitsStub(PillowTestCase):
|
class TestFileFitsStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "FITS")
|
self.assertEqual(im.format, "FITS")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,19 +28,19 @@ class TestFileFitsStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_fp = None
|
dummy_fp = None
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, im.save, dummy_filename)
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
|
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import FliImagePlugin, Image
|
from PIL import FliImagePlugin, Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
# created as an export of a palette image from Gimp2.6
|
# created as an export of a palette image from Gimp2.6
|
||||||
# save as...-> hopper.fli, default options.
|
# save as...-> hopper.fli, default options.
|
||||||
|
@ -12,36 +14,52 @@ animated_test_file = "Tests/images/a.fli"
|
||||||
|
|
||||||
class TestFileFli(PillowTestCase):
|
class TestFileFli(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "FLI")
|
self.assertEqual(im.format, "FLI")
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (320, 200))
|
self.assertEqual(im.size, (320, 200))
|
||||||
self.assertEqual(im.format, "FLI")
|
self.assertEqual(im.format, "FLI")
|
||||||
self.assertEqual(im.info["duration"], 71)
|
self.assertEqual(im.info["duration"], 71)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(static_test_file)
|
im = Image.open(static_test_file)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(static_test_file)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(static_test_file) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -49,50 +67,50 @@ class TestFileFli(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
|
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 384)
|
self.assertEqual(im.n_frames, 384)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
|
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 0)
|
self.assertEqual(layer_number, 0)
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 0)
|
self.assertEqual(layer_number, 0)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 2)
|
self.assertEqual(layer_number, 2)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
im.seek(50)
|
im.seek(50)
|
||||||
|
|
||||||
expected = Image.open("Tests/images/a_fli.png")
|
with Image.open("Tests/images/a_fli.png") as expected:
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from .helper import PillowTestCase, unittest
|
import unittest
|
||||||
|
|
||||||
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import FpxImagePlugin
|
from PIL import FpxImagePlugin
|
||||||
|
|
|
@ -5,12 +5,11 @@ from .helper import PillowTestCase
|
||||||
|
|
||||||
class TestFileFtex(PillowTestCase):
|
class TestFileFtex(PillowTestCase):
|
||||||
def test_load_raw(self):
|
def test_load_raw(self):
|
||||||
im = Image.open("Tests/images/ftex_uncompressed.ftu")
|
with Image.open("Tests/images/ftex_uncompressed.ftu") as im:
|
||||||
target = Image.open("Tests/images/ftex_uncompressed.png")
|
with Image.open("Tests/images/ftex_uncompressed.png") as target:
|
||||||
|
self.assert_image_equal(im, target)
|
||||||
self.assert_image_equal(im, target)
|
|
||||||
|
|
||||||
def test_load_dxt1(self):
|
def test_load_dxt1(self):
|
||||||
im = Image.open("Tests/images/ftex_dxt1.ftc")
|
with Image.open("Tests/images/ftex_dxt1.ftc") as im:
|
||||||
target = Image.open("Tests/images/ftex_dxt1.png")
|
with Image.open("Tests/images/ftex_dxt1.png") as target:
|
||||||
self.assert_image_similar(im, target.convert("RGBA"), 15)
|
self.assert_image_similar(im, target.convert("RGBA"), 15)
|
||||||
|
|
|
@ -10,8 +10,6 @@ class TestFileGbr(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
|
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
|
||||||
|
|
||||||
def test_gbr_file(self):
|
def test_gbr_file(self):
|
||||||
im = Image.open("Tests/images/gbr.gbr")
|
with Image.open("Tests/images/gbr.gbr") as im:
|
||||||
|
with Image.open("Tests/images/gbr.png") as target:
|
||||||
target = Image.open("Tests/images/gbr.png")
|
self.assert_image_equal(target, im)
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from PIL import GdImageFile
|
from PIL import GdImageFile, UnidentifiedImageError
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||||
|
|
||||||
class TestFileGd(PillowTestCase):
|
class TestFileGd(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = GdImageFile.open(TEST_GD_FILE)
|
with GdImageFile.open(TEST_GD_FILE) as im:
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "GD")
|
self.assertEqual(im.format, "GD")
|
||||||
|
|
||||||
def test_bad_mode(self):
|
def test_bad_mode(self):
|
||||||
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
|
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
|
||||||
|
@ -17,4 +17,4 @@ class TestFileGd(PillowTestCase):
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(IOError, GdImageFile.open, invalid_file)
|
self.assertRaises(UnidentifiedImageError, GdImageFile.open, invalid_file)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette
|
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, netpbm_available, unittest
|
from .helper import PillowTestCase, hopper, is_pypy, netpbm_available
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -26,18 +27,34 @@ class TestFileGif(PillowTestCase):
|
||||||
self.skipTest("gif support not available") # can this happen?
|
self.skipTest("gif support not available") # can this happen?
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "GIF")
|
self.assertEqual(im.format, "GIF")
|
||||||
self.assertEqual(im.info["version"], b"GIF89a")
|
self.assertEqual(im.info["version"], b"GIF89a")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_GIF)
|
im = Image.open(TEST_GIF)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_GIF)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_GIF) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -71,21 +88,20 @@ class TestFileGif(PillowTestCase):
|
||||||
def check(colors, size, expected_palette_length):
|
def check(colors, size, expected_palette_length):
|
||||||
# make an image with empty colors in the start of the palette range
|
# make an image with empty colors in the start of the palette range
|
||||||
im = Image.frombytes(
|
im = Image.frombytes(
|
||||||
"P",
|
"P", (colors, colors), bytes(range(256 - colors, 256)) * colors
|
||||||
(colors, colors),
|
|
||||||
bytes(bytearray(range(256 - colors, 256)) * colors),
|
|
||||||
)
|
)
|
||||||
im = im.resize((size, size))
|
im = im.resize((size, size))
|
||||||
outfile = BytesIO()
|
outfile = BytesIO()
|
||||||
im.save(outfile, "GIF")
|
im.save(outfile, "GIF")
|
||||||
outfile.seek(0)
|
outfile.seek(0)
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
|
# check palette length
|
||||||
|
palette_length = max(
|
||||||
|
i + 1 for i, v in enumerate(reloaded.histogram()) if v
|
||||||
|
)
|
||||||
|
self.assertEqual(expected_palette_length, palette_length)
|
||||||
|
|
||||||
# check palette length
|
self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
|
||||||
palette_length = max(i + 1 for i, v in enumerate(reloaded.histogram()) if v)
|
|
||||||
self.assertEqual(expected_palette_length, palette_length)
|
|
||||||
|
|
||||||
self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
|
|
||||||
|
|
||||||
# These do optimize the palette
|
# These do optimize the palette
|
||||||
check(128, 511, 128)
|
check(128, 511, 128)
|
||||||
|
@ -103,7 +119,7 @@ class TestFileGif(PillowTestCase):
|
||||||
check(256, 511, 256)
|
check(256, 511, 256)
|
||||||
|
|
||||||
def test_optimize_full_l(self):
|
def test_optimize_full_l(self):
|
||||||
im = Image.frombytes("L", (16, 16), bytes(bytearray(range(256))))
|
im = Image.frombytes("L", (16, 16), bytes(range(256)))
|
||||||
test_file = BytesIO()
|
test_file = BytesIO()
|
||||||
im.save(test_file, "GIF", optimize=True)
|
im.save(test_file, "GIF", optimize=True)
|
||||||
self.assertEqual(im.mode, "L")
|
self.assertEqual(im.mode, "L")
|
||||||
|
@ -112,67 +128,67 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
||||||
|
|
||||||
def test_roundtrip2(self):
|
def test_roundtrip2(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/403
|
# see https://github.com/python-pillow/Pillow/issues/403
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im2 = im.copy()
|
im2 = im.copy()
|
||||||
im2.save(out)
|
im2.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_similar(reread.convert("RGB"), hopper(), 50)
|
self.assert_image_similar(reread.convert("RGB"), hopper(), 50)
|
||||||
|
|
||||||
def test_roundtrip_save_all(self):
|
def test_roundtrip_save_all(self):
|
||||||
# Single frame image
|
# Single frame image
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
self.assert_image_similar(reread.convert("RGB"), im, 50)
|
||||||
|
|
||||||
# Multiframe image
|
# Multiframe image
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.n_frames, 5)
|
self.assertEqual(reread.n_frames, 5)
|
||||||
|
|
||||||
def test_headers_saving_for_animated_gifs(self):
|
def test_headers_saving_for_animated_gifs(self):
|
||||||
important_headers = ["background", "version", "duration", "loop"]
|
important_headers = ["background", "version", "duration", "loop"]
|
||||||
# Multiframe image
|
# Multiframe image
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
info = im.info.copy()
|
info = im.info.copy()
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for header in important_headers:
|
for header in important_headers:
|
||||||
self.assertEqual(info[header], reread.info[header])
|
self.assertEqual(info[header], reread.info[header])
|
||||||
|
|
||||||
def test_palette_handling(self):
|
def test_palette_handling(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/513
|
# see https://github.com/python-pillow/Pillow/issues/513
|
||||||
|
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
|
|
||||||
im = im.resize((100, 100), Image.LANCZOS)
|
im = im.resize((100, 100), Image.LANCZOS)
|
||||||
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
|
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
|
||||||
|
|
||||||
f = self.tempfile("temp.gif")
|
f = self.tempfile("temp.gif")
|
||||||
im2.save(f, optimize=True)
|
im2.save(f, optimize=True)
|
||||||
|
|
||||||
reloaded = Image.open(f)
|
with Image.open(f) as reloaded:
|
||||||
|
|
||||||
self.assert_image_similar(im, reloaded.convert("RGB"), 10)
|
self.assert_image_similar(im, reloaded.convert("RGB"), 10)
|
||||||
|
|
||||||
def test_palette_434(self):
|
def test_palette_434(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/434
|
# see https://github.com/python-pillow/Pillow/issues/434
|
||||||
|
@ -185,108 +201,115 @@ class TestFileGif(PillowTestCase):
|
||||||
return reloaded
|
return reloaded
|
||||||
|
|
||||||
orig = "Tests/images/test.colors.gif"
|
orig = "Tests/images/test.colors.gif"
|
||||||
im = Image.open(orig)
|
with Image.open(orig) as im:
|
||||||
|
|
||||||
self.assert_image_similar(im, roundtrip(im), 1)
|
with roundtrip(im) as reloaded:
|
||||||
self.assert_image_similar(im, roundtrip(im, optimize=True), 1)
|
self.assert_image_similar(im, reloaded, 1)
|
||||||
|
with roundtrip(im, optimize=True) as reloaded:
|
||||||
|
self.assert_image_similar(im, reloaded, 1)
|
||||||
|
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
# check automatic P conversion
|
# check automatic P conversion
|
||||||
reloaded = roundtrip(im).convert("RGB")
|
with roundtrip(im) as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
reloaded = reloaded.convert("RGB")
|
||||||
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_bmp_mode(self):
|
def test_save_netpbm_bmp_mode(self):
|
||||||
img = Image.open(TEST_GIF).convert("RGB")
|
with Image.open(TEST_GIF) as img:
|
||||||
|
img = img.convert("RGB")
|
||||||
|
|
||||||
tempfile = self.tempfile("temp.gif")
|
tempfile = self.tempfile("temp.gif")
|
||||||
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
||||||
self.assert_image_similar(img, Image.open(tempfile).convert("RGB"), 0)
|
with Image.open(tempfile) as reloaded:
|
||||||
|
self.assert_image_similar(img, reloaded.convert("RGB"), 0)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_l_mode(self):
|
def test_save_netpbm_l_mode(self):
|
||||||
img = Image.open(TEST_GIF).convert("L")
|
with Image.open(TEST_GIF) as img:
|
||||||
|
img = img.convert("L")
|
||||||
|
|
||||||
tempfile = self.tempfile("temp.gif")
|
tempfile = self.tempfile("temp.gif")
|
||||||
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
||||||
self.assert_image_similar(img, Image.open(tempfile).convert("L"), 0)
|
with Image.open(tempfile) as reloaded:
|
||||||
|
self.assert_image_similar(img, reloaded.convert("L"), 0)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
img = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||||
framecount = 0
|
framecount = 0
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
framecount += 1
|
framecount += 1
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
self.assertEqual(framecount, 5)
|
self.assertEqual(framecount, 5)
|
||||||
|
|
||||||
def test_seek_info(self):
|
def test_seek_info(self):
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
info = im.info.copy()
|
info = im.info.copy()
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
|
|
||||||
self.assertEqual(im.info, info)
|
self.assertEqual(im.info, info)
|
||||||
|
|
||||||
def test_seek_rewind(self):
|
def test_seek_rewind(self):
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
|
||||||
expected = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as expected:
|
||||||
expected.seek(1)
|
expected.seek(1)
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
|
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
|
||||||
# Test is_animated before n_frames
|
# Test is_animated before n_frames
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
# Test is_animated after n_frames
|
# Test is_animated after n_frames
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.n_frames, n_frames)
|
self.assertEqual(im.n_frames, n_frames)
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_dispose_none(self):
|
def test_dispose_none(self):
|
||||||
img = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 1)
|
self.assertEqual(img.disposal_method, 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dispose_background(self):
|
def test_dispose_background(self):
|
||||||
img = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 2)
|
self.assertEqual(img.disposal_method, 2)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dispose_previous(self):
|
def test_dispose_previous(self):
|
||||||
img = Image.open("Tests/images/dispose_prev.gif")
|
with Image.open("Tests/images/dispose_prev.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 3)
|
self.assertEqual(img.disposal_method, 3)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_save_dispose(self):
|
def test_save_dispose(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -299,10 +322,10 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=method
|
out, save_all=True, append_images=im_list[1:], disposal=method
|
||||||
)
|
)
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
for _ in range(2):
|
for _ in range(2):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, method)
|
self.assertEqual(img.disposal_method, method)
|
||||||
|
|
||||||
# check per frame disposal
|
# check per frame disposal
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
|
@ -312,11 +335,11 @@ class TestFileGif(PillowTestCase):
|
||||||
disposal=tuple(range(len(im_list))),
|
disposal=tuple(range(len(im_list))),
|
||||||
)
|
)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, i + 1)
|
self.assertEqual(img.disposal_method, i + 1)
|
||||||
|
|
||||||
def test_dispose2_palette(self):
|
def test_dispose2_palette(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -336,17 +359,16 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
|
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
for i, circle in enumerate(circles):
|
||||||
|
img.seek(i)
|
||||||
|
rgb_img = img.convert("RGB")
|
||||||
|
|
||||||
for i, circle in enumerate(circles):
|
# Check top left pixel matches background
|
||||||
img.seek(i)
|
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
||||||
rgb_img = img.convert("RGB")
|
|
||||||
|
|
||||||
# Check top left pixel matches background
|
# Center remains red every frame
|
||||||
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
|
||||||
|
|
||||||
# Center remains red every frame
|
|
||||||
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
|
|
||||||
|
|
||||||
def test_dispose2_diff(self):
|
def test_dispose2_diff(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -375,20 +397,19 @@ class TestFileGif(PillowTestCase):
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=2, transparency=0
|
out, save_all=True, append_images=im_list[1:], disposal=2, transparency=0
|
||||||
)
|
)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
for i, colours in enumerate(circles):
|
||||||
|
img.seek(i)
|
||||||
|
rgb_img = img.convert("RGBA")
|
||||||
|
|
||||||
for i, colours in enumerate(circles):
|
# Check left circle is correct colour
|
||||||
img.seek(i)
|
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
||||||
rgb_img = img.convert("RGBA")
|
|
||||||
|
|
||||||
# Check left circle is correct colour
|
# Check right circle is correct colour
|
||||||
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
||||||
|
|
||||||
# Check right circle is correct colour
|
# Check BG is correct colour
|
||||||
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
|
||||||
|
|
||||||
# Check BG is correct colour
|
|
||||||
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
|
|
||||||
|
|
||||||
def test_dispose2_background(self):
|
def test_dispose2_background(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -411,17 +432,17 @@ class TestFileGif(PillowTestCase):
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
|
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
|
||||||
)
|
)
|
||||||
|
|
||||||
im = Image.open(out)
|
with Image.open(out) as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.getpixel((0, 0)), 0)
|
self.assertEqual(im.getpixel((0, 0)), 0)
|
||||||
|
|
||||||
def test_iss634(self):
|
def test_iss634(self):
|
||||||
img = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as img:
|
||||||
# seek to the second frame
|
# seek to the second frame
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
# all transparent pixels should be replaced with the color from the
|
# all transparent pixels should be replaced with the color from the
|
||||||
# first frame
|
# first frame
|
||||||
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
|
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
|
||||||
|
|
||||||
def test_duration(self):
|
def test_duration(self):
|
||||||
duration = 1000
|
duration = 1000
|
||||||
|
@ -433,8 +454,8 @@ class TestFileGif(PillowTestCase):
|
||||||
im.info["duration"] = 100
|
im.info["duration"] = 100
|
||||||
im.save(out, duration=duration)
|
im.save(out, duration=duration)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
|
|
||||||
def test_multiple_duration(self):
|
def test_multiple_duration(self):
|
||||||
duration_list = [1000, 2000, 3000]
|
duration_list = [1000, 2000, 3000]
|
||||||
|
@ -450,27 +471,27 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for duration in duration_list:
|
for duration in duration_list:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
try:
|
try:
|
||||||
reread.seek(reread.tell() + 1)
|
reread.seek(reread.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# duration as tuple
|
# duration as tuple
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
|
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for duration in duration_list:
|
for duration in duration_list:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
try:
|
try:
|
||||||
reread.seek(reread.tell() + 1)
|
reread.seek(reread.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_identical_frames(self):
|
def test_identical_frames(self):
|
||||||
duration_list = [1000, 1500, 2000, 4000]
|
duration_list = [1000, 1500, 2000, 4000]
|
||||||
|
@ -487,13 +508,13 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
# Assert that the first three frames were combined
|
# Assert that the first three frames were combined
|
||||||
self.assertEqual(reread.n_frames, 2)
|
self.assertEqual(reread.n_frames, 2)
|
||||||
|
|
||||||
# Assert that the new duration is the total of the identical frames
|
# Assert that the new duration is the total of the identical frames
|
||||||
self.assertEqual(reread.info["duration"], 4500)
|
self.assertEqual(reread.info["duration"], 4500)
|
||||||
|
|
||||||
def test_identical_frames_to_single_frame(self):
|
def test_identical_frames_to_single_frame(self):
|
||||||
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
|
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
|
||||||
|
@ -507,13 +528,12 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration
|
out, save_all=True, append_images=im_list[1:], duration=duration
|
||||||
)
|
)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
# Assert that all frames were combined
|
||||||
|
self.assertEqual(reread.n_frames, 1)
|
||||||
|
|
||||||
# Assert that all frames were combined
|
# Assert that the new duration is the total of the identical frames
|
||||||
self.assertEqual(reread.n_frames, 1)
|
self.assertEqual(reread.info["duration"], 8500)
|
||||||
|
|
||||||
# Assert that the new duration is the total of the identical frames
|
|
||||||
self.assertEqual(reread.info["duration"], 8500)
|
|
||||||
|
|
||||||
def test_number_of_loops(self):
|
def test_number_of_loops(self):
|
||||||
number_of_loops = 2
|
number_of_loops = 2
|
||||||
|
@ -521,35 +541,37 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.save(out, loop=number_of_loops)
|
im.save(out, loop=number_of_loops)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["loop"], number_of_loops)
|
self.assertEqual(reread.info["loop"], number_of_loops)
|
||||||
|
|
||||||
def test_background(self):
|
def test_background(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.info["background"] = 1
|
im.info["background"] = 1
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["background"], im.info["background"])
|
self.assertEqual(reread.info["background"], im.info["background"])
|
||||||
|
|
||||||
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
||||||
im = Image.open("Tests/images/hopper.webp")
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
self.assertIsInstance(im.info["background"], tuple)
|
self.assertIsInstance(im.info["background"], tuple)
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
def test_comment(self):
|
def test_comment(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assertEqual(im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0")
|
self.assertEqual(
|
||||||
|
im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0"
|
||||||
|
)
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.info["comment"] = b"Test comment text"
|
im.info["comment"] = b"Test comment text"
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["comment"], im.info["comment"])
|
self.assertEqual(reread.info["comment"], im.info["comment"])
|
||||||
|
|
||||||
def test_comment_over_255(self):
|
def test_comment_over_255(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -559,22 +581,22 @@ class TestFileGif(PillowTestCase):
|
||||||
comment += comment
|
comment += comment
|
||||||
im.info["comment"] = comment
|
im.info["comment"] = comment
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assertEqual(reread.info["comment"], comment)
|
self.assertEqual(reread.info["comment"], comment)
|
||||||
|
|
||||||
def test_zero_comment_subblocks(self):
|
def test_zero_comment_subblocks(self):
|
||||||
im = Image.open("Tests/images/hopper_zero_comment_subblocks.gif")
|
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
|
||||||
expected = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as expected:
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
|
||||||
def assertVersionAfterSave(im, version):
|
def assertVersionAfterSave(im, version):
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["version"], version)
|
self.assertEqual(reread.info["version"], version)
|
||||||
|
|
||||||
# Test that GIF87a is used by default
|
# Test that GIF87a is used by default
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
|
@ -590,12 +612,12 @@ class TestFileGif(PillowTestCase):
|
||||||
assertVersionAfterSave(im, b"GIF89a")
|
assertVersionAfterSave(im, b"GIF89a")
|
||||||
|
|
||||||
# Test that a GIF87a image is also saved in that format
|
# Test that a GIF87a image is also saved in that format
|
||||||
im = Image.open("Tests/images/test.colors.gif")
|
with Image.open("Tests/images/test.colors.gif") as im:
|
||||||
assertVersionAfterSave(im, b"GIF87a")
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
|
|
||||||
# Test that a GIF89a image is also saved in that format
|
# Test that a GIF89a image is also saved in that format
|
||||||
im.info["version"] = b"GIF89a"
|
im.info["version"] = b"GIF89a"
|
||||||
assertVersionAfterSave(im, b"GIF87a")
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
|
|
||||||
def test_append_images(self):
|
def test_append_images(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -605,26 +627,25 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
||||||
im.copy().save(out, save_all=True, append_images=ims)
|
im.copy().save(out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Tests appending using a generator
|
# Tests appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
for im in ims:
|
yield from ims
|
||||||
yield im
|
|
||||||
|
|
||||||
im.save(out, save_all=True, append_images=imGenerator(ims))
|
im.save(out, save_all=True, append_images=imGenerator(ims))
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Tests appending single and multiple frame images
|
# Tests appending single and multiple frame images
|
||||||
im = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as im:
|
||||||
ims = [Image.open("Tests/images/dispose_prev.gif")]
|
with Image.open("Tests/images/dispose_prev.gif") as im2:
|
||||||
im.save(out, save_all=True, append_images=ims)
|
im.save(out, save_all=True, append_images=[im2])
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 10)
|
self.assertEqual(reread.n_frames, 10)
|
||||||
|
|
||||||
def test_transparent_optimize(self):
|
def test_transparent_optimize(self):
|
||||||
# from issue #2195, if the transparent color is incorrectly
|
# from issue #2195, if the transparent color is incorrectly
|
||||||
|
@ -633,7 +654,7 @@ class TestFileGif(PillowTestCase):
|
||||||
# that's > 128 items where the transparent color is actually
|
# that's > 128 items where the transparent color is actually
|
||||||
# the top palette entry to trigger the bug.
|
# the top palette entry to trigger the bug.
|
||||||
|
|
||||||
data = bytes(bytearray(range(1, 254)))
|
data = bytes(range(1, 254))
|
||||||
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
|
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
|
||||||
|
|
||||||
im = Image.new("L", (253, 1))
|
im = Image.new("L", (253, 1))
|
||||||
|
@ -642,9 +663,9 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, transparency=253)
|
im.save(out, transparency=253)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
|
|
||||||
self.assertEqual(reloaded.info["transparency"], 253)
|
self.assertEqual(reloaded.info["transparency"], 253)
|
||||||
|
|
||||||
def test_rgb_transparency(self):
|
def test_rgb_transparency(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -654,8 +675,8 @@ class TestFileGif(PillowTestCase):
|
||||||
im.info["transparency"] = (255, 0, 0)
|
im.info["transparency"] = (255, 0, 0)
|
||||||
self.assert_warning(UserWarning, im.save, out)
|
self.assert_warning(UserWarning, im.save, out)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIn("transparency", reloaded.info)
|
self.assertNotIn("transparency", reloaded.info)
|
||||||
|
|
||||||
# Multiple frames
|
# Multiple frames
|
||||||
im = Image.new("RGB", (1, 1))
|
im = Image.new("RGB", (1, 1))
|
||||||
|
@ -663,8 +684,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (1, 1))]
|
ims = [Image.new("RGB", (1, 1))]
|
||||||
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
|
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIn("transparency", reloaded.info)
|
self.assertNotIn("transparency", reloaded.info)
|
||||||
|
|
||||||
def test_bbox(self):
|
def test_bbox(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -673,22 +694,22 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (100, 100), "#000")]
|
ims = [Image.new("RGB", (100, 100), "#000")]
|
||||||
im.save(out, save_all=True, append_images=ims)
|
im.save(out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 2)
|
self.assertEqual(reread.n_frames, 2)
|
||||||
|
|
||||||
def test_palette_save_L(self):
|
def test_palette_save_L(self):
|
||||||
# generate an L mode image with a separate palette
|
# generate an L mode image with a separate palette
|
||||||
|
|
||||||
im = hopper("P")
|
im = hopper("P")
|
||||||
im_l = Image.frombytes("L", im.size, im.tobytes())
|
im_l = Image.frombytes("L", im.size, im.tobytes())
|
||||||
palette = bytes(bytearray(im.getpalette()))
|
palette = bytes(im.getpalette())
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im_l.save(out, palette=palette)
|
im_l.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
|
|
||||||
self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
|
self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
|
||||||
|
|
||||||
def test_palette_save_P(self):
|
def test_palette_save_P(self):
|
||||||
# pass in a different palette, then construct what the image
|
# pass in a different palette, then construct what the image
|
||||||
|
@ -696,14 +717,14 @@ class TestFileGif(PillowTestCase):
|
||||||
# Forcing a non-straight grayscale palette.
|
# Forcing a non-straight grayscale palette.
|
||||||
|
|
||||||
im = hopper("P")
|
im = hopper("P")
|
||||||
palette = bytes(bytearray([255 - i // 3 for i in range(768)]))
|
palette = bytes([255 - i // 3 for i in range(768)])
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
im.putpalette(palette)
|
im.putpalette(palette)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_palette_save_ImagePalette(self):
|
def test_palette_save_ImagePalette(self):
|
||||||
# pass in a different palette, as an ImagePalette.ImagePalette
|
# pass in a different palette, as an ImagePalette.ImagePalette
|
||||||
|
@ -715,9 +736,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
im.putpalette(palette)
|
im.putpalette(palette)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_save_I(self):
|
def test_save_I(self):
|
||||||
# Test saving something that would trigger the auto-convert to 'L'
|
# Test saving something that would trigger the auto-convert to 'L'
|
||||||
|
@ -727,8 +748,8 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
||||||
|
|
||||||
def test_getdata(self):
|
def test_getdata(self):
|
||||||
# test getheader/getdata against legacy values
|
# test getheader/getdata against legacy values
|
||||||
|
@ -737,7 +758,7 @@ class TestFileGif(PillowTestCase):
|
||||||
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
im.putpalette(ImagePalette.ImagePalette("RGB"))
|
||||||
im.info = {"background": 0}
|
im.info = {"background": 0}
|
||||||
|
|
||||||
passed_palette = bytes(bytearray([255 - i // 3 for i in range(768)]))
|
passed_palette = bytes([255 - i // 3 for i in range(768)])
|
||||||
|
|
||||||
GifImagePlugin._FORCE_OPTIMIZE = True
|
GifImagePlugin._FORCE_OPTIMIZE = True
|
||||||
try:
|
try:
|
||||||
|
@ -759,14 +780,13 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
def test_lzw_bits(self):
|
def test_lzw_bits(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/2811
|
# see https://github.com/python-pillow/Pillow/issues/2811
|
||||||
im = Image.open("Tests/images/issue_2811.gif")
|
with Image.open("Tests/images/issue_2811.gif") as im:
|
||||||
|
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
|
||||||
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
|
# codec error prepatch
|
||||||
# codec error prepatch
|
im.load()
|
||||||
im.load()
|
|
||||||
|
|
||||||
def test_extents(self):
|
def test_extents(self):
|
||||||
im = Image.open("Tests/images/test_extents.gif")
|
with Image.open("Tests/images/test_extents.gif") as im:
|
||||||
self.assertEqual(im.size, (100, 100))
|
self.assertEqual(im.size, (100, 100))
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (150, 150))
|
self.assertEqual(im.size, (150, 150))
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/WAlaska.wind.7days.grb"
|
||||||
class TestFileGribStub(PillowTestCase):
|
class TestFileGribStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "GRIB")
|
self.assertEqual(im.format, "GRIB")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,10 +28,10 @@ class TestFileGribStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hdf5.h5"
|
||||||
class TestFileHdf5Stub(PillowTestCase):
|
class TestFileHdf5Stub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "HDF5")
|
self.assertEqual(im.format, "HDF5")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,19 +28,19 @@ class TestFileHdf5Stub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_fp = None
|
dummy_fp = None
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, im.save, dummy_filename)
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
|
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import IcnsImagePlugin, Image
|
from PIL import IcnsImagePlugin, Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, unittest
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
# sample icon file
|
# sample icon file
|
||||||
TEST_FILE = "Tests/images/pillow.icns"
|
TEST_FILE = "Tests/images/pillow.icns"
|
||||||
|
@ -15,72 +16,71 @@ class TestFileIcns(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Loading this icon by default should result in the largest size
|
# Loading this icon by default should result in the largest size
|
||||||
# (512x512@2x) being loaded
|
# (512x512@2x) being loaded
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert that there is no unclosed file warning
|
# Assert that there is no unclosed file warning
|
||||||
self.assert_warning(None, im.load)
|
self.assert_warning(None, im.load)
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (1024, 1024))
|
self.assertEqual(im.size, (1024, 1024))
|
||||||
self.assertEqual(im.format, "ICNS")
|
self.assertEqual(im.format, "ICNS")
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
im = Image.open(TEST_FILE)
|
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.icns")
|
temp_file = self.tempfile("temp.icns")
|
||||||
im.save(temp_file)
|
|
||||||
|
|
||||||
reread = Image.open(temp_file)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
im.save(temp_file)
|
||||||
|
|
||||||
self.assertEqual(reread.mode, "RGBA")
|
with Image.open(temp_file) as reread:
|
||||||
self.assertEqual(reread.size, (1024, 1024))
|
self.assertEqual(reread.mode, "RGBA")
|
||||||
self.assertEqual(reread.format, "ICNS")
|
self.assertEqual(reread.size, (1024, 1024))
|
||||||
|
self.assertEqual(reread.format, "ICNS")
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
||||||
def test_save_append_images(self):
|
def test_save_append_images(self):
|
||||||
im = Image.open(TEST_FILE)
|
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.icns")
|
temp_file = self.tempfile("temp.icns")
|
||||||
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128))
|
provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128))
|
||||||
im.save(temp_file, append_images=[provided_im])
|
|
||||||
|
|
||||||
reread = Image.open(temp_file)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assert_image_similar(reread, im, 1)
|
im.save(temp_file, append_images=[provided_im])
|
||||||
|
|
||||||
reread = Image.open(temp_file)
|
with Image.open(temp_file) as reread:
|
||||||
reread.size = (16, 16, 2)
|
self.assert_image_similar(reread, im, 1)
|
||||||
reread.load()
|
|
||||||
self.assert_image_equal(reread, provided_im)
|
with Image.open(temp_file) as reread:
|
||||||
|
reread.size = (16, 16, 2)
|
||||||
|
reread.load()
|
||||||
|
self.assert_image_equal(reread, provided_im)
|
||||||
|
|
||||||
def test_sizes(self):
|
def test_sizes(self):
|
||||||
# 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
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im.size = (w, h, r)
|
im.size = (w, h, r)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (wr, hr))
|
self.assertEqual(im.size, (wr, hr))
|
||||||
|
|
||||||
# Check that we cannot load an incorrect size
|
# Check that we cannot load an incorrect size
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 1)
|
||||||
|
|
||||||
def test_older_icon(self):
|
def test_older_icon(self):
|
||||||
# This icon was made with Icon Composer rather than iconutil; it still
|
# This icon was made with Icon Composer rather than iconutil; it still
|
||||||
# uses PNG rather than JP2, however (since it was made on 10.9).
|
# uses PNG rather than JP2, however (since it was made on 10.9).
|
||||||
im = Image.open("Tests/images/pillow2.icns")
|
with Image.open("Tests/images/pillow2.icns") as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im2 = Image.open("Tests/images/pillow2.icns")
|
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h, r)
|
||||||
im2.load()
|
im2.load()
|
||||||
self.assertEqual(im2.mode, "RGBA")
|
self.assertEqual(im2.mode, "RGBA")
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
def test_jp2_icon(self):
|
def test_jp2_icon(self):
|
||||||
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
||||||
|
@ -93,15 +93,15 @@ class TestFileIcns(PillowTestCase):
|
||||||
if not enable_jpeg2k:
|
if not enable_jpeg2k:
|
||||||
return
|
return
|
||||||
|
|
||||||
im = Image.open("Tests/images/pillow3.icns")
|
with Image.open("Tests/images/pillow3.icns") as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im2 = Image.open("Tests/images/pillow3.icns")
|
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h, r)
|
||||||
im2.load()
|
im2.load()
|
||||||
self.assertEqual(im2.mode, "RGBA")
|
self.assertEqual(im2.mode, "RGBA")
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
def test_getimage(self):
|
def test_getimage(self):
|
||||||
with open(TEST_FILE, "rb") as fp:
|
with open(TEST_FILE, "rb") as fp:
|
||||||
|
|
|
@ -9,8 +9,8 @@ TEST_ICO_FILE = "Tests/images/hopper.ico"
|
||||||
|
|
||||||
class TestFileIco(PillowTestCase):
|
class TestFileIco(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (16, 16))
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.format, "ICO")
|
self.assertEqual(im.format, "ICO")
|
||||||
|
@ -27,41 +27,41 @@ class TestFileIco(PillowTestCase):
|
||||||
|
|
||||||
# the default image
|
# the default image
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
reloaded = Image.open(output)
|
with Image.open(output) as reloaded:
|
||||||
self.assertEqual(reloaded.info["sizes"], {(32, 32), (64, 64)})
|
self.assertEqual(reloaded.info["sizes"], {(32, 32), (64, 64)})
|
||||||
|
|
||||||
self.assertEqual(im.mode, reloaded.mode)
|
self.assertEqual(im.mode, reloaded.mode)
|
||||||
self.assertEqual((64, 64), reloaded.size)
|
self.assertEqual((64, 64), reloaded.size)
|
||||||
self.assertEqual(reloaded.format, "ICO")
|
self.assertEqual(reloaded.format, "ICO")
|
||||||
self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS))
|
self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS))
|
||||||
|
|
||||||
# the other one
|
# the other one
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
reloaded = Image.open(output)
|
with Image.open(output) as reloaded:
|
||||||
reloaded.size = (32, 32)
|
reloaded.size = (32, 32)
|
||||||
|
|
||||||
self.assertEqual(im.mode, reloaded.mode)
|
self.assertEqual(im.mode, reloaded.mode)
|
||||||
self.assertEqual((32, 32), reloaded.size)
|
self.assertEqual((32, 32), reloaded.size)
|
||||||
self.assertEqual(reloaded.format, "ICO")
|
self.assertEqual(reloaded.format, "ICO")
|
||||||
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
||||||
|
|
||||||
def test_incorrect_size(self):
|
def test_incorrect_size(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 1)
|
||||||
|
|
||||||
def test_save_256x256(self):
|
def test_save_256x256(self):
|
||||||
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/hopper_256x256.ico")
|
with Image.open("Tests/images/hopper_256x256.ico") as im:
|
||||||
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
im_saved = Image.open(outfile)
|
with Image.open(outfile) as im_saved:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im_saved.size, (256, 256))
|
self.assertEqual(im_saved.size, (256, 256))
|
||||||
|
|
||||||
def test_only_save_relevant_sizes(self):
|
def test_only_save_relevant_sizes(self):
|
||||||
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
||||||
|
@ -69,35 +69,35 @@ class TestFileIco(PillowTestCase):
|
||||||
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
||||||
"""
|
"""
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/python.ico") # 16x16, 32x32, 48x48
|
with Image.open("Tests/images/python.ico") as im: # 16x16, 32x32, 48x48
|
||||||
outfile = self.tempfile("temp_saved_python.ico")
|
outfile = self.tempfile("temp_saved_python.ico")
|
||||||
|
# Act
|
||||||
|
im.save(outfile)
|
||||||
|
|
||||||
# Act
|
with Image.open(outfile) as im_saved:
|
||||||
im.save(outfile)
|
# Assert
|
||||||
im_saved = Image.open(outfile)
|
self.assertEqual(
|
||||||
|
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
|
||||||
# Assert
|
)
|
||||||
self.assertEqual(
|
|
||||||
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_unexpected_size(self):
|
def test_unexpected_size(self):
|
||||||
# This image has been manually hexedited to state that it is 16x32
|
# This image has been manually hexedited to state that it is 16x32
|
||||||
# while the image within is still 16x16
|
# while the image within is still 16x16
|
||||||
im = self.assert_warning(
|
def open():
|
||||||
UserWarning, Image.open, "Tests/images/hopper_unexpected.ico"
|
with Image.open("Tests/images/hopper_unexpected.ico") as im:
|
||||||
)
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.size, (16, 16))
|
|
||||||
|
self.assert_warning(UserWarning, open)
|
||||||
|
|
||||||
def test_draw_reloaded(self):
|
def test_draw_reloaded(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
||||||
|
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.line((0, 0) + im.size, "#f00")
|
draw.line((0, 0) + im.size, "#f00")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
im = Image.open(outfile)
|
with Image.open(outfile) as im:
|
||||||
im.save("Tests/images/hopper_draw.ico")
|
im.save("Tests/images/hopper_draw.ico")
|
||||||
reloaded = Image.open("Tests/images/hopper_draw.ico")
|
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, ImImagePlugin
|
from PIL import Image, ImImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
# sample im
|
# sample im
|
||||||
TEST_IM = "Tests/images/hopper.im"
|
TEST_IM = "Tests/images/hopper.im"
|
||||||
|
@ -8,53 +10,69 @@ TEST_IM = "Tests/images/hopper.im"
|
||||||
|
|
||||||
class TestFileIm(PillowTestCase):
|
class TestFileIm(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "IM")
|
self.assertEqual(im.format, "IM")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_IM)
|
im = Image.open(TEST_IM)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_IM)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_IM) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_roundtrip(self):
|
def test_roundtrip(self):
|
||||||
for mode in ["RGB", "P", "PA"]:
|
for mode in ["RGB", "P", "PA"]:
|
||||||
out = self.tempfile("temp.im")
|
out = self.tempfile("temp.im")
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
self.assert_image_equal(reread, im)
|
self.assert_image_equal(reread, im)
|
||||||
|
|
||||||
def test_save_unsupported_mode(self):
|
def test_save_unsupported_mode(self):
|
||||||
out = self.tempfile("temp.im")
|
out = self.tempfile("temp.im")
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import sys
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
from PIL import Image, IptcImagePlugin
|
from PIL import Image, IptcImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper
|
||||||
|
@ -8,20 +11,20 @@ TEST_FILE = "Tests/images/iptc.jpg"
|
||||||
class TestFileIptc(PillowTestCase):
|
class TestFileIptc(PillowTestCase):
|
||||||
def test_getiptcinfo_jpg_none(self):
|
def test_getiptcinfo_jpg_none(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsNone(iptc)
|
self.assertIsNone(iptc)
|
||||||
|
|
||||||
def test_getiptcinfo_jpg_found(self):
|
def test_getiptcinfo_jpg_found(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(iptc, dict)
|
self.assertIsInstance(iptc, dict)
|
||||||
|
@ -30,10 +33,10 @@ class TestFileIptc(PillowTestCase):
|
||||||
|
|
||||||
def test_getiptcinfo_tiff_none(self):
|
def test_getiptcinfo_tiff_none(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsNone(iptc)
|
self.assertIsNone(iptc)
|
||||||
|
@ -52,12 +55,6 @@ class TestFileIptc(PillowTestCase):
|
||||||
# Arrange
|
# Arrange
|
||||||
c = b"abc"
|
c = b"abc"
|
||||||
# Temporarily redirect stdout
|
# Temporarily redirect stdout
|
||||||
try:
|
|
||||||
from cStringIO import StringIO
|
|
||||||
except ImportError:
|
|
||||||
from io import StringIO
|
|
||||||
import sys
|
|
||||||
|
|
||||||
old_stdout = sys.stdout
|
old_stdout = sys.stdout
|
||||||
sys.stdout = mystdout = StringIO()
|
sys.stdout = mystdout = StringIO()
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageFile, JpegImagePlugin
|
from PIL import Image, ImageFile, JpegImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, cjpeg_available, djpeg_available, hopper, unittest
|
from .helper import (
|
||||||
|
PillowTestCase,
|
||||||
|
cjpeg_available,
|
||||||
|
djpeg_available,
|
||||||
|
hopper,
|
||||||
|
is_win32,
|
||||||
|
unittest,
|
||||||
|
)
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
|
@ -38,52 +44,56 @@ class TestFileJpeg(PillowTestCase):
|
||||||
# internal version number
|
# internal version number
|
||||||
self.assertRegex(Image.core.jpeglib_version, r"\d+\.\d+$")
|
self.assertRegex(Image.core.jpeglib_version, r"\d+\.\d+$")
|
||||||
|
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "JPEG")
|
self.assertEqual(im.format, "JPEG")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jpeg")
|
self.assertEqual(im.get_format_mimetype(), "image/jpeg")
|
||||||
|
|
||||||
def test_app(self):
|
def test_app(self):
|
||||||
# Test APP/COM reader (@PIL135)
|
# Test APP/COM reader (@PIL135)
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
|
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
|
||||||
)
|
)
|
||||||
self.assertEqual(len(im.applist), 2)
|
self.assertEqual(len(im.applist), 2)
|
||||||
|
|
||||||
def test_cmyk(self):
|
def test_cmyk(self):
|
||||||
# Test CMYK handling. Thanks to Tim and Charlie for test data,
|
# Test CMYK handling. Thanks to Tim and Charlie for test data,
|
||||||
# Michael for getting me to look one more time.
|
# Michael for getting me to look one more time.
|
||||||
f = "Tests/images/pil_sample_cmyk.jpg"
|
f = "Tests/images/pil_sample_cmyk.jpg"
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
# the source image has red pixels in the upper left corner.
|
# the source image has red pixels in the upper left corner.
|
||||||
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
|
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
|
||||||
self.assertEqual(c, 0.0)
|
self.assertEqual(c, 0.0)
|
||||||
self.assertGreater(m, 0.8)
|
self.assertGreater(m, 0.8)
|
||||||
self.assertGreater(y, 0.8)
|
self.assertGreater(y, 0.8)
|
||||||
self.assertEqual(k, 0.0)
|
self.assertEqual(k, 0.0)
|
||||||
# the opposite corner is black
|
# the opposite corner is black
|
||||||
c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))]
|
c, m, y, k = [
|
||||||
self.assertGreater(k, 0.9)
|
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||||
# roundtrip, and check again
|
]
|
||||||
im = self.roundtrip(im)
|
self.assertGreater(k, 0.9)
|
||||||
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
|
# roundtrip, and check again
|
||||||
self.assertEqual(c, 0.0)
|
im = self.roundtrip(im)
|
||||||
self.assertGreater(m, 0.8)
|
c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
|
||||||
self.assertGreater(y, 0.8)
|
self.assertEqual(c, 0.0)
|
||||||
self.assertEqual(k, 0.0)
|
self.assertGreater(m, 0.8)
|
||||||
c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))]
|
self.assertGreater(y, 0.8)
|
||||||
self.assertGreater(k, 0.9)
|
self.assertEqual(k, 0.0)
|
||||||
|
c, m, y, k = [
|
||||||
|
x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))
|
||||||
|
]
|
||||||
|
self.assertGreater(k, 0.9)
|
||||||
|
|
||||||
def test_dpi(self):
|
def test_dpi(self):
|
||||||
def test(xdpi, ydpi=None):
|
def test(xdpi, ydpi=None):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) 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")
|
||||||
|
|
||||||
self.assertEqual(test(72), (72, 72))
|
self.assertEqual(test(72), (72, 72))
|
||||||
|
@ -93,20 +103,20 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_icc(self):
|
def test_icc(self):
|
||||||
# Test ICC support
|
# Test ICC support
|
||||||
im1 = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as im1:
|
||||||
icc_profile = im1.info["icc_profile"]
|
icc_profile = im1.info["icc_profile"]
|
||||||
self.assertEqual(len(icc_profile), 3144)
|
self.assertEqual(len(icc_profile), 3144)
|
||||||
# Roundtrip via physical file.
|
# Roundtrip via physical file.
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im1.save(f, icc_profile=icc_profile)
|
im1.save(f, icc_profile=icc_profile)
|
||||||
im2 = Image.open(f)
|
with Image.open(f) as im2:
|
||||||
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
|
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
|
||||||
# Roundtrip via memory buffer.
|
# Roundtrip via memory buffer.
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
|
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
|
||||||
self.assert_image_equal(im1, im2)
|
self.assert_image_equal(im1, im2)
|
||||||
self.assertFalse(im1.info.get("icc_profile"))
|
self.assertFalse(im1.info.get("icc_profile"))
|
||||||
self.assertTrue(im2.info.get("icc_profile"))
|
self.assertTrue(im2.info.get("icc_profile"))
|
||||||
|
|
||||||
def test_icc_big(self):
|
def test_icc_big(self):
|
||||||
# Make sure that the "extra" support handles large blocks
|
# Make sure that the "extra" support handles large blocks
|
||||||
|
@ -134,18 +144,18 @@ class TestFileJpeg(PillowTestCase):
|
||||||
# https://github.com/python-pillow/Pillow/issues/148
|
# https://github.com/python-pillow/Pillow/issues/148
|
||||||
# Sometimes the meta data on the icc_profile block is bigger than
|
# Sometimes the meta data on the icc_profile block is bigger than
|
||||||
# Image.MAXBLOCK or the image size.
|
# Image.MAXBLOCK or the image size.
|
||||||
im = Image.open("Tests/images/icc_profile_big.jpg")
|
with Image.open("Tests/images/icc_profile_big.jpg") as im:
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
icc_profile = im.info["icc_profile"]
|
icc_profile = im.info["icc_profile"]
|
||||||
# Should not raise IOError for image with icc larger than image size.
|
# Should not raise IOError for image with icc larger than image size.
|
||||||
im.save(
|
im.save(
|
||||||
f,
|
f,
|
||||||
format="JPEG",
|
format="JPEG",
|
||||||
progressive=True,
|
progressive=True,
|
||||||
quality=95,
|
quality=95,
|
||||||
icc_profile=icc_profile,
|
icc_profile=icc_profile,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_optimize(self):
|
def test_optimize(self):
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
|
@ -199,24 +209,24 @@ class TestFileJpeg(PillowTestCase):
|
||||||
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
|
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
|
||||||
|
|
||||||
def test_exif_typeerror(self):
|
def test_exif_typeerror(self):
|
||||||
im = Image.open("Tests/images/exif_typeerror.jpg")
|
with Image.open("Tests/images/exif_typeerror.jpg") as im:
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_exif_gps(self):
|
def test_exif_gps(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/exif_gps.jpg")
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
gps_index = 34853
|
gps_index = 34853
|
||||||
expected_exif_gps = {
|
expected_exif_gps = {
|
||||||
0: b"\x00\x00\x00\x01",
|
0: b"\x00\x00\x00\x01",
|
||||||
2: (4294967295, 1),
|
2: (4294967295, 1),
|
||||||
5: b"\x01",
|
5: b"\x01",
|
||||||
30: 65535,
|
30: 65535,
|
||||||
29: "1999:99:99 99:99:99",
|
29: "1999:99:99 99:99:99",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(exif[gps_index], expected_exif_gps)
|
self.assertEqual(exif[gps_index], expected_exif_gps)
|
||||||
|
@ -250,17 +260,17 @@ class TestFileJpeg(PillowTestCase):
|
||||||
33434: (4294967295, 1),
|
33434: (4294967295, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
im = Image.open("Tests/images/exif_gps.jpg")
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
|
||||||
for tag, value in expected_exif.items():
|
for tag, value in expected_exif.items():
|
||||||
self.assertEqual(value, exif[tag])
|
self.assertEqual(value, exif[tag])
|
||||||
|
|
||||||
def test_exif_gps_typeerror(self):
|
def test_exif_gps_typeerror(self):
|
||||||
im = Image.open("Tests/images/exif_gps_typeerror.jpg")
|
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
|
||||||
|
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_progressive_compat(self):
|
def test_progressive_compat(self):
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
|
@ -323,172 +333,172 @@ class TestFileJpeg(PillowTestCase):
|
||||||
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
|
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
|
||||||
|
|
||||||
def test_mp(self):
|
def test_mp(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
self.assertIsNone(im._getmp())
|
self.assertIsNone(im._getmp())
|
||||||
|
|
||||||
def test_quality_keep(self):
|
def test_quality_keep(self):
|
||||||
# RGB
|
# RGB
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im.save(f, quality="keep")
|
im.save(f, quality="keep")
|
||||||
# Grayscale
|
# Grayscale
|
||||||
im = Image.open("Tests/images/hopper_gray.jpg")
|
with Image.open("Tests/images/hopper_gray.jpg") as im:
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im.save(f, quality="keep")
|
im.save(f, quality="keep")
|
||||||
# CMYK
|
# CMYK
|
||||||
im = Image.open("Tests/images/pil_sample_cmyk.jpg")
|
with Image.open("Tests/images/pil_sample_cmyk.jpg") as im:
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im.save(f, quality="keep")
|
im.save(f, quality="keep")
|
||||||
|
|
||||||
def test_junk_jpeg_header(self):
|
def test_junk_jpeg_header(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/630
|
# https://github.com/python-pillow/Pillow/issues/630
|
||||||
filename = "Tests/images/junk_jpeg_header.jpg"
|
filename = "Tests/images/junk_jpeg_header.jpg"
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_ff00_jpeg_header(self):
|
def test_ff00_jpeg_header(self):
|
||||||
filename = "Tests/images/jpeg_ff00_header.jpg"
|
filename = "Tests/images/jpeg_ff00_header.jpg"
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_truncated_jpeg_should_read_all_the_data(self):
|
def test_truncated_jpeg_should_read_all_the_data(self):
|
||||||
filename = "Tests/images/truncated_jpeg.jpg"
|
filename = "Tests/images/truncated_jpeg.jpg"
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.load()
|
im.load()
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||||
self.assertIsNotNone(im.getbbox())
|
self.assertIsNotNone(im.getbbox())
|
||||||
|
|
||||||
def test_truncated_jpeg_throws_IOError(self):
|
def test_truncated_jpeg_throws_IOError(self):
|
||||||
filename = "Tests/images/truncated_jpeg.jpg"
|
filename = "Tests/images/truncated_jpeg.jpg"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
with self.assertRaises(IOError):
|
||||||
|
im.load()
|
||||||
|
|
||||||
with self.assertRaises(IOError):
|
# Test that the error is raised if loaded a second time
|
||||||
im.load()
|
with self.assertRaises(IOError):
|
||||||
|
im.load()
|
||||||
# Test that the error is raised if loaded a second time
|
|
||||||
with self.assertRaises(IOError):
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
def _n_qtables_helper(self, n, test_file):
|
def _n_qtables_helper(self, n, test_file):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im.save(f, qtables=[[n] * 64] * n)
|
im.save(f, qtables=[[n] * 64] * n)
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
self.assertEqual(len(im.quantization), n)
|
self.assertEqual(len(im.quantization), n)
|
||||||
reloaded = self.roundtrip(im, qtables="keep")
|
reloaded = self.roundtrip(im, qtables="keep")
|
||||||
self.assertEqual(im.quantization, reloaded.quantization)
|
self.assertEqual(im.quantization, reloaded.quantization)
|
||||||
|
|
||||||
def test_qtables(self):
|
def test_qtables(self):
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
qtables = im.quantization
|
qtables = im.quantization
|
||||||
reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)
|
reloaded = self.roundtrip(im, qtables=qtables, subsampling=0)
|
||||||
self.assertEqual(im.quantization, reloaded.quantization)
|
self.assertEqual(im.quantization, reloaded.quantization)
|
||||||
self.assert_image_similar(im, self.roundtrip(im, qtables="web_low"), 30)
|
self.assert_image_similar(im, self.roundtrip(im, qtables="web_low"), 30)
|
||||||
self.assert_image_similar(im, self.roundtrip(im, qtables="web_high"), 30)
|
self.assert_image_similar(im, self.roundtrip(im, qtables="web_high"), 30)
|
||||||
self.assert_image_similar(im, self.roundtrip(im, qtables="keep"), 30)
|
self.assert_image_similar(im, self.roundtrip(im, qtables="keep"), 30)
|
||||||
|
|
||||||
# valid bounds for baseline qtable
|
# valid bounds for baseline qtable
|
||||||
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
|
bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)]
|
||||||
self.roundtrip(im, qtables=[bounds_qtable])
|
self.roundtrip(im, qtables=[bounds_qtable])
|
||||||
|
|
||||||
# values from wizard.txt in jpeg9-a src package.
|
# values from wizard.txt in jpeg9-a src package.
|
||||||
standard_l_qtable = [
|
standard_l_qtable = [
|
||||||
int(s)
|
int(s)
|
||||||
for s in """
|
for s in """
|
||||||
16 11 10 16 24 40 51 61
|
16 11 10 16 24 40 51 61
|
||||||
12 12 14 19 26 58 60 55
|
12 12 14 19 26 58 60 55
|
||||||
14 13 16 24 40 57 69 56
|
14 13 16 24 40 57 69 56
|
||||||
14 17 22 29 51 87 80 62
|
14 17 22 29 51 87 80 62
|
||||||
18 22 37 56 68 109 103 77
|
18 22 37 56 68 109 103 77
|
||||||
24 35 55 64 81 104 113 92
|
24 35 55 64 81 104 113 92
|
||||||
49 64 78 87 103 121 120 101
|
49 64 78 87 103 121 120 101
|
||||||
72 92 95 98 112 100 103 99
|
72 92 95 98 112 100 103 99
|
||||||
""".split(
|
""".split(
|
||||||
None
|
None
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
standard_chrominance_qtable = [
|
||||||
|
int(s)
|
||||||
|
for s in """
|
||||||
|
17 18 24 47 99 99 99 99
|
||||||
|
18 21 26 66 99 99 99 99
|
||||||
|
24 26 56 99 99 99 99 99
|
||||||
|
47 66 99 99 99 99 99 99
|
||||||
|
99 99 99 99 99 99 99 99
|
||||||
|
99 99 99 99 99 99 99 99
|
||||||
|
99 99 99 99 99 99 99 99
|
||||||
|
99 99 99 99 99 99 99 99
|
||||||
|
""".split(
|
||||||
|
None
|
||||||
|
)
|
||||||
|
]
|
||||||
|
# list of qtable lists
|
||||||
|
self.assert_image_similar(
|
||||||
|
im,
|
||||||
|
self.roundtrip(
|
||||||
|
im, qtables=[standard_l_qtable, standard_chrominance_qtable]
|
||||||
|
),
|
||||||
|
30,
|
||||||
)
|
)
|
||||||
]
|
|
||||||
|
|
||||||
standard_chrominance_qtable = [
|
# tuple of qtable lists
|
||||||
int(s)
|
self.assert_image_similar(
|
||||||
for s in """
|
im,
|
||||||
17 18 24 47 99 99 99 99
|
self.roundtrip(
|
||||||
18 21 26 66 99 99 99 99
|
im, qtables=(standard_l_qtable, standard_chrominance_qtable)
|
||||||
24 26 56 99 99 99 99 99
|
),
|
||||||
47 66 99 99 99 99 99 99
|
30,
|
||||||
99 99 99 99 99 99 99 99
|
|
||||||
99 99 99 99 99 99 99 99
|
|
||||||
99 99 99 99 99 99 99 99
|
|
||||||
99 99 99 99 99 99 99 99
|
|
||||||
""".split(
|
|
||||||
None
|
|
||||||
)
|
)
|
||||||
]
|
|
||||||
# list of qtable lists
|
|
||||||
self.assert_image_similar(
|
|
||||||
im,
|
|
||||||
self.roundtrip(
|
|
||||||
im, qtables=[standard_l_qtable, standard_chrominance_qtable]
|
|
||||||
),
|
|
||||||
30,
|
|
||||||
)
|
|
||||||
|
|
||||||
# tuple of qtable lists
|
# dict of qtable lists
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(
|
||||||
im,
|
im,
|
||||||
self.roundtrip(
|
self.roundtrip(
|
||||||
im, qtables=(standard_l_qtable, standard_chrominance_qtable)
|
im, qtables={0: standard_l_qtable, 1: standard_chrominance_qtable}
|
||||||
),
|
),
|
||||||
30,
|
30,
|
||||||
)
|
)
|
||||||
|
|
||||||
# dict of qtable lists
|
self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg")
|
||||||
self.assert_image_similar(
|
self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg")
|
||||||
im,
|
self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg")
|
||||||
self.roundtrip(
|
self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg")
|
||||||
im, qtables={0: standard_l_qtable, 1: standard_chrominance_qtable}
|
self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg")
|
||||||
),
|
self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg")
|
||||||
30,
|
self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg")
|
||||||
)
|
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
|
||||||
|
|
||||||
self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg")
|
# not a sequence
|
||||||
self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg")
|
self.assertRaises(ValueError, self.roundtrip, im, qtables="a")
|
||||||
self._n_qtables_helper(2, "Tests/images/pil_sample_rgb.jpg")
|
# sequence wrong length
|
||||||
self._n_qtables_helper(3, "Tests/images/pil_sample_rgb.jpg")
|
self.assertRaises(ValueError, self.roundtrip, im, qtables=[])
|
||||||
self._n_qtables_helper(1, "Tests/images/pil_sample_cmyk.jpg")
|
# sequence wrong length
|
||||||
self._n_qtables_helper(2, "Tests/images/pil_sample_cmyk.jpg")
|
self.assertRaises(ValueError, self.roundtrip, im, qtables=[1, 2, 3, 4, 5])
|
||||||
self._n_qtables_helper(3, "Tests/images/pil_sample_cmyk.jpg")
|
|
||||||
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
|
|
||||||
|
|
||||||
# not a sequence
|
# qtable entry not a sequence
|
||||||
self.assertRaises(ValueError, self.roundtrip, im, qtables="a")
|
self.assertRaises(ValueError, self.roundtrip, im, qtables=[1])
|
||||||
# sequence wrong length
|
# qtable entry has wrong number of items
|
||||||
self.assertRaises(ValueError, self.roundtrip, im, qtables=[])
|
self.assertRaises(ValueError, self.roundtrip, im, qtables=[[1, 2, 3, 4]])
|
||||||
# sequence wrong length
|
|
||||||
self.assertRaises(ValueError, self.roundtrip, im, qtables=[1, 2, 3, 4, 5])
|
|
||||||
|
|
||||||
# qtable entry not a sequence
|
|
||||||
self.assertRaises(ValueError, self.roundtrip, im, qtables=[1])
|
|
||||||
# qtable entry has wrong number of items
|
|
||||||
self.assertRaises(ValueError, self.roundtrip, im, qtables=[[1, 2, 3, 4]])
|
|
||||||
|
|
||||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||||
def test_load_djpeg(self):
|
def test_load_djpeg(self):
|
||||||
img = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as img:
|
||||||
img.load_djpeg()
|
img.load_djpeg()
|
||||||
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
|
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
|
||||||
|
|
||||||
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
||||||
def test_save_cjpeg(self):
|
def test_save_cjpeg(self):
|
||||||
img = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as img:
|
||||||
|
tempfile = self.tempfile("temp.jpg")
|
||||||
tempfile = self.tempfile("temp.jpg")
|
JpegImagePlugin._save_cjpeg(img, 0, tempfile)
|
||||||
JpegImagePlugin._save_cjpeg(img, 0, tempfile)
|
# Default save quality is 75%, so a tiny bit of difference is alright
|
||||||
# Default save quality is 75%, so a tiny bit of difference is alright
|
self.assert_image_similar(img, Image.open(tempfile), 17)
|
||||||
self.assert_image_similar(img, Image.open(tempfile), 17)
|
|
||||||
|
|
||||||
def test_no_duplicate_0x1001_tag(self):
|
def test_no_duplicate_0x1001_tag(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -505,12 +515,11 @@ class TestFileJpeg(PillowTestCase):
|
||||||
f = self.tempfile("temp.jpeg")
|
f = self.tempfile("temp.jpeg")
|
||||||
im.save(f, quality=100, optimize=True)
|
im.save(f, quality=100, optimize=True)
|
||||||
|
|
||||||
reloaded = Image.open(f)
|
with Image.open(f) as reloaded:
|
||||||
|
# none of these should crash
|
||||||
# none of these should crash
|
reloaded.save(f, quality="keep")
|
||||||
reloaded.save(f, quality="keep")
|
reloaded.save(f, quality="keep", progressive=True)
|
||||||
reloaded.save(f, quality="keep", progressive=True)
|
reloaded.save(f, quality="keep", optimize=True)
|
||||||
reloaded.save(f, quality="keep", optimize=True)
|
|
||||||
|
|
||||||
def test_bad_mpo_header(self):
|
def test_bad_mpo_header(self):
|
||||||
""" Treat unknown MPO as JPEG """
|
""" Treat unknown MPO as JPEG """
|
||||||
|
@ -519,10 +528,10 @@ class TestFileJpeg(PillowTestCase):
|
||||||
# Act
|
# Act
|
||||||
# Shouldn't raise error
|
# Shouldn't raise error
|
||||||
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
||||||
im = self.assert_warning(UserWarning, Image.open, fn)
|
with self.assert_warning(UserWarning, Image.open, fn) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "JPEG")
|
self.assertEqual(im.format, "JPEG")
|
||||||
|
|
||||||
def test_save_correct_modes(self):
|
def test_save_correct_modes(self):
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
|
@ -540,121 +549,127 @@ class TestFileJpeg(PillowTestCase):
|
||||||
def test_save_tiff_with_dpi(self):
|
def test_save_tiff_with_dpi(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
outfile = self.tempfile("temp.tif")
|
outfile = self.tempfile("temp.tif")
|
||||||
im = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im.save(outfile, "JPEG", dpi=im.info["dpi"])
|
im.save(outfile, "JPEG", dpi=im.info["dpi"])
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
reloaded.load()
|
reloaded.load()
|
||||||
self.assertEqual(im.info["dpi"], reloaded.info["dpi"])
|
self.assertEqual(im.info["dpi"], reloaded.info["dpi"])
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/iptc_roundUp.jpg")
|
with Image.open("Tests/images/iptc_roundUp.jpg") as im:
|
||||||
self.assertEqual(im.info["dpi"], (44, 44))
|
self.assertEqual(im.info["dpi"], (44, 44))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/iptc_roundDown.jpg")
|
with Image.open("Tests/images/iptc_roundDown.jpg") as im:
|
||||||
self.assertEqual(im.info["dpi"], (2, 2))
|
self.assertEqual(im.info["dpi"], (2, 2))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.jpg")
|
outfile = self.tempfile("temp.jpg")
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
|
im.save(outfile, dpi=(72.2, 72.2))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.2, 72.2))
|
with Image.open(outfile) as reloaded:
|
||||||
reloaded = Image.open(outfile)
|
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.8, 72.8))
|
im.save(outfile, dpi=(72.8, 72.8))
|
||||||
reloaded = Image.open(outfile)
|
|
||||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
with Image.open(outfile) as reloaded:
|
||||||
|
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||||
|
|
||||||
def test_dpi_tuple_from_exif(self):
|
def test_dpi_tuple_from_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
||||||
# EXIF XResolution is (2000000, 10000)
|
# EXIF XResolution is (2000000, 10000)
|
||||||
im = Image.open("Tests/images/photoshop-200dpi.jpg")
|
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (200, 200))
|
self.assertEqual(im.info.get("dpi"), (200, 200))
|
||||||
|
|
||||||
def test_dpi_int_from_exif(self):
|
def test_dpi_int_from_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This image has DPI in EXIF not metadata
|
# This image has DPI in EXIF not metadata
|
||||||
# EXIF XResolution is 72
|
# EXIF XResolution is 72
|
||||||
im = Image.open("Tests/images/exif-72dpi-int.jpg")
|
with Image.open("Tests/images/exif-72dpi-int.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_dpi_from_dpcm_exif(self):
|
def test_dpi_from_dpcm_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
|
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
|
||||||
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
|
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
|
||||||
im = Image.open("Tests/images/exif-200dpcm.jpg")
|
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (508, 508))
|
self.assertEqual(im.info.get("dpi"), (508, 508))
|
||||||
|
|
||||||
def test_dpi_exif_zero_division(self):
|
def test_dpi_exif_zero_division(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
|
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
|
||||||
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
|
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
|
||||||
im = Image.open("Tests/images/exif-dpi-zerodivision.jpg")
|
with Image.open("Tests/images/exif-dpi-zerodivision.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# This should return the default, and not raise a ZeroDivisionError
|
# This should return the default, and not raise a ZeroDivisionError
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_no_dpi_in_exif(self):
|
def test_no_dpi_in_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
|
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
|
||||||
# exiftool "-*resolution*"= photoshop-200dpi.jpg
|
# exiftool "-*resolution*"= photoshop-200dpi.jpg
|
||||||
im = Image.open("Tests/images/no-dpi-in-exif.jpg")
|
with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# "When the image resolution is unknown, 72 [dpi] is designated."
|
# "When the image resolution is unknown, 72 [dpi] is designated."
|
||||||
# http://www.exiv2.org/tags.html
|
# http://www.exiv2.org/tags.html
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_invalid_exif(self):
|
def test_invalid_exif(self):
|
||||||
# This is no-dpi-in-exif with the tiff header of the exif block
|
# This is no-dpi-in-exif with the tiff header of the exif block
|
||||||
# hexedited from MM * to FF FF FF FF
|
# hexedited from MM * to FF FF FF FF
|
||||||
im = Image.open("Tests/images/invalid-exif.jpg")
|
with Image.open("Tests/images/invalid-exif.jpg") as im:
|
||||||
|
|
||||||
# This should return the default, and not a SyntaxError or
|
# This should return the default, and not a SyntaxError or
|
||||||
# OSError for unidentified image.
|
# OSError for unidentified image.
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_ifd_offset_exif(self):
|
def test_ifd_offset_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This image has been manually hexedited to have an IFD offset of 10,
|
# This image has been manually hexedited to have an IFD offset of 10,
|
||||||
# in contrast to normal 8
|
# in contrast to normal 8
|
||||||
im = Image.open("Tests/images/exif-ifd-offset.jpg")
|
with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
|
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
|
||||||
|
|
||||||
def test_photoshop(self):
|
def test_photoshop(self):
|
||||||
im = Image.open("Tests/images/photoshop-200dpi.jpg")
|
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.info["photoshop"][0x03ED],
|
im.info["photoshop"][0x03ED],
|
||||||
{
|
{
|
||||||
"XResolution": 200.0,
|
"XResolution": 200.0,
|
||||||
"DisplayedUnitsX": 1,
|
"DisplayedUnitsX": 1,
|
||||||
"YResolution": 200.0,
|
"YResolution": 200.0,
|
||||||
"DisplayedUnitsY": 1,
|
"DisplayedUnitsY": 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Test that the image can still load, even with broken Photoshop data
|
||||||
|
# This image had the APP13 length hexedited to be smaller
|
||||||
|
with Image.open("Tests/images/photoshop-200dpi-broken.jpg") as im_broken:
|
||||||
|
self.assert_image_equal(im_broken, im)
|
||||||
|
|
||||||
# This image does not contain a Photoshop header string
|
# This image does not contain a Photoshop header string
|
||||||
im = Image.open("Tests/images/app13.jpg")
|
with Image.open("Tests/images/app13.jpg") as im:
|
||||||
self.assertNotIn("photoshop", im.info)
|
self.assertNotIn("photoshop", im.info)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only")
|
@unittest.skipUnless(is_win32(), "Windows only")
|
||||||
class TestFileCloseW32(PillowTestCase):
|
class TestFileCloseW32(PillowTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
|
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
|
||||||
|
|
|
@ -33,18 +33,18 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
# Internal version number
|
# Internal version number
|
||||||
self.assertRegex(Image.core.jp2klib_version, r"\d+\.\d+\.\d+$")
|
self.assertRegex(Image.core.jp2klib_version, r"\d+\.\d+\.\d+$")
|
||||||
|
|
||||||
im = Image.open("Tests/images/test-card-lossless.jp2")
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
px = im.load()
|
px = im.load()
|
||||||
self.assertEqual(px[0, 0], (0, 0, 0))
|
self.assertEqual(px[0, 0], (0, 0, 0))
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
self.assertEqual(im.format, "JPEG2000")
|
self.assertEqual(im.format, "JPEG2000")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jp2")
|
self.assertEqual(im.get_format_mimetype(), "image/jp2")
|
||||||
|
|
||||||
def test_jpf(self):
|
def test_jpf(self):
|
||||||
im = Image.open("Tests/images/balloon.jpf")
|
with Image.open("Tests/images/balloon.jpf") as im:
|
||||||
self.assertEqual(im.format, "JPEG2000")
|
self.assertEqual(im.format, "JPEG2000")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jpx")
|
self.assertEqual(im.get_format_mimetype(), "image/jpx")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -54,24 +54,24 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
def test_bytesio(self):
|
def test_bytesio(self):
|
||||||
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
|
with open("Tests/images/test-card-lossless.jp2", "rb") as f:
|
||||||
data = BytesIO(f.read())
|
data = BytesIO(f.read())
|
||||||
im = Image.open(data)
|
with Image.open(data) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, test_card, 1.0e-3)
|
self.assert_image_similar(im, test_card, 1.0e-3)
|
||||||
|
|
||||||
# These two test pre-written JPEG 2000 files that were not written with
|
# These two test pre-written JPEG 2000 files that were not written with
|
||||||
# PIL (they were made using Adobe Photoshop)
|
# PIL (they were made using Adobe Photoshop)
|
||||||
|
|
||||||
def test_lossless(self):
|
def test_lossless(self):
|
||||||
im = Image.open("Tests/images/test-card-lossless.jp2")
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
im.load()
|
im.load()
|
||||||
outfile = self.tempfile("temp_test-card.png")
|
outfile = self.tempfile("temp_test-card.png")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
self.assert_image_similar(im, test_card, 1.0e-3)
|
self.assert_image_similar(im, test_card, 1.0e-3)
|
||||||
|
|
||||||
def test_lossy_tiled(self):
|
def test_lossy_tiled(self):
|
||||||
im = Image.open("Tests/images/test-card-lossy-tiled.jp2")
|
with Image.open("Tests/images/test-card-lossy-tiled.jp2") as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, test_card, 2.0)
|
self.assert_image_similar(im, test_card, 2.0)
|
||||||
|
|
||||||
def test_lossless_rt(self):
|
def test_lossless_rt(self):
|
||||||
im = self.roundtrip(test_card)
|
im = self.roundtrip(test_card)
|
||||||
|
@ -110,10 +110,10 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
self.assert_image_equal(im, test_card)
|
self.assert_image_equal(im, test_card)
|
||||||
|
|
||||||
def test_reduce(self):
|
def test_reduce(self):
|
||||||
im = Image.open("Tests/images/test-card-lossless.jp2")
|
with Image.open("Tests/images/test-card-lossless.jp2") as im:
|
||||||
im.reduce = 2
|
im.reduce = 2
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (160, 120))
|
self.assertEqual(im.size, (160, 120))
|
||||||
|
|
||||||
def test_layers_type(self):
|
def test_layers_type(self):
|
||||||
outfile = self.tempfile("temp_layers.jp2")
|
outfile = self.tempfile("temp_layers.jp2")
|
||||||
|
@ -132,64 +132,58 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
)
|
)
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
|
|
||||||
im = Image.open(out)
|
with Image.open(out) as im:
|
||||||
im.layers = 1
|
im.layers = 1
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, test_card, 13)
|
self.assert_image_similar(im, test_card, 13)
|
||||||
|
|
||||||
out.seek(0)
|
out.seek(0)
|
||||||
im = Image.open(out)
|
with Image.open(out) as im:
|
||||||
im.layers = 3
|
im.layers = 3
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, test_card, 0.4)
|
self.assert_image_similar(im, test_card, 0.4)
|
||||||
|
|
||||||
def test_rgba(self):
|
def test_rgba(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
j2k = Image.open("Tests/images/rgb_trns_ycbc.j2k")
|
with Image.open("Tests/images/rgb_trns_ycbc.j2k") as j2k:
|
||||||
jp2 = Image.open("Tests/images/rgb_trns_ycbc.jp2")
|
with Image.open("Tests/images/rgb_trns_ycbc.jp2") as jp2:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
j2k.load()
|
j2k.load()
|
||||||
jp2.load()
|
jp2.load()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(j2k.mode, "RGBA")
|
self.assertEqual(j2k.mode, "RGBA")
|
||||||
self.assertEqual(jp2.mode, "RGBA")
|
self.assertEqual(jp2.mode, "RGBA")
|
||||||
|
|
||||||
def test_16bit_monochrome_has_correct_mode(self):
|
def test_16bit_monochrome_has_correct_mode(self):
|
||||||
|
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
|
||||||
|
j2k.load()
|
||||||
|
self.assertEqual(j2k.mode, "I;16")
|
||||||
|
|
||||||
j2k = Image.open("Tests/images/16bit.cropped.j2k")
|
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
|
||||||
jp2 = Image.open("Tests/images/16bit.cropped.jp2")
|
jp2.load()
|
||||||
|
self.assertEqual(jp2.mode, "I;16")
|
||||||
j2k.load()
|
|
||||||
jp2.load()
|
|
||||||
|
|
||||||
self.assertEqual(j2k.mode, "I;16")
|
|
||||||
self.assertEqual(jp2.mode, "I;16")
|
|
||||||
|
|
||||||
def test_16bit_monochrome_jp2_like_tiff(self):
|
def test_16bit_monochrome_jp2_like_tiff(self):
|
||||||
|
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
||||||
tiff_16bit = Image.open("Tests/images/16bit.cropped.tif")
|
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
|
||||||
jp2 = Image.open("Tests/images/16bit.cropped.jp2")
|
self.assert_image_similar(jp2, tiff_16bit, 1e-3)
|
||||||
self.assert_image_similar(jp2, tiff_16bit, 1e-3)
|
|
||||||
|
|
||||||
def test_16bit_monochrome_j2k_like_tiff(self):
|
def test_16bit_monochrome_j2k_like_tiff(self):
|
||||||
|
with Image.open("Tests/images/16bit.cropped.tif") as tiff_16bit:
|
||||||
tiff_16bit = Image.open("Tests/images/16bit.cropped.tif")
|
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
|
||||||
j2k = Image.open("Tests/images/16bit.cropped.j2k")
|
self.assert_image_similar(j2k, tiff_16bit, 1e-3)
|
||||||
self.assert_image_similar(j2k, tiff_16bit, 1e-3)
|
|
||||||
|
|
||||||
def test_16bit_j2k_roundtrips(self):
|
def test_16bit_j2k_roundtrips(self):
|
||||||
|
with Image.open("Tests/images/16bit.cropped.j2k") as j2k:
|
||||||
j2k = Image.open("Tests/images/16bit.cropped.j2k")
|
im = self.roundtrip(j2k)
|
||||||
im = self.roundtrip(j2k)
|
self.assert_image_equal(im, j2k)
|
||||||
self.assert_image_equal(im, j2k)
|
|
||||||
|
|
||||||
def test_16bit_jp2_roundtrips(self):
|
def test_16bit_jp2_roundtrips(self):
|
||||||
|
with Image.open("Tests/images/16bit.cropped.jp2") as jp2:
|
||||||
jp2 = Image.open("Tests/images/16bit.cropped.jp2")
|
im = self.roundtrip(jp2)
|
||||||
im = self.roundtrip(jp2)
|
self.assert_image_equal(im, jp2)
|
||||||
self.assert_image_equal(im, jp2)
|
|
||||||
|
|
||||||
def test_unbound_local(self):
|
def test_unbound_local(self):
|
||||||
# prepatch, a malformed jp2 file could cause an UnboundLocalError
|
# prepatch, a malformed jp2 file could cause an UnboundLocalError
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from __future__ import print_function
|
import base64
|
||||||
|
|
||||||
import distutils.version
|
import distutils.version
|
||||||
import io
|
import io
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -9,7 +8,6 @@ from collections import namedtuple
|
||||||
from ctypes import c_float
|
from ctypes import c_float
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin, TiffTags, features
|
from PIL import Image, TiffImagePlugin, TiffTags, features
|
||||||
from PIL._util import py3
|
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
|
@ -49,25 +47,23 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
"""Test the ordinary file path load path"""
|
"""Test the ordinary file path load path"""
|
||||||
|
|
||||||
test_file = "Tests/images/hopper_g4_500.tif"
|
test_file = "Tests/images/hopper_g4_500.tif"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assertEqual(im.size, (500, 500))
|
||||||
self.assertEqual(im.size, (500, 500))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
||||||
def test_g4_large(self):
|
def test_g4_large(self):
|
||||||
test_file = "Tests/images/pport_g4.tif"
|
test_file = "Tests/images/pport_g4.tif"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self._assert_noerr(im)
|
self._assert_noerr(im)
|
||||||
|
|
||||||
def test_g4_tiff_file(self):
|
def test_g4_tiff_file(self):
|
||||||
"""Testing the string load path"""
|
"""Testing the string load path"""
|
||||||
|
|
||||||
test_file = "Tests/images/hopper_g4_500.tif"
|
test_file = "Tests/images/hopper_g4_500.tif"
|
||||||
with open(test_file, "rb") as f:
|
with open(test_file, "rb") as f:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
|
self.assertEqual(im.size, (500, 500))
|
||||||
self.assertEqual(im.size, (500, 500))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
||||||
def test_g4_tiff_bytesio(self):
|
def test_g4_tiff_bytesio(self):
|
||||||
"""Testing the stringio loading code path"""
|
"""Testing the stringio loading code path"""
|
||||||
|
@ -76,10 +72,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
with open(test_file, "rb") as f:
|
with open(test_file, "rb") as f:
|
||||||
s.write(f.read())
|
s.write(f.read())
|
||||||
s.seek(0)
|
s.seek(0)
|
||||||
im = Image.open(s)
|
with Image.open(s) as im:
|
||||||
|
self.assertEqual(im.size, (500, 500))
|
||||||
self.assertEqual(im.size, (500, 500))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
||||||
def test_g4_non_disk_file_object(self):
|
def test_g4_non_disk_file_object(self):
|
||||||
"""Testing loading from non-disk non-BytesIO file object"""
|
"""Testing loading from non-disk non-BytesIO file object"""
|
||||||
|
@ -89,69 +84,63 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
s.write(f.read())
|
s.write(f.read())
|
||||||
s.seek(0)
|
s.seek(0)
|
||||||
r = io.BufferedReader(s)
|
r = io.BufferedReader(s)
|
||||||
im = Image.open(r)
|
with Image.open(r) as im:
|
||||||
|
self.assertEqual(im.size, (500, 500))
|
||||||
self.assertEqual(im.size, (500, 500))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
||||||
def test_g4_eq_png(self):
|
def test_g4_eq_png(self):
|
||||||
""" Checking that we're actually getting the data that we expect"""
|
""" Checking that we're actually getting the data that we expect"""
|
||||||
png = Image.open("Tests/images/hopper_bw_500.png")
|
with Image.open("Tests/images/hopper_bw_500.png") as png:
|
||||||
g4 = Image.open("Tests/images/hopper_g4_500.tif")
|
with Image.open("Tests/images/hopper_g4_500.tif") as g4:
|
||||||
|
self.assert_image_equal(g4, png)
|
||||||
self.assert_image_equal(g4, png)
|
|
||||||
|
|
||||||
# see https://github.com/python-pillow/Pillow/issues/279
|
# see https://github.com/python-pillow/Pillow/issues/279
|
||||||
def test_g4_fillorder_eq_png(self):
|
def test_g4_fillorder_eq_png(self):
|
||||||
""" Checking that we're actually getting the data that we expect"""
|
""" Checking that we're actually getting the data that we expect"""
|
||||||
png = Image.open("Tests/images/g4-fillorder-test.png")
|
with Image.open("Tests/images/g4-fillorder-test.png") as png:
|
||||||
g4 = Image.open("Tests/images/g4-fillorder-test.tif")
|
with Image.open("Tests/images/g4-fillorder-test.tif") as g4:
|
||||||
|
self.assert_image_equal(g4, png)
|
||||||
self.assert_image_equal(g4, png)
|
|
||||||
|
|
||||||
def test_g4_write(self):
|
def test_g4_write(self):
|
||||||
"""Checking to see that the saved image is the same as what we wrote"""
|
"""Checking to see that the saved image is the same as what we wrote"""
|
||||||
test_file = "Tests/images/hopper_g4_500.tif"
|
test_file = "Tests/images/hopper_g4_500.tif"
|
||||||
orig = Image.open(test_file)
|
with Image.open(test_file) as orig:
|
||||||
|
out = self.tempfile("temp.tif")
|
||||||
|
rot = orig.transpose(Image.ROTATE_90)
|
||||||
|
self.assertEqual(rot.size, (500, 500))
|
||||||
|
rot.save(out)
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
with Image.open(out) as reread:
|
||||||
rot = orig.transpose(Image.ROTATE_90)
|
self.assertEqual(reread.size, (500, 500))
|
||||||
self.assertEqual(rot.size, (500, 500))
|
self._assert_noerr(reread)
|
||||||
rot.save(out)
|
self.assert_image_equal(reread, rot)
|
||||||
|
self.assertEqual(reread.info["compression"], "group4")
|
||||||
|
|
||||||
reread = Image.open(out)
|
self.assertEqual(reread.info["compression"], orig.info["compression"])
|
||||||
self.assertEqual(reread.size, (500, 500))
|
|
||||||
self._assert_noerr(reread)
|
|
||||||
self.assert_image_equal(reread, rot)
|
|
||||||
self.assertEqual(reread.info["compression"], "group4")
|
|
||||||
|
|
||||||
self.assertEqual(reread.info["compression"], orig.info["compression"])
|
self.assertNotEqual(orig.tobytes(), reread.tobytes())
|
||||||
|
|
||||||
self.assertNotEqual(orig.tobytes(), reread.tobytes())
|
|
||||||
|
|
||||||
def test_adobe_deflate_tiff(self):
|
def test_adobe_deflate_tiff(self):
|
||||||
test_file = "Tests/images/tiff_adobe_deflate.tif"
|
test_file = "Tests/images/tiff_adobe_deflate.tif"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assertEqual(im.mode, "RGB")
|
||||||
|
self.assertEqual(im.size, (278, 374))
|
||||||
|
self.assertEqual(im.tile[0][:3], ("libtiff", (0, 0, 278, 374), 0))
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assertEqual(im.size, (278, 374))
|
|
||||||
self.assertEqual(im.tile[0][:3], ("libtiff", (0, 0, 278, 374), 0))
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_write_metadata(self):
|
def test_write_metadata(self):
|
||||||
""" Test metadata writing through libtiff """
|
""" Test metadata writing through libtiff """
|
||||||
for legacy_api in [False, True]:
|
for legacy_api in [False, True]:
|
||||||
img = Image.open("Tests/images/hopper_g4.tif")
|
|
||||||
f = self.tempfile("temp.tiff")
|
f = self.tempfile("temp.tiff")
|
||||||
|
with Image.open("Tests/images/hopper_g4.tif") as img:
|
||||||
|
img.save(f, tiffinfo=img.tag)
|
||||||
|
|
||||||
img.save(f, tiffinfo=img.tag)
|
if legacy_api:
|
||||||
|
original = img.tag.named()
|
||||||
if legacy_api:
|
else:
|
||||||
original = img.tag.named()
|
original = img.tag_v2.named()
|
||||||
else:
|
|
||||||
original = img.tag_v2.named()
|
|
||||||
|
|
||||||
# PhotometricInterpretation is set from SAVE_INFO,
|
# PhotometricInterpretation is set from SAVE_INFO,
|
||||||
# not the original image.
|
# not the original image.
|
||||||
|
@ -162,11 +151,11 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
"PhotometricInterpretation",
|
"PhotometricInterpretation",
|
||||||
]
|
]
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
if legacy_api:
|
if legacy_api:
|
||||||
reloaded = loaded.tag.named()
|
reloaded = loaded.tag.named()
|
||||||
else:
|
else:
|
||||||
reloaded = loaded.tag_v2.named()
|
reloaded = loaded.tag_v2.named()
|
||||||
|
|
||||||
for tag, value in itertools.chain(reloaded.items(), original.items()):
|
for tag, value in itertools.chain(reloaded.items(), original.items()):
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
|
@ -207,44 +196,44 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
# Exclude ones that have special meaning
|
# Exclude ones that have special meaning
|
||||||
# that we're already testing them
|
# that we're already testing them
|
||||||
im = Image.open("Tests/images/hopper_g4.tif")
|
with Image.open("Tests/images/hopper_g4.tif") as im:
|
||||||
for tag in im.tag_v2:
|
for tag in im.tag_v2:
|
||||||
try:
|
try:
|
||||||
del core_items[tag]
|
del core_items[tag]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Type codes:
|
# Type codes:
|
||||||
# 2: "ascii",
|
# 2: "ascii",
|
||||||
# 3: "short",
|
# 3: "short",
|
||||||
# 4: "long",
|
# 4: "long",
|
||||||
# 5: "rational",
|
# 5: "rational",
|
||||||
# 12: "double",
|
# 12: "double",
|
||||||
# Type: dummy value
|
# Type: dummy value
|
||||||
values = {
|
values = {
|
||||||
2: "test",
|
2: "test",
|
||||||
3: 1,
|
3: 1,
|
||||||
4: 2 ** 20,
|
4: 2 ** 20,
|
||||||
5: TiffImagePlugin.IFDRational(100, 1),
|
5: TiffImagePlugin.IFDRational(100, 1),
|
||||||
12: 1.05,
|
12: 1.05,
|
||||||
}
|
}
|
||||||
|
|
||||||
new_ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
new_ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
for tag, info in core_items.items():
|
for tag, info in core_items.items():
|
||||||
if info.length == 1:
|
if info.length == 1:
|
||||||
new_ifd[tag] = values[info.type]
|
new_ifd[tag] = values[info.type]
|
||||||
if info.length == 0:
|
if info.length == 0:
|
||||||
new_ifd[tag] = tuple(values[info.type] for _ in range(3))
|
new_ifd[tag] = tuple(values[info.type] for _ in range(3))
|
||||||
else:
|
else:
|
||||||
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length))
|
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length))
|
||||||
|
|
||||||
# Extra samples really doesn't make sense in this application.
|
# Extra samples really doesn't make sense in this application.
|
||||||
del new_ifd[338]
|
del new_ifd[338]
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
|
|
||||||
im.save(out, tiffinfo=new_ifd)
|
im.save(out, tiffinfo=new_ifd)
|
||||||
|
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
@ -263,7 +252,6 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
tc(4.25, TiffTags.FLOAT, True),
|
tc(4.25, TiffTags.FLOAT, True),
|
||||||
tc(4.25, TiffTags.DOUBLE, True),
|
tc(4.25, TiffTags.DOUBLE, True),
|
||||||
tc("custom tag value", TiffTags.ASCII, True),
|
tc("custom tag value", TiffTags.ASCII, True),
|
||||||
tc(u"custom tag value", TiffTags.ASCII, True),
|
|
||||||
tc(b"custom tag value", TiffTags.BYTE, True),
|
tc(b"custom tag value", TiffTags.BYTE, True),
|
||||||
tc((4, 5, 6), TiffTags.SHORT, True),
|
tc((4, 5, 6), TiffTags.SHORT, True),
|
||||||
tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True),
|
tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True),
|
||||||
|
@ -301,21 +289,21 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
im.save(out, tiffinfo=tiffinfo)
|
im.save(out, tiffinfo=tiffinfo)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
for tag, value in tiffinfo.items():
|
for tag, value in tiffinfo.items():
|
||||||
reloaded_value = reloaded.tag_v2[tag]
|
reloaded_value = reloaded.tag_v2[tag]
|
||||||
if (
|
if (
|
||||||
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
|
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
|
||||||
and libtiff
|
and libtiff
|
||||||
):
|
):
|
||||||
# libtiff does not support real RATIONALS
|
# libtiff does not support real RATIONALS
|
||||||
self.assertAlmostEqual(float(reloaded_value), float(value))
|
self.assertAlmostEqual(float(reloaded_value), float(value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if libtiff and isinstance(value, bytes):
|
if libtiff and isinstance(value, bytes):
|
||||||
value = value.decode()
|
value = value.decode()
|
||||||
|
|
||||||
self.assertEqual(reloaded_value, value)
|
self.assertEqual(reloaded_value, value)
|
||||||
|
|
||||||
# Test with types
|
# Test with types
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
@ -342,93 +330,81 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
im.save(out, dpi=(72, 72))
|
im.save(out, dpi=(72, 72))
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
|
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
|
||||||
|
|
||||||
def test_g3_compression(self):
|
def test_g3_compression(self):
|
||||||
i = Image.open("Tests/images/hopper_g4_500.tif")
|
with Image.open("Tests/images/hopper_g4_500.tif") as i:
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
i.save(out, compression="group3")
|
i.save(out, compression="group3")
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["compression"], "group3")
|
self.assertEqual(reread.info["compression"], "group3")
|
||||||
self.assert_image_equal(reread, i)
|
self.assert_image_equal(reread, i)
|
||||||
|
|
||||||
def test_little_endian(self):
|
def test_little_endian(self):
|
||||||
im = Image.open("Tests/images/16bit.deflate.tif")
|
with Image.open("Tests/images/16bit.deflate.tif") as im:
|
||||||
self.assertEqual(im.getpixel((0, 0)), 480)
|
self.assertEqual(im.getpixel((0, 0)), 480)
|
||||||
self.assertEqual(im.mode, "I;16")
|
self.assertEqual(im.mode, "I;16")
|
||||||
|
|
||||||
b = im.tobytes()
|
b = im.tobytes()
|
||||||
# Bytes are in image native order (little endian)
|
# Bytes are in image native order (little endian)
|
||||||
if py3:
|
|
||||||
self.assertEqual(b[0], ord(b"\xe0"))
|
self.assertEqual(b[0], ord(b"\xe0"))
|
||||||
self.assertEqual(b[1], ord(b"\x01"))
|
self.assertEqual(b[1], ord(b"\x01"))
|
||||||
else:
|
|
||||||
self.assertEqual(b[0], b"\xe0")
|
|
||||||
self.assertEqual(b[1], b"\x01")
|
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
# out = "temp.le.tif"
|
# out = "temp.le.tif"
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
self.assertEqual(reread.info["compression"], im.info["compression"])
|
||||||
self.assertEqual(reread.info["compression"], im.info["compression"])
|
self.assertEqual(reread.getpixel((0, 0)), 480)
|
||||||
self.assertEqual(reread.getpixel((0, 0)), 480)
|
|
||||||
# UNDONE - libtiff defaults to writing in native endian, so
|
# UNDONE - libtiff defaults to writing in native endian, so
|
||||||
# on big endian, we'll get back mode = 'I;16B' here.
|
# on big endian, we'll get back mode = 'I;16B' here.
|
||||||
|
|
||||||
def test_big_endian(self):
|
def test_big_endian(self):
|
||||||
im = Image.open("Tests/images/16bit.MM.deflate.tif")
|
with Image.open("Tests/images/16bit.MM.deflate.tif") as im:
|
||||||
|
self.assertEqual(im.getpixel((0, 0)), 480)
|
||||||
|
self.assertEqual(im.mode, "I;16B")
|
||||||
|
|
||||||
self.assertEqual(im.getpixel((0, 0)), 480)
|
b = im.tobytes()
|
||||||
self.assertEqual(im.mode, "I;16B")
|
|
||||||
|
|
||||||
b = im.tobytes()
|
# Bytes are in image native order (big endian)
|
||||||
|
|
||||||
# Bytes are in image native order (big endian)
|
|
||||||
if py3:
|
|
||||||
self.assertEqual(b[0], ord(b"\x01"))
|
self.assertEqual(b[0], ord(b"\x01"))
|
||||||
self.assertEqual(b[1], ord(b"\xe0"))
|
self.assertEqual(b[1], ord(b"\xe0"))
|
||||||
else:
|
|
||||||
self.assertEqual(b[0], b"\x01")
|
|
||||||
self.assertEqual(b[1], b"\xe0")
|
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
self.assertEqual(reread.info["compression"], im.info["compression"])
|
||||||
self.assertEqual(reread.info["compression"], im.info["compression"])
|
self.assertEqual(reread.getpixel((0, 0)), 480)
|
||||||
self.assertEqual(reread.getpixel((0, 0)), 480)
|
|
||||||
|
|
||||||
def test_g4_string_info(self):
|
def test_g4_string_info(self):
|
||||||
"""Tests String data in info directory"""
|
"""Tests String data in info directory"""
|
||||||
test_file = "Tests/images/hopper_g4_500.tif"
|
test_file = "Tests/images/hopper_g4_500.tif"
|
||||||
orig = Image.open(test_file)
|
with Image.open(test_file) as orig:
|
||||||
|
out = self.tempfile("temp.tif")
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
orig.tag[269] = "temp.tif"
|
||||||
|
orig.save(out)
|
||||||
|
|
||||||
orig.tag[269] = "temp.tif"
|
with Image.open(out) as reread:
|
||||||
orig.save(out)
|
self.assertEqual("temp.tif", reread.tag_v2[269])
|
||||||
|
self.assertEqual("temp.tif", reread.tag[269][0])
|
||||||
reread = Image.open(out)
|
|
||||||
self.assertEqual("temp.tif", reread.tag_v2[269])
|
|
||||||
self.assertEqual("temp.tif", reread.tag[269][0])
|
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
""" Are we generating the same interpretation
|
||||||
of the image as Imagemagick is? """
|
of the image as Imagemagick is? """
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/12bit.cropped.tif")
|
with Image.open("Tests/images/12bit.cropped.tif") as im:
|
||||||
im.load()
|
im.load()
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
# to make the target --
|
# to make the target --
|
||||||
# convert 12bit.cropped.tif -depth 16 tmp.tif
|
# convert 12bit.cropped.tif -depth 16 tmp.tif
|
||||||
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
|
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
|
||||||
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
|
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
|
||||||
# so we need to unshift so that the integer values are the same.
|
# so we need to unshift so that the integer values are the same.
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
|
self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
|
||||||
|
|
||||||
def test_blur(self):
|
def test_blur(self):
|
||||||
# test case from irc, how to do blur on b/w image
|
# test case from irc, how to do blur on b/w image
|
||||||
|
@ -436,16 +412,16 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
from PIL import ImageFilter
|
from PIL import ImageFilter
|
||||||
|
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
im = Image.open("Tests/images/pport_g4.tif")
|
with Image.open("Tests/images/pport_g4.tif") as im:
|
||||||
im = im.convert("L")
|
im = im.convert("L")
|
||||||
|
|
||||||
im = im.filter(ImageFilter.GaussianBlur(4))
|
im = im.filter(ImageFilter.GaussianBlur(4))
|
||||||
im.save(out, compression="tiff_adobe_deflate")
|
im.save(out, compression="tiff_adobe_deflate")
|
||||||
|
|
||||||
im2 = Image.open(out)
|
with Image.open(out) as im2:
|
||||||
im2.load()
|
im2.load()
|
||||||
|
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
||||||
def test_compressions(self):
|
def test_compressions(self):
|
||||||
# Test various tiff compressions and assert similar image content but reduced
|
# Test various tiff compressions and assert similar image content but reduced
|
||||||
|
@ -458,18 +434,18 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
for compression in ("packbits", "tiff_lzw"):
|
for compression in ("packbits", "tiff_lzw"):
|
||||||
im.save(out, compression=compression)
|
im.save(out, compression=compression)
|
||||||
size_compressed = os.path.getsize(out)
|
size_compressed = os.path.getsize(out)
|
||||||
im2 = Image.open(out)
|
with Image.open(out) as im2:
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
||||||
im.save(out, compression="jpeg")
|
im.save(out, compression="jpeg")
|
||||||
size_jpeg = os.path.getsize(out)
|
size_jpeg = os.path.getsize(out)
|
||||||
im2 = Image.open(out)
|
with Image.open(out) as im2:
|
||||||
self.assert_image_similar(im, im2, 30)
|
self.assert_image_similar(im, im2, 30)
|
||||||
|
|
||||||
im.save(out, compression="jpeg", quality=30)
|
im.save(out, compression="jpeg", quality=30)
|
||||||
size_jpeg_30 = os.path.getsize(out)
|
size_jpeg_30 = os.path.getsize(out)
|
||||||
im3 = Image.open(out)
|
with Image.open(out) as im3:
|
||||||
self.assert_image_similar(im2, im3, 30)
|
self.assert_image_similar(im2, im3, 30)
|
||||||
|
|
||||||
self.assertGreater(size_raw, size_compressed)
|
self.assertGreater(size_raw, size_compressed)
|
||||||
self.assertGreater(size_compressed, size_jpeg)
|
self.assertGreater(size_compressed, size_jpeg)
|
||||||
|
@ -491,8 +467,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
|
|
||||||
im.save(out, compression="tiff_adobe_deflate")
|
im.save(out, compression="tiff_adobe_deflate")
|
||||||
im2 = Image.open(out)
|
with Image.open(out) as im2:
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
||||||
def xtest_bw_compression_w_rgb(self):
|
def xtest_bw_compression_w_rgb(self):
|
||||||
""" This test passes, but when running all tests causes a failure due
|
""" This test passes, but when running all tests causes a failure due
|
||||||
|
@ -520,45 +496,45 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
self.assertTrue(im.tag.next)
|
self.assertTrue(im.tag.next)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
||||||
self.assertTrue(im.tag.next)
|
self.assertTrue(im.tag.next)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
self.assertFalse(im.tag.next)
|
self.assertFalse(im.tag.next)
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
def test_multipage_nframes(self):
|
def test_multipage_nframes(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
frames = im.n_frames
|
frames = im.n_frames
|
||||||
self.assertEqual(frames, 3)
|
self.assertEqual(frames, 3)
|
||||||
for _ in range(frames):
|
for _ in range(frames):
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
# Should not raise ValueError: I/O operation on closed file
|
# Should not raise ValueError: I/O operation on closed file
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
def test__next(self):
|
def test__next(self):
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
self.assertFalse(im.tag.next)
|
self.assertFalse(im.tag.next)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertFalse(im.tag.next)
|
self.assertFalse(im.tag.next)
|
||||||
|
|
||||||
def test_4bit(self):
|
def test_4bit(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -567,13 +543,13 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.mode, "L")
|
self.assertEqual(im.mode, "L")
|
||||||
self.assert_image_similar(im, original, 7.3)
|
self.assert_image_similar(im, original, 7.3)
|
||||||
|
|
||||||
def test_gray_semibyte_per_pixel(self):
|
def test_gray_semibyte_per_pixel(self):
|
||||||
test_files = (
|
test_files = (
|
||||||
|
@ -598,15 +574,15 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
)
|
)
|
||||||
original = hopper("L")
|
original = hopper("L")
|
||||||
for epsilon, group in test_files:
|
for epsilon, group in test_files:
|
||||||
im = Image.open(group[0])
|
with Image.open(group[0]) as im:
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.mode, "L")
|
self.assertEqual(im.mode, "L")
|
||||||
self.assert_image_similar(im, original, epsilon)
|
self.assert_image_similar(im, original, epsilon)
|
||||||
for file in group[1:]:
|
for file in group[1:]:
|
||||||
im2 = Image.open(file)
|
with Image.open(file) as im2:
|
||||||
self.assertEqual(im2.size, (128, 128))
|
self.assertEqual(im2.size, (128, 128))
|
||||||
self.assertEqual(im2.mode, "L")
|
self.assertEqual(im2.mode, "L")
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
||||||
def test_save_bytesio(self):
|
def test_save_bytesio(self):
|
||||||
# PR 1011
|
# PR 1011
|
||||||
|
@ -624,8 +600,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
pilim.save(buffer_io, format="tiff", compression=compression)
|
pilim.save(buffer_io, format="tiff", compression=compression)
|
||||||
buffer_io.seek(0)
|
buffer_io.seek(0)
|
||||||
|
|
||||||
pilim_load = Image.open(buffer_io)
|
with Image.open(buffer_io) as pilim_load:
|
||||||
self.assert_image_similar(pilim, pilim_load, 0)
|
self.assert_image_similar(pilim, pilim_load, 0)
|
||||||
|
|
||||||
save_bytesio()
|
save_bytesio()
|
||||||
save_bytesio("raw")
|
save_bytesio("raw")
|
||||||
|
@ -637,12 +613,12 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
def test_crashing_metadata(self):
|
def test_crashing_metadata(self):
|
||||||
# issue 1597
|
# issue 1597
|
||||||
im = Image.open("Tests/images/rdf.tif")
|
with Image.open("Tests/images/rdf.tif") as im:
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
|
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
# this shouldn't crash
|
# this shouldn't crash
|
||||||
im.save(out, format="TIFF")
|
im.save(out, format="TIFF")
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
def test_page_number_x_0(self):
|
def test_page_number_x_0(self):
|
||||||
|
@ -655,9 +631,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
|
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
|
||||||
# -dNOPAUSE /tmp/test.pdf -c quit
|
# -dNOPAUSE /tmp/test.pdf -c quit
|
||||||
infile = "Tests/images/total-pages-zero.tif"
|
infile = "Tests/images/total-pages-zero.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
# Should not divide by zero
|
# Should not divide by zero
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
def test_fd_duplication(self):
|
def test_fd_duplication(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1651
|
# https://github.com/python-pillow/Pillow/issues/1651
|
||||||
|
@ -685,21 +661,21 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
self.assertEqual(icc, icc_libtiff)
|
self.assertEqual(icc, icc_libtiff)
|
||||||
|
|
||||||
def test_multipage_compression(self):
|
def test_multipage_compression(self):
|
||||||
im = Image.open("Tests/images/compression.tif")
|
with Image.open("Tests/images/compression.tif") as im:
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im._compression, "tiff_ccitt")
|
self.assertEqual(im._compression, "tiff_ccitt")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im._compression, "packbits")
|
self.assertEqual(im._compression, "packbits")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im._compression, "tiff_ccitt")
|
self.assertEqual(im._compression, "tiff_ccitt")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_save_tiff_with_jpegtables(self):
|
def test_save_tiff_with_jpegtables(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -708,44 +684,50 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
|
# Created with ImageMagick: convert hopper.jpg hopper_jpg.tif
|
||||||
# Contains JPEGTables (347) tag
|
# Contains JPEGTables (347) tag
|
||||||
infile = "Tests/images/hopper_jpg.tif"
|
infile = "Tests/images/hopper_jpg.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
# Act / Assert
|
||||||
# Act / Assert
|
# Should not raise UnicodeDecodeError or anything else
|
||||||
# Should not raise UnicodeDecodeError or anything else
|
im.save(outfile)
|
||||||
im.save(outfile)
|
|
||||||
|
|
||||||
def test_16bit_RGB_tiff(self):
|
def test_16bit_RGB_tiff(self):
|
||||||
im = Image.open("Tests/images/tiff_16bit_RGB.tiff")
|
with Image.open("Tests/images/tiff_16bit_RGB.tiff") as im:
|
||||||
|
self.assertEqual(im.mode, "RGB")
|
||||||
|
self.assertEqual(im.size, (100, 40))
|
||||||
|
self.assertEqual(
|
||||||
|
im.tile,
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"libtiff",
|
||||||
|
(0, 0, 100, 40),
|
||||||
|
0,
|
||||||
|
("RGB;16N", "tiff_adobe_deflate", False, 8),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
|
||||||
self.assertEqual(im.size, (100, 40))
|
|
||||||
self.assertEqual(
|
|
||||||
im.tile,
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"libtiff",
|
|
||||||
(0, 0, 100, 40),
|
|
||||||
0,
|
|
||||||
("RGB;16N", "tiff_adobe_deflate", False, 8),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png")
|
|
||||||
|
|
||||||
def test_16bit_RGBa_tiff(self):
|
def test_16bit_RGBa_tiff(self):
|
||||||
im = Image.open("Tests/images/tiff_16bit_RGBa.tiff")
|
with Image.open("Tests/images/tiff_16bit_RGBa.tiff") as im:
|
||||||
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (100, 40))
|
||||||
|
self.assertEqual(
|
||||||
|
im.tile,
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"libtiff",
|
||||||
|
(0, 0, 100, 40),
|
||||||
|
0,
|
||||||
|
("RGBa;16N", "tiff_lzw", False, 38236),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assert_image_equal_tofile(
|
||||||
self.assertEqual(im.size, (100, 40))
|
im, "Tests/images/tiff_16bit_RGBa_target.png"
|
||||||
self.assertEqual(
|
)
|
||||||
im.tile,
|
|
||||||
[("libtiff", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False, 38236))],
|
|
||||||
)
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
|
||||||
|
|
||||||
def test_gimp_tiff(self):
|
def test_gimp_tiff(self):
|
||||||
# Read TIFF JPEG images from GIMP [@PIL168]
|
# Read TIFF JPEG images from GIMP [@PIL168]
|
||||||
|
@ -755,105 +737,99 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
self.skipTest("jpeg support not available")
|
self.skipTest("jpeg support not available")
|
||||||
|
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
self.assertEqual(im.mode, "RGB")
|
||||||
|
self.assertEqual(im.size, (256, 256))
|
||||||
|
self.assertEqual(
|
||||||
|
im.tile,
|
||||||
|
[("libtiff", (0, 0, 256, 256), 0, ("RGB", "jpeg", False, 5122))],
|
||||||
|
)
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assert_image_equal_tofile(im, "Tests/images/pil168.png")
|
||||||
self.assertEqual(im.size, (256, 256))
|
|
||||||
self.assertEqual(
|
|
||||||
im.tile, [("libtiff", (0, 0, 256, 256), 0, ("RGB", "jpeg", False, 5122))]
|
|
||||||
)
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/pil168.png")
|
|
||||||
|
|
||||||
def test_sampleformat(self):
|
def test_sampleformat(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1466
|
# https://github.com/python-pillow/Pillow/issues/1466
|
||||||
im = Image.open("Tests/images/copyleft.tiff")
|
with Image.open("Tests/images/copyleft.tiff") as im:
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB")
|
self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB")
|
||||||
|
|
||||||
def test_lzw(self):
|
def test_lzw(self):
|
||||||
im = Image.open("Tests/images/hopper_lzw.tif")
|
with Image.open("Tests/images/hopper_lzw.tif") as im:
|
||||||
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.format, "TIFF")
|
||||||
self.assertEqual(im.format, "TIFF")
|
im2 = hopper()
|
||||||
im2 = hopper()
|
self.assert_image_similar(im, im2, 5)
|
||||||
self.assert_image_similar(im, im2, 5)
|
|
||||||
|
|
||||||
def test_strip_cmyk_jpeg(self):
|
def test_strip_cmyk_jpeg(self):
|
||||||
infile = "Tests/images/tiff_strip_cmyk_jpeg.tif"
|
infile = "Tests/images/tiff_strip_cmyk_jpeg.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_similar_tofile(
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
im, "Tests/images/pil_sample_cmyk.jpg", 0.5
|
||||||
|
)
|
||||||
|
|
||||||
def test_strip_cmyk_16l_jpeg(self):
|
def test_strip_cmyk_16l_jpeg(self):
|
||||||
infile = "Tests/images/tiff_strip_cmyk_16l_jpeg.tif"
|
infile = "Tests/images/tiff_strip_cmyk_16l_jpeg.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_similar_tofile(
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
im, "Tests/images/pil_sample_cmyk.jpg", 0.5
|
||||||
|
)
|
||||||
|
|
||||||
def test_strip_ycbcr_jpeg_2x2_sampling(self):
|
def test_strip_ycbcr_jpeg_2x2_sampling(self):
|
||||||
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
|
infile = "Tests/images/tiff_strip_ycbcr_jpeg_2x2_sampling.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
|
||||||
|
|
||||||
def test_strip_ycbcr_jpeg_1x1_sampling(self):
|
def test_strip_ycbcr_jpeg_1x1_sampling(self):
|
||||||
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
|
infile = "Tests/images/tiff_strip_ycbcr_jpeg_1x1_sampling.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
|
|
||||||
|
|
||||||
def test_tiled_cmyk_jpeg(self):
|
def test_tiled_cmyk_jpeg(self):
|
||||||
infile = "Tests/images/tiff_tiled_cmyk_jpeg.tif"
|
infile = "Tests/images/tiff_tiled_cmyk_jpeg.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_similar_tofile(
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/pil_sample_cmyk.jpg", 0.5)
|
im, "Tests/images/pil_sample_cmyk.jpg", 0.5
|
||||||
|
)
|
||||||
|
|
||||||
def test_tiled_ycbcr_jpeg_1x1_sampling(self):
|
def test_tiled_ycbcr_jpeg_1x1_sampling(self):
|
||||||
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
|
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_1x1_sampling.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/flower2.jpg")
|
|
||||||
|
|
||||||
def test_tiled_ycbcr_jpeg_2x2_sampling(self):
|
def test_tiled_ycbcr_jpeg_2x2_sampling(self):
|
||||||
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
|
infile = "Tests/images/tiff_tiled_ycbcr_jpeg_2x2_sampling.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/flower.jpg", 0.5)
|
|
||||||
|
|
||||||
def test_old_style_jpeg(self):
|
def test_old_style_jpeg(self):
|
||||||
infile = "Tests/images/old-style-jpeg-compression.tif"
|
infile = "Tests/images/old-style-jpeg-compression.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(
|
||||||
self.assert_image_equal_tofile(
|
im, "Tests/images/old-style-jpeg-compression.png"
|
||||||
im, "Tests/images/old-style-jpeg-compression.png"
|
)
|
||||||
)
|
|
||||||
|
|
||||||
def test_no_rows_per_strip(self):
|
def test_no_rows_per_strip(self):
|
||||||
# This image does not have a RowsPerStrip TIFF tag
|
# This image does not have a RowsPerStrip TIFF tag
|
||||||
infile = "Tests/images/no_rows_per_strip.tif"
|
infile = "Tests/images/no_rows_per_strip.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (950, 975))
|
self.assertEqual(im.size, (950, 975))
|
||||||
|
|
||||||
def test_orientation(self):
|
def test_orientation(self):
|
||||||
base_im = Image.open("Tests/images/g4_orientation_1.tif")
|
with Image.open("Tests/images/g4_orientation_1.tif") as base_im:
|
||||||
|
for i in range(2, 9):
|
||||||
|
with Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
for i in range(2, 9):
|
self.assert_image_similar(base_im, im, 0.7)
|
||||||
im = Image.open("Tests/images/g4_orientation_" + str(i) + ".tif")
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assert_image_similar(base_im, im, 0.7)
|
|
||||||
|
|
||||||
def test_sampleformat_not_corrupted(self):
|
def test_sampleformat_not_corrupted(self):
|
||||||
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
|
# Assert that a TIFF image with SampleFormat=UINT tag is not corrupted
|
||||||
# when saving to a new file.
|
# when saving to a new file.
|
||||||
# Pillow 6.0 fails with "OSError: cannot identify image file".
|
# Pillow 6.0 fails with "OSError: cannot identify image file".
|
||||||
import base64
|
|
||||||
|
|
||||||
tiff = io.BytesIO(
|
tiff = io.BytesIO(
|
||||||
base64.b64decode(
|
base64.b64decode(
|
||||||
b"SUkqAAgAAAAPAP4ABAABAAAAAAAAAAABBAABAAAAAQAAAAEBBAABAAAAAQAA"
|
b"SUkqAAgAAAAPAP4ABAABAAAAAAAAAAABBAABAAAAAQAAAAEBBAABAAAAAQAA"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .test_file_libtiff import LibTiffTestCase
|
from .test_file_libtiff import LibTiffTestCase
|
||||||
|
@ -18,30 +20,25 @@ class TestFileLibTiffSmall(LibTiffTestCase):
|
||||||
|
|
||||||
test_file = "Tests/images/hopper_g4.tif"
|
test_file = "Tests/images/hopper_g4.tif"
|
||||||
with open(test_file, "rb") as f:
|
with open(test_file, "rb") as f:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.size, (128, 128))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
||||||
def test_g4_hopper_bytesio(self):
|
def test_g4_hopper_bytesio(self):
|
||||||
"""Testing the bytesio loading code path"""
|
"""Testing the bytesio loading code path"""
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
test_file = "Tests/images/hopper_g4.tif"
|
test_file = "Tests/images/hopper_g4.tif"
|
||||||
s = BytesIO()
|
s = BytesIO()
|
||||||
with open(test_file, "rb") as f:
|
with open(test_file, "rb") as f:
|
||||||
s.write(f.read())
|
s.write(f.read())
|
||||||
s.seek(0)
|
s.seek(0)
|
||||||
im = Image.open(s)
|
with Image.open(s) as im:
|
||||||
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.size, (128, 128))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
||||||
def test_g4_hopper(self):
|
def test_g4_hopper(self):
|
||||||
"""The 128x128 lena image failed for some reason."""
|
"""The 128x128 lena image failed for some reason."""
|
||||||
|
|
||||||
test_file = "Tests/images/hopper_g4.tif"
|
test_file = "Tests/images/hopper_g4.tif"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.size, (128, 128))
|
self._assert_noerr(im)
|
||||||
self._assert_noerr(im)
|
|
||||||
|
|
|
@ -17,12 +17,12 @@ class TestFileMcIdas(PillowTestCase):
|
||||||
saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.png"
|
saved_file = "Tests/images/cmx3g8_wv_1998.260_0745_mcidas.png"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "MCIDAS")
|
self.assertEqual(im.format, "MCIDAS")
|
||||||
self.assertEqual(im.mode, "I")
|
self.assertEqual(im.mode, "I")
|
||||||
self.assertEqual(im.size, (1800, 400))
|
self.assertEqual(im.size, (1800, 400))
|
||||||
im2 = Image.open(saved_file)
|
with Image.open(saved_file) as im2:
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, ImagePalette, features
|
from PIL import Image, ImagePalette, features
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import MicImagePlugin
|
from PIL import MicImagePlugin
|
||||||
|
@ -16,42 +18,42 @@ TEST_FILE = "Tests/images/hopper.mic"
|
||||||
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
||||||
class TestFileMic(PillowTestCase):
|
class TestFileMic(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "MIC")
|
self.assertEqual(im.format, "MIC")
|
||||||
|
|
||||||
# Adjust for the gamma of 2.2 encoded into the file
|
# Adjust for the gamma of 2.2 encoded into the file
|
||||||
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
||||||
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
||||||
|
|
||||||
im2 = hopper("RGBA")
|
im2 = hopper("RGBA")
|
||||||
self.assert_image_similar(im, im2, 10)
|
self.assert_image_similar(im, im2, 10)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
|
|
||||||
def test_is_animated(self):
|
def test_is_animated(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, 99)
|
self.assertRaises(EOFError, im.seek, 99)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Test an invalid OLE file
|
# Test an invalid OLE file
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
||||||
|
|
||||||
|
@ -25,78 +26,104 @@ class TestFileMpo(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
self.assertEqual(im.format, "MPO")
|
self.assertEqual(im.format, "MPO")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(test_files[0])
|
im = Image.open(test_files[0])
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(test_files[0])
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(test_files[0]) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_app(self):
|
def test_app(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
# Test APP/COM reader (@PIL135)
|
# Test APP/COM reader (@PIL135)
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.applist[0][0], "APP1")
|
self.assertEqual(im.applist[0][0], "APP1")
|
||||||
self.assertEqual(im.applist[1][0], "APP2")
|
self.assertEqual(im.applist[1][0], "APP2")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[1][1][:16], b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00"
|
im.applist[1][1][:16],
|
||||||
)
|
b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00",
|
||||||
self.assertEqual(len(im.applist), 2)
|
)
|
||||||
|
self.assertEqual(len(im.applist), 2)
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
self.assertEqual(info[272], "Nintendo 3DS")
|
self.assertEqual(info[272], "Nintendo 3DS")
|
||||||
self.assertEqual(info[296], 2)
|
self.assertEqual(info[296], 2)
|
||||||
self.assertEqual(info[34665], 188)
|
self.assertEqual(info[34665], 188)
|
||||||
|
|
||||||
def test_frame_size(self):
|
def test_frame_size(self):
|
||||||
# This image has been hexedited to contain a different size
|
# This image has been hexedited to contain a different size
|
||||||
# in the EXIF data of the second frame
|
# in the EXIF data of the second frame
|
||||||
im = Image.open("Tests/images/sugarshack_frame_size.mpo")
|
with Image.open("Tests/images/sugarshack_frame_size.mpo") as im:
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (680, 480))
|
self.assertEqual(im.size, (680, 480))
|
||||||
|
|
||||||
def test_parallax(self):
|
def test_parallax(self):
|
||||||
# Nintendo
|
# Nintendo
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375)
|
self.assertEqual(
|
||||||
|
exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375
|
||||||
|
)
|
||||||
|
|
||||||
# Fujifilm
|
# Fujifilm
|
||||||
im = Image.open("Tests/images/fujifilm.mpo")
|
with Image.open("Tests/images/fujifilm.mpo") as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
|
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
|
||||||
|
|
||||||
def test_mp(self):
|
def test_mp(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
self.assertEqual(mpinfo[45056], b"0100")
|
self.assertEqual(mpinfo[45056], b"0100")
|
||||||
self.assertEqual(mpinfo[45057], 2)
|
self.assertEqual(mpinfo[45057], 2)
|
||||||
|
|
||||||
def test_mp_offset(self):
|
def test_mp_offset(self):
|
||||||
# This image has been manually hexedited to have an IFD offset of 10
|
# This image has been manually hexedited to have an IFD offset of 10
|
||||||
# in APP2 data, in contrast to normal 8
|
# in APP2 data, in contrast to normal 8
|
||||||
im = Image.open("Tests/images/sugarshack_ifd_offset.mpo")
|
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
self.assertEqual(mpinfo[45056], b"0100")
|
self.assertEqual(mpinfo[45056], b"0100")
|
||||||
self.assertEqual(mpinfo[45057], 2)
|
self.assertEqual(mpinfo[45057], 2)
|
||||||
|
|
||||||
|
def test_mp_no_data(self):
|
||||||
|
# This image has been manually hexedited to have the second frame
|
||||||
|
# beyond the end of the file
|
||||||
|
with Image.open("Tests/images/sugarshack_no_data.mpo") as im:
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
im.seek(1)
|
||||||
|
|
||||||
def test_mp_attribute(self):
|
def test_mp_attribute(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
frameNumber = 0
|
frameNumber = 0
|
||||||
for mpentry in mpinfo[45058]:
|
for mpentry in mpinfo[45058]:
|
||||||
mpattr = mpentry["Attribute"]
|
mpattr = mpentry["Attribute"]
|
||||||
|
@ -113,62 +140,62 @@ class TestFileMpo(PillowTestCase):
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
# prior to first image raises an error, both blatant and borderline
|
# prior to first image raises an error, both blatant and borderline
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
self.assertRaises(EOFError, im.seek, -523)
|
self.assertRaises(EOFError, im.seek, -523)
|
||||||
# after the final image raises an error,
|
# after the final image raises an error,
|
||||||
# both blatant and borderline
|
# both blatant and borderline
|
||||||
self.assertRaises(EOFError, im.seek, 2)
|
self.assertRaises(EOFError, im.seek, 2)
|
||||||
self.assertRaises(EOFError, im.seek, 523)
|
self.assertRaises(EOFError, im.seek, 523)
|
||||||
# bad calls shouldn't change the frame
|
# bad calls shouldn't change the frame
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
# this one will work
|
# this one will work
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
# and this one, too
|
# and this one, too
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_image_grab(self):
|
def test_image_grab(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
im0 = im.tobytes()
|
im0 = im.tobytes()
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
im1 = im.tobytes()
|
im1 = im.tobytes()
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
im02 = im.tobytes()
|
im02 = im.tobytes()
|
||||||
self.assertEqual(im0, im02)
|
self.assertEqual(im0, im02)
|
||||||
self.assertNotEqual(im0, im1)
|
self.assertNotEqual(im0, im1)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Note that only individual frames can be saved at present
|
# Note that only individual frames can be saved at present
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
jpg0 = self.frame_roundtrip(im)
|
jpg0 = self.frame_roundtrip(im)
|
||||||
self.assert_image_similar(im, jpg0, 30)
|
self.assert_image_similar(im, jpg0, 30)
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
jpg1 = self.frame_roundtrip(im)
|
jpg1 = self.frame_roundtrip(im)
|
||||||
self.assert_image_similar(im, jpg1, 30)
|
self.assert_image_similar(im, jpg1, 30)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import os
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, MspImagePlugin
|
from PIL import Image, MspImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.msp"
|
TEST_FILE = "Tests/images/hopper.msp"
|
||||||
EXTRA_DIR = "Tests/images/picins"
|
EXTRA_DIR = "Tests/images/picins"
|
||||||
|
@ -15,11 +16,11 @@ class TestFileMsp(PillowTestCase):
|
||||||
|
|
||||||
hopper("1").save(test_file)
|
hopper("1").save(test_file)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "MSP")
|
self.assertEqual(im.format, "MSP")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -37,16 +38,16 @@ class TestFileMsp(PillowTestCase):
|
||||||
def test_open_windows_v1(self):
|
def test_open_windows_v1(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(im, hopper("1"))
|
self.assert_image_equal(im, hopper("1"))
|
||||||
self.assertIsInstance(im, MspImagePlugin.MspImageFile)
|
self.assertIsInstance(im, MspImagePlugin.MspImageFile)
|
||||||
|
|
||||||
def _assert_file_image_equal(self, source_path, target_path):
|
def _assert_file_image_equal(self, source_path, target_path):
|
||||||
with Image.open(source_path) as im:
|
with Image.open(source_path) as im:
|
||||||
target = Image.open(target_path)
|
with Image.open(target_path) as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
@unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed")
|
@unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed")
|
||||||
def test_open_windows_v2(self):
|
def test_open_windows_v2(self):
|
||||||
|
|
|
@ -5,8 +5,8 @@ from .helper import PillowTestCase
|
||||||
|
|
||||||
class TestFilePcd(PillowTestCase):
|
class TestFilePcd(PillowTestCase):
|
||||||
def test_load_raw(self):
|
def test_load_raw(self):
|
||||||
im = Image.open("Tests/images/hopper.pcd")
|
with Image.open("Tests/images/hopper.pcd") as im:
|
||||||
im.load() # should not segfault.
|
im.load() # should not segfault.
|
||||||
|
|
||||||
# Note that this image was created with a resized hopper
|
# Note that this image was created with a resized hopper
|
||||||
# image, which was then converted to pcd with imagemagick
|
# image, which was then converted to pcd with imagemagick
|
||||||
|
|
|
@ -7,13 +7,12 @@ class TestFilePcx(PillowTestCase):
|
||||||
def _roundtrip(self, im):
|
def _roundtrip(self, im):
|
||||||
f = self.tempfile("temp.pcx")
|
f = self.tempfile("temp.pcx")
|
||||||
im.save(f)
|
im.save(f)
|
||||||
im2 = Image.open(f)
|
with Image.open(f) as im2:
|
||||||
|
self.assertEqual(im2.mode, im.mode)
|
||||||
self.assertEqual(im2.mode, im.mode)
|
self.assertEqual(im2.size, im.size)
|
||||||
self.assertEqual(im2.size, im.size)
|
self.assertEqual(im2.format, "PCX")
|
||||||
self.assertEqual(im2.format, "PCX")
|
self.assertEqual(im2.get_format_mimetype(), "image/x-pcx")
|
||||||
self.assertEqual(im2.get_format_mimetype(), "image/x-pcx")
|
self.assert_image_equal(im2, im)
|
||||||
self.assert_image_equal(im2, im)
|
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
for mode in ("1", "L", "P", "RGB"):
|
for mode in ("1", "L", "P", "RGB"):
|
||||||
|
@ -42,13 +41,12 @@ class TestFilePcx(PillowTestCase):
|
||||||
# Check reading of files where xmin/xmax is not zero.
|
# Check reading of files where xmin/xmax is not zero.
|
||||||
|
|
||||||
test_file = "Tests/images/pil184.pcx"
|
test_file = "Tests/images/pil184.pcx"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assertEqual(im.size, (447, 144))
|
||||||
|
self.assertEqual(im.tile[0][1], (0, 0, 447, 144))
|
||||||
|
|
||||||
self.assertEqual(im.size, (447, 144))
|
# Make sure all pixels are either 0 or 255.
|
||||||
self.assertEqual(im.tile[0][1], (0, 0, 447, 144))
|
self.assertEqual(im.histogram()[0] + im.histogram()[255], 447 * 144)
|
||||||
|
|
||||||
# Make sure all pixels are either 0 or 255.
|
|
||||||
self.assertEqual(im.histogram()[0] + im.histogram()[255], 447 * 144)
|
|
||||||
|
|
||||||
def test_1px_width(self):
|
def test_1px_width(self):
|
||||||
im = Image.new("L", (1, 256))
|
im = Image.new("L", (1, 256))
|
||||||
|
|
|
@ -82,44 +82,43 @@ class TestFilePdf(PillowTestCase):
|
||||||
self.helper_save_as_pdf("RGB", save_all=True)
|
self.helper_save_as_pdf("RGB", save_all=True)
|
||||||
|
|
||||||
# Multiframe image
|
# Multiframe image
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
outfile = self.tempfile("temp.pdf")
|
outfile = self.tempfile("temp.pdf")
|
||||||
im.save(outfile, save_all=True)
|
im.save(outfile, save_all=True)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Append images
|
# Append images
|
||||||
ims = [hopper()]
|
ims = [hopper()]
|
||||||
im.copy().save(outfile, save_all=True, append_images=ims)
|
im.copy().save(outfile, save_all=True, append_images=ims)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Test appending using a generator
|
# Test appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
for im in ims:
|
yield from ims
|
||||||
yield im
|
|
||||||
|
|
||||||
im.save(outfile, save_all=True, append_images=imGenerator(ims))
|
im.save(outfile, save_all=True, append_images=imGenerator(ims))
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Append JPEG images
|
# Append JPEG images
|
||||||
jpeg = Image.open("Tests/images/flower.jpg")
|
with Image.open("Tests/images/flower.jpg") as jpeg:
|
||||||
jpeg.save(outfile, save_all=True, append_images=[jpeg.copy()])
|
jpeg.save(outfile, save_all=True, append_images=[jpeg.copy()])
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
def test_multiframe_normal_save(self):
|
def test_multiframe_normal_save(self):
|
||||||
# Test saving a multiframe image without save_all
|
# Test saving a multiframe image without save_all
|
||||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as im:
|
||||||
|
|
||||||
outfile = self.tempfile("temp.pdf")
|
outfile = self.tempfile("temp.pdf")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
@ -163,13 +162,10 @@ class TestFilePdf(PillowTestCase):
|
||||||
|
|
||||||
def test_pdf_append_fails_on_nonexistent_file(self):
|
def test_pdf_append_fails_on_nonexistent_file(self):
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
temp_dir = tempfile.mkdtemp()
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
try:
|
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True
|
IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True
|
||||||
)
|
)
|
||||||
finally:
|
|
||||||
os.rmdir(temp_dir)
|
|
||||||
|
|
||||||
def check_pdf_pages_consistency(self, pdf):
|
def check_pdf_pages_consistency(self, pdf):
|
||||||
pages_info = pdf.read_indirect(pdf.pages_ref)
|
pages_info = pdf.read_indirect(pdf.pages_ref)
|
||||||
|
@ -207,7 +203,7 @@ class TestFilePdf(PillowTestCase):
|
||||||
# append some info
|
# append some info
|
||||||
pdf.info.Title = "abc"
|
pdf.info.Title = "abc"
|
||||||
pdf.info.Author = "def"
|
pdf.info.Author = "def"
|
||||||
pdf.info.Subject = u"ghi\uABCD"
|
pdf.info.Subject = "ghi\uABCD"
|
||||||
pdf.info.Keywords = "qw)e\\r(ty"
|
pdf.info.Keywords = "qw)e\\r(ty"
|
||||||
pdf.info.Creator = "hopper()"
|
pdf.info.Creator = "hopper()"
|
||||||
pdf.start_writing()
|
pdf.start_writing()
|
||||||
|
@ -235,7 +231,7 @@ class TestFilePdf(PillowTestCase):
|
||||||
self.assertEqual(pdf.info.Title, "abc")
|
self.assertEqual(pdf.info.Title, "abc")
|
||||||
self.assertEqual(pdf.info.Producer, "PdfParser")
|
self.assertEqual(pdf.info.Producer, "PdfParser")
|
||||||
self.assertEqual(pdf.info.Keywords, "qw)e\\r(ty")
|
self.assertEqual(pdf.info.Keywords, "qw)e\\r(ty")
|
||||||
self.assertEqual(pdf.info.Subject, u"ghi\uABCD")
|
self.assertEqual(pdf.info.Subject, "ghi\uABCD")
|
||||||
self.assertIn(b"CreationDate", pdf.info)
|
self.assertIn(b"CreationDate", pdf.info)
|
||||||
self.assertIn(b"ModDate", pdf.info)
|
self.assertIn(b"ModDate", pdf.info)
|
||||||
self.check_pdf_pages_consistency(pdf)
|
self.check_pdf_pages_consistency(pdf)
|
||||||
|
|
|
@ -7,15 +7,15 @@ TEST_FILE = "Tests/images/hopper.pxr"
|
||||||
|
|
||||||
class TestFilePixar(PillowTestCase):
|
class TestFilePixar(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PIXAR")
|
self.assertEqual(im.format, "PIXAR")
|
||||||
self.assertIsNone(im.get_format_mimetype())
|
self.assertIsNone(im.get_format_mimetype())
|
||||||
|
|
||||||
im2 = hopper()
|
im2 = hopper()
|
||||||
self.assert_image_similar(im, im2, 4.8)
|
self.assert_image_similar(im, im2, 4.8)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import sys
|
import unittest
|
||||||
import zlib
|
import zlib
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageFile, PngImagePlugin
|
from PIL import Image, ImageFile, PngImagePlugin
|
||||||
from PIL._util import py3
|
|
||||||
|
|
||||||
from .helper import PillowLeakTestCase, PillowTestCase, hopper, unittest
|
from .helper import PillowLeakTestCase, PillowTestCase, hopper, is_win32
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -82,20 +81,20 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
hopper("RGB").save(test_file)
|
hopper("RGB").save(test_file)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PNG")
|
self.assertEqual(im.format, "PNG")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/png")
|
self.assertEqual(im.get_format_mimetype(), "image/png")
|
||||||
|
|
||||||
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
if mode == "I;16":
|
if mode == "I;16":
|
||||||
reloaded = reloaded.convert(mode)
|
reloaded = reloaded.convert(mode)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -196,27 +195,24 @@ class TestFilePng(PillowTestCase):
|
||||||
def test_interlace(self):
|
def test_interlace(self):
|
||||||
|
|
||||||
test_file = "Tests/images/pil123p.png"
|
test_file = "Tests/images/pil123p.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assert_image(im, "P", (162, 150))
|
||||||
|
self.assertTrue(im.info.get("interlace"))
|
||||||
|
|
||||||
self.assert_image(im, "P", (162, 150))
|
im.load()
|
||||||
self.assertTrue(im.info.get("interlace"))
|
|
||||||
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
test_file = "Tests/images/pil123rgba.png"
|
test_file = "Tests/images/pil123rgba.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assert_image(im, "RGBA", (162, 150))
|
||||||
|
self.assertTrue(im.info.get("interlace"))
|
||||||
|
|
||||||
self.assert_image(im, "RGBA", (162, 150))
|
im.load()
|
||||||
self.assertTrue(im.info.get("interlace"))
|
|
||||||
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
def test_load_transparent_p(self):
|
def test_load_transparent_p(self):
|
||||||
test_file = "Tests/images/pil123p.png"
|
test_file = "Tests/images/pil123p.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
self.assert_image(im, "P", (162, 150))
|
||||||
self.assert_image(im, "P", (162, 150))
|
im = im.convert("RGBA")
|
||||||
im = im.convert("RGBA")
|
|
||||||
self.assert_image(im, "RGBA", (162, 150))
|
self.assert_image(im, "RGBA", (162, 150))
|
||||||
|
|
||||||
# image has 124 unique alpha values
|
# image has 124 unique alpha values
|
||||||
|
@ -224,11 +220,11 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
def test_load_transparent_rgb(self):
|
def test_load_transparent_rgb(self):
|
||||||
test_file = "Tests/images/rgb_trns.png"
|
test_file = "Tests/images/rgb_trns.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.info["transparency"], (0, 255, 52))
|
self.assertEqual(im.info["transparency"], (0, 255, 52))
|
||||||
|
|
||||||
self.assert_image(im, "RGB", (64, 64))
|
self.assert_image(im, "RGB", (64, 64))
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
self.assert_image(im, "RGBA", (64, 64))
|
self.assert_image(im, "RGBA", (64, 64))
|
||||||
|
|
||||||
# image has 876 transparent pixels
|
# image has 876 transparent pixels
|
||||||
|
@ -236,21 +232,20 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
def test_save_p_transparent_palette(self):
|
def test_save_p_transparent_palette(self):
|
||||||
in_file = "Tests/images/pil123p.png"
|
in_file = "Tests/images/pil123p.png"
|
||||||
im = Image.open(in_file)
|
with Image.open(in_file) as im:
|
||||||
|
# 'transparency' contains a byte string with the opacity for
|
||||||
|
# each palette entry
|
||||||
|
self.assertEqual(len(im.info["transparency"]), 256)
|
||||||
|
|
||||||
# 'transparency' contains a byte string with the opacity for
|
test_file = self.tempfile("temp.png")
|
||||||
# each palette entry
|
im.save(test_file)
|
||||||
self.assertEqual(len(im.info["transparency"]), 256)
|
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
|
||||||
im.save(test_file)
|
|
||||||
|
|
||||||
# check if saved image contains same transparency
|
# check if saved image contains same transparency
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(len(im.info["transparency"]), 256)
|
self.assertEqual(len(im.info["transparency"]), 256)
|
||||||
|
|
||||||
self.assert_image(im, "P", (162, 150))
|
self.assert_image(im, "P", (162, 150))
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
self.assert_image(im, "RGBA", (162, 150))
|
self.assert_image(im, "RGBA", (162, 150))
|
||||||
|
|
||||||
# image has 124 unique alpha values
|
# image has 124 unique alpha values
|
||||||
|
@ -258,21 +253,20 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
def test_save_p_single_transparency(self):
|
def test_save_p_single_transparency(self):
|
||||||
in_file = "Tests/images/p_trns_single.png"
|
in_file = "Tests/images/p_trns_single.png"
|
||||||
im = Image.open(in_file)
|
with Image.open(in_file) as im:
|
||||||
|
# pixel value 164 is full transparent
|
||||||
|
self.assertEqual(im.info["transparency"], 164)
|
||||||
|
self.assertEqual(im.getpixel((31, 31)), 164)
|
||||||
|
|
||||||
# pixel value 164 is full transparent
|
test_file = self.tempfile("temp.png")
|
||||||
self.assertEqual(im.info["transparency"], 164)
|
im.save(test_file)
|
||||||
self.assertEqual(im.getpixel((31, 31)), 164)
|
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
|
||||||
im.save(test_file)
|
|
||||||
|
|
||||||
# check if saved image contains same transparency
|
# check if saved image contains same transparency
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.info["transparency"], 164)
|
self.assertEqual(im.info["transparency"], 164)
|
||||||
self.assertEqual(im.getpixel((31, 31)), 164)
|
self.assertEqual(im.getpixel((31, 31)), 164)
|
||||||
self.assert_image(im, "P", (64, 64))
|
self.assert_image(im, "P", (64, 64))
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
self.assert_image(im, "RGBA", (64, 64))
|
self.assert_image(im, "RGBA", (64, 64))
|
||||||
|
|
||||||
self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0))
|
self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0))
|
||||||
|
@ -291,30 +285,30 @@ class TestFilePng(PillowTestCase):
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
# check if saved image contains same transparency
|
# check if saved image contains same transparency
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(len(im.info["transparency"]), 256)
|
self.assertEqual(len(im.info["transparency"]), 256)
|
||||||
self.assert_image(im, "P", (10, 10))
|
self.assert_image(im, "P", (10, 10))
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
self.assert_image(im, "RGBA", (10, 10))
|
self.assert_image(im, "RGBA", (10, 10))
|
||||||
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
|
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
|
||||||
|
|
||||||
def test_save_greyscale_transparency(self):
|
def test_save_greyscale_transparency(self):
|
||||||
for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items():
|
for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items():
|
||||||
in_file = "Tests/images/" + mode.lower() + "_trns.png"
|
in_file = "Tests/images/" + mode.lower() + "_trns.png"
|
||||||
im = Image.open(in_file)
|
with Image.open(in_file) as im:
|
||||||
self.assertEqual(im.mode, mode)
|
self.assertEqual(im.mode, mode)
|
||||||
self.assertEqual(im.info["transparency"], 255)
|
self.assertEqual(im.info["transparency"], 255)
|
||||||
|
|
||||||
im_rgba = im.convert("RGBA")
|
im_rgba = im.convert("RGBA")
|
||||||
self.assertEqual(im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
self.assertEqual(im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
test_im = Image.open(test_file)
|
with Image.open(test_file) as test_im:
|
||||||
self.assertEqual(test_im.mode, mode)
|
self.assertEqual(test_im.mode, mode)
|
||||||
self.assertEqual(test_im.info["transparency"], 255)
|
self.assertEqual(test_im.info["transparency"], 255)
|
||||||
self.assert_image_equal(im, test_im)
|
self.assert_image_equal(im, test_im)
|
||||||
|
|
||||||
test_im_rgba = test_im.convert("RGBA")
|
test_im_rgba = test_im.convert("RGBA")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -323,22 +317,20 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
def test_save_rgb_single_transparency(self):
|
def test_save_rgb_single_transparency(self):
|
||||||
in_file = "Tests/images/caption_6_33_22.png"
|
in_file = "Tests/images/caption_6_33_22.png"
|
||||||
im = Image.open(in_file)
|
with Image.open(in_file) as im:
|
||||||
|
test_file = self.tempfile("temp.png")
|
||||||
test_file = self.tempfile("temp.png")
|
im.save(test_file)
|
||||||
im.save(test_file)
|
|
||||||
|
|
||||||
def test_load_verify(self):
|
def test_load_verify(self):
|
||||||
# Check open/load/verify exception (@PIL150)
|
# Check open/load/verify exception (@PIL150)
|
||||||
|
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
|
# Assert that there is no unclosed file warning
|
||||||
|
self.assert_warning(None, im.verify)
|
||||||
|
|
||||||
# Assert that there is no unclosed file warning
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
self.assert_warning(None, im.verify)
|
im.load()
|
||||||
|
self.assertRaises(RuntimeError, im.verify)
|
||||||
im = Image.open(TEST_PNG_FILE)
|
|
||||||
im.load()
|
|
||||||
self.assertRaises(RuntimeError, im.verify)
|
|
||||||
|
|
||||||
def test_verify_struct_error(self):
|
def test_verify_struct_error(self):
|
||||||
# Check open/load/verify exception (#1755)
|
# Check open/load/verify exception (#1755)
|
||||||
|
@ -351,9 +343,9 @@ class TestFilePng(PillowTestCase):
|
||||||
with open(TEST_PNG_FILE, "rb") as f:
|
with open(TEST_PNG_FILE, "rb") as f:
|
||||||
test_file = f.read()[:offset]
|
test_file = f.read()[:offset]
|
||||||
|
|
||||||
im = Image.open(BytesIO(test_file))
|
with Image.open(BytesIO(test_file)) as im:
|
||||||
self.assertIsNotNone(im.fp)
|
self.assertIsNotNone(im.fp)
|
||||||
self.assertRaises((IOError, SyntaxError), im.verify)
|
self.assertRaises((IOError, SyntaxError), im.verify)
|
||||||
|
|
||||||
def test_verify_ignores_crc_error(self):
|
def test_verify_ignores_crc_error(self):
|
||||||
# check ignores crc errors in ancillary chunks
|
# check ignores crc errors in ancillary chunks
|
||||||
|
@ -387,24 +379,22 @@ class TestFilePng(PillowTestCase):
|
||||||
def test_roundtrip_dpi(self):
|
def test_roundtrip_dpi(self):
|
||||||
# Check dpi roundtripping
|
# Check dpi roundtripping
|
||||||
|
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
|
im = roundtrip(im, dpi=(100, 100))
|
||||||
im = roundtrip(im, dpi=(100, 100))
|
|
||||||
self.assertEqual(im.info["dpi"], (100, 100))
|
self.assertEqual(im.info["dpi"], (100, 100))
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
self.assertEqual(im.info["dpi"], (96, 96))
|
self.assertEqual(im.info["dpi"], (96, 96))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
|
im = roundtrip(im, dpi=(72.2, 72.2))
|
||||||
im = roundtrip(im, dpi=(72.2, 72.2))
|
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
im = roundtrip(im, dpi=(72.8, 72.8))
|
im = roundtrip(im, dpi=(72.8, 72.8))
|
||||||
|
@ -413,13 +403,12 @@ class TestFilePng(PillowTestCase):
|
||||||
def test_roundtrip_text(self):
|
def test_roundtrip_text(self):
|
||||||
# Check text roundtripping
|
# Check text roundtripping
|
||||||
|
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
|
info = PngImagePlugin.PngInfo()
|
||||||
|
info.add_text("TXT", "VALUE")
|
||||||
|
info.add_text("ZIP", "VALUE", zip=True)
|
||||||
|
|
||||||
info = PngImagePlugin.PngInfo()
|
im = roundtrip(im, pnginfo=info)
|
||||||
info.add_text("TXT", "VALUE")
|
|
||||||
info.add_text("ZIP", "VALUE", zip=True)
|
|
||||||
|
|
||||||
im = roundtrip(im, pnginfo=info)
|
|
||||||
self.assertEqual(im.info, {"TXT": "VALUE", "ZIP": "VALUE"})
|
self.assertEqual(im.info, {"TXT": "VALUE", "ZIP": "VALUE"})
|
||||||
self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"})
|
self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"})
|
||||||
|
|
||||||
|
@ -449,9 +438,7 @@ class TestFilePng(PillowTestCase):
|
||||||
self.assertIsInstance(im.info["Text"], str)
|
self.assertIsInstance(im.info["Text"], str)
|
||||||
|
|
||||||
def test_unicode_text(self):
|
def test_unicode_text(self):
|
||||||
# Check preservation of non-ASCII characters on Python 3
|
# Check preservation of non-ASCII characters
|
||||||
# This cannot really be meaningfully tested on Python 2,
|
|
||||||
# since it didn't preserve charsets to begin with.
|
|
||||||
|
|
||||||
def rt_text(value):
|
def rt_text(value):
|
||||||
im = Image.new("RGB", (32, 32))
|
im = Image.new("RGB", (32, 32))
|
||||||
|
@ -460,12 +447,11 @@ class TestFilePng(PillowTestCase):
|
||||||
im = roundtrip(im, pnginfo=info)
|
im = roundtrip(im, pnginfo=info)
|
||||||
self.assertEqual(im.info, {"Text": value})
|
self.assertEqual(im.info, {"Text": value})
|
||||||
|
|
||||||
if py3:
|
rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1
|
||||||
rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1
|
rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic
|
||||||
rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic
|
# CJK:
|
||||||
# CJK:
|
rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00))
|
||||||
rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00))
|
rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
|
||||||
rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
|
|
||||||
|
|
||||||
def test_scary(self):
|
def test_scary(self):
|
||||||
# Check reading of evil PNG file. For information, see:
|
# Check reading of evil PNG file. For information, see:
|
||||||
|
@ -484,11 +470,11 @@ class TestFilePng(PillowTestCase):
|
||||||
# Independent file sample provided by Sebastian Spaeth.
|
# Independent file sample provided by Sebastian Spaeth.
|
||||||
|
|
||||||
test_file = "Tests/images/caption_6_33_22.png"
|
test_file = "Tests/images/caption_6_33_22.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.info["transparency"], (248, 248, 248))
|
self.assertEqual(im.info["transparency"], (248, 248, 248))
|
||||||
|
|
||||||
# check saving transparency by default
|
# check saving transparency by default
|
||||||
im = roundtrip(im)
|
im = roundtrip(im)
|
||||||
self.assertEqual(im.info["transparency"], (248, 248, 248))
|
self.assertEqual(im.info["transparency"], (248, 248, 248))
|
||||||
|
|
||||||
im = roundtrip(im, transparency=(0, 1, 2))
|
im = roundtrip(im, transparency=(0, 1, 2))
|
||||||
|
@ -502,59 +488,58 @@ class TestFilePng(PillowTestCase):
|
||||||
f = self.tempfile("temp.png")
|
f = self.tempfile("temp.png")
|
||||||
im.save(f)
|
im.save(f)
|
||||||
|
|
||||||
im2 = Image.open(f)
|
with Image.open(f) as im2:
|
||||||
self.assertIn("transparency", im2.info)
|
self.assertIn("transparency", im2.info)
|
||||||
|
|
||||||
self.assert_image_equal(im2.convert("RGBA"), im.convert("RGBA"))
|
self.assert_image_equal(im2.convert("RGBA"), im.convert("RGBA"))
|
||||||
|
|
||||||
def test_trns_null(self):
|
def test_trns_null(self):
|
||||||
# Check reading images with null tRNS value, issue #1239
|
# Check reading images with null tRNS value, issue #1239
|
||||||
test_file = "Tests/images/tRNS_null_1x1.png"
|
test_file = "Tests/images/tRNS_null_1x1.png"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
self.assertEqual(im.info["transparency"], 0)
|
self.assertEqual(im.info["transparency"], 0)
|
||||||
|
|
||||||
def test_save_icc_profile(self):
|
def test_save_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertIsNone(im.info["icc_profile"])
|
self.assertIsNone(im.info["icc_profile"])
|
||||||
|
|
||||||
with_icc = Image.open("Tests/images/icc_profile.png")
|
with Image.open("Tests/images/icc_profile.png") as with_icc:
|
||||||
expected_icc = with_icc.info["icc_profile"]
|
expected_icc = with_icc.info["icc_profile"]
|
||||||
|
|
||||||
im = roundtrip(im, icc_profile=expected_icc)
|
im = roundtrip(im, icc_profile=expected_icc)
|
||||||
self.assertEqual(im.info["icc_profile"], expected_icc)
|
self.assertEqual(im.info["icc_profile"], expected_icc)
|
||||||
|
|
||||||
def test_discard_icc_profile(self):
|
def test_discard_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile.png")
|
with Image.open("Tests/images/icc_profile.png") as im:
|
||||||
|
im = roundtrip(im, icc_profile=None)
|
||||||
im = roundtrip(im, icc_profile=None)
|
|
||||||
self.assertNotIn("icc_profile", im.info)
|
self.assertNotIn("icc_profile", im.info)
|
||||||
|
|
||||||
def test_roundtrip_icc_profile(self):
|
def test_roundtrip_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile.png")
|
with Image.open("Tests/images/icc_profile.png") as im:
|
||||||
expected_icc = im.info["icc_profile"]
|
expected_icc = im.info["icc_profile"]
|
||||||
|
|
||||||
im = roundtrip(im)
|
im = roundtrip(im)
|
||||||
self.assertEqual(im.info["icc_profile"], expected_icc)
|
self.assertEqual(im.info["icc_profile"], expected_icc)
|
||||||
|
|
||||||
def test_roundtrip_no_icc_profile(self):
|
def test_roundtrip_no_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertIsNone(im.info["icc_profile"])
|
self.assertIsNone(im.info["icc_profile"])
|
||||||
|
|
||||||
im = roundtrip(im)
|
im = roundtrip(im)
|
||||||
self.assertNotIn("icc_profile", im.info)
|
self.assertNotIn("icc_profile", im.info)
|
||||||
|
|
||||||
def test_repr_png(self):
|
def test_repr_png(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
repr_png = Image.open(BytesIO(im._repr_png_()))
|
with Image.open(BytesIO(im._repr_png_())) as repr_png:
|
||||||
self.assertEqual(repr_png.format, "PNG")
|
self.assertEqual(repr_png.format, "PNG")
|
||||||
self.assert_image_equal(im, repr_png)
|
self.assert_image_equal(im, repr_png)
|
||||||
|
|
||||||
def test_chunk_order(self):
|
def test_chunk_order(self):
|
||||||
im = Image.open("Tests/images/icc_profile.png")
|
with Image.open("Tests/images/icc_profile.png") as im:
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.convert("P").save(test_file, dpi=(100, 100))
|
im.convert("P").save(test_file, dpi=(100, 100))
|
||||||
|
|
||||||
chunks = self.get_chunks(test_file)
|
chunks = self.get_chunks(test_file)
|
||||||
|
|
||||||
|
@ -579,78 +564,75 @@ class TestFilePng(PillowTestCase):
|
||||||
self.assertEqual(len(chunks), 3)
|
self.assertEqual(len(chunks), 3)
|
||||||
|
|
||||||
def test_textual_chunks_after_idat(self):
|
def test_textual_chunks_after_idat(self):
|
||||||
im = Image.open("Tests/images/hopper.png")
|
with Image.open("Tests/images/hopper.png") as im:
|
||||||
self.assertIn("comment", im.text.keys())
|
self.assertIn("comment", im.text.keys())
|
||||||
for k, v in {
|
for k, v in {
|
||||||
"date:create": "2014-09-04T09:37:08+03:00",
|
"date:create": "2014-09-04T09:37:08+03:00",
|
||||||
"date:modify": "2014-09-04T09:37:08+03:00",
|
"date:modify": "2014-09-04T09:37:08+03:00",
|
||||||
}.items():
|
}.items():
|
||||||
self.assertEqual(im.text[k], v)
|
self.assertEqual(im.text[k], v)
|
||||||
|
|
||||||
# Raises a SyntaxError in load_end
|
# Raises a SyntaxError in load_end
|
||||||
im = Image.open("Tests/images/broken_data_stream.png")
|
with Image.open("Tests/images/broken_data_stream.png") as im:
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
self.assertIsInstance(im.text, dict)
|
self.assertIsInstance(im.text, dict)
|
||||||
|
|
||||||
# Raises a UnicodeDecodeError in load_end
|
# Raises a UnicodeDecodeError in load_end
|
||||||
im = Image.open("Tests/images/truncated_image.png")
|
with Image.open("Tests/images/truncated_image.png") as im:
|
||||||
# The file is truncated
|
# The file is truncated
|
||||||
self.assertRaises(IOError, lambda: im.text)
|
self.assertRaises(IOError, lambda: im.text)
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
self.assertIsInstance(im.text, dict)
|
self.assertIsInstance(im.text, dict)
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||||
|
|
||||||
# Raises an EOFError in load_end
|
# Raises an EOFError in load_end
|
||||||
im = Image.open("Tests/images/hopper_idat_after_image_end.png")
|
with Image.open("Tests/images/hopper_idat_after_image_end.png") as im:
|
||||||
self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"})
|
self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"})
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
im = Image.open("Tests/images/exif.png")
|
with Image.open("Tests/images/exif.png") as im:
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
self.assertEqual(exif[274], 1)
|
self.assertEqual(exif[274], 1)
|
||||||
|
|
||||||
def test_exif_save(self):
|
def test_exif_save(self):
|
||||||
im = Image.open("Tests/images/exif.png")
|
with Image.open("Tests/images/exif.png") as im:
|
||||||
|
test_file = self.tempfile("temp.png")
|
||||||
|
im.save(test_file)
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
with Image.open(test_file) as reloaded:
|
||||||
im.save(test_file)
|
exif = reloaded._getexif()
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
|
||||||
exif = reloaded._getexif()
|
|
||||||
self.assertEqual(exif[274], 1)
|
self.assertEqual(exif[274], 1)
|
||||||
|
|
||||||
def test_exif_from_jpg(self):
|
def test_exif_from_jpg(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
|
test_file = self.tempfile("temp.png")
|
||||||
|
im.save(test_file)
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
with Image.open(test_file) as reloaded:
|
||||||
im.save(test_file)
|
exif = reloaded._getexif()
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
|
||||||
exif = reloaded._getexif()
|
|
||||||
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
|
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
|
||||||
|
|
||||||
def test_exif_argument(self):
|
def test_exif_argument(self):
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
|
test_file = self.tempfile("temp.png")
|
||||||
|
im.save(test_file, exif=b"exifstring")
|
||||||
|
|
||||||
test_file = self.tempfile("temp.png")
|
with Image.open(test_file) as reloaded:
|
||||||
im.save(test_file, exif=b"exifstring")
|
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
|
||||||
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
|
|
||||||
|
|
||||||
@unittest.skipUnless(
|
@unittest.skipUnless(
|
||||||
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"
|
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"
|
||||||
)
|
)
|
||||||
def test_apng(self):
|
def test_apng(self):
|
||||||
im = Image.open("Tests/images/iss634.apng")
|
with Image.open("Tests/images/iss634.apng") as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/apng")
|
self.assertEqual(im.get_format_mimetype(), "image/apng")
|
||||||
|
|
||||||
# This also tests reading unknown PNG chunks (fcTL and fdAT) in load_end
|
# This also tests reading unknown PNG chunks (fcTL and fdAT) in load_end
|
||||||
expected = Image.open("Tests/images/iss634.webp")
|
with Image.open("Tests/images/iss634.webp") as expected:
|
||||||
self.assert_image_similar(im, expected, 0.23)
|
self.assert_image_similar(im, expected, 0.23)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
|
@unittest.skipIf(is_win32(), "requires Unix or macOS")
|
||||||
class TestTruncatedPngPLeaks(PillowLeakTestCase):
|
class TestTruncatedPngPLeaks(PillowLeakTestCase):
|
||||||
mem_limit = 2 * 1024 # max increase in K
|
mem_limit = 2 * 1024 # max increase in K
|
||||||
iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs
|
iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs
|
||||||
|
|
|
@ -8,42 +8,42 @@ test_file = "Tests/images/hopper.ppm"
|
||||||
|
|
||||||
class TestFilePpm(PillowTestCase):
|
class TestFilePpm(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PPM")
|
self.assertEqual(im.format, "PPM")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap")
|
||||||
|
|
||||||
def test_16bit_pgm(self):
|
def test_16bit_pgm(self):
|
||||||
im = Image.open("Tests/images/16_bit_binary.pgm")
|
with Image.open("Tests/images/16_bit_binary.pgm") as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "I")
|
self.assertEqual(im.mode, "I")
|
||||||
self.assertEqual(im.size, (20, 100))
|
self.assertEqual(im.size, (20, 100))
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-graymap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-graymap")
|
||||||
|
|
||||||
tgt = Image.open("Tests/images/16_bit_binary_pgm.png")
|
with Image.open("Tests/images/16_bit_binary_pgm.png") as tgt:
|
||||||
self.assert_image_equal(im, tgt)
|
self.assert_image_equal(im, tgt)
|
||||||
|
|
||||||
def test_16bit_pgm_write(self):
|
def test_16bit_pgm_write(self):
|
||||||
im = Image.open("Tests/images/16_bit_binary.pgm")
|
with Image.open("Tests/images/16_bit_binary.pgm") as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
f = self.tempfile("temp.pgm")
|
f = self.tempfile("temp.pgm")
|
||||||
im.save(f, "PPM")
|
im.save(f, "PPM")
|
||||||
|
|
||||||
reloaded = Image.open(f)
|
with Image.open(f) as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
def test_pnm(self):
|
def test_pnm(self):
|
||||||
im = Image.open("Tests/images/hopper.pnm")
|
with Image.open("Tests/images/hopper.pnm") as im:
|
||||||
self.assert_image_similar(im, hopper(), 0.0001)
|
self.assert_image_similar(im, hopper(), 0.0001)
|
||||||
|
|
||||||
f = self.tempfile("temp.pnm")
|
f = self.tempfile("temp.pnm")
|
||||||
im.save(f)
|
im.save(f)
|
||||||
|
|
||||||
reloaded = Image.open(f)
|
with Image.open(f) as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
def test_truncated_file(self):
|
def test_truncated_file(self):
|
||||||
path = self.tempfile("temp.pgm")
|
path = self.tempfile("temp.pgm")
|
||||||
|
@ -66,10 +66,10 @@ class TestFilePpm(PillowTestCase):
|
||||||
|
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("P4\n128 128\n255")
|
f.write("P4\n128 128\n255")
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
|
||||||
|
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("PyCMYK\n128 128\n255")
|
f.write("PyCMYK\n128 128\n255")
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
|
||||||
|
|
|
@ -1,26 +1,44 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, PsdImagePlugin
|
from PIL import Image, PsdImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
test_file = "Tests/images/hopper.psd"
|
test_file = "Tests/images/hopper.psd"
|
||||||
|
|
||||||
|
|
||||||
class TestImagePsd(PillowTestCase):
|
class TestImagePsd(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PSD")
|
self.assertEqual(im.format, "PSD")
|
||||||
|
|
||||||
im2 = hopper()
|
im2 = hopper()
|
||||||
self.assert_image_similar(im, im2, 4.8)
|
self.assert_image_similar(im, im2, 4.8)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(test_file)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(test_file) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -29,64 +47,63 @@ class TestImagePsd(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
|
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
# PSD seek index starts at 1 rather than 0
|
# PSD seek index starts at 1 rather than 0
|
||||||
n_frames = im.n_frames + 1
|
n_frames = im.n_frames + 1
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, 0)
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 2)
|
self.assertEqual(layer_number, 2)
|
||||||
|
|
||||||
def test_seek_eoferror(self):
|
def test_seek_eoferror(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
|
|
||||||
def test_open_after_exclusive_load(self):
|
def test_open_after_exclusive_load(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
im.seek(im.tell() + 1)
|
im.seek(im.tell() + 1)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_icc_profile(self):
|
def test_icc_profile(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertIn("icc_profile", im.info)
|
self.assertIn("icc_profile", im.info)
|
||||||
|
|
||||||
icc_profile = im.info["icc_profile"]
|
icc_profile = im.info["icc_profile"]
|
||||||
self.assertEqual(len(icc_profile), 3144)
|
self.assertEqual(len(icc_profile), 3144)
|
||||||
|
|
||||||
def test_no_icc_profile(self):
|
def test_no_icc_profile(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||||
|
self.assertNotIn("icc_profile", im.info)
|
||||||
self.assertNotIn("icc_profile", im.info)
|
|
||||||
|
|
||||||
def test_combined_larger_than_size(self):
|
def test_combined_larger_than_size(self):
|
||||||
# The 'combined' sizes of the individual parts is larger than the
|
# The 'combined' sizes of the individual parts is larger than the
|
||||||
|
|
|
@ -9,50 +9,50 @@ class TestFileSgi(PillowTestCase):
|
||||||
# convert hopper.ppm -compress None sgi:hopper.rgb
|
# convert hopper.ppm -compress None sgi:hopper.rgb
|
||||||
test_file = "Tests/images/hopper.rgb"
|
test_file = "Tests/images/hopper.rgb"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assert_image_equal(im, hopper())
|
self.assert_image_equal(im, hopper())
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/rgb")
|
self.assertEqual(im.get_format_mimetype(), "image/rgb")
|
||||||
|
|
||||||
def test_rgb16(self):
|
def test_rgb16(self):
|
||||||
test_file = "Tests/images/hopper16.rgb"
|
test_file = "Tests/images/hopper16.rgb"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assert_image_equal(im, hopper())
|
self.assert_image_equal(im, hopper())
|
||||||
|
|
||||||
def test_l(self):
|
def test_l(self):
|
||||||
# Created with ImageMagick
|
# Created with ImageMagick
|
||||||
# convert hopper.ppm -monochrome -compress None sgi:hopper.bw
|
# convert hopper.ppm -monochrome -compress None sgi:hopper.bw
|
||||||
test_file = "Tests/images/hopper.bw"
|
test_file = "Tests/images/hopper.bw"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assert_image_similar(im, hopper("L"), 2)
|
self.assert_image_similar(im, hopper("L"), 2)
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/sgi")
|
self.assertEqual(im.get_format_mimetype(), "image/sgi")
|
||||||
|
|
||||||
def test_rgba(self):
|
def test_rgba(self):
|
||||||
# Created with ImageMagick:
|
# Created with ImageMagick:
|
||||||
# convert transparent.png -compress None transparent.sgi
|
# convert transparent.png -compress None transparent.sgi
|
||||||
test_file = "Tests/images/transparent.sgi"
|
test_file = "Tests/images/transparent.sgi"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
target = Image.open("Tests/images/transparent.png")
|
with Image.open("Tests/images/transparent.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/sgi")
|
self.assertEqual(im.get_format_mimetype(), "image/sgi")
|
||||||
|
|
||||||
def test_rle(self):
|
def test_rle(self):
|
||||||
# Created with ImageMagick:
|
# Created with ImageMagick:
|
||||||
# convert hopper.ppm hopper.sgi
|
# convert hopper.ppm hopper.sgi
|
||||||
test_file = "Tests/images/hopper.sgi"
|
test_file = "Tests/images/hopper.sgi"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
target = Image.open("Tests/images/hopper.rgb")
|
with Image.open("Tests/images/hopper.rgb") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_rle16(self):
|
def test_rle16(self):
|
||||||
test_file = "Tests/images/tv16.sgi"
|
test_file = "Tests/images/tv16.sgi"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
target = Image.open("Tests/images/tv.rgb")
|
with Image.open("Tests/images/tv.rgb") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -63,8 +63,8 @@ class TestFileSgi(PillowTestCase):
|
||||||
def roundtrip(img):
|
def roundtrip(img):
|
||||||
out = self.tempfile("temp.sgi")
|
out = self.tempfile("temp.sgi")
|
||||||
img.save(out, format="sgi")
|
img.save(out, format="sgi")
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assert_image_equal(img, reloaded)
|
self.assert_image_equal(img, reloaded)
|
||||||
|
|
||||||
for mode in ("L", "RGB", "RGBA"):
|
for mode in ("L", "RGB", "RGBA"):
|
||||||
roundtrip(hopper(mode))
|
roundtrip(hopper(mode))
|
||||||
|
@ -75,12 +75,12 @@ class TestFileSgi(PillowTestCase):
|
||||||
def test_write16(self):
|
def test_write16(self):
|
||||||
test_file = "Tests/images/hopper16.rgb"
|
test_file = "Tests/images/hopper16.rgb"
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
out = self.tempfile("temp.sgi")
|
out = self.tempfile("temp.sgi")
|
||||||
im.save(out, format="sgi", bpc=2)
|
im.save(out, format="sgi", bpc=2)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
def test_unsupported_mode(self):
|
def test_unsupported_mode(self):
|
||||||
im = hopper("LA")
|
im = hopper("LA")
|
||||||
|
|
|
@ -1,26 +1,43 @@
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.spider"
|
TEST_FILE = "Tests/images/hopper.spider"
|
||||||
|
|
||||||
|
|
||||||
class TestImageSpider(PillowTestCase):
|
class TestImageSpider(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "SPIDER")
|
self.assertEqual(im.format, "SPIDER")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open(TEST_FILE)
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open(TEST_FILE) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
|
@ -32,10 +49,10 @@ class TestImageSpider(PillowTestCase):
|
||||||
im.save(temp, "SPIDER")
|
im.save(temp, "SPIDER")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
im2 = Image.open(temp)
|
with Image.open(temp) as im2:
|
||||||
self.assertEqual(im2.mode, "F")
|
self.assertEqual(im2.mode, "F")
|
||||||
self.assertEqual(im2.size, (128, 128))
|
self.assertEqual(im2.size, (128, 128))
|
||||||
self.assertEqual(im2.format, "SPIDER")
|
self.assertEqual(im2.format, "SPIDER")
|
||||||
|
|
||||||
def test_tempfile(self):
|
def test_tempfile(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -47,28 +64,28 @@ class TestImageSpider(PillowTestCase):
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
reloaded = Image.open(fp)
|
with Image.open(fp) as reloaded:
|
||||||
self.assertEqual(reloaded.mode, "F")
|
self.assertEqual(reloaded.mode, "F")
|
||||||
self.assertEqual(reloaded.size, (128, 128))
|
self.assertEqual(reloaded.size, (128, 128))
|
||||||
self.assertEqual(reloaded.format, "SPIDER")
|
self.assertEqual(reloaded.format, "SPIDER")
|
||||||
|
|
||||||
def test_isSpiderImage(self):
|
def test_isSpiderImage(self):
|
||||||
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
|
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
index = im.tell()
|
index = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(index, 0)
|
self.assertEqual(index, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_loadImageSeries(self):
|
def test_loadImageSeries(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -109,15 +126,14 @@ class TestImageSpider(PillowTestCase):
|
||||||
self.assertRaises(IOError, Image.open, invalid_file)
|
self.assertRaises(IOError, Image.open, invalid_file)
|
||||||
|
|
||||||
def test_nonstack_file(self):
|
def test_nonstack_file(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
self.assertRaises(EOFError, im.seek, 0)
|
|
||||||
|
|
||||||
def test_nonstack_dos(self):
|
def test_nonstack_dos(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||||
if i > 1:
|
if i > 1:
|
||||||
self.fail("Non-stack DOS file test failed")
|
self.fail("Non-stack DOS file test failed")
|
||||||
|
|
||||||
# for issue #4093
|
# for issue #4093
|
||||||
def test_odd_size(self):
|
def test_odd_size(self):
|
||||||
|
@ -127,5 +143,5 @@ class TestImageSpider(PillowTestCase):
|
||||||
im.save(data, format="SPIDER")
|
im.save(data, format="SPIDER")
|
||||||
|
|
||||||
data.seek(0)
|
data.seek(0)
|
||||||
im2 = Image.open(data)
|
with Image.open(data) as im2:
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import os
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, SunImagePlugin
|
from PIL import Image, SunImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
EXTRA_DIR = "Tests/images/sunraster"
|
EXTRA_DIR = "Tests/images/sunraster"
|
||||||
|
|
||||||
|
@ -14,20 +15,20 @@ class TestFileSun(PillowTestCase):
|
||||||
test_file = "Tests/images/hopper.ras"
|
test_file = "Tests/images/hopper.ras"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
||||||
self.assert_image_similar(im, hopper(), 5) # visually verified
|
self.assert_image_similar(im, hopper(), 5) # visually verified
|
||||||
|
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
self.assertRaises(SyntaxError, SunImagePlugin.SunImageFile, invalid_file)
|
self.assertRaises(SyntaxError, SunImagePlugin.SunImageFile, invalid_file)
|
||||||
|
|
||||||
def test_im1(self):
|
def test_im1(self):
|
||||||
im = Image.open("Tests/images/sunraster.im1")
|
with Image.open("Tests/images/sunraster.im1") as im:
|
||||||
target = Image.open("Tests/images/sunraster.im1.png")
|
with Image.open("Tests/images/sunraster.im1.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
@unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed")
|
@unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed")
|
||||||
def test_others(self):
|
def test_others(self):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, TarIO
|
from PIL import Image, TarIO
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
|
@ -19,17 +21,30 @@ class TestFileTar(PillowTestCase):
|
||||||
["jpeg_decoder", "hopper.jpg", "JPEG"],
|
["jpeg_decoder", "hopper.jpg", "JPEG"],
|
||||||
]:
|
]:
|
||||||
if codec in codecs:
|
if codec in codecs:
|
||||||
tar = TarIO.TarIO(TEST_TAR_FILE, test_path)
|
with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar:
|
||||||
im = Image.open(tar)
|
with Image.open(tar) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, format)
|
self.assertEqual(im.format, format)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
|
def test_unclosed_file(self):
|
||||||
|
def open():
|
||||||
|
TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
def test_close(self):
|
def test_close(self):
|
||||||
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
def open():
|
||||||
tar.close()
|
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
||||||
|
tar.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_contextmanager(self):
|
def test_contextmanager(self):
|
||||||
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
def open():
|
||||||
pass
|
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
|
@ -24,177 +24,179 @@ class TestFileTga(PillowTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
for png_path in png_paths:
|
for png_path in png_paths:
|
||||||
reference_im = Image.open(png_path)
|
with Image.open(png_path) as reference_im:
|
||||||
self.assertEqual(reference_im.mode, mode)
|
self.assertEqual(reference_im.mode, mode)
|
||||||
|
|
||||||
path_no_ext = os.path.splitext(png_path)[0]
|
path_no_ext = os.path.splitext(png_path)[0]
|
||||||
for origin, rle in product(self._ORIGINS, (True, False)):
|
for origin, rle in product(self._ORIGINS, (True, False)):
|
||||||
tga_path = "{}_{}_{}.tga".format(
|
tga_path = "{}_{}_{}.tga".format(
|
||||||
path_no_ext, origin, "rle" if rle else "raw"
|
path_no_ext, origin, "rle" if rle else "raw"
|
||||||
)
|
|
||||||
|
|
||||||
original_im = Image.open(tga_path)
|
|
||||||
self.assertEqual(original_im.format, "TGA")
|
|
||||||
self.assertEqual(original_im.get_format_mimetype(), "image/x-tga")
|
|
||||||
if rle:
|
|
||||||
self.assertEqual(original_im.info["compression"], "tga_rle")
|
|
||||||
self.assertEqual(
|
|
||||||
original_im.info["orientation"],
|
|
||||||
self._ORIGIN_TO_ORIENTATION[origin],
|
|
||||||
)
|
|
||||||
if mode == "P":
|
|
||||||
self.assertEqual(
|
|
||||||
original_im.getpalette(), reference_im.getpalette()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assert_image_equal(original_im, reference_im)
|
with Image.open(tga_path) as original_im:
|
||||||
|
self.assertEqual(original_im.format, "TGA")
|
||||||
|
self.assertEqual(
|
||||||
|
original_im.get_format_mimetype(), "image/x-tga"
|
||||||
|
)
|
||||||
|
if rle:
|
||||||
|
self.assertEqual(
|
||||||
|
original_im.info["compression"], "tga_rle"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
original_im.info["orientation"],
|
||||||
|
self._ORIGIN_TO_ORIENTATION[origin],
|
||||||
|
)
|
||||||
|
if mode == "P":
|
||||||
|
self.assertEqual(
|
||||||
|
original_im.getpalette(), reference_im.getpalette()
|
||||||
|
)
|
||||||
|
|
||||||
# Generate a new test name every time so the
|
self.assert_image_equal(original_im, reference_im)
|
||||||
# test will not fail with permission error
|
|
||||||
# on Windows.
|
|
||||||
out = self.tempfile("temp.tga")
|
|
||||||
|
|
||||||
original_im.save(out, rle=rle)
|
# Generate a new test name every time so the
|
||||||
saved_im = Image.open(out)
|
# test will not fail with permission error
|
||||||
if rle:
|
# on Windows.
|
||||||
self.assertEqual(
|
out = self.tempfile("temp.tga")
|
||||||
saved_im.info["compression"],
|
|
||||||
original_im.info["compression"],
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
saved_im.info["orientation"], original_im.info["orientation"]
|
|
||||||
)
|
|
||||||
if mode == "P":
|
|
||||||
self.assertEqual(
|
|
||||||
saved_im.getpalette(), original_im.getpalette()
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assert_image_equal(saved_im, original_im)
|
original_im.save(out, rle=rle)
|
||||||
|
with Image.open(out) as saved_im:
|
||||||
|
if rle:
|
||||||
|
self.assertEqual(
|
||||||
|
saved_im.info["compression"],
|
||||||
|
original_im.info["compression"],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
saved_im.info["orientation"],
|
||||||
|
original_im.info["orientation"],
|
||||||
|
)
|
||||||
|
if mode == "P":
|
||||||
|
self.assertEqual(
|
||||||
|
saved_im.getpalette(), original_im.getpalette()
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assert_image_equal(saved_im, original_im)
|
||||||
|
|
||||||
def test_id_field(self):
|
def test_id_field(self):
|
||||||
# tga file with id field
|
# tga file with id field
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (100, 100))
|
self.assertEqual(im.size, (100, 100))
|
||||||
|
|
||||||
def test_id_field_rle(self):
|
def test_id_field_rle(self):
|
||||||
# tga file with id field
|
# tga file with id field
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (199, 199))
|
self.assertEqual(im.size, (199, 199))
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
out = self.tempfile("temp.tga")
|
||||||
|
|
||||||
out = self.tempfile("temp.tga")
|
# Save
|
||||||
|
im.save(out)
|
||||||
|
with Image.open(out) as test_im:
|
||||||
|
self.assertEqual(test_im.size, (100, 100))
|
||||||
|
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
|
||||||
|
|
||||||
# Save
|
# RGBA save
|
||||||
im.save(out)
|
im.convert("RGBA").save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (100, 100))
|
self.assertEqual(test_im.size, (100, 100))
|
||||||
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
|
|
||||||
|
|
||||||
# RGBA save
|
|
||||||
im.convert("RGBA").save(out)
|
|
||||||
test_im = Image.open(out)
|
|
||||||
self.assertEqual(test_im.size, (100, 100))
|
|
||||||
|
|
||||||
def test_save_id_section(self):
|
def test_save_id_section(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
out = self.tempfile("temp.tga")
|
||||||
|
|
||||||
out = self.tempfile("temp.tga")
|
# Check there is no id section
|
||||||
|
im.save(out)
|
||||||
# Check there is no id section
|
with Image.open(out) as test_im:
|
||||||
im.save(out)
|
self.assertNotIn("id_section", test_im.info)
|
||||||
test_im = Image.open(out)
|
|
||||||
self.assertNotIn("id_section", test_im.info)
|
|
||||||
|
|
||||||
# Save with custom id section
|
# Save with custom id section
|
||||||
im.save(out, id_section=b"Test content")
|
im.save(out, id_section=b"Test content")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["id_section"], b"Test content")
|
self.assertEqual(test_im.info["id_section"], b"Test content")
|
||||||
|
|
||||||
# Save with custom id section greater than 255 characters
|
# Save with custom id section greater than 255 characters
|
||||||
id_section = b"Test content" * 25
|
id_section = b"Test content" * 25
|
||||||
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
|
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["id_section"], id_section[:255])
|
self.assertEqual(test_im.info["id_section"], id_section[:255])
|
||||||
|
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Save with no id section
|
# Save with no id section
|
||||||
im.save(out, id_section="")
|
im.save(out, id_section="")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("id_section", test_im.info)
|
self.assertNotIn("id_section", test_im.info)
|
||||||
|
|
||||||
def test_save_orientation(self):
|
def test_save_orientation(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
im = Image.open(test_file)
|
|
||||||
self.assertEqual(im.info["orientation"], -1)
|
|
||||||
|
|
||||||
out = self.tempfile("temp.tga")
|
out = self.tempfile("temp.tga")
|
||||||
|
with Image.open(test_file) as im:
|
||||||
|
self.assertEqual(im.info["orientation"], -1)
|
||||||
|
|
||||||
im.save(out, orientation=1)
|
im.save(out, orientation=1)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["orientation"], 1)
|
self.assertEqual(test_im.info["orientation"], 1)
|
||||||
|
|
||||||
def test_save_rle(self):
|
def test_save_rle(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.info["compression"], "tga_rle")
|
self.assertEqual(im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
out = self.tempfile("temp.tga")
|
out = self.tempfile("temp.tga")
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (199, 199))
|
self.assertEqual(test_im.size, (199, 199))
|
||||||
self.assertEqual(test_im.info["compression"], "tga_rle")
|
self.assertEqual(test_im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
# Save without compression
|
# Save without compression
|
||||||
im.save(out, compression=None)
|
im.save(out, compression=None)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("compression", test_im.info)
|
self.assertNotIn("compression", test_im.info)
|
||||||
|
|
||||||
# RGBA save
|
# RGBA save
|
||||||
im.convert("RGBA").save(out)
|
im.convert("RGBA").save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (199, 199))
|
self.assertEqual(test_im.size, (199, 199))
|
||||||
|
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertNotIn("compression", im.info)
|
self.assertNotIn("compression", im.info)
|
||||||
|
|
||||||
# Save with compression
|
# Save with compression
|
||||||
im.save(out, compression="tga_rle")
|
im.save(out, compression="tga_rle")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["compression"], "tga_rle")
|
self.assertEqual(test_im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
def test_save_l_transparency(self):
|
def test_save_l_transparency(self):
|
||||||
# There are 559 transparent pixels in la.tga.
|
# There are 559 transparent pixels in la.tga.
|
||||||
num_transparent = 559
|
num_transparent = 559
|
||||||
|
|
||||||
in_file = "Tests/images/la.tga"
|
in_file = "Tests/images/la.tga"
|
||||||
im = Image.open(in_file)
|
with Image.open(in_file) as im:
|
||||||
self.assertEqual(im.mode, "LA")
|
self.assertEqual(im.mode, "LA")
|
||||||
self.assertEqual(im.getchannel("A").getcolors()[0][0], num_transparent)
|
self.assertEqual(im.getchannel("A").getcolors()[0][0], num_transparent)
|
||||||
|
|
||||||
out = self.tempfile("temp.tga")
|
out = self.tempfile("temp.tga")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.mode, "LA")
|
self.assertEqual(test_im.mode, "LA")
|
||||||
self.assertEqual(test_im.getchannel("A").getcolors()[0][0], num_transparent)
|
self.assertEqual(test_im.getchannel("A").getcolors()[0][0], num_transparent)
|
||||||
|
|
||||||
self.assert_image_equal(im, test_im)
|
self.assert_image_equal(im, test_im)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import os
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
import pytest
|
||||||
from PIL import Image, TiffImagePlugin
|
from PIL import Image, TiffImagePlugin
|
||||||
from PIL._util import py3
|
|
||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper, is_pypy, is_win32
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -18,54 +19,73 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
|
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "TIFF")
|
self.assertEqual(im.format, "TIFF")
|
||||||
|
|
||||||
hopper("1").save(filename)
|
hopper("1").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("L").save(filename)
|
hopper("L").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("P").save(filename)
|
hopper("P").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("I").save(filename)
|
hopper("I").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
im = Image.open("Tests/images/multipage.tiff")
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
|
self.assert_warning(ResourceWarning, open)
|
||||||
|
|
||||||
|
def test_closed_file(self):
|
||||||
|
def open():
|
||||||
|
im = Image.open("Tests/images/multipage.tiff")
|
||||||
|
im.load()
|
||||||
|
im.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
|
def test_context_manager(self):
|
||||||
|
def open():
|
||||||
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assert_warning(None, open)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_mac_tiff(self):
|
def test_mac_tiff(self):
|
||||||
# Read RGBa images from macOS [@PIL136]
|
# Read RGBa images from macOS [@PIL136]
|
||||||
|
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (55, 43))
|
||||||
|
self.assertEqual(im.tile, [("raw", (0, 0, 55, 43), 8, ("RGBa", 0, 1))])
|
||||||
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assert_image_similar_tofile(im, "Tests/images/pil136.png", 1)
|
||||||
self.assertEqual(im.size, (55, 43))
|
|
||||||
self.assertEqual(im.tile, [("raw", (0, 0, 55, 43), 8, ("RGBa", 0, 1))])
|
|
||||||
im.load()
|
|
||||||
|
|
||||||
self.assert_image_similar_tofile(im, "Tests/images/pil136.png", 1)
|
|
||||||
|
|
||||||
def test_wrong_bits_per_sample(self):
|
def test_wrong_bits_per_sample(self):
|
||||||
im = Image.open("Tests/images/tiff_wrong_bits_per_sample.tiff")
|
with Image.open("Tests/images/tiff_wrong_bits_per_sample.tiff") as im:
|
||||||
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.size, (52, 53))
|
||||||
self.assertEqual(im.size, (52, 53))
|
self.assertEqual(im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))])
|
||||||
self.assertEqual(im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))])
|
im.load()
|
||||||
im.load()
|
|
||||||
|
|
||||||
def test_set_legacy_api(self):
|
def test_set_legacy_api(self):
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
@ -73,86 +93,76 @@ class TestFileTiff(PillowTestCase):
|
||||||
ifd.legacy_api = None
|
ifd.legacy_api = None
|
||||||
self.assertEqual(str(e.exception), "Not allowing setting of legacy api")
|
self.assertEqual(str(e.exception), "Not allowing setting of legacy api")
|
||||||
|
|
||||||
def test_size(self):
|
|
||||||
filename = "Tests/images/pil168.tif"
|
|
||||||
im = Image.open(filename)
|
|
||||||
|
|
||||||
def set_size():
|
|
||||||
im.size = (256, 256)
|
|
||||||
|
|
||||||
self.assert_warning(DeprecationWarning, set_size)
|
|
||||||
|
|
||||||
def test_xyres_tiff(self):
|
def test_xyres_tiff(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# legacy api
|
# legacy api
|
||||||
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
|
||||||
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
|
|
||||||
self.assertEqual(im.info["dpi"], (72.0, 72.0))
|
self.assertEqual(im.info["dpi"], (72.0, 72.0))
|
||||||
|
|
||||||
def test_xyres_fallback_tiff(self):
|
def test_xyres_fallback_tiff(self):
|
||||||
filename = "Tests/images/compression.tif"
|
filename = "Tests/images/compression.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
||||||
|
|
||||||
# Legacy.
|
# Legacy.
|
||||||
self.assertEqual(im.info["resolution"], (100.0, 100.0))
|
self.assertEqual(im.info["resolution"], (100.0, 100.0))
|
||||||
# Fallback "inch".
|
# Fallback "inch".
|
||||||
self.assertEqual(im.info["dpi"], (100.0, 100.0))
|
self.assertEqual(im.info["dpi"], (100.0, 100.0))
|
||||||
|
|
||||||
def test_int_resolution(self):
|
def test_int_resolution(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Try to read a file where X,Y_RESOLUTION are ints
|
# Try to read a file where X,Y_RESOLUTION are ints
|
||||||
im.tag_v2[X_RESOLUTION] = 71
|
im.tag_v2[X_RESOLUTION] = 71
|
||||||
im.tag_v2[Y_RESOLUTION] = 71
|
im.tag_v2[Y_RESOLUTION] = 71
|
||||||
im._setup()
|
im._setup()
|
||||||
self.assertEqual(im.info["dpi"], (71.0, 71.0))
|
self.assertEqual(im.info["dpi"], (71.0, 71.0))
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))):
|
for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))):
|
||||||
im = Image.open(
|
with Image.open(
|
||||||
"Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif"
|
"Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif"
|
||||||
)
|
) as im:
|
||||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||||
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
|
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
|
||||||
|
|
||||||
im = Image.open(
|
with Image.open(
|
||||||
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
|
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
|
||||||
)
|
) as im:
|
||||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||||
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
|
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.tif")
|
outfile = self.tempfile("temp.tif")
|
||||||
im = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
|
for dpi in (72.2, 72.8):
|
||||||
|
im.save(outfile, dpi=(dpi, dpi))
|
||||||
|
|
||||||
for dpi in (72.2, 72.8):
|
with Image.open(outfile) as reloaded:
|
||||||
im.save(outfile, dpi=(dpi, dpi))
|
reloaded.load()
|
||||||
|
self.assertEqual((round(dpi), round(dpi)), reloaded.info["dpi"])
|
||||||
reloaded = Image.open(outfile)
|
|
||||||
reloaded.load()
|
|
||||||
self.assertEqual((round(dpi), round(dpi)), reloaded.info["dpi"])
|
|
||||||
|
|
||||||
def test_save_setting_missing_resolution(self):
|
def test_save_setting_missing_resolution(self):
|
||||||
b = BytesIO()
|
b = BytesIO()
|
||||||
Image.open("Tests/images/10ct_32bit_128.tiff").save(
|
Image.open("Tests/images/10ct_32bit_128.tiff").save(
|
||||||
b, format="tiff", resolution=123.45
|
b, format="tiff", resolution=123.45
|
||||||
)
|
)
|
||||||
im = Image.open(b)
|
with Image.open(b) as im:
|
||||||
self.assertEqual(float(im.tag_v2[X_RESOLUTION]), 123.45)
|
self.assertEqual(float(im.tag_v2[X_RESOLUTION]), 123.45)
|
||||||
self.assertEqual(float(im.tag_v2[Y_RESOLUTION]), 123.45)
|
self.assertEqual(float(im.tag_v2[Y_RESOLUTION]), 123.45)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -164,9 +174,9 @@ class TestFileTiff(PillowTestCase):
|
||||||
TiffImagePlugin.PREFIXES.pop()
|
TiffImagePlugin.PREFIXES.pop()
|
||||||
|
|
||||||
def test_bad_exif(self):
|
def test_bad_exif(self):
|
||||||
i = Image.open("Tests/images/hopper_bad_exif.jpg")
|
with Image.open("Tests/images/hopper_bad_exif.jpg") as i:
|
||||||
# Should not raise struct.error.
|
# Should not raise struct.error.
|
||||||
self.assert_warning(UserWarning, i._getexif)
|
self.assert_warning(UserWarning, i._getexif)
|
||||||
|
|
||||||
def test_save_rgba(self):
|
def test_save_rgba(self):
|
||||||
im = hopper("RGBA")
|
im = hopper("RGBA")
|
||||||
|
@ -179,63 +189,53 @@ class TestFileTiff(PillowTestCase):
|
||||||
self.assertRaises(IOError, im.save, outfile)
|
self.assertRaises(IOError, im.save, outfile)
|
||||||
|
|
||||||
def test_little_endian(self):
|
def test_little_endian(self):
|
||||||
im = Image.open("Tests/images/16bit.cropped.tif")
|
with Image.open("Tests/images/16bit.cropped.tif") as im:
|
||||||
self.assertEqual(im.getpixel((0, 0)), 480)
|
self.assertEqual(im.getpixel((0, 0)), 480)
|
||||||
self.assertEqual(im.mode, "I;16")
|
self.assertEqual(im.mode, "I;16")
|
||||||
|
|
||||||
b = im.tobytes()
|
b = im.tobytes()
|
||||||
# Bytes are in image native order (little endian)
|
# Bytes are in image native order (little endian)
|
||||||
if py3:
|
self.assertEqual(b[0], ord(b"\xe0"))
|
||||||
self.assertEqual(b[0], ord(b"\xe0"))
|
self.assertEqual(b[1], ord(b"\x01"))
|
||||||
self.assertEqual(b[1], ord(b"\x01"))
|
|
||||||
else:
|
|
||||||
self.assertEqual(b[0], b"\xe0")
|
|
||||||
self.assertEqual(b[1], b"\x01")
|
|
||||||
|
|
||||||
def test_big_endian(self):
|
def test_big_endian(self):
|
||||||
im = Image.open("Tests/images/16bit.MM.cropped.tif")
|
with Image.open("Tests/images/16bit.MM.cropped.tif") as im:
|
||||||
self.assertEqual(im.getpixel((0, 0)), 480)
|
self.assertEqual(im.getpixel((0, 0)), 480)
|
||||||
self.assertEqual(im.mode, "I;16B")
|
self.assertEqual(im.mode, "I;16B")
|
||||||
|
|
||||||
b = im.tobytes()
|
|
||||||
|
|
||||||
|
b = im.tobytes()
|
||||||
# Bytes are in image native order (big endian)
|
# Bytes are in image native order (big endian)
|
||||||
if py3:
|
self.assertEqual(b[0], ord(b"\x01"))
|
||||||
self.assertEqual(b[0], ord(b"\x01"))
|
self.assertEqual(b[1], ord(b"\xe0"))
|
||||||
self.assertEqual(b[1], ord(b"\xe0"))
|
|
||||||
else:
|
|
||||||
self.assertEqual(b[0], b"\x01")
|
|
||||||
self.assertEqual(b[1], b"\xe0")
|
|
||||||
|
|
||||||
def test_16bit_s(self):
|
def test_16bit_s(self):
|
||||||
im = Image.open("Tests/images/16bit.s.tif")
|
with Image.open("Tests/images/16bit.s.tif") as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "I")
|
self.assertEqual(im.mode, "I")
|
||||||
self.assertEqual(im.getpixel((0, 0)), 32767)
|
self.assertEqual(im.getpixel((0, 0)), 32767)
|
||||||
self.assertEqual(im.getpixel((0, 1)), 0)
|
self.assertEqual(im.getpixel((0, 1)), 0)
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
""" Are we generating the same interpretation
|
||||||
of the image as Imagemagick is? """
|
of the image as Imagemagick is? """
|
||||||
|
|
||||||
im = Image.open("Tests/images/12bit.cropped.tif")
|
with Image.open("Tests/images/12bit.cropped.tif") as im:
|
||||||
|
# to make the target --
|
||||||
|
# convert 12bit.cropped.tif -depth 16 tmp.tif
|
||||||
|
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
|
||||||
|
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
|
||||||
|
# so we need to unshift so that the integer values are the same.
|
||||||
|
|
||||||
# to make the target --
|
self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
|
||||||
# convert 12bit.cropped.tif -depth 16 tmp.tif
|
|
||||||
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
|
|
||||||
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
|
|
||||||
# so we need to unshift so that the integer values are the same.
|
|
||||||
|
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif")
|
|
||||||
|
|
||||||
def test_32bit_float(self):
|
def test_32bit_float(self):
|
||||||
# Issue 614, specific 32-bit float format
|
# Issue 614, specific 32-bit float format
|
||||||
path = "Tests/images/10ct_32bit_128.tiff"
|
path = "Tests/images/10ct_32bit_128.tiff"
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343)
|
self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343)
|
||||||
self.assertEqual(im.getextrema(), (-3.140936851501465, 3.140684127807617))
|
self.assertEqual(im.getextrema(), (-3.140936851501465, 3.140684127807617))
|
||||||
|
|
||||||
def test_unknown_pixel_mode(self):
|
def test_unknown_pixel_mode(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
|
@ -247,109 +247,109 @@ class TestFileTiff(PillowTestCase):
|
||||||
["Tests/images/multipage-lastframe.tif", 1],
|
["Tests/images/multipage-lastframe.tif", 1],
|
||||||
["Tests/images/multipage.tiff", 3],
|
["Tests/images/multipage.tiff", 3],
|
||||||
]:
|
]:
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.n_frames, n_frames)
|
self.assertEqual(im.n_frames, n_frames)
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open("Tests/images/multipage-lastframe.tif")
|
with Image.open("Tests/images/multipage-lastframe.tif") as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
|
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
def test_multipage_last_frame(self):
|
def test_multipage_last_frame(self):
|
||||||
im = Image.open("Tests/images/multipage-lastframe.tif")
|
with Image.open("Tests/images/multipage-lastframe.tif") as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
def test___str__(self):
|
def test___str__(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
ret = str(im.ifd)
|
ret = str(im.ifd)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(ret, str)
|
self.assertIsInstance(ret, str)
|
||||||
|
|
||||||
def test_dict(self):
|
def test_dict(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# v2 interface
|
# v2 interface
|
||||||
v2_tags = {
|
v2_tags = {
|
||||||
256: 55,
|
256: 55,
|
||||||
257: 43,
|
257: 43,
|
||||||
258: (8, 8, 8, 8),
|
258: (8, 8, 8, 8),
|
||||||
259: 1,
|
259: 1,
|
||||||
262: 2,
|
262: 2,
|
||||||
296: 2,
|
296: 2,
|
||||||
273: (8,),
|
273: (8,),
|
||||||
338: (1,),
|
338: (1,),
|
||||||
277: 4,
|
277: 4,
|
||||||
279: (9460,),
|
279: (9460,),
|
||||||
282: 72.0,
|
282: 72.0,
|
||||||
283: 72.0,
|
283: 72.0,
|
||||||
284: 1,
|
284: 1,
|
||||||
}
|
}
|
||||||
self.assertEqual(dict(im.tag_v2), v2_tags)
|
self.assertEqual(dict(im.tag_v2), v2_tags)
|
||||||
|
|
||||||
# legacy interface
|
# legacy interface
|
||||||
legacy_tags = {
|
legacy_tags = {
|
||||||
256: (55,),
|
256: (55,),
|
||||||
257: (43,),
|
257: (43,),
|
||||||
258: (8, 8, 8, 8),
|
258: (8, 8, 8, 8),
|
||||||
259: (1,),
|
259: (1,),
|
||||||
262: (2,),
|
262: (2,),
|
||||||
296: (2,),
|
296: (2,),
|
||||||
273: (8,),
|
273: (8,),
|
||||||
338: (1,),
|
338: (1,),
|
||||||
277: (4,),
|
277: (4,),
|
||||||
279: (9460,),
|
279: (9460,),
|
||||||
282: ((720000, 10000),),
|
282: ((720000, 10000),),
|
||||||
283: ((720000, 10000),),
|
283: ((720000, 10000),),
|
||||||
284: (1,),
|
284: (1,),
|
||||||
}
|
}
|
||||||
self.assertEqual(dict(im.tag), legacy_tags)
|
self.assertEqual(dict(im.tag), legacy_tags)
|
||||||
|
|
||||||
def test__delitem__(self):
|
def test__delitem__(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
len_before = len(dict(im.ifd))
|
len_before = len(dict(im.ifd))
|
||||||
del im.ifd[256]
|
del im.ifd[256]
|
||||||
len_after = len(dict(im.ifd))
|
len_after = len(dict(im.ifd))
|
||||||
self.assertEqual(len_before, len_after + 1)
|
self.assertEqual(len_before, len_after + 1)
|
||||||
|
|
||||||
def test_load_byte(self):
|
def test_load_byte(self):
|
||||||
for legacy_api in [False, True]:
|
for legacy_api in [False, True]:
|
||||||
|
@ -378,16 +378,16 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_seek_eof(self):
|
def test_seek_eof(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
self.assertRaises(EOFError, im.seek, 1)
|
self.assertRaises(EOFError, im.seek, 1)
|
||||||
|
|
||||||
def test__limit_rational_int(self):
|
def test__limit_rational_int(self):
|
||||||
from PIL.TiffImagePlugin import _limit_rational
|
from PIL.TiffImagePlugin import _limit_rational
|
||||||
|
@ -406,10 +406,10 @@ class TestFileTiff(PillowTestCase):
|
||||||
def test_4bit(self):
|
def test_4bit(self):
|
||||||
test_file = "Tests/images/hopper_gray_4bpp.tif"
|
test_file = "Tests/images/hopper_gray_4bpp.tif"
|
||||||
original = hopper("L")
|
original = hopper("L")
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.mode, "L")
|
self.assertEqual(im.mode, "L")
|
||||||
self.assert_image_similar(im, original, 7.3)
|
self.assert_image_similar(im, original, 7.3)
|
||||||
|
|
||||||
def test_gray_semibyte_per_pixel(self):
|
def test_gray_semibyte_per_pixel(self):
|
||||||
test_files = (
|
test_files = (
|
||||||
|
@ -434,73 +434,68 @@ class TestFileTiff(PillowTestCase):
|
||||||
)
|
)
|
||||||
original = hopper("L")
|
original = hopper("L")
|
||||||
for epsilon, group in test_files:
|
for epsilon, group in test_files:
|
||||||
im = Image.open(group[0])
|
with Image.open(group[0]) as im:
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.mode, "L")
|
self.assertEqual(im.mode, "L")
|
||||||
self.assert_image_similar(im, original, epsilon)
|
self.assert_image_similar(im, original, epsilon)
|
||||||
for file in group[1:]:
|
for file in group[1:]:
|
||||||
im2 = Image.open(file)
|
with Image.open(file) as im2:
|
||||||
self.assertEqual(im2.size, (128, 128))
|
self.assertEqual(im2.size, (128, 128))
|
||||||
self.assertEqual(im2.mode, "L")
|
self.assertEqual(im2.mode, "L")
|
||||||
self.assert_image_equal(im, im2)
|
self.assert_image_equal(im, im2)
|
||||||
|
|
||||||
def test_with_underscores(self):
|
def test_with_underscores(self):
|
||||||
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
||||||
filename = self.tempfile("temp.tif")
|
filename = self.tempfile("temp.tif")
|
||||||
hopper("RGB").save(filename, **kwargs)
|
hopper("RGB").save(filename, **kwargs)
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# legacy interface
|
# legacy interface
|
||||||
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
|
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
|
||||||
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
|
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
|
||||||
|
|
||||||
# v2 interface
|
# v2 interface
|
||||||
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
||||||
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
||||||
|
|
||||||
def test_roundtrip_tiff_uint16(self):
|
def test_roundtrip_tiff_uint16(self):
|
||||||
# Test an image of all '0' values
|
# Test an image of all '0' values
|
||||||
pixel_value = 0x1234
|
pixel_value = 0x1234
|
||||||
infile = "Tests/images/uint16_1_4660.tif"
|
infile = "Tests/images/uint16_1_4660.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
self.assertEqual(im.getpixel((0, 0)), pixel_value)
|
self.assertEqual(im.getpixel((0, 0)), pixel_value)
|
||||||
|
|
||||||
tmpfile = self.tempfile("temp.tif")
|
tmpfile = self.tempfile("temp.tif")
|
||||||
im.save(tmpfile)
|
im.save(tmpfile)
|
||||||
|
|
||||||
reloaded = Image.open(tmpfile)
|
with Image.open(tmpfile) as reloaded:
|
||||||
|
self.assert_image_equal(im, reloaded)
|
||||||
self.assert_image_equal(im, reloaded)
|
|
||||||
|
|
||||||
def test_strip_raw(self):
|
def test_strip_raw(self):
|
||||||
infile = "Tests/images/tiff_strip_raw.tif"
|
infile = "Tests/images/tiff_strip_raw.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_strip_planar_raw(self):
|
def test_strip_planar_raw(self):
|
||||||
# gdal_translate -of GTiff -co INTERLEAVE=BAND \
|
# gdal_translate -of GTiff -co INTERLEAVE=BAND \
|
||||||
# tiff_strip_raw.tif tiff_strip_planar_raw.tiff
|
# tiff_strip_raw.tif tiff_strip_planar_raw.tiff
|
||||||
infile = "Tests/images/tiff_strip_planar_raw.tif"
|
infile = "Tests/images/tiff_strip_planar_raw.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_strip_planar_raw_with_overviews(self):
|
def test_strip_planar_raw_with_overviews(self):
|
||||||
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
||||||
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_tiled_planar_raw(self):
|
def test_tiled_planar_raw(self):
|
||||||
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
||||||
# -co BLOCKYSIZE=32 -co INTERLEAVE=BAND \
|
# -co BLOCKYSIZE=32 -co INTERLEAVE=BAND \
|
||||||
# tiff_tiled_raw.tif tiff_tiled_planar_raw.tiff
|
# tiff_tiled_raw.tif tiff_tiled_planar_raw.tiff
|
||||||
infile = "Tests/images/tiff_tiled_planar_raw.tif"
|
infile = "Tests/images/tiff_tiled_planar_raw.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_palette(self):
|
def test_palette(self):
|
||||||
for mode in ["P", "PA"]:
|
for mode in ["P", "PA"]:
|
||||||
|
@ -509,14 +504,11 @@ class TestFileTiff(PillowTestCase):
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
|
self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
|
||||||
|
|
||||||
def test_tiff_save_all(self):
|
def test_tiff_save_all(self):
|
||||||
import io
|
mp = BytesIO()
|
||||||
import os
|
|
||||||
|
|
||||||
mp = io.BytesIO()
|
|
||||||
with Image.open("Tests/images/multipage.tiff") as im:
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
im.save(mp, format="tiff", save_all=True)
|
im.save(mp, format="tiff", save_all=True)
|
||||||
|
|
||||||
|
@ -525,26 +517,25 @@ class TestFileTiff(PillowTestCase):
|
||||||
self.assertEqual(im.n_frames, 3)
|
self.assertEqual(im.n_frames, 3)
|
||||||
|
|
||||||
# Test appending images
|
# Test appending images
|
||||||
mp = io.BytesIO()
|
mp = BytesIO()
|
||||||
im = Image.new("RGB", (100, 100), "#f00")
|
im = Image.new("RGB", (100, 100), "#f00")
|
||||||
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
||||||
im.copy().save(mp, format="TIFF", save_all=True, append_images=ims)
|
im.copy().save(mp, format="TIFF", save_all=True, append_images=ims)
|
||||||
|
|
||||||
mp.seek(0, os.SEEK_SET)
|
mp.seek(0, os.SEEK_SET)
|
||||||
reread = Image.open(mp)
|
with Image.open(mp) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Test appending using a generator
|
# Test appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
for im in ims:
|
yield from ims
|
||||||
yield im
|
|
||||||
|
|
||||||
mp = io.BytesIO()
|
mp = BytesIO()
|
||||||
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims))
|
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims))
|
||||||
|
|
||||||
mp.seek(0, os.SEEK_SET)
|
mp.seek(0, os.SEEK_SET)
|
||||||
reread = Image.open(mp)
|
with Image.open(mp) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
def test_saving_icc_profile(self):
|
def test_saving_icc_profile(self):
|
||||||
# Tests saving TIFF with icc_profile set.
|
# Tests saving TIFF with icc_profile set.
|
||||||
|
@ -557,9 +548,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
# Try save-load round trip to make sure both handle icc_profile.
|
# Try save-load round trip to make sure both handle icc_profile.
|
||||||
tmpfile = self.tempfile("temp.tif")
|
tmpfile = self.tempfile("temp.tif")
|
||||||
im.save(tmpfile, "TIFF", compression="raw")
|
im.save(tmpfile, "TIFF", compression="raw")
|
||||||
reloaded = Image.open(tmpfile)
|
with Image.open(tmpfile) as reloaded:
|
||||||
|
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
|
||||||
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
|
|
||||||
|
|
||||||
def test_close_on_load_exclusive(self):
|
def test_close_on_load_exclusive(self):
|
||||||
# similar to test_fd_leak, but runs on unixlike os
|
# similar to test_fd_leak, but runs on unixlike os
|
||||||
|
@ -587,17 +577,19 @@ class TestFileTiff(PillowTestCase):
|
||||||
im.load()
|
im.load()
|
||||||
self.assertFalse(fp.closed)
|
self.assertFalse(fp.closed)
|
||||||
|
|
||||||
|
# Ignore this UserWarning which triggers for four tags:
|
||||||
|
# "Possibly corrupt EXIF data. Expecting to read 50404352 bytes but..."
|
||||||
|
@pytest.mark.filterwarnings("ignore:Possibly corrupt EXIF data")
|
||||||
def test_string_dimension(self):
|
def test_string_dimension(self):
|
||||||
# Assert that an error is raised if one of the dimensions is a string
|
# Assert that an error is raised if one of the dimensions is a string
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
Image.open("Tests/images/string_dimension.tiff")
|
Image.open("Tests/images/string_dimension.tiff")
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only")
|
@unittest.skipUnless(is_win32(), "Windows only")
|
||||||
class TestFileTiffW32(PillowTestCase):
|
class TestFileTiffW32(PillowTestCase):
|
||||||
def test_fd_leak(self):
|
def test_fd_leak(self):
|
||||||
tmpfile = self.tempfile("temp.tif")
|
tmpfile = self.tempfile("temp.tif")
|
||||||
import os
|
|
||||||
|
|
||||||
# this is an mmaped file.
|
# this is an mmaped file.
|
||||||
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
||||||
|
|
|
@ -52,89 +52,92 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
|
|
||||||
img.save(f, tiffinfo=info)
|
img.save(f, tiffinfo=info)
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
|
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
|
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
|
||||||
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
|
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
|
||||||
|
|
||||||
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
|
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
|
||||||
self.assertAlmostEqual(loaded_float, floatdata, places=5)
|
self.assertAlmostEqual(loaded_float, floatdata, places=5)
|
||||||
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
|
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
|
||||||
self.assertAlmostEqual(loaded_double, doubledata)
|
self.assertAlmostEqual(loaded_double, doubledata)
|
||||||
|
|
||||||
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
||||||
|
|
||||||
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
||||||
img.save(f, tiffinfo=info)
|
img.save(f, tiffinfo=info)
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
self.assertEqual(
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
|
||||||
|
)
|
||||||
|
|
||||||
def test_read_metadata(self):
|
def test_read_metadata(self):
|
||||||
img = Image.open("Tests/images/hopper_g4.tif")
|
with Image.open("Tests/images/hopper_g4.tif") as img:
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"YResolution": IFDRational(4294967295, 113653537),
|
"YResolution": IFDRational(4294967295, 113653537),
|
||||||
"PlanarConfiguration": 1,
|
"PlanarConfiguration": 1,
|
||||||
"BitsPerSample": (1,),
|
"BitsPerSample": (1,),
|
||||||
"ImageLength": 128,
|
"ImageLength": 128,
|
||||||
"Compression": 4,
|
"Compression": 4,
|
||||||
"FillOrder": 1,
|
"FillOrder": 1,
|
||||||
"RowsPerStrip": 128,
|
"RowsPerStrip": 128,
|
||||||
"ResolutionUnit": 3,
|
"ResolutionUnit": 3,
|
||||||
"PhotometricInterpretation": 0,
|
"PhotometricInterpretation": 0,
|
||||||
"PageNumber": (0, 1),
|
"PageNumber": (0, 1),
|
||||||
"XResolution": IFDRational(4294967295, 113653537),
|
"XResolution": IFDRational(4294967295, 113653537),
|
||||||
"ImageWidth": 128,
|
"ImageWidth": 128,
|
||||||
"Orientation": 1,
|
"Orientation": 1,
|
||||||
"StripByteCounts": (1968,),
|
"StripByteCounts": (1968,),
|
||||||
"SamplesPerPixel": 1,
|
"SamplesPerPixel": 1,
|
||||||
"StripOffsets": (8,),
|
"StripOffsets": (8,),
|
||||||
},
|
},
|
||||||
img.tag_v2.named(),
|
img.tag_v2.named(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"YResolution": ((4294967295, 113653537),),
|
"YResolution": ((4294967295, 113653537),),
|
||||||
"PlanarConfiguration": (1,),
|
"PlanarConfiguration": (1,),
|
||||||
"BitsPerSample": (1,),
|
"BitsPerSample": (1,),
|
||||||
"ImageLength": (128,),
|
"ImageLength": (128,),
|
||||||
"Compression": (4,),
|
"Compression": (4,),
|
||||||
"FillOrder": (1,),
|
"FillOrder": (1,),
|
||||||
"RowsPerStrip": (128,),
|
"RowsPerStrip": (128,),
|
||||||
"ResolutionUnit": (3,),
|
"ResolutionUnit": (3,),
|
||||||
"PhotometricInterpretation": (0,),
|
"PhotometricInterpretation": (0,),
|
||||||
"PageNumber": (0, 1),
|
"PageNumber": (0, 1),
|
||||||
"XResolution": ((4294967295, 113653537),),
|
"XResolution": ((4294967295, 113653537),),
|
||||||
"ImageWidth": (128,),
|
"ImageWidth": (128,),
|
||||||
"Orientation": (1,),
|
"Orientation": (1,),
|
||||||
"StripByteCounts": (1968,),
|
"StripByteCounts": (1968,),
|
||||||
"SamplesPerPixel": (1,),
|
"SamplesPerPixel": (1,),
|
||||||
"StripOffsets": (8,),
|
"StripOffsets": (8,),
|
||||||
},
|
},
|
||||||
img.tag.named(),
|
img.tag.named(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_write_metadata(self):
|
def test_write_metadata(self):
|
||||||
""" Test metadata writing through the python code """
|
""" Test metadata writing through the python code """
|
||||||
img = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as img:
|
||||||
|
f = self.tempfile("temp.tiff")
|
||||||
|
img.save(f, tiffinfo=img.tag)
|
||||||
|
|
||||||
f = self.tempfile("temp.tiff")
|
original = img.tag_v2.named()
|
||||||
img.save(f, tiffinfo=img.tag)
|
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
reloaded = loaded.tag_v2.named()
|
||||||
original = img.tag_v2.named()
|
|
||||||
reloaded = loaded.tag_v2.named()
|
|
||||||
|
|
||||||
for k, v in original.items():
|
for k, v in original.items():
|
||||||
if isinstance(v, IFDRational):
|
if isinstance(v, IFDRational):
|
||||||
|
@ -157,13 +160,13 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
self.assert_deep_equal(
|
self.assert_deep_equal(
|
||||||
original[tag],
|
original[tag],
|
||||||
value,
|
value,
|
||||||
"%s didn't roundtrip, %s, %s" % (tag, original[tag], value),
|
"{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
original[tag],
|
original[tag],
|
||||||
value,
|
value,
|
||||||
"%s didn't roundtrip, %s, %s" % (tag, original[tag], value),
|
"{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
|
||||||
)
|
)
|
||||||
|
|
||||||
for tag, value in original.items():
|
for tag, value in original.items():
|
||||||
|
@ -183,32 +186,32 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
|
|
||||||
def test_iccprofile(self):
|
def test_iccprofile(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1462
|
# https://github.com/python-pillow/Pillow/issues/1462
|
||||||
im = Image.open("Tests/images/hopper.iccprofile.tif")
|
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
|
with Image.open("Tests/images/hopper.iccprofile.tif") as im:
|
||||||
|
im.save(out)
|
||||||
|
|
||||||
im.save(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded = Image.open(out)
|
self.assertNotIsInstance(im.info["icc_profile"], tuple)
|
||||||
self.assertNotIsInstance(im.info["icc_profile"], tuple)
|
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
|
||||||
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
|
|
||||||
|
|
||||||
def test_iccprofile_binary(self):
|
def test_iccprofile_binary(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1526
|
# https://github.com/python-pillow/Pillow/issues/1526
|
||||||
# We should be able to load this,
|
# We should be able to load this,
|
||||||
# but probably won't be able to save it.
|
# but probably won't be able to save it.
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.iccprofile_binary.tif")
|
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
|
||||||
self.assertEqual(im.tag_v2.tagtype[34675], 1)
|
self.assertEqual(im.tag_v2.tagtype[34675], 1)
|
||||||
self.assertTrue(im.info["icc_profile"])
|
self.assertTrue(im.info["icc_profile"])
|
||||||
|
|
||||||
def test_iccprofile_save_png(self):
|
def test_iccprofile_save_png(self):
|
||||||
im = Image.open("Tests/images/hopper.iccprofile.tif")
|
with Image.open("Tests/images/hopper.iccprofile.tif") as im:
|
||||||
outfile = self.tempfile("temp.png")
|
outfile = self.tempfile("temp.png")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
def test_iccprofile_binary_save_png(self):
|
def test_iccprofile_binary_save_png(self):
|
||||||
im = Image.open("Tests/images/hopper.iccprofile_binary.tif")
|
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
|
||||||
outfile = self.tempfile("temp.png")
|
outfile = self.tempfile("temp.png")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
def test_exif_div_zero(self):
|
def test_exif_div_zero(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
@ -218,9 +221,9 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
im.save(out, tiffinfo=info, compression="raw")
|
im.save(out, tiffinfo=info, compression="raw")
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
||||||
|
|
||||||
def test_empty_values(self):
|
def test_empty_values(self):
|
||||||
data = io.BytesIO(
|
data = io.BytesIO(
|
||||||
|
@ -237,15 +240,14 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
self.assertIn(33432, info)
|
self.assertIn(33432, info)
|
||||||
|
|
||||||
def test_PhotoshopInfo(self):
|
def test_PhotoshopInfo(self):
|
||||||
im = Image.open("Tests/images/issue_2278.tif")
|
with Image.open("Tests/images/issue_2278.tif") as im:
|
||||||
|
self.assertEqual(len(im.tag_v2[34377]), 1)
|
||||||
self.assertEqual(len(im.tag_v2[34377]), 1)
|
self.assertIsInstance(im.tag_v2[34377][0], bytes)
|
||||||
self.assertIsInstance(im.tag_v2[34377][0], bytes)
|
out = self.tempfile("temp.tiff")
|
||||||
out = self.tempfile("temp.tiff")
|
im.save(out)
|
||||||
im.save(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded = Image.open(out)
|
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
|
||||||
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
|
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
|
||||||
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
|
|
||||||
|
|
||||||
def test_too_many_entries(self):
|
def test_too_many_entries(self):
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, WebPImagePlugin
|
from PIL import Image, WebPImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -39,19 +41,18 @@ class TestFileWebp(PillowTestCase):
|
||||||
Does it have the bits we expect?
|
Does it have the bits we expect?
|
||||||
"""
|
"""
|
||||||
|
|
||||||
image = Image.open("Tests/images/hopper.webp")
|
with Image.open("Tests/images/hopper.webp") as image:
|
||||||
|
self.assertEqual(image.mode, self.rgb_mode)
|
||||||
|
self.assertEqual(image.size, (128, 128))
|
||||||
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
|
|
||||||
self.assertEqual(image.mode, self.rgb_mode)
|
# generated with:
|
||||||
self.assertEqual(image.size, (128, 128))
|
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assert_image_similar_tofile(
|
||||||
image.load()
|
image, "Tests/images/hopper_webp_bits.ppm", 1.0
|
||||||
image.getdata()
|
)
|
||||||
|
|
||||||
# generated with:
|
|
||||||
# dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm
|
|
||||||
self.assert_image_similar_tofile(
|
|
||||||
image, "Tests/images/hopper_webp_bits.ppm", 1.0
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_write_rgb(self):
|
def test_write_rgb(self):
|
||||||
"""
|
"""
|
||||||
|
@ -62,26 +63,25 @@ class TestFileWebp(PillowTestCase):
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
|
|
||||||
hopper(self.rgb_mode).save(temp_file)
|
hopper(self.rgb_mode).save(temp_file)
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
|
self.assertEqual(image.mode, self.rgb_mode)
|
||||||
|
self.assertEqual(image.size, (128, 128))
|
||||||
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
|
|
||||||
self.assertEqual(image.mode, self.rgb_mode)
|
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
|
||||||
self.assertEqual(image.size, (128, 128))
|
self.assert_image_similar_tofile(
|
||||||
self.assertEqual(image.format, "WEBP")
|
image, "Tests/images/hopper_webp_write.ppm", 12.0
|
||||||
image.load()
|
)
|
||||||
image.getdata()
|
|
||||||
|
|
||||||
# generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm
|
# This test asserts that the images are similar. If the average pixel
|
||||||
self.assert_image_similar_tofile(
|
# difference between the two images is less than the epsilon value,
|
||||||
image, "Tests/images/hopper_webp_write.ppm", 12.0
|
# then we're going to accept that it's a reasonable lossy version of
|
||||||
)
|
# the image. The old lena images for WebP are showing ~16 on
|
||||||
|
# Ubuntu, the jpegs are showing ~18.
|
||||||
# This test asserts that the images are similar. If the average pixel
|
target = hopper(self.rgb_mode)
|
||||||
# difference between the two images is less than the epsilon value,
|
self.assert_image_similar(image, target, 12.0)
|
||||||
# then we're going to accept that it's a reasonable lossy version of
|
|
||||||
# the image. The old lena images for WebP are showing ~16 on
|
|
||||||
# Ubuntu, the jpegs are showing ~18.
|
|
||||||
target = hopper(self.rgb_mode)
|
|
||||||
self.assert_image_similar(image, target, 12.0)
|
|
||||||
|
|
||||||
def test_write_unsupported_mode_L(self):
|
def test_write_unsupported_mode_L(self):
|
||||||
"""
|
"""
|
||||||
|
@ -91,17 +91,16 @@ class TestFileWebp(PillowTestCase):
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
hopper("L").save(temp_file)
|
hopper("L").save(temp_file)
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
|
self.assertEqual(image.mode, self.rgb_mode)
|
||||||
|
self.assertEqual(image.size, (128, 128))
|
||||||
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
|
||||||
self.assertEqual(image.mode, self.rgb_mode)
|
image.load()
|
||||||
self.assertEqual(image.size, (128, 128))
|
image.getdata()
|
||||||
self.assertEqual(image.format, "WEBP")
|
target = hopper("L").convert(self.rgb_mode)
|
||||||
|
|
||||||
image.load()
|
self.assert_image_similar(image, target, 10.0)
|
||||||
image.getdata()
|
|
||||||
target = hopper("L").convert(self.rgb_mode)
|
|
||||||
|
|
||||||
self.assert_image_similar(image, target, 10.0)
|
|
||||||
|
|
||||||
def test_write_unsupported_mode_P(self):
|
def test_write_unsupported_mode_P(self):
|
||||||
"""
|
"""
|
||||||
|
@ -111,17 +110,16 @@ class TestFileWebp(PillowTestCase):
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
hopper("P").save(temp_file)
|
hopper("P").save(temp_file)
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
|
self.assertEqual(image.mode, self.rgb_mode)
|
||||||
|
self.assertEqual(image.size, (128, 128))
|
||||||
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
|
||||||
self.assertEqual(image.mode, self.rgb_mode)
|
image.load()
|
||||||
self.assertEqual(image.size, (128, 128))
|
image.getdata()
|
||||||
self.assertEqual(image.format, "WEBP")
|
target = hopper("P").convert(self.rgb_mode)
|
||||||
|
|
||||||
image.load()
|
self.assert_image_similar(image, target, 50.0)
|
||||||
image.getdata()
|
|
||||||
target = hopper("P").convert(self.rgb_mode)
|
|
||||||
|
|
||||||
self.assert_image_similar(image, target, 50.0)
|
|
||||||
|
|
||||||
def test_WebPEncode_with_invalid_args(self):
|
def test_WebPEncode_with_invalid_args(self):
|
||||||
"""
|
"""
|
||||||
|
@ -143,10 +141,9 @@ class TestFileWebp(PillowTestCase):
|
||||||
|
|
||||||
def test_no_resource_warning(self):
|
def test_no_resource_warning(self):
|
||||||
file_path = "Tests/images/hopper.webp"
|
file_path = "Tests/images/hopper.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
temp_file = self.tempfile("temp.webp")
|
||||||
temp_file = self.tempfile("temp.webp")
|
self.assert_warning(None, image.save, temp_file)
|
||||||
self.assert_warning(None, image.save, temp_file)
|
|
||||||
|
|
||||||
def test_file_pointer_could_be_reused(self):
|
def test_file_pointer_could_be_reused(self):
|
||||||
file_path = "Tests/images/hopper.webp"
|
file_path = "Tests/images/hopper.webp"
|
||||||
|
@ -158,19 +155,19 @@ class TestFileWebp(PillowTestCase):
|
||||||
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
|
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
|
||||||
)
|
)
|
||||||
def test_background_from_gif(self):
|
def test_background_from_gif(self):
|
||||||
im = Image.open("Tests/images/chi.gif")
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
original_value = im.convert("RGB").getpixel((1, 1))
|
original_value = im.convert("RGB").getpixel((1, 1))
|
||||||
|
|
||||||
# Save as WEBP
|
# Save as WEBP
|
||||||
out_webp = self.tempfile("temp.webp")
|
out_webp = self.tempfile("temp.webp")
|
||||||
im.save(out_webp, save_all=True)
|
im.save(out_webp, save_all=True)
|
||||||
|
|
||||||
# Save as GIF
|
# Save as GIF
|
||||||
out_gif = self.tempfile("temp.gif")
|
out_gif = self.tempfile("temp.gif")
|
||||||
Image.open(out_webp).save(out_gif)
|
Image.open(out_webp).save(out_gif)
|
||||||
|
|
||||||
reread = Image.open(out_gif)
|
with Image.open(out_gif) as reread:
|
||||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||||
difference = sum(
|
difference = sum(
|
||||||
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper, unittest
|
from .helper import PillowTestCase, hopper
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -24,18 +26,17 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
# Generated with `cwebp transparent.png -o transparent.webp`
|
# Generated with `cwebp transparent.png -o transparent.webp`
|
||||||
file_path = "Tests/images/transparent.webp"
|
file_path = "Tests/images/transparent.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
self.assertEqual(image.mode, "RGBA")
|
||||||
|
self.assertEqual(image.size, (200, 150))
|
||||||
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
image.load()
|
||||||
|
image.getdata()
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
image.tobytes()
|
||||||
self.assertEqual(image.size, (200, 150))
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
|
||||||
image.load()
|
|
||||||
image.getdata()
|
|
||||||
|
|
||||||
image.tobytes()
|
with Image.open("Tests/images/transparent.png") as target:
|
||||||
|
self.assert_image_similar(image, target, 20.0)
|
||||||
target = Image.open("Tests/images/transparent.png")
|
|
||||||
self.assert_image_similar(image, target, 20.0)
|
|
||||||
|
|
||||||
def test_write_lossless_rgb(self):
|
def test_write_lossless_rgb(self):
|
||||||
"""
|
"""
|
||||||
|
@ -54,16 +55,16 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
pil_image.save(temp_file, lossless=True)
|
pil_image.save(temp_file, lossless=True)
|
||||||
|
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
self.assertEqual(image.mode, "RGBA")
|
||||||
self.assertEqual(image.size, pil_image.size)
|
self.assertEqual(image.size, pil_image.size)
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
image.getdata()
|
||||||
|
|
||||||
self.assert_image_equal(image, pil_image)
|
self.assert_image_equal(image, pil_image)
|
||||||
|
|
||||||
def test_write_rgba(self):
|
def test_write_rgba(self):
|
||||||
"""
|
"""
|
||||||
|
@ -79,21 +80,21 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
if _webp.WebPDecoderBuggyAlpha(self):
|
if _webp.WebPDecoderBuggyAlpha(self):
|
||||||
return
|
return
|
||||||
|
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
self.assertEqual(image.mode, "RGBA")
|
||||||
self.assertEqual(image.size, (10, 10))
|
self.assertEqual(image.size, (10, 10))
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
image.getdata()
|
||||||
|
|
||||||
# early versions of webp are known to produce higher deviations:
|
# early versions of webp are known to produce higher deviations:
|
||||||
# deal with it
|
# deal with it
|
||||||
if _webp.WebPDecoderVersion(self) <= 0x201:
|
if _webp.WebPDecoderVersion(self) <= 0x201:
|
||||||
self.assert_image_similar(image, pil_image, 3.0)
|
self.assert_image_similar(image, pil_image, 3.0)
|
||||||
else:
|
else:
|
||||||
self.assert_image_similar(image, pil_image, 1.0)
|
self.assert_image_similar(image, pil_image, 1.0)
|
||||||
|
|
||||||
def test_write_unsupported_mode_PA(self):
|
def test_write_unsupported_mode_PA(self):
|
||||||
"""
|
"""
|
||||||
|
@ -103,15 +104,16 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
file_path = "Tests/images/transparent.gif"
|
file_path = "Tests/images/transparent.gif"
|
||||||
Image.open(file_path).save(temp_file)
|
with Image.open(file_path) as im:
|
||||||
image = Image.open(temp_file)
|
im.save(temp_file)
|
||||||
|
with Image.open(temp_file) as image:
|
||||||
|
self.assertEqual(image.mode, "RGBA")
|
||||||
|
self.assertEqual(image.size, (200, 150))
|
||||||
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
image.load()
|
||||||
self.assertEqual(image.size, (200, 150))
|
image.getdata()
|
||||||
self.assertEqual(image.format, "WEBP")
|
with Image.open(file_path) as im:
|
||||||
|
target = im.convert("RGBA")
|
||||||
|
|
||||||
image.load()
|
self.assert_image_similar(image, target, 25.0)
|
||||||
image.getdata()
|
|
||||||
target = Image.open(file_path).convert("RGBA")
|
|
||||||
|
|
||||||
self.assert_image_similar(image, target, 25.0)
|
|
||||||
|
|
|
@ -28,13 +28,13 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
attributes correctly.
|
attributes correctly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.webp")
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open("Tests/images/iss634.webp")
|
with Image.open("Tests/images/iss634.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 42)
|
self.assertEqual(im.n_frames, 42)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_write_animation_L(self):
|
def test_write_animation_L(self):
|
||||||
"""
|
"""
|
||||||
|
@ -43,23 +43,23 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
visually similar.
|
visually similar.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
orig = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as orig:
|
||||||
self.assertGreater(orig.n_frames, 1)
|
self.assertGreater(orig.n_frames, 1)
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
orig.save(temp_file, save_all=True)
|
orig.save(temp_file, save_all=True)
|
||||||
im = Image.open(temp_file)
|
with Image.open(temp_file) as im:
|
||||||
self.assertEqual(im.n_frames, orig.n_frames)
|
self.assertEqual(im.n_frames, orig.n_frames)
|
||||||
|
|
||||||
# Compare first and last frames to the original animated GIF
|
# Compare first and last frames to the original animated GIF
|
||||||
orig.load()
|
orig.load()
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
orig.seek(orig.n_frames - 1)
|
orig.seek(orig.n_frames - 1)
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
orig.load()
|
orig.load()
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
|
|
||||||
def test_write_animation_RGB(self):
|
def test_write_animation_RGB(self):
|
||||||
"""
|
"""
|
||||||
|
@ -68,40 +68,38 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def check(temp_file):
|
def check(temp_file):
|
||||||
im = Image.open(temp_file)
|
with Image.open(temp_file) as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
|
|
||||||
# Compare first frame to original
|
# Compare first frame to original
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_equal(im, frame1.convert("RGBA"))
|
self.assert_image_equal(im, frame1.convert("RGBA"))
|
||||||
|
|
||||||
# Compare second frame to original
|
# Compare second frame to original
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_equal(im, frame2.convert("RGBA"))
|
self.assert_image_equal(im, frame2.convert("RGBA"))
|
||||||
|
|
||||||
frame1 = Image.open("Tests/images/anim_frame1.webp")
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
frame2 = Image.open("Tests/images/anim_frame2.webp")
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
|
temp_file1 = self.tempfile("temp.webp")
|
||||||
|
frame1.copy().save(
|
||||||
|
temp_file1, save_all=True, append_images=[frame2], lossless=True
|
||||||
|
)
|
||||||
|
check(temp_file1)
|
||||||
|
|
||||||
temp_file1 = self.tempfile("temp.webp")
|
# Tests appending using a generator
|
||||||
frame1.copy().save(
|
def imGenerator(ims):
|
||||||
temp_file1, save_all=True, append_images=[frame2], lossless=True
|
yield from ims
|
||||||
)
|
|
||||||
check(temp_file1)
|
|
||||||
|
|
||||||
# Tests appending using a generator
|
temp_file2 = self.tempfile("temp_generator.webp")
|
||||||
def imGenerator(ims):
|
frame1.copy().save(
|
||||||
for im in ims:
|
temp_file2,
|
||||||
yield im
|
save_all=True,
|
||||||
|
append_images=imGenerator([frame2]),
|
||||||
temp_file2 = self.tempfile("temp_generator.webp")
|
lossless=True,
|
||||||
frame1.copy().save(
|
)
|
||||||
temp_file2,
|
check(temp_file2)
|
||||||
save_all=True,
|
|
||||||
append_images=imGenerator([frame2]),
|
|
||||||
lossless=True,
|
|
||||||
)
|
|
||||||
check(temp_file2)
|
|
||||||
|
|
||||||
def test_timestamp_and_duration(self):
|
def test_timestamp_and_duration(self):
|
||||||
"""
|
"""
|
||||||
|
@ -111,27 +109,27 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
|
|
||||||
durations = [0, 10, 20, 30, 40]
|
durations = [0, 10, 20, 30, 40]
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
frame1 = Image.open("Tests/images/anim_frame1.webp")
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
frame2 = Image.open("Tests/images/anim_frame2.webp")
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
frame1.save(
|
frame1.save(
|
||||||
temp_file,
|
temp_file,
|
||||||
save_all=True,
|
save_all=True,
|
||||||
append_images=[frame2, frame1, frame2, frame1],
|
append_images=[frame2, frame1, frame2, frame1],
|
||||||
duration=durations,
|
duration=durations,
|
||||||
)
|
)
|
||||||
|
|
||||||
im = Image.open(temp_file)
|
with Image.open(temp_file) as im:
|
||||||
self.assertEqual(im.n_frames, 5)
|
self.assertEqual(im.n_frames, 5)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
# Check that timestamps and durations match original values specified
|
# Check that timestamps and durations match original values specified
|
||||||
ts = 0
|
ts = 0
|
||||||
for frame in range(im.n_frames):
|
for frame in range(im.n_frames):
|
||||||
im.seek(frame)
|
im.seek(frame)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.info["duration"], durations[frame])
|
self.assertEqual(im.info["duration"], durations[frame])
|
||||||
self.assertEqual(im.info["timestamp"], ts)
|
self.assertEqual(im.info["timestamp"], ts)
|
||||||
ts += durations[frame]
|
ts += durations[frame]
|
||||||
|
|
||||||
def test_seeking(self):
|
def test_seeking(self):
|
||||||
"""
|
"""
|
||||||
|
@ -142,24 +140,24 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
|
|
||||||
dur = 33
|
dur = 33
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
frame1 = Image.open("Tests/images/anim_frame1.webp")
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
frame2 = Image.open("Tests/images/anim_frame2.webp")
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
frame1.save(
|
frame1.save(
|
||||||
temp_file,
|
temp_file,
|
||||||
save_all=True,
|
save_all=True,
|
||||||
append_images=[frame2, frame1, frame2, frame1],
|
append_images=[frame2, frame1, frame2, frame1],
|
||||||
duration=dur,
|
duration=dur,
|
||||||
)
|
)
|
||||||
|
|
||||||
im = Image.open(temp_file)
|
with Image.open(temp_file) as im:
|
||||||
self.assertEqual(im.n_frames, 5)
|
self.assertEqual(im.n_frames, 5)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
# Traverse frames in reverse, checking timestamps and durations
|
# Traverse frames in reverse, checking timestamps and durations
|
||||||
ts = dur * (im.n_frames - 1)
|
ts = dur * (im.n_frames - 1)
|
||||||
for frame in reversed(range(im.n_frames)):
|
for frame in reversed(range(im.n_frames)):
|
||||||
im.seek(frame)
|
im.seek(frame)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.info["duration"], dur)
|
self.assertEqual(im.info["duration"], dur)
|
||||||
self.assertEqual(im.info["timestamp"], ts)
|
self.assertEqual(im.info["timestamp"], ts)
|
||||||
ts -= dur
|
ts -= dur
|
||||||
|
|
|
@ -26,13 +26,13 @@ class TestFileWebpLossless(PillowTestCase):
|
||||||
|
|
||||||
hopper(self.rgb_mode).save(temp_file, lossless=True)
|
hopper(self.rgb_mode).save(temp_file, lossless=True)
|
||||||
|
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
|
|
||||||
self.assertEqual(image.mode, self.rgb_mode)
|
self.assertEqual(image.mode, self.rgb_mode)
|
||||||
self.assertEqual(image.size, (128, 128))
|
self.assertEqual(image.size, (128, 128))
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
image.getdata()
|
||||||
|
|
||||||
self.assert_image_equal(image, hopper(self.rgb_mode))
|
self.assert_image_equal(image, hopper(self.rgb_mode))
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase
|
||||||
|
@ -22,37 +24,33 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
def test_read_exif_metadata(self):
|
def test_read_exif_metadata(self):
|
||||||
|
|
||||||
file_path = "Tests/images/flower.webp"
|
file_path = "Tests/images/flower.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
exif_data = image.info.get("exif", None)
|
exif_data = image.info.get("exif", None)
|
||||||
self.assertTrue(exif_data)
|
self.assertTrue(exif_data)
|
||||||
|
|
||||||
exif = image._getexif()
|
exif = image._getexif()
|
||||||
|
|
||||||
# camera make
|
# camera make
|
||||||
self.assertEqual(exif[271], "Canon")
|
self.assertEqual(exif[271], "Canon")
|
||||||
|
|
||||||
jpeg_image = Image.open("Tests/images/flower.jpg")
|
with Image.open("Tests/images/flower.jpg") as jpeg_image:
|
||||||
expected_exif = jpeg_image.info["exif"]
|
expected_exif = jpeg_image.info["exif"]
|
||||||
|
|
||||||
self.assertEqual(exif_data, expected_exif)
|
self.assertEqual(exif_data, expected_exif)
|
||||||
|
|
||||||
def test_write_exif_metadata(self):
|
def test_write_exif_metadata(self):
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
file_path = "Tests/images/flower.jpg"
|
file_path = "Tests/images/flower.jpg"
|
||||||
image = Image.open(file_path)
|
|
||||||
expected_exif = image.info["exif"]
|
|
||||||
|
|
||||||
test_buffer = BytesIO()
|
test_buffer = BytesIO()
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
expected_exif = image.info["exif"]
|
||||||
|
|
||||||
image.save(test_buffer, "webp", exif=expected_exif)
|
image.save(test_buffer, "webp", exif=expected_exif)
|
||||||
|
|
||||||
test_buffer.seek(0)
|
test_buffer.seek(0)
|
||||||
webp_image = Image.open(test_buffer)
|
with Image.open(test_buffer) as webp_image:
|
||||||
|
webp_exif = webp_image.info.get("exif", None)
|
||||||
webp_exif = webp_image.info.get("exif", None)
|
|
||||||
self.assertTrue(webp_exif)
|
self.assertTrue(webp_exif)
|
||||||
if webp_exif:
|
if webp_exif:
|
||||||
self.assertEqual(webp_exif, expected_exif, "WebP EXIF didn't match")
|
self.assertEqual(webp_exif, expected_exif, "WebP EXIF didn't match")
|
||||||
|
@ -60,33 +58,29 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
def test_read_icc_profile(self):
|
def test_read_icc_profile(self):
|
||||||
|
|
||||||
file_path = "Tests/images/flower2.webp"
|
file_path = "Tests/images/flower2.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
self.assertTrue(image.info.get("icc_profile", None))
|
self.assertTrue(image.info.get("icc_profile", None))
|
||||||
|
|
||||||
icc = image.info["icc_profile"]
|
icc = image.info["icc_profile"]
|
||||||
|
|
||||||
jpeg_image = Image.open("Tests/images/flower2.jpg")
|
with Image.open("Tests/images/flower2.jpg") as jpeg_image:
|
||||||
expected_icc = jpeg_image.info["icc_profile"]
|
expected_icc = jpeg_image.info["icc_profile"]
|
||||||
|
|
||||||
self.assertEqual(icc, expected_icc)
|
self.assertEqual(icc, expected_icc)
|
||||||
|
|
||||||
def test_write_icc_metadata(self):
|
def test_write_icc_metadata(self):
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
file_path = "Tests/images/flower2.jpg"
|
file_path = "Tests/images/flower2.jpg"
|
||||||
image = Image.open(file_path)
|
|
||||||
expected_icc_profile = image.info["icc_profile"]
|
|
||||||
|
|
||||||
test_buffer = BytesIO()
|
test_buffer = BytesIO()
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
expected_icc_profile = image.info["icc_profile"]
|
||||||
|
|
||||||
image.save(test_buffer, "webp", icc_profile=expected_icc_profile)
|
image.save(test_buffer, "webp", icc_profile=expected_icc_profile)
|
||||||
|
|
||||||
test_buffer.seek(0)
|
test_buffer.seek(0)
|
||||||
webp_image = Image.open(test_buffer)
|
with Image.open(test_buffer) as webp_image:
|
||||||
|
webp_icc_profile = webp_image.info.get("icc_profile", None)
|
||||||
webp_icc_profile = webp_image.info.get("icc_profile", None)
|
|
||||||
|
|
||||||
self.assertTrue(webp_icc_profile)
|
self.assertTrue(webp_icc_profile)
|
||||||
if webp_icc_profile:
|
if webp_icc_profile:
|
||||||
|
@ -95,45 +89,41 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_read_no_exif(self):
|
def test_read_no_exif(self):
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
file_path = "Tests/images/flower.jpg"
|
file_path = "Tests/images/flower.jpg"
|
||||||
image = Image.open(file_path)
|
|
||||||
self.assertIn("exif", image.info)
|
|
||||||
|
|
||||||
test_buffer = BytesIO()
|
test_buffer = BytesIO()
|
||||||
|
with Image.open(file_path) as image:
|
||||||
|
self.assertIn("exif", image.info)
|
||||||
|
|
||||||
image.save(test_buffer, "webp")
|
image.save(test_buffer, "webp")
|
||||||
|
|
||||||
test_buffer.seek(0)
|
test_buffer.seek(0)
|
||||||
webp_image = Image.open(test_buffer)
|
with Image.open(test_buffer) as webp_image:
|
||||||
|
self.assertFalse(webp_image._getexif())
|
||||||
self.assertFalse(webp_image._getexif())
|
|
||||||
|
|
||||||
def test_write_animated_metadata(self):
|
def test_write_animated_metadata(self):
|
||||||
if not _webp.HAVE_WEBPANIM:
|
if not _webp.HAVE_WEBPANIM:
|
||||||
self.skipTest("WebP animation support not available")
|
self.skipTest("WebP animation support not available")
|
||||||
|
|
||||||
iccp_data = "<iccp_data>".encode("utf-8")
|
iccp_data = b"<iccp_data>"
|
||||||
exif_data = "<exif_data>".encode("utf-8")
|
exif_data = b"<exif_data>"
|
||||||
xmp_data = "<xmp_data>".encode("utf-8")
|
xmp_data = b"<xmp_data>"
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
frame1 = Image.open("Tests/images/anim_frame1.webp")
|
with Image.open("Tests/images/anim_frame1.webp") as frame1:
|
||||||
frame2 = Image.open("Tests/images/anim_frame2.webp")
|
with Image.open("Tests/images/anim_frame2.webp") as frame2:
|
||||||
frame1.save(
|
frame1.save(
|
||||||
temp_file,
|
temp_file,
|
||||||
save_all=True,
|
save_all=True,
|
||||||
append_images=[frame2, frame1, frame2],
|
append_images=[frame2, frame1, frame2],
|
||||||
icc_profile=iccp_data,
|
icc_profile=iccp_data,
|
||||||
exif=exif_data,
|
exif=exif_data,
|
||||||
xmp=xmp_data,
|
xmp=xmp_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
self.assertIn("icc_profile", image.info)
|
self.assertIn("icc_profile", image.info)
|
||||||
self.assertIn("exif", image.info)
|
self.assertIn("exif", image.info)
|
||||||
self.assertIn("xmp", image.info)
|
self.assertIn("xmp", image.info)
|
||||||
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
||||||
self.assertEqual(exif_data, image.info.get("exif", None))
|
self.assertEqual(exif_data, image.info.get("exif", None))
|
||||||
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
||||||
|
|
|
@ -7,24 +7,24 @@ class TestFileWmf(PillowTestCase):
|
||||||
def test_load_raw(self):
|
def test_load_raw(self):
|
||||||
|
|
||||||
# Test basic EMF open and rendering
|
# Test basic EMF open and rendering
|
||||||
im = Image.open("Tests/images/drawing.emf")
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
# Currently, support for WMF/EMF is Windows-only
|
# Currently, support for WMF/EMF is Windows-only
|
||||||
im.load()
|
im.load()
|
||||||
# Compare to reference rendering
|
# Compare to reference rendering
|
||||||
imref = Image.open("Tests/images/drawing_emf_ref.png")
|
with Image.open("Tests/images/drawing_emf_ref.png") as imref:
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 0)
|
self.assert_image_similar(im, imref, 0)
|
||||||
|
|
||||||
# Test basic WMF open and rendering
|
# Test basic WMF open and rendering
|
||||||
im = Image.open("Tests/images/drawing.wmf")
|
with Image.open("Tests/images/drawing.wmf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
# Currently, support for WMF/EMF is Windows-only
|
# Currently, support for WMF/EMF is Windows-only
|
||||||
im.load()
|
im.load()
|
||||||
# Compare to reference rendering
|
# Compare to reference rendering
|
||||||
imref = Image.open("Tests/images/drawing_wmf_ref.png")
|
with Image.open("Tests/images/drawing_wmf_ref.png") as imref:
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 2.0)
|
self.assert_image_similar(im, imref, 2.0)
|
||||||
|
|
||||||
def test_register_handler(self):
|
def test_register_handler(self):
|
||||||
class TestHandler:
|
class TestHandler:
|
||||||
|
@ -46,12 +46,12 @@ class TestFileWmf(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/drawing.emf")
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
self.assertEqual(im.info["dpi"], 1424)
|
self.assertEqual(im.info["dpi"], 1424)
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/drawing_roundDown.emf")
|
with Image.open("Tests/images/drawing_roundDown.emf") as im:
|
||||||
self.assertEqual(im.info["dpi"], 1426)
|
self.assertEqual(im.info["dpi"], 1426)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase
|
||||||
|
@ -28,13 +30,10 @@ static char basic_bits[] = {
|
||||||
|
|
||||||
class TestFileXbm(PillowTestCase):
|
class TestFileXbm(PillowTestCase):
|
||||||
def test_pil151(self):
|
def test_pil151(self):
|
||||||
from io import BytesIO
|
with Image.open(BytesIO(PIL151)) as im:
|
||||||
|
im.load()
|
||||||
im = Image.open(BytesIO(PIL151))
|
self.assertEqual(im.mode, "1")
|
||||||
|
self.assertEqual(im.size, (32, 32))
|
||||||
im.load()
|
|
||||||
self.assertEqual(im.mode, "1")
|
|
||||||
self.assertEqual(im.size, (32, 32))
|
|
||||||
|
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -42,11 +41,11 @@ class TestFileXbm(PillowTestCase):
|
||||||
filename = "Tests/images/hopper.xbm"
|
filename = "Tests/images/hopper.xbm"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
||||||
def test_open_filename_with_underscore(self):
|
def test_open_filename_with_underscore(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -54,8 +53,8 @@ class TestFileXbm(PillowTestCase):
|
||||||
filename = "Tests/images/hopper_underscore.xbm"
|
filename = "Tests/images/hopper_underscore.xbm"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
|
@ -7,14 +7,14 @@ TEST_FILE = "Tests/images/hopper.xpm"
|
||||||
|
|
||||||
class TestFileXpm(PillowTestCase):
|
class TestFileXpm(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "XPM")
|
self.assertEqual(im.format, "XPM")
|
||||||
|
|
||||||
# large error due to quantization->44 colors.
|
# large error due to quantization->44 colors.
|
||||||
self.assert_image_similar(im.convert("RGB"), hopper("RGB"), 60)
|
self.assert_image_similar(im.convert("RGB"), hopper("RGB"), 60)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -23,11 +23,11 @@ class TestFileXpm(PillowTestCase):
|
||||||
|
|
||||||
def test_load_read(self):
|
def test_load_read(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_bytes = 1
|
dummy_bytes = 1
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
data = im.load_read(dummy_bytes)
|
data = im.load_read(dummy_bytes)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(len(data), 16384)
|
self.assertEqual(len(data), 16384)
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hopper.p7"
|
||||||
class TestFileXVThumb(PillowTestCase):
|
class TestFileXVThumb(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "XVThumb")
|
self.assertEqual(im.format, "XVThumb")
|
||||||
|
|
||||||
# Create a Hopper image with a similar XV palette
|
# Create a Hopper image with a similar XV palette
|
||||||
im_hopper = hopper().quantize(palette=im)
|
im_hopper = hopper().quantize(palette=im)
|
||||||
self.assert_image_similar(im, im_hopper, 9)
|
self.assert_image_similar(im, im_hopper, 9)
|
||||||
|
|
||||||
def test_unexpected_eof(self):
|
def test_unexpected_eof(self):
|
||||||
# Test unexpected EOF reading XV thumbnail file
|
# Test unexpected EOF reading XV thumbnail file
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user