Merge branch 'master' into gitignore-review
6
.github/CONTRIBUTING.md
vendored
|
@ -4,7 +4,7 @@ Bug fixes, feature additions, tests, documentation and more can be contributed v
|
|||
|
||||
## Bug fixes, feature additions, etc.
|
||||
|
||||
Please send a pull request to the master branch. Please include [documentation](https://pillow.readthedocs.io) and [tests](../Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new) or irc://irc.freenode.net#pil
|
||||
Please send a pull request to the master branch. Please include [documentation](https://pillow.readthedocs.io) and [tests](../Tests/README.rst) for new features. Tests or documentation without bug fixes or feature additions are welcome too. Feel free to ask questions [via issues](https://github.com/python-pillow/Pillow/issues/new), [Gitter](https://gitter.im/python-pillow/Pillow) or irc://irc.freenode.net#pil
|
||||
|
||||
- Fork the Pillow repository.
|
||||
- Create a branch from master.
|
||||
|
@ -21,7 +21,9 @@ Please send a pull request to the master branch. Please include [documentation](
|
|||
|
||||
## Reporting Issues
|
||||
|
||||
When reporting issues, please include code that reproduces the issue and whenever possible, an image that demonstrates the issue. The best reproductions are self-contained scripts with minimal dependencies.
|
||||
When reporting issues, please include code that reproduces the issue and whenever possible, an image that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.
|
||||
|
||||
The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as plone, Django, or buildout, try to replicate the issue just using Pillow.
|
||||
|
||||
### Provide details
|
||||
|
||||
|
|
49
.travis.yml
|
@ -1,3 +1,5 @@
|
|||
dist: xenial
|
||||
sudo: required
|
||||
language: python
|
||||
cache: pip
|
||||
|
||||
|
@ -12,13 +14,24 @@ matrix:
|
|||
fast_finish: true
|
||||
include:
|
||||
- python: "pypy"
|
||||
dist: trusty
|
||||
- python: "pypy3"
|
||||
- python: '3.6'
|
||||
dist: trusty
|
||||
- python: '3.7'
|
||||
- python: '2.7'
|
||||
- python: '2.7'
|
||||
dist: trusty
|
||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||
dist: trusty
|
||||
- python: '3.6'
|
||||
- python: '3.6'
|
||||
dist: trusty
|
||||
- python: '3.5'
|
||||
- python: '3.5'
|
||||
dist: trusty
|
||||
- python: '3.4'
|
||||
- python: '3.7-dev'
|
||||
dist: trusty
|
||||
- env: DOCKER="alpine" DOCKER_TAG="pytest"
|
||||
- env: DOCKER="arch" DOCKER_TAG="pytest" # contains PyQt5
|
||||
- env: DOCKER="ubuntu-trusty-x86" DOCKER_TAG="pytest"
|
||||
|
@ -31,10 +44,6 @@ matrix:
|
|||
- env: DOCKER="fedora-26-amd64" DOCKER_TAG="pytest"
|
||||
- env: DOCKER="fedora-27-amd64" DOCKER_TAG="pytest"
|
||||
|
||||
dist: trusty
|
||||
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
|
@ -42,7 +51,7 @@ install:
|
|||
- if [ "$DOCKER" == "" ]; then .travis/install.sh; fi
|
||||
|
||||
before_install:
|
||||
- if [ "$DOCKER" ]; then docker pull pythonpillow/$DOCKER:$DOCKER_TAG; fi
|
||||
- if [ "$DOCKER" ]; then travis_retry docker pull pythonpillow/$DOCKER:$DOCKER_TAG; fi
|
||||
|
||||
before_script:
|
||||
# Qt needs a display for some of the tests, and it's only run on the system site packages install
|
||||
|
@ -61,29 +70,3 @@ script:
|
|||
|
||||
after_success:
|
||||
- .travis/after_success.sh
|
||||
|
||||
after_failure:
|
||||
- |
|
||||
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||
curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py
|
||||
python travis_after_all.py
|
||||
export $(cat .to_export_back)
|
||||
if [ "$BUILD_LEADER" = "YES" ]; then
|
||||
if [ "$BUILD_AGGREGATE_STATUS" = "others_failed" ]; then
|
||||
echo "All jobs failed"
|
||||
else
|
||||
echo "Some jobs failed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
after_script:
|
||||
- |
|
||||
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||
echo leader=$BUILD_LEADER status=$BUILD_AGGREGATE_STATUS
|
||||
fi
|
||||
|
||||
env:
|
||||
global:
|
||||
# travis encrypt AUTH_TOKEN=
|
||||
secure: "Vzm7aG1Qv0SDQcqiPzZMedNLn5ZmpL7IzF0DYnqcD+/l+zmKU22SnJBcX0uVXumo+r7eZfpsShpqfcdsZvMlvmQnwz+Y6AGKQru9tCKZbTMnuRjWKKXekC+tr8Xt9CKvRVtte5PyXW31paxUI3/e+fQGBwoFjEEC+6EpEOjeRfE="
|
||||
|
|
|
@ -30,20 +30,3 @@ if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then
|
|||
depends/diffcover-install.sh
|
||||
depends/diffcover-run.sh
|
||||
fi
|
||||
|
||||
# after_all
|
||||
|
||||
if [ "$TRAVIS_REPO_SLUG" = "python-pillow/Pillow" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
|
||||
curl -Lo travis_after_all.py https://raw.github.com/dmakhno/travis_after_all/master/travis_after_all.py
|
||||
python travis_after_all.py
|
||||
export $(cat .to_export_back)
|
||||
if [ "$BUILD_LEADER" = "YES" ]; then
|
||||
if [ "$BUILD_AGGREGATE_STATUS" = "others_succeeded" ]; then
|
||||
echo "All jobs succeeded! Triggering macOS build..."
|
||||
# Trigger a macOS build at the pillow-wheels repo
|
||||
./build_children.sh
|
||||
else
|
||||
echo "Some jobs failed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -15,6 +15,7 @@ pip install -U pytest
|
|||
pip install -U pytest-cov
|
||||
pip install pyroma
|
||||
pip install test-image-results
|
||||
pip install numpy
|
||||
|
||||
# docs only on Python 2.7
|
||||
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi
|
||||
|
|
188
CHANGES.rst
|
@ -2,9 +2,159 @@
|
|||
Changelog (Pillow)
|
||||
==================
|
||||
|
||||
5.1.0 (unreleased)
|
||||
5.3.0 (unreleased)
|
||||
------------------
|
||||
|
||||
- Depends: Update libimagequant to 2.12.1 #3281
|
||||
[radarhere]
|
||||
|
||||
- Add three-color support to ImageOps.colorize #3242
|
||||
[tsennott]
|
||||
|
||||
- Tests: Add LA to TGA test modes #3222
|
||||
[danpla]
|
||||
|
||||
- Skip outline if the draw operation fills with the same colour #2922
|
||||
[radarhere]
|
||||
|
||||
- Flake8 fixes #3173
|
||||
[radarhere]
|
||||
|
||||
- Avoid deprecated 'U' mode when opening files #2187
|
||||
[jdufresne]
|
||||
|
||||
5.2.0 (2018-07-01)
|
||||
------------------
|
||||
|
||||
- Fixed saving a multiframe image as a single frame PDF #3137
|
||||
[radarhere]
|
||||
|
||||
- If a Qt version is already imported, attempt to use it first #3143
|
||||
[radarhere]
|
||||
|
||||
- Fix transform fill color for alpha images #3147
|
||||
[fozcode]
|
||||
|
||||
- TGA: Add support for writing RLE data #3186
|
||||
[danpla]
|
||||
|
||||
- TGA: Read and write LA data #3178
|
||||
[danpla]
|
||||
|
||||
- QuantOctree.c: Remove erroneous attempt to average over an empty range #3196
|
||||
[tkoeppe]
|
||||
|
||||
- Changed ICNS format tests to pass on OS X 10.11 #3202
|
||||
[radarhere]
|
||||
|
||||
- Fixed bug in ImageDraw.multiline_textsize() #3114
|
||||
[tianyu139]
|
||||
|
||||
- Added getsize_multiline support for PIL.ImageFont #3113
|
||||
[tianyu139]
|
||||
|
||||
- Added ImageFile get_format_mimetype method #3190
|
||||
[radarhere]
|
||||
|
||||
- Changed mmap file pointer to use context manager #3216
|
||||
[radarhere]
|
||||
|
||||
- Changed ellipse point calculations to be more evenly distributed #3142
|
||||
[radarhere]
|
||||
|
||||
- Only extract first Exif segment #2946
|
||||
[hugovk]
|
||||
|
||||
- Tests: Test ImageDraw2, WalImageFile #3135, #2989
|
||||
[hugovk]
|
||||
|
||||
- Remove unnecessary '#if 0' code #3075
|
||||
[hugovk]
|
||||
|
||||
- Tests: Added GD tests #1817
|
||||
[radarhere]
|
||||
|
||||
- Fix collections ABCs DeprecationWarning in Python 3.7 #3123
|
||||
[hugovk]
|
||||
|
||||
- unpack_from is faster than unpack of slice #3201
|
||||
[landfillbaby]
|
||||
|
||||
- Docs: Add coordinate system links and file handling links in documentation #3204, #3214
|
||||
[radarhere]
|
||||
|
||||
- Tests: TestFilePng: Fix test_save_l_transparency() #3182
|
||||
[danpla]
|
||||
|
||||
- Docs: Correct argument name #3171
|
||||
[radarhere]
|
||||
|
||||
- Docs: Update CMake download URL #3166
|
||||
[radarhere]
|
||||
|
||||
- Docs: Improve Image.transform documentation #3164
|
||||
[radarhere]
|
||||
|
||||
- Fix transform fillcolor argument when image mode is RGBA or LA #3163
|
||||
[radarhere]
|
||||
|
||||
- Tests: More specific Exception testing #3158
|
||||
[radarhere]
|
||||
|
||||
- Add getrgb HSB/HSV color strings #3148
|
||||
[radarhere]
|
||||
|
||||
- Allow float values in getrgb HSL color string #3146
|
||||
[radarhere]
|
||||
|
||||
- AppVeyor: Upgrade to Python 2.7.15 and 3.4.4 #3140
|
||||
[radarhere]
|
||||
|
||||
- AppVeyor: Upgrade to PyPy 6.0.0 #3133
|
||||
[hugovk]
|
||||
|
||||
- Deprecate PILLOW_VERSION and VERSION #3090
|
||||
[hugovk]
|
||||
|
||||
- Support Python 3.7 #3076
|
||||
[hugovk]
|
||||
|
||||
- Depends: Update freetype to 2.9.1, libjpeg to 9c, libwebp to 1.0.0 #3121, #3136, #3108
|
||||
[radarhere]
|
||||
|
||||
- Build macOS wheels with Xcode 6.4, supporting older macOS versions #3068
|
||||
[wiredfool]
|
||||
|
||||
- Fix _i2f compilation on some GCC versions #3067
|
||||
[homm]
|
||||
|
||||
- Changed encoderinfo to have priority over info when saving GIF images #3086
|
||||
[radarhere]
|
||||
|
||||
- Rename PIL.version to PIL._version and remove it from module #3083
|
||||
[homm]
|
||||
|
||||
- Enable background colour parameter on rotate #3057
|
||||
[storesource]
|
||||
|
||||
- Remove unnecessary `#if 1` directive #3072
|
||||
[jdufresne]
|
||||
|
||||
- Remove unused Python class, Path #3070
|
||||
[jdufresne]
|
||||
|
||||
- Fix dereferencing type-punned pointer will break strict-aliasing #3069
|
||||
[jdufresne]
|
||||
|
||||
5.1.0 (2018-04-02)
|
||||
------------------
|
||||
|
||||
- Close fp before return in ImagingSavePPM #3061
|
||||
[kathryndavies]
|
||||
|
||||
- Added documentation for ICNS append_images #3051
|
||||
[radarhere]
|
||||
|
||||
- Docs: Move intro text below its header #3021
|
||||
[hugovk]
|
||||
|
||||
|
@ -13,12 +163,12 @@ Changelog (Pillow)
|
|||
|
||||
- Fix TypeError for JPEG2000 parser feed #3042
|
||||
[hugovk]
|
||||
|
||||
|
||||
- Certain corrupted jpegs can result in no data read #3023
|
||||
[kkopachev]
|
||||
|
||||
|
||||
- Add support for BLP file format #3007
|
||||
[jleclanche]
|
||||
[jleclanche]
|
||||
|
||||
- Simplify version checks #2998
|
||||
[hugovk]
|
||||
|
@ -83,10 +233,6 @@ Changelog (Pillow)
|
|||
- Docs: Changed documentation references to 2.x to 2.7 #2921
|
||||
[radarhere]
|
||||
|
||||
|
||||
5.0.1 (unreleased)
|
||||
------------------
|
||||
|
||||
- Fix memory leak when opening webp files #2974
|
||||
[wiredfool]
|
||||
|
||||
|
@ -177,7 +323,7 @@ Changelog (Pillow)
|
|||
- Add eog support for Ubuntu Image Viewer #2864
|
||||
[NafisFaysal]
|
||||
|
||||
- Test: Test on 3.7-dev on Travis.ci #2870
|
||||
- Test: Test on 3.7-dev on Travis CI #2870
|
||||
[hugovk]
|
||||
|
||||
- Dependencies: Update libtiff to 4.0.9 #2871
|
||||
|
@ -306,7 +452,7 @@ Changelog (Pillow)
|
|||
- Fixed doc syntax in ImageDraw #2752
|
||||
[radarhere]
|
||||
|
||||
- Fixed support for building on Windows/msys2. Added Appveyor CI coverage for python3 on msys2 #2476
|
||||
- Fixed support for building on Windows/msys2. Added Appveyor CI coverage for python3 on msys2 #2746
|
||||
[wiredfool]
|
||||
|
||||
- Fix ValueError in Exif/Tiff IFD #2719
|
||||
|
@ -378,7 +524,7 @@ Changelog (Pillow)
|
|||
- Use RGBX rawmode for RGB JPEG images where possible #1989
|
||||
[homm]
|
||||
|
||||
- Remove palettes from non-palette modes in _new #2702
|
||||
- Remove palettes from non-palette modes in _new #2704
|
||||
[wiredfool]
|
||||
|
||||
- Delete transparency info when convert'ing RGB/L to RGBA #2633
|
||||
|
@ -498,7 +644,7 @@ Changelog (Pillow)
|
|||
- Doc: Clarified Image.save:append_images documentation #2604
|
||||
[radarhere]
|
||||
|
||||
- CI: Amazon Linux and Centos6 docker images added to TravisCI #2585
|
||||
- CI: Amazon Linux and Centos6 docker images added to Travis CI #2585
|
||||
[wiredfool]
|
||||
|
||||
- Image.alpha_composite added #2595
|
||||
|
@ -567,7 +713,7 @@ Changelog (Pillow)
|
|||
- Update Feature Detection #2520
|
||||
[wiredfool]
|
||||
|
||||
- CI: Update pypy on TravisCI #2573
|
||||
- CI: Update pypy on Travis CI #2573
|
||||
[hugovk]
|
||||
|
||||
- ImageMorph: Fix wrong expected size of MRLs read from disk #2561
|
||||
|
@ -801,10 +947,10 @@ Changelog (Pillow)
|
|||
- Add center and translate option to Image.rotate. #2328
|
||||
[lambdafu]
|
||||
|
||||
- Test: Relax WMF test condition, fixes #2323
|
||||
- Test: Relax WMF test condition, fixes #2323. #2327
|
||||
[wiredfool]
|
||||
|
||||
- Allow 0 size images, Fixes #2259, Reverts to pre-3.4 behavior.
|
||||
- Allow 0 size images, Fixes #2259, Reverts to pre-3.4 behavior. #2262
|
||||
[wiredfool]
|
||||
|
||||
- SGI: Save uncompressed SGI/BW/RGB/RGBA files #2325
|
||||
|
@ -1135,10 +1281,10 @@ Changelog (Pillow)
|
|||
3.3.2 (2016-10-03)
|
||||
------------------
|
||||
|
||||
- Fix negative image sizes in Storage.c #2105
|
||||
- Fix negative image sizes in Storage.c #2146
|
||||
[wiredfool]
|
||||
|
||||
- Fix integer overflow in map.c #2105
|
||||
- Fix integer overflow in map.c #2146
|
||||
[wiredfool]
|
||||
|
||||
3.3.1 (2016-08-18)
|
||||
|
@ -1276,7 +1422,7 @@ Changelog (Pillow)
|
|||
- Skip tests that require libtiff if it is not installed #1893 (fixes #1866)
|
||||
[wiredfool]
|
||||
|
||||
- Skip test when icc profile is not available, fixes #1887
|
||||
- Skip test when icc profile is not available, fixes #1887. #1892
|
||||
[doko42]
|
||||
|
||||
- Make deprecated functions raise NotImplementedError instead of Exception. #1862, #1890
|
||||
|
@ -1881,7 +2027,7 @@ Changelog (Pillow)
|
|||
2.8.1 (2015-04-02)
|
||||
------------------
|
||||
|
||||
- Bug fix: Catch struct.error on invalid JPEG, fixes #1163
|
||||
- Bug fix: Catch struct.error on invalid JPEG, fixes #1163. #1165
|
||||
[wiredfool, hugovk]
|
||||
|
||||
2.8.0 (2015-04-01)
|
||||
|
@ -2016,7 +2162,7 @@ Changelog (Pillow)
|
|||
- Updated manifest #957
|
||||
[wiredfool]
|
||||
|
||||
- Fix PyPy 2.4 regression #952
|
||||
- Fix PyPy 2.4 regression #958
|
||||
[wiredfool]
|
||||
|
||||
- Webp Metadata Skip Test comments #954
|
||||
|
@ -2058,7 +2204,7 @@ Changelog (Pillow)
|
|||
- Use redistributable ICC profiles for testing, skip if not available #923
|
||||
[wiredfool]
|
||||
|
||||
- Additional documentation for JPEG info and save options #890
|
||||
- Additional documentation for JPEG info and save options #922
|
||||
[wiredfool]
|
||||
|
||||
- Fix JPEG Encoding memory leak when exif or qtables were specified #921
|
||||
|
|
|
@ -24,7 +24,6 @@ exclude .editorconfig
|
|||
exclude .landscape.yaml
|
||||
exclude .travis
|
||||
exclude .travis/*
|
||||
exclude build_children.sh
|
||||
exclude tox.ini
|
||||
global-exclude .git*
|
||||
global-exclude *.pyc
|
||||
|
|
2
Makefile
|
@ -91,7 +91,7 @@ sdist:
|
|||
test:
|
||||
pytest -qq
|
||||
|
||||
# https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file
|
||||
# https://docs.python.org/3/distutils/packageindex.html#the-pypirc-file
|
||||
upload-test:
|
||||
# [test]
|
||||
# username:
|
||||
|
|
|
@ -14,7 +14,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
* - docs
|
||||
- |docs|
|
||||
* - tests
|
||||
- | |linux| |macos| |windows| |coverage|
|
||||
- |linux| |macos| |windows| |coverage|
|
||||
* - package
|
||||
- |zenodo| |version|
|
||||
* - social
|
||||
|
@ -44,7 +44,7 @@ Pillow is the friendly PIL fork by `Alex Clark and Contributors <https://github.
|
|||
:target: https://zenodo.org/badge/latestdoi/17549/python-pillow/Pillow
|
||||
|
||||
.. |version| image:: https://img.shields.io/pypi/v/pillow.svg
|
||||
:target: https://pypi.python.org/pypi/Pillow/
|
||||
:target: https://pypi.org/project/Pillow/
|
||||
:alt: Latest PyPI version
|
||||
|
||||
.. |gitter| image:: https://badges.gitter.im/python-pillow/Pillow.svg
|
||||
|
|
37
RELEASING.md
|
@ -4,17 +4,17 @@
|
|||
|
||||
Released quarterly on the first day of January, April, July, October.
|
||||
|
||||
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/1174
|
||||
* [ ] Open a release ticket e.g. https://github.com/python-pillow/Pillow/issues/3154
|
||||
* [ ] Develop and prepare release in ``master`` branch.
|
||||
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) and [AppVeyor CI](https://ci.appveyor.com/project/python-pillow/Pillow) to confirm passing tests in ``master`` branch.
|
||||
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in TravisCI.
|
||||
* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `PIL/version.py`
|
||||
* [ ] Check that all of the wheel builds [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels) pass the tests in Travis CI.
|
||||
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
|
||||
* [ ] Update `CHANGES.rst`.
|
||||
* [ ] Run pre-release check via `make release-test` in a freshly cloned repo.
|
||||
* [ ] Create branch and tag for release e.g.:
|
||||
```
|
||||
$ git branch 2.9.x
|
||||
$ git tag 2.9.0
|
||||
$ git branch 5.2.x
|
||||
$ git tag 5.2.0
|
||||
$ git push --all
|
||||
$ git push --tags
|
||||
```
|
||||
|
@ -23,8 +23,9 @@ Released quarterly on the first day of January, April, July, October.
|
|||
$ make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](#binary-distributions)
|
||||
* [ ] Upload all binaries and source distributions with ``twine upload dist/Pillow-4.1.0-*``
|
||||
* [ ] Manually hide old versions on PyPI such that only the latest major release is visible when viewing https://pypi.python.org/pypi/Pillow (https://pypi.python.org/pypi?:action=pkg_edit&name=Pillow)
|
||||
* [ ] Upload all binaries and source distributions e.g. ``twine upload dist/Pillow-5.2.0-*``
|
||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
||||
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), append `.dev0` to version identifier in `src/PIL/_version.py`
|
||||
|
||||
## Point Release
|
||||
|
||||
|
@ -32,17 +33,17 @@ Released as needed for security, installation or critical bug fixes.
|
|||
|
||||
* [ ] Make necessary changes in ``master`` branch.
|
||||
* [ ] Update `CHANGES.rst`.
|
||||
* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``2.9.x``.
|
||||
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``2.9.x``.
|
||||
* [ ] Checkout release branch e.g.:
|
||||
* [ ] Cherry pick individual commits from ``master`` branch to release branch e.g. ``5.2.x``.
|
||||
* [ ] Check [Travis CI](https://travis-ci.org/python-pillow/Pillow) to confirm passing tests in release branch e.g. ``5.2.x``.
|
||||
* [ ] Check out release branch e.g.:
|
||||
```
|
||||
git checkout -t remotes/origin/2.9.x
|
||||
git checkout -t remotes/origin/5.2.x
|
||||
```
|
||||
* [ ] In compliance with https://www.python.org/dev/peps/pep-0440/, update version identifier in `PIL/version.py`
|
||||
* [ ] In compliance with [PEP 440](https://www.python.org/dev/peps/pep-0440/), update version identifier in `src/PIL/_version.py`
|
||||
* [ ] Run pre-release check via `make release-test`.
|
||||
* [ ] Create tag for release e.g.:
|
||||
```
|
||||
$ git tag 2.9.1
|
||||
$ git tag 5.2.1
|
||||
$ git push --tags
|
||||
```
|
||||
* [ ] Create source distributions e.g.:
|
||||
|
@ -50,6 +51,7 @@ Released as needed for security, installation or critical bug fixes.
|
|||
$ make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](#binary-distributions)
|
||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
||||
|
||||
## Embargoed Release
|
||||
|
||||
|
@ -73,6 +75,7 @@ Released as needed privately to individual vendors for critical security-related
|
|||
$ make sdist
|
||||
```
|
||||
* [ ] Create [binary distributions](#binary-distributions)
|
||||
* [ ] Create a [new release on GitHub](https://github.com/python-pillow/Pillow/releases/new)
|
||||
|
||||
## Binary Distributions
|
||||
|
||||
|
@ -83,7 +86,7 @@ Released as needed privately to individual vendors for critical security-related
|
|||
### Mac and Linux
|
||||
* [ ] Use the [Pillow Wheel Builder](https://github.com/python-pillow/pillow-wheels):
|
||||
```
|
||||
$ git checkout https://github.com/python-pillow/pillow-wheels
|
||||
$ git clone https://github.com/python-pillow/pillow-wheels
|
||||
$ cd pillow-wheels
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
|
@ -91,7 +94,7 @@ Released as needed privately to individual vendors for critical security-related
|
|||
$ git fetch --all
|
||||
$ git checkout [[release tag]]
|
||||
$ cd ..
|
||||
$ git commit -m "Pillow -> 2.9.0" Pillow
|
||||
$ git commit -m "Pillow -> 5.2.0" Pillow
|
||||
$ git push
|
||||
```
|
||||
* [ ] Download distributions from the [Pillow Wheel Builder container](http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/).
|
||||
|
@ -99,8 +102,8 @@ Released as needed privately to individual vendors for critical security-related
|
|||
|
||||
## Publicize Release
|
||||
|
||||
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/aclark4life/status/583366798302691328.
|
||||
* [ ] Announce release availability via [Twitter](https://twitter.com/pythonpillow) e.g. https://twitter.com/PythonPillow/status/1013789184354603010
|
||||
|
||||
## Documentation
|
||||
|
||||
* [ ] Make sure the default version for Read the Docs is the latest release version, e.g. ``3.1.x`` rather than ``latest``: https://readthedocs.org/projects/pillow/versions/
|
||||
* [ ] Make sure the default version for Read the Docs is the latest release version, i.e. ``5.2.0`` rather than ``latest`` e.g. https://pillow.readthedocs.io/en/5.2.x/
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
# Run from anywhere that PIL is importable.
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
from io import BytesIO
|
||||
|
||||
if bytes is str:
|
||||
Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00')))
|
||||
else:
|
||||
if py3:
|
||||
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')))
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
# Run from anywhere that PIL is importable.
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
from io import BytesIO
|
||||
|
||||
if bytes is str:
|
||||
Image.open(BytesIO(bytes(
|
||||
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang')))
|
||||
else:
|
||||
if py3:
|
||||
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')))
|
||||
|
|
|
@ -8,6 +8,7 @@ import os
|
|||
import unittest
|
||||
|
||||
from PIL import Image, ImageMath
|
||||
from PIL._util import py3
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -152,7 +153,8 @@ class PillowTestCase(unittest.TestCase):
|
|||
pass
|
||||
raise e
|
||||
|
||||
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None):
|
||||
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None,
|
||||
mode=None):
|
||||
with Image.open(filename) as img:
|
||||
if mode:
|
||||
img = img.convert(mode)
|
||||
|
@ -190,6 +192,16 @@ class PillowTestCase(unittest.TestCase):
|
|||
def assert_not_all_same(self, items, msg=None):
|
||||
self.assertFalse(items.count(items[0]) == len(items), msg)
|
||||
|
||||
def assert_tuple_approx_equal(self, actuals, targets, threshold, msg):
|
||||
"""Tests if actuals has values within threshold from targets"""
|
||||
|
||||
value = True
|
||||
for i, target in enumerate(targets):
|
||||
value *= (target - threshold <= actuals[i] <= target + threshold)
|
||||
|
||||
self.assertTrue(value,
|
||||
msg + ': ' + repr(actuals) + ' != ' + repr(targets))
|
||||
|
||||
def skipKnownBadTest(self, msg=None, platform=None,
|
||||
travis=None, interpreter=None):
|
||||
# Skip if platform/travis matches, and
|
||||
|
@ -229,23 +241,24 @@ class PillowTestCase(unittest.TestCase):
|
|||
|
||||
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS")
|
||||
class PillowLeakTestCase(PillowTestCase):
|
||||
# requires unix/osx
|
||||
# requires unix/macOS
|
||||
iterations = 100 # count
|
||||
mem_limit = 512 # k
|
||||
|
||||
def _get_mem_usage(self):
|
||||
"""
|
||||
Gets the RUSAGE memory usage, returns in K. Encapsulates the difference
|
||||
between OSX and Linux rss reporting
|
||||
between macOS and Linux rss reporting
|
||||
|
||||
:returns; memory usage in kilobytes
|
||||
:returns: memory usage in kilobytes
|
||||
"""
|
||||
|
||||
from resource import getrusage, RUSAGE_SELF
|
||||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||
if sys.platform == 'darwin':
|
||||
# man 2 getrusage:
|
||||
# ru_maxrss the maximum resident set size utilized (in bytes).
|
||||
# ru_maxrss
|
||||
# This is the maximum resident set size utilized (in bytes).
|
||||
return mem / 1024 # Kb
|
||||
else:
|
||||
# linux
|
||||
|
@ -265,7 +278,10 @@ class PillowLeakTestCase(PillowTestCase):
|
|||
|
||||
# helpers
|
||||
|
||||
py3 = sys.version_info.major >= 3
|
||||
if not py3:
|
||||
# Remove DeprecationWarning in Python 3
|
||||
PillowTestCase.assertRaisesRegex = PillowTestCase.assertRaisesRegexp
|
||||
PillowTestCase.assertRegex = PillowTestCase.assertRegexpMatches
|
||||
|
||||
|
||||
def fromstring(data):
|
||||
|
|
BIN
Tests/images/bw_gradient.png
Normal file
After Width: | Height: | Size: 102 B |
BIN
Tests/images/hopper.gd
Normal file
BIN
Tests/images/hopper.wal
Normal file
BIN
Tests/images/imagedraw2_text.png
Normal file
After Width: | Height: | Size: 974 B |
BIN
Tests/images/imagedraw_outline_chord_L.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
Tests/images/imagedraw_outline_chord_RGB.png
Normal file
After Width: | Height: | Size: 259 B |
BIN
Tests/images/imagedraw_outline_ellipse_L.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
Tests/images/imagedraw_outline_ellipse_RGB.png
Normal file
After Width: | Height: | Size: 378 B |
BIN
Tests/images/imagedraw_outline_pieslice_L.png
Normal file
After Width: | Height: | Size: 266 B |
BIN
Tests/images/imagedraw_outline_pieslice_RGB.png
Normal file
After Width: | Height: | Size: 340 B |
BIN
Tests/images/imagedraw_outline_polygon_L.png
Normal file
After Width: | Height: | Size: 309 B |
BIN
Tests/images/imagedraw_outline_polygon_RGB.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
Tests/images/imagedraw_outline_rectangle_L.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
Tests/images/imagedraw_outline_rectangle_RGB.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
Tests/images/imagedraw_outline_shape_L.png
Normal file
After Width: | Height: | Size: 263 B |
BIN
Tests/images/imagedraw_outline_shape_RGB.png
Normal file
After Width: | Height: | Size: 316 B |
BIN
Tests/images/la.tga
Normal file
BIN
Tests/images/rotate_45_no_fill.png
Normal file
After Width: | Height: | Size: 639 B |
BIN
Tests/images/rotate_45_with_fill.png
Normal file
After Width: | Height: | Size: 625 B |
BIN
Tests/images/tga/common/1x1_l.png
Normal file
After Width: | Height: | Size: 67 B |
BIN
Tests/images/tga/common/1x1_l_bl_raw.tga
Normal file
BIN
Tests/images/tga/common/1x1_l_bl_rle.tga
Normal file
BIN
Tests/images/tga/common/1x1_l_tl_raw.tga
Normal file
BIN
Tests/images/tga/common/1x1_l_tl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_l.png
Normal file
After Width: | Height: | Size: 385 B |
BIN
Tests/images/tga/common/200x32_l_bl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_l_bl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_l_tl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_l_tl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_la.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Tests/images/tga/common/200x32_la_bl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_la_bl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_la_tl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_la_tl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_p.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
Tests/images/tga/common/200x32_p_bl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_p_bl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_p_tl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_p_tl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_rgb.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
Tests/images/tga/common/200x32_rgb_bl_raw.tga
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
Tests/images/tga/common/200x32_rgb_bl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_rgb_tl_raw.tga
Normal file
BIN
Tests/images/tga/common/200x32_rgb_tl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_rgba.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
Tests/images/tga/common/200x32_rgba_bl_raw.tga
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
Tests/images/tga/common/200x32_rgba_bl_rle.tga
Normal file
BIN
Tests/images/tga/common/200x32_rgba_tl_raw.tga
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
Tests/images/tga/common/200x32_rgba_tl_rle.tga
Normal file
12
Tests/images/tga/common/readme.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
Images in this directory were created with GIMP.
|
||||
|
||||
TGAs have names in the following format:
|
||||
|
||||
{width}x{height}_{mode}_{origin}_{compression}.tga
|
||||
|
||||
Where:
|
||||
mode is PIL mode in lower case (L, P, RGB, etc.)
|
||||
origin:
|
||||
"bl" - bottom left
|
||||
"tl" - top left
|
||||
compression is either "raw" or "rle"
|
|
@ -1,6 +1,6 @@
|
|||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
from PIL import Image, ImageFilter
|
||||
|
||||
|
||||
sample = Image.new("L", (7, 5))
|
||||
|
@ -16,7 +16,7 @@ sample.putdata(sum([
|
|||
class TestBoxBlurApi(PillowTestCase):
|
||||
|
||||
def test_imageops_box_blur(self):
|
||||
i = ImageOps.box_blur(sample, 1)
|
||||
i = sample.filter(ImageFilter.BoxBlur(1))
|
||||
self.assertEqual(i.mode, sample.mode)
|
||||
self.assertEqual(i.size, sample.size)
|
||||
self.assertIsInstance(i, Image.Image)
|
||||
|
|
523
Tests/test_color_lut.py
Normal file
|
@ -0,0 +1,523 @@
|
|||
from __future__ import division
|
||||
|
||||
from array import array
|
||||
|
||||
from PIL import Image, ImageFilter
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
try:
|
||||
import numpy
|
||||
except ImportError:
|
||||
numpy = None
|
||||
|
||||
|
||||
class TestColorLut3DCoreAPI(PillowTestCase):
|
||||
def generate_identity_table(self, channels, size):
|
||||
if isinstance(size, tuple):
|
||||
size1D, size2D, size3D = size
|
||||
else:
|
||||
size1D, size2D, size3D = (size, size, size)
|
||||
|
||||
table = [
|
||||
[
|
||||
r / float(size1D-1) if size1D != 1 else 0,
|
||||
g / float(size2D-1) if size2D != 1 else 0,
|
||||
b / float(size3D-1) if size3D != 1 else 0,
|
||||
r / float(size1D-1) if size1D != 1 else 0,
|
||||
g / float(size2D-1) if size2D != 1 else 0,
|
||||
][:channels]
|
||||
for b in range(size3D)
|
||||
for g in range(size2D)
|
||||
for r in range(size1D)
|
||||
]
|
||||
return (
|
||||
channels, size1D, size2D, size3D,
|
||||
[item for sublist in table for item in sublist])
|
||||
|
||||
def test_wrong_args(self):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "filter"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.CUBIC,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "image mode"):
|
||||
im.im.color_lut_3d('wrong',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table_channels"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(5, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table_channels"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(1, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table_channels"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(2, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Table size"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(3, (1, 3, 3)))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Table size"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
*self.generate_identity_table(3, (66, 3, 3)))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, 0] * 7)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, 0] * 9)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, [0, 0, "0"] * 8)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
im.im.color_lut_3d('RGB',
|
||||
Image.LINEAR,
|
||||
3, 2, 2, 2, 16)
|
||||
|
||||
def test_correct_args(self):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
im.im.color_lut_3d('CMYK', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (2, 3, 3)))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (65, 3, 3)))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (3, 65, 3)))
|
||||
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (3, 3, 65)))
|
||||
|
||||
def test_wrong_mode(self):
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('L', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('L', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('L', (10, 10), 0)
|
||||
im.im.color_lut_3d('L', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "wrong mode"):
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
def test_correct_mode(self):
|
||||
im = Image.new('RGBA', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
im = Image.new('RGBA', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('HSV', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 3))
|
||||
|
||||
im = Image.new('RGB', (10, 10), 0)
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 3))
|
||||
|
||||
def test_identities(self):
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180)])
|
||||
|
||||
# Fast test with small cubes
|
||||
for size in [2, 3, 5, 7, 11, 16, 17]:
|
||||
self.assert_image_equal(im, im._new(
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, size))))
|
||||
|
||||
# Not so fast
|
||||
self.assert_image_equal(im, im._new(
|
||||
im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
*self.generate_identity_table(3, (2, 2, 65)))))
|
||||
|
||||
def test_identities_4_channels(self):
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180)])
|
||||
|
||||
# Red channel copied to alpha
|
||||
self.assert_image_equal(
|
||||
Image.merge('RGBA', (im.split()*2)[:4]),
|
||||
im._new(im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(4, 17))))
|
||||
|
||||
def test_copy_alpha_channel(self):
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGBA', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180),
|
||||
g.transpose(Image.ROTATE_270)])
|
||||
|
||||
self.assert_image_equal(im, im._new(
|
||||
im.im.color_lut_3d('RGBA', Image.LINEAR,
|
||||
*self.generate_identity_table(3, 17))))
|
||||
|
||||
def test_channels_order(self):
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180)])
|
||||
|
||||
# Reverse channels by splitting and using table
|
||||
self.assert_image_equal(
|
||||
Image.merge('RGB', im.split()[::-1]),
|
||||
im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2, [
|
||||
0, 0, 0, 0, 0, 1,
|
||||
0, 1, 0, 0, 1, 1,
|
||||
|
||||
1, 0, 0, 1, 0, 1,
|
||||
1, 1, 0, 1, 1, 1,
|
||||
])))
|
||||
|
||||
def test_overflow(self):
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180)])
|
||||
|
||||
transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2,
|
||||
[
|
||||
-1, -1, -1, 2, -1, -1,
|
||||
-1, 2, -1, 2, 2, -1,
|
||||
|
||||
-1, -1, 2, 2, -1, 2,
|
||||
-1, 2, 2, 2, 2, 2,
|
||||
])).load()
|
||||
self.assertEqual(transformed[0, 0], (0, 0, 255))
|
||||
self.assertEqual(transformed[50, 50], (0, 0, 255))
|
||||
self.assertEqual(transformed[255, 0], (0, 255, 255))
|
||||
self.assertEqual(transformed[205, 50], (0, 255, 255))
|
||||
self.assertEqual(transformed[0, 255], (255, 0, 0))
|
||||
self.assertEqual(transformed[50, 205], (255, 0, 0))
|
||||
self.assertEqual(transformed[255, 255], (255, 255, 0))
|
||||
self.assertEqual(transformed[205, 205], (255, 255, 0))
|
||||
|
||||
transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
|
||||
3, 2, 2, 2,
|
||||
[
|
||||
-3, -3, -3, 5, -3, -3,
|
||||
-3, 5, -3, 5, 5, -3,
|
||||
|
||||
-3, -3, 5, 5, -3, 5,
|
||||
-3, 5, 5, 5, 5, 5,
|
||||
])).load()
|
||||
self.assertEqual(transformed[0, 0], (0, 0, 255))
|
||||
self.assertEqual(transformed[50, 50], (0, 0, 255))
|
||||
self.assertEqual(transformed[255, 0], (0, 255, 255))
|
||||
self.assertEqual(transformed[205, 50], (0, 255, 255))
|
||||
self.assertEqual(transformed[0, 255], (255, 0, 0))
|
||||
self.assertEqual(transformed[50, 205], (255, 0, 0))
|
||||
self.assertEqual(transformed[255, 255], (255, 255, 0))
|
||||
self.assertEqual(transformed[205, 205], (255, 255, 0))
|
||||
|
||||
|
||||
class TestColorLut3DFilter(PillowTestCase):
|
||||
def test_wrong_args(self):
|
||||
with self.assertRaisesRegex(ValueError, "should be either an integer"):
|
||||
ImageFilter.Color3DLUT("small", [1])
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should be either an integer"):
|
||||
ImageFilter.Color3DLUT((11, 11), [1])
|
||||
|
||||
with self.assertRaisesRegex(ValueError, r"in \[2, 65\] range"):
|
||||
ImageFilter.Color3DLUT((11, 11, 1), [1])
|
||||
|
||||
with self.assertRaisesRegex(ValueError, r"in \[2, 65\] range"):
|
||||
ImageFilter.Color3DLUT((11, 11, 66), [1])
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table should have .+ items"):
|
||||
ImageFilter.Color3DLUT((3, 3, 3), [1, 1, 1])
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "table should have .+ items"):
|
||||
ImageFilter.Color3DLUT((3, 3, 3), [[1, 1, 1]] * 2)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have a length of 4"):
|
||||
ImageFilter.Color3DLUT((3, 3, 3), [[1, 1, 1]] * 27, channels=4)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have a length of 3"):
|
||||
ImageFilter.Color3DLUT((2, 2, 2), [[1, 1]] * 8)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Only 3 or 4 output"):
|
||||
ImageFilter.Color3DLUT((2, 2, 2), [[1, 1]] * 8, channels=2)
|
||||
|
||||
def test_convert_table(self):
|
||||
lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8)
|
||||
self.assertEqual(tuple(lut.size), (2, 2, 2))
|
||||
self.assertEqual(lut.name, "Color 3D LUT")
|
||||
|
||||
lut = ImageFilter.Color3DLUT((2, 2, 2), [
|
||||
(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11),
|
||||
(12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23)])
|
||||
self.assertEqual(tuple(lut.size), (2, 2, 2))
|
||||
self.assertEqual(lut.table, list(range(24)))
|
||||
|
||||
lut = ImageFilter.Color3DLUT((2, 2, 2), [(0, 1, 2, 3)] * 8,
|
||||
channels=4)
|
||||
|
||||
@unittest.skipIf(numpy is None, "Numpy is not installed")
|
||||
def test_numpy_sources(self):
|
||||
table = numpy.ones((5, 6, 7, 3), dtype=numpy.float16)
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
|
||||
|
||||
table = numpy.ones((7, 6, 5, 3), dtype=numpy.float16)
|
||||
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
|
||||
self.assertIsInstance(lut.table, numpy.ndarray)
|
||||
self.assertEqual(lut.table.dtype, table.dtype)
|
||||
self.assertEqual(lut.table.shape, (table.size,))
|
||||
|
||||
table = numpy.ones((7 * 6 * 5, 3), dtype=numpy.float16)
|
||||
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
|
||||
self.assertEqual(lut.table.shape, (table.size,))
|
||||
|
||||
table = numpy.ones((7 * 6 * 5 * 3), dtype=numpy.float16)
|
||||
lut = ImageFilter.Color3DLUT((5, 6, 7), table)
|
||||
self.assertEqual(lut.table.shape, (table.size,))
|
||||
|
||||
# Check application
|
||||
Image.new('RGB', (10, 10), 0).filter(lut)
|
||||
|
||||
# Check copy
|
||||
table[0] = 33
|
||||
self.assertEqual(lut.table[0], 1)
|
||||
|
||||
# Check not copy
|
||||
table = numpy.ones((7 * 6 * 5 * 3), dtype=numpy.float16)
|
||||
lut = ImageFilter.Color3DLUT((5, 6, 7), table, _copy_table=False)
|
||||
table[0] = 33
|
||||
self.assertEqual(lut.table[0], 33)
|
||||
|
||||
@unittest.skipIf(numpy is None, "Numpy is not installed")
|
||||
def test_numpy_formats(self):
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180)])
|
||||
|
||||
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
|
||||
lambda r, g, b: (r, g, b))
|
||||
lut.table = numpy.array(lut.table, dtype=numpy.float32)[:-1]
|
||||
with self.assertRaisesRegex(ValueError, "should have table_channels"):
|
||||
im.filter(lut)
|
||||
|
||||
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
|
||||
lambda r, g, b: (r, g, b))
|
||||
lut.table = (numpy.array(lut.table, dtype=numpy.float32)
|
||||
.reshape((7 * 9 * 11), 3))
|
||||
with self.assertRaisesRegex(ValueError, "should have table_channels"):
|
||||
im.filter(lut)
|
||||
|
||||
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
|
||||
lambda r, g, b: (r, g, b))
|
||||
lut.table = numpy.array(lut.table, dtype=numpy.float16)
|
||||
self.assert_image_equal(im, im.filter(lut))
|
||||
|
||||
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
|
||||
lambda r, g, b: (r, g, b))
|
||||
lut.table = numpy.array(lut.table, dtype=numpy.float32)
|
||||
self.assert_image_equal(im, im.filter(lut))
|
||||
|
||||
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
|
||||
lambda r, g, b: (r, g, b))
|
||||
lut.table = numpy.array(lut.table, dtype=numpy.float64)
|
||||
self.assert_image_equal(im, im.filter(lut))
|
||||
|
||||
lut = ImageFilter.Color3DLUT.generate((7, 9, 11),
|
||||
lambda r, g, b: (r, g, b))
|
||||
lut.table = numpy.array(lut.table, dtype=numpy.int32)
|
||||
im.filter(lut)
|
||||
lut.table = numpy.array(lut.table, dtype=numpy.int8)
|
||||
im.filter(lut)
|
||||
|
||||
def test_repr(self):
|
||||
lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8)
|
||||
self.assertEqual(repr(lut),
|
||||
"<Color3DLUT from list size=2x2x2 channels=3>")
|
||||
|
||||
lut = ImageFilter.Color3DLUT(
|
||||
(3, 4, 5), array('f', [0, 0, 0, 0] * (3 * 4 * 5)),
|
||||
channels=4, target_mode='YCbCr', _copy_table=False)
|
||||
self.assertEqual(
|
||||
repr(lut),
|
||||
"<Color3DLUT from array size=3x4x5 channels=4 target_mode=YCbCr>")
|
||||
|
||||
|
||||
class TestGenerateColorLut3D(PillowTestCase):
|
||||
def test_wrong_channels_count(self):
|
||||
with self.assertRaisesRegex(ValueError, "3 or 4 output channels"):
|
||||
ImageFilter.Color3DLUT.generate(
|
||||
5, channels=2, callback=lambda r, g, b: (r, g, b))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
ImageFilter.Color3DLUT.generate(
|
||||
5, channels=4, callback=lambda r, g, b: (r, g, b))
|
||||
|
||||
def test_3_channels(self):
|
||||
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
|
||||
self.assertEqual(tuple(lut.size), (5, 5, 5))
|
||||
self.assertEqual(lut.name, "Color 3D LUT")
|
||||
self.assertEqual(lut.table[:24], [
|
||||
0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.5, 0.0, 0.0, 0.75, 0.0, 0.0,
|
||||
1.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.25, 0.0, 0.5, 0.25, 0.0])
|
||||
|
||||
def test_4_channels(self):
|
||||
lut = ImageFilter.Color3DLUT.generate(
|
||||
5, channels=4, callback=lambda r, g, b: (b, r, g, (r+g+b) / 2))
|
||||
self.assertEqual(tuple(lut.size), (5, 5, 5))
|
||||
self.assertEqual(lut.name, "Color 3D LUT")
|
||||
self.assertEqual(lut.table[:24], [
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25,
|
||||
0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125
|
||||
])
|
||||
|
||||
def test_apply(self):
|
||||
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
|
||||
|
||||
g = Image.linear_gradient('L')
|
||||
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90),
|
||||
g.transpose(Image.ROTATE_180)])
|
||||
self.assertEqual(im, im.filter(lut))
|
||||
|
||||
|
||||
class TestTransformColorLut3D(PillowTestCase):
|
||||
def test_wrong_args(self):
|
||||
source = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Only 3 or 4 output"):
|
||||
source.transform(lambda r, g, b: (r, g, b), channels=8)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
source.transform(lambda r, g, b: (r, g, b), channels=4)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "should have either channels"):
|
||||
source.transform(lambda r, g, b: (r, g, b, 1))
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
source.transform(lambda r, g, b, a: (r, g, b))
|
||||
|
||||
def test_target_mode(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
2, lambda r, g, b: (r, g, b), target_mode='HSV')
|
||||
|
||||
lut = source.transform(lambda r, g, b: (r, g, b))
|
||||
self.assertEqual(lut.mode, 'HSV')
|
||||
|
||||
lut = source.transform(lambda r, g, b: (r, g, b), target_mode='RGB')
|
||||
self.assertEqual(lut.mode, 'RGB')
|
||||
|
||||
def test_3_to_3_channels(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
(3, 4, 5), lambda r, g, b: (r, g, b))
|
||||
lut = source.transform(lambda r, g, b: (r*r, g*g, b*b))
|
||||
self.assertEqual(tuple(lut.size), tuple(source.size))
|
||||
self.assertEqual(len(lut.table), len(source.table))
|
||||
self.assertNotEqual(lut.table, source.table)
|
||||
self.assertEqual(lut.table[0:10], [
|
||||
0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0])
|
||||
|
||||
def test_3_to_4_channels(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
(6, 5, 4), lambda r, g, b: (r, g, b))
|
||||
lut = source.transform(lambda r, g, b: (r*r, g*g, b*b, 1), channels=4)
|
||||
self.assertEqual(tuple(lut.size), tuple(source.size))
|
||||
self.assertNotEqual(len(lut.table), len(source.table))
|
||||
self.assertNotEqual(lut.table, source.table)
|
||||
self.assertEqual(lut.table[0:16], [
|
||||
0.0, 0.0, 0.0, 1, 0.2**2, 0.0, 0.0, 1,
|
||||
0.4**2, 0.0, 0.0, 1, 0.6**2, 0.0, 0.0, 1])
|
||||
|
||||
def test_4_to_3_channels(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
(3, 6, 5), lambda r, g, b: (r, g, b, 1), channels=4)
|
||||
lut = source.transform(lambda r, g, b, a: (a - r*r, a - g*g, a - b*b),
|
||||
channels=3)
|
||||
self.assertEqual(tuple(lut.size), tuple(source.size))
|
||||
self.assertNotEqual(len(lut.table), len(source.table))
|
||||
self.assertNotEqual(lut.table, source.table)
|
||||
self.assertEqual(lut.table[0:18], [
|
||||
1.0, 1.0, 1.0, 0.75, 1.0, 1.0, 0.0, 1.0, 1.0,
|
||||
1.0, 0.96, 1.0, 0.75, 0.96, 1.0, 0.0, 0.96, 1.0])
|
||||
|
||||
def test_4_to_4_channels(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
(6, 5, 4), lambda r, g, b: (r, g, b, 1), channels=4)
|
||||
lut = source.transform(lambda r, g, b, a: (r*r, g*g, b*b, a - 0.5))
|
||||
self.assertEqual(tuple(lut.size), tuple(source.size))
|
||||
self.assertEqual(len(lut.table), len(source.table))
|
||||
self.assertNotEqual(lut.table, source.table)
|
||||
self.assertEqual(lut.table[0:16], [
|
||||
0.0, 0.0, 0.0, 0.5, 0.2**2, 0.0, 0.0, 0.5,
|
||||
0.4**2, 0.0, 0.0, 0.5, 0.6**2, 0.0, 0.0, 0.5])
|
||||
|
||||
def test_with_normals_3_channels(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
(6, 5, 4), lambda r, g, b: (r*r, g*g, b*b))
|
||||
lut = source.transform(
|
||||
lambda nr, ng, nb, r, g, b: (nr - r, ng - g, nb - b),
|
||||
with_normals=True)
|
||||
self.assertEqual(tuple(lut.size), tuple(source.size))
|
||||
self.assertEqual(len(lut.table), len(source.table))
|
||||
self.assertNotEqual(lut.table, source.table)
|
||||
self.assertEqual(lut.table[0:18], [
|
||||
0.0, 0.0, 0.0, 0.16, 0.0, 0.0, 0.24, 0.0, 0.0,
|
||||
0.24, 0.0, 0.0, 0.8 - (0.8**2), 0, 0, 0, 0, 0])
|
||||
|
||||
def test_with_normals_4_channels(self):
|
||||
source = ImageFilter.Color3DLUT.generate(
|
||||
(3, 6, 5), lambda r, g, b: (r*r, g*g, b*b, 1), channels=4)
|
||||
lut = source.transform(
|
||||
lambda nr, ng, nb, r, g, b, a: (nr - r, ng - g, nb - b, a-0.5),
|
||||
with_normals=True)
|
||||
self.assertEqual(tuple(lut.size), tuple(source.size))
|
||||
self.assertEqual(len(lut.table), len(source.table))
|
||||
self.assertNotEqual(lut.table, source.table)
|
||||
self.assertEqual(lut.table[0:16], [
|
||||
0.0, 0.0, 0.0, 0.5, 0.25, 0.0, 0.0, 0.5,
|
||||
0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -76,7 +76,7 @@ class TestFileEps(PillowTestCase):
|
|||
plot_image = Image.open("Tests/images/reqd_showpage.eps")
|
||||
target = Image.open("Tests/images/reqd_showpage.png")
|
||||
|
||||
#should not crash/hang
|
||||
# should not crash/hang
|
||||
plot_image.load()
|
||||
# fonts could be slightly different
|
||||
self.assert_image_similar(plot_image, target, 6)
|
||||
|
@ -195,41 +195,15 @@ class TestFileEps(PillowTestCase):
|
|||
self.assertEqual(t.readline().strip('\r\n'), 'baz', ending)
|
||||
self.assertEqual(t.readline().strip('\r\n'), 'bif', ending)
|
||||
|
||||
def _test_readline_stringio(self, test_string, ending):
|
||||
# check all the freaking line endings possible
|
||||
try:
|
||||
import StringIO
|
||||
except ImportError:
|
||||
# don't skip, it skips everything in the parent test
|
||||
return
|
||||
t = StringIO.StringIO(test_string)
|
||||
def _test_readline_io_psfile(self, test_string, ending):
|
||||
f = io.BytesIO(test_string.encode('latin-1'))
|
||||
t = EpsImagePlugin.PSFile(f)
|
||||
self._test_readline(t, ending)
|
||||
|
||||
def _test_readline_io(self, test_string, ending):
|
||||
if str is bytes:
|
||||
t = io.StringIO(unicode(test_string))
|
||||
else:
|
||||
t = io.StringIO(test_string)
|
||||
self._test_readline(t, ending)
|
||||
|
||||
def _test_readline_file_universal(self, test_string, ending):
|
||||
f = self.tempfile('temp.txt')
|
||||
with open(f, 'wb') as w:
|
||||
if str is bytes:
|
||||
w.write(test_string)
|
||||
else:
|
||||
w.write(test_string.encode('UTF-8'))
|
||||
|
||||
with open(f, 'rU') as t:
|
||||
self._test_readline(t, ending)
|
||||
|
||||
def _test_readline_file_psfile(self, test_string, ending):
|
||||
f = self.tempfile('temp.txt')
|
||||
with open(f, 'wb') as w:
|
||||
if str is bytes:
|
||||
w.write(test_string)
|
||||
else:
|
||||
w.write(test_string.encode('UTF-8'))
|
||||
w.write(test_string.encode('latin-1'))
|
||||
|
||||
with open(f, 'rb') as r:
|
||||
t = EpsImagePlugin.PSFile(r)
|
||||
|
@ -238,29 +212,12 @@ class TestFileEps(PillowTestCase):
|
|||
def test_readline(self):
|
||||
# check all the freaking line endings possible from the spec
|
||||
# test_string = u'something\r\nelse\n\rbaz\rbif\n'
|
||||
line_endings = ['\r\n', '\n']
|
||||
not_working_endings = ['\n\r', '\r']
|
||||
line_endings = ['\r\n', '\n', '\n\r', '\r']
|
||||
strings = ['something', 'else', 'baz', 'bif']
|
||||
|
||||
for ending in line_endings:
|
||||
s = ending.join(strings)
|
||||
# Native Python versions will pass these endings.
|
||||
# self._test_readline_stringio(s, ending)
|
||||
# self._test_readline_io(s, ending)
|
||||
# self._test_readline_file_universal(s, ending)
|
||||
|
||||
self._test_readline_file_psfile(s, ending)
|
||||
|
||||
for ending in not_working_endings:
|
||||
# these only work with the PSFile, while they're in spec,
|
||||
# they're not likely to be used
|
||||
s = ending.join(strings)
|
||||
|
||||
# Native Python versions may fail on these endings.
|
||||
# self._test_readline_stringio(s, ending)
|
||||
# self._test_readline_io(s, ending)
|
||||
# self._test_readline_file_universal(s, ending)
|
||||
|
||||
self._test_readline_io_psfile(s, ending)
|
||||
self._test_readline_file_psfile(s, ending)
|
||||
|
||||
def test_open_eps(self):
|
||||
|
|
28
Tests/test_file_gd.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL import GdImageFile
|
||||
|
||||
import io
|
||||
|
||||
TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||
|
||||
|
||||
class TestFileGd(PillowTestCase):
|
||||
|
||||
def test_sanity(self):
|
||||
im = GdImageFile.open(TEST_GD_FILE)
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
self.assertEqual(im.format, "GD")
|
||||
|
||||
def test_bad_mode(self):
|
||||
self.assertRaises(ValueError,
|
||||
GdImageFile.open, TEST_GD_FILE, 'bad mode')
|
||||
|
||||
def test_invalid_file(self):
|
||||
invalid_file = "Tests/images/flower.jpg"
|
||||
|
||||
self.assertRaises(IOError, GdImageFile.open, invalid_file)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -305,9 +305,12 @@ class TestFileGif(PillowTestCase):
|
|||
|
||||
out = self.tempfile('temp.gif')
|
||||
im = Image.new('L', (100, 100), '#000')
|
||||
im.save(out, duration=duration)
|
||||
reread = Image.open(out)
|
||||
|
||||
# Check that the argument has priority over the info settings
|
||||
im.info['duration'] = 100
|
||||
im.save(out, duration=duration)
|
||||
|
||||
reread = Image.open(out)
|
||||
self.assertEqual(reread.info['duration'], duration)
|
||||
|
||||
def test_multiple_duration(self):
|
||||
|
@ -399,7 +402,8 @@ class TestFileGif(PillowTestCase):
|
|||
|
||||
def test_comment(self):
|
||||
im = Image.open(TEST_GIF)
|
||||
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')
|
||||
im = Image.new('L', (100, 100), '#000')
|
||||
|
|
|
@ -40,11 +40,11 @@ class TestFileIcns(PillowTestCase):
|
|||
im = Image.open(TEST_FILE)
|
||||
|
||||
temp_file = self.tempfile("temp.icns")
|
||||
provided_im = Image.new('RGBA', (32, 32), (255, 0, 0, 0))
|
||||
provided_im = Image.new('RGBA', (32, 32), (255, 0, 0, 128))
|
||||
im.save(temp_file, append_images=[provided_im])
|
||||
|
||||
reread = Image.open(temp_file)
|
||||
self.assert_image_equal(reread, im)
|
||||
self.assert_image_similar(reread, im, 1)
|
||||
|
||||
reread = Image.open(temp_file)
|
||||
reread.size = (16, 16, 2)
|
||||
|
|
|
@ -41,13 +41,14 @@ class TestFileJpeg(PillowTestCase):
|
|||
def test_sanity(self):
|
||||
|
||||
# internal version number
|
||||
self.assertRegexpMatches(Image.core.jpeglib_version, r"\d+\.\d+$")
|
||||
self.assertRegex(Image.core.jpeglib_version, r"\d+\.\d+$")
|
||||
|
||||
im = Image.open(TEST_FILE)
|
||||
im.load()
|
||||
self.assertEqual(im.mode, "RGB")
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
self.assertEqual(im.format, "JPEG")
|
||||
self.assertEqual(im.get_format_mimetype(), "image/jpeg")
|
||||
|
||||
def test_app(self):
|
||||
# Test APP/COM reader (@PIL135)
|
||||
|
@ -363,7 +364,6 @@ class TestFileJpeg(PillowTestCase):
|
|||
with self.assertRaises(IOError):
|
||||
im.load()
|
||||
|
||||
|
||||
def _n_qtables_helper(self, n, test_file):
|
||||
im = Image.open(test_file)
|
||||
f = self.tempfile('temp.jpg')
|
||||
|
@ -439,17 +439,17 @@ class TestFileJpeg(PillowTestCase):
|
|||
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
|
||||
|
||||
# not a sequence
|
||||
self.assertRaises(Exception, self.roundtrip, im, qtables='a')
|
||||
self.assertRaises(ValueError, self.roundtrip, im, qtables='a')
|
||||
# sequence wrong length
|
||||
self.assertRaises(Exception, self.roundtrip, im, qtables=[])
|
||||
self.assertRaises(ValueError, self.roundtrip, im, qtables=[])
|
||||
# sequence wrong length
|
||||
self.assertRaises(Exception,
|
||||
self.assertRaises(ValueError,
|
||||
self.roundtrip, im, qtables=[1, 2, 3, 4, 5])
|
||||
|
||||
# qtable entry not a sequence
|
||||
self.assertRaises(Exception, self.roundtrip, im, qtables=[1])
|
||||
self.assertRaises(ValueError, self.roundtrip, im, qtables=[1])
|
||||
# qtable entry has wrong number of items
|
||||
self.assertRaises(Exception,
|
||||
self.assertRaises(ValueError,
|
||||
self.roundtrip, im, qtables=[[1, 2, 3, 4]])
|
||||
|
||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||
|
@ -599,7 +599,7 @@ class TestFileCloseW32(PillowTestCase):
|
|||
im = Image.open(tmpfile)
|
||||
fp = im.fp
|
||||
self.assertFalse(fp.closed)
|
||||
self.assertRaises(Exception, os.remove, tmpfile)
|
||||
self.assertRaises(WindowsError, os.remove, tmpfile)
|
||||
im.load()
|
||||
self.assertTrue(fp.closed)
|
||||
# this should not fail, as load should have closed the file.
|
||||
|
|
|
@ -31,7 +31,7 @@ class TestFileJpeg2k(PillowTestCase):
|
|||
|
||||
def test_sanity(self):
|
||||
# Internal version number
|
||||
self.assertRegexpMatches(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')
|
||||
px = im.load()
|
||||
|
@ -146,13 +146,13 @@ class TestFileJpeg2k(PillowTestCase):
|
|||
self.assertEqual(j2k.mode, 'I;16')
|
||||
self.assertEqual(jp2.mode, 'I;16')
|
||||
|
||||
def test_16bit_monchrome_jp2_like_tiff(self):
|
||||
def test_16bit_monochrome_jp2_like_tiff(self):
|
||||
|
||||
tiff_16bit = Image.open('Tests/images/16bit.cropped.tif')
|
||||
jp2 = Image.open('Tests/images/16bit.cropped.jp2')
|
||||
self.assert_image_similar(jp2, tiff_16bit, 1e-3)
|
||||
|
||||
def test_16bit_monchrome_j2k_like_tiff(self):
|
||||
def test_16bit_monochrome_j2k_like_tiff(self):
|
||||
|
||||
tiff_16bit = Image.open('Tests/images/16bit.cropped.tif')
|
||||
j2k = Image.open('Tests/images/16bit.cropped.j2k')
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import print_function
|
||||
from helper import unittest, PillowTestCase, hopper, py3
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
from PIL import features
|
||||
from PIL._util import py3
|
||||
|
||||
from ctypes import c_float
|
||||
import io
|
||||
|
@ -30,7 +31,7 @@ class LibTiffTestCase(PillowTestCase):
|
|||
|
||||
try:
|
||||
self.assertEqual(im._compression, 'group4')
|
||||
except:
|
||||
except AttributeError:
|
||||
print("No _compression")
|
||||
print(dir(im))
|
||||
|
||||
|
@ -125,7 +126,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
|
||||
im.load()
|
||||
|
||||
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_write_metadata(self):
|
||||
""" Test metadata writing through libtiff """
|
||||
|
@ -193,7 +195,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
for tag in im.tag_v2:
|
||||
try:
|
||||
del(core_items[tag])
|
||||
except:
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Type codes:
|
||||
|
@ -216,7 +218,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
if info.length == 0:
|
||||
new_ifd[tag] = tuple(values[info.type] for _ in range(3))
|
||||
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.
|
||||
del(new_ifd[338])
|
||||
|
@ -524,7 +527,7 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
f.write(src.read())
|
||||
|
||||
im = Image.open(tmpfile)
|
||||
count = im.n_frames
|
||||
im.n_frames
|
||||
im.close()
|
||||
try:
|
||||
os.remove(tmpfile) # Windows PermissionError here!
|
||||
|
@ -577,10 +580,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
|
||||
self.assertEqual(im.mode, "RGBA")
|
||||
self.assertEqual(im.size, (100, 40))
|
||||
self.assertEqual(im.tile, [('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))])
|
||||
self.assertEqual(
|
||||
im.tile,
|
||||
[('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))]
|
||||
)
|
||||
im.load()
|
||||
|
||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||
self.assert_image_equal_tofile(
|
||||
im, "Tests/images/tiff_16bit_RGBa_target.png")
|
||||
|
||||
def test_gimp_tiff(self):
|
||||
# Read TIFF JPEG images from GIMP [@PIL168]
|
||||
|
@ -606,7 +613,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
im = Image.open("Tests/images/copyleft.tiff")
|
||||
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):
|
||||
im = Image.open("Tests/images/hopper_lzw.tif")
|
||||
|
|
|
@ -20,7 +20,8 @@ class TestFilePdf(PillowTestCase):
|
|||
self.assertTrue(os.path.isfile(outfile))
|
||||
self.assertGreater(os.path.getsize(outfile), 0)
|
||||
with PdfParser.PdfParser(outfile) as pdf:
|
||||
if kwargs.get("append_images", False) or kwargs.get("append", False):
|
||||
if kwargs.get("append_images", False) or \
|
||||
kwargs.get("append", False):
|
||||
self.assertGreater(len(pdf.pages), 1)
|
||||
else:
|
||||
self.assertGreater(len(pdf.pages), 0)
|
||||
|
@ -104,9 +105,21 @@ class TestFilePdf(PillowTestCase):
|
|||
self.assertTrue(os.path.isfile(outfile))
|
||||
self.assertGreater(os.path.getsize(outfile), 0)
|
||||
|
||||
def test_multiframe_normal_save(self):
|
||||
# Test saving a multiframe image without save_all
|
||||
im = Image.open("Tests/images/dispose_bgnd.gif")
|
||||
|
||||
outfile = self.tempfile('temp.pdf')
|
||||
im.save(outfile)
|
||||
|
||||
self.assertTrue(os.path.isfile(outfile))
|
||||
self.assertGreater(os.path.getsize(outfile), 0)
|
||||
|
||||
def test_pdf_open(self):
|
||||
# fail on a buffer full of null bytes
|
||||
self.assertRaises(PdfParser.PdfFormatError, PdfParser.PdfParser, buf=bytearray(65536))
|
||||
self.assertRaises(
|
||||
PdfParser.PdfFormatError,
|
||||
PdfParser.PdfParser, buf=bytearray(65536))
|
||||
|
||||
# make an empty PDF object
|
||||
with PdfParser.PdfParser() as empty_pdf:
|
||||
|
@ -143,7 +156,10 @@ class TestFilePdf(PillowTestCase):
|
|||
im = hopper("RGB")
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
self.assertRaises(IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True)
|
||||
self.assertRaises(IOError,
|
||||
im.save,
|
||||
os.path.join(temp_dir, "nonexistent.pdf"),
|
||||
append=True)
|
||||
finally:
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
|
@ -194,7 +210,8 @@ class TestFilePdf(PillowTestCase):
|
|||
# append two images
|
||||
mode_CMYK = hopper("CMYK")
|
||||
mode_P = hopper("P")
|
||||
mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P])
|
||||
mode_CMYK.save(pdf_filename,
|
||||
append=True, save_all=True, append_images=[mode_P])
|
||||
|
||||
# open the PDF again, check pages and info again
|
||||
with PdfParser.PdfParser(pdf_filename) as pdf:
|
||||
|
@ -209,7 +226,9 @@ class TestFilePdf(PillowTestCase):
|
|||
|
||||
def test_pdf_info(self):
|
||||
# make a PDF file
|
||||
pdf_filename = self.helper_save_as_pdf("RGB", title="title", author="author", subject="subject", keywords="keywords", creator="creator", producer="producer")
|
||||
pdf_filename = self.helper_save_as_pdf(
|
||||
"RGB", title="title", author="author", subject="subject",
|
||||
keywords="keywords", creator="creator", producer="producer")
|
||||
|
||||
# open it, check pages and info
|
||||
with PdfParser.PdfParser(pdf_filename) as pdf:
|
||||
|
|
|
@ -13,6 +13,7 @@ class TestFilePixar(PillowTestCase):
|
|||
self.assertEqual(im.mode, "RGB")
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
self.assertEqual(im.format, "PIXAR")
|
||||
self.assertIsNone(im.get_format_mimetype())
|
||||
|
||||
im2 = hopper()
|
||||
self.assert_image_similar(im, im2, 4.8)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from helper import unittest, PillowTestCase, PillowLeakTestCase, hopper
|
||||
from PIL import Image, ImageFile, PngImagePlugin
|
||||
from PIL._util import py3
|
||||
|
||||
from io import BytesIO
|
||||
import zlib
|
||||
|
@ -68,8 +69,7 @@ class TestFilePng(PillowTestCase):
|
|||
def test_sanity(self):
|
||||
|
||||
# internal version number
|
||||
self.assertRegexpMatches(
|
||||
Image.core.zlib_version, r"\d+\.\d+\.\d+(\.\d+)?$")
|
||||
self.assertRegex(Image.core.zlib_version, r"\d+\.\d+\.\d+(\.\d+)?$")
|
||||
|
||||
test_file = self.tempfile("temp.png")
|
||||
|
||||
|
@ -292,15 +292,29 @@ class TestFilePng(PillowTestCase):
|
|||
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
|
||||
|
||||
def test_save_l_transparency(self):
|
||||
# There are 559 transparent pixels in l_trns.png.
|
||||
num_transparent = 559
|
||||
|
||||
in_file = "Tests/images/l_trns.png"
|
||||
im = Image.open(in_file)
|
||||
self.assertEqual(im.mode, "L")
|
||||
self.assertEqual(im.info["transparency"], 255)
|
||||
|
||||
im_rgba = im.convert('RGBA')
|
||||
self.assertEqual(
|
||||
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
|
||||
|
||||
test_file = self.tempfile("temp.png")
|
||||
im.save(test_file)
|
||||
|
||||
# There are 559 transparent pixels.
|
||||
im = im.convert('RGBA')
|
||||
self.assertEqual(im.getchannel('A').getcolors()[0][0], 559)
|
||||
test_im = Image.open(test_file)
|
||||
self.assertEqual(test_im.mode, "L")
|
||||
self.assertEqual(test_im.info["transparency"], 255)
|
||||
self.assert_image_equal(im, test_im)
|
||||
|
||||
test_im_rgba = test_im.convert('RGBA')
|
||||
self.assertEqual(
|
||||
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
|
||||
|
||||
def test_save_rgb_single_transparency(self):
|
||||
in_file = "Tests/images/caption_6_33_22.png"
|
||||
|
@ -341,7 +355,8 @@ class TestFilePng(PillowTestCase):
|
|||
broken_crc_chunk_data = chunk_data[:-1] + b'q' # break CRC
|
||||
|
||||
image_data = HEAD + broken_crc_chunk_data + TAIL
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data))
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile,
|
||||
BytesIO(image_data))
|
||||
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
try:
|
||||
|
@ -357,7 +372,8 @@ class TestFilePng(PillowTestCase):
|
|||
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
try:
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data))
|
||||
self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile,
|
||||
BytesIO(image_data))
|
||||
finally:
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||
|
||||
|
@ -420,7 +436,7 @@ class TestFilePng(PillowTestCase):
|
|||
im = roundtrip(im, pnginfo=info)
|
||||
self.assertEqual(im.info, {"Text": value})
|
||||
|
||||
if str is not bytes:
|
||||
if py3:
|
||||
rt_text(" Aa" + chr(0xa0) + chr(0xc4) + chr(0xff)) # Latin1
|
||||
rt_text(chr(0x400) + chr(0x472) + chr(0x4ff)) # Cyrillic
|
||||
rt_text(chr(0x4e00) + chr(0x66f0) + # CJK
|
||||
|
|
|
@ -12,7 +12,7 @@ class TestFileTar(PillowTestCase):
|
|||
|
||||
def setUp(self):
|
||||
if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs:
|
||||
self.skipTest("neither jpeg nor zip support not available")
|
||||
self.skipTest("neither jpeg nor zip support available")
|
||||
|
||||
def test_sanity(self):
|
||||
if "zip_decoder" in codecs:
|
||||
|
|
|
@ -1,10 +1,76 @@
|
|||
import os
|
||||
from glob import glob
|
||||
from itertools import product
|
||||
|
||||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
_TGA_DIR = os.path.join("Tests", "images", "tga")
|
||||
_TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common")
|
||||
|
||||
|
||||
class TestFileTga(PillowTestCase):
|
||||
|
||||
_MODES = ("L", "LA", "P", "RGB", "RGBA")
|
||||
_ORIGINS = ("tl", "bl")
|
||||
|
||||
_ORIGIN_TO_ORIENTATION = {
|
||||
"tl": 1,
|
||||
"bl": -1
|
||||
}
|
||||
|
||||
def test_sanity(self):
|
||||
for mode in self._MODES:
|
||||
png_paths = glob(
|
||||
os.path.join(
|
||||
_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower())))
|
||||
|
||||
for png_path in png_paths:
|
||||
reference_im = Image.open(png_path)
|
||||
self.assertEqual(reference_im.mode, mode)
|
||||
|
||||
path_no_ext = os.path.splitext(png_path)[0]
|
||||
for origin, rle in product(self._ORIGINS, (True, False)):
|
||||
tga_path = "{}_{}_{}.tga".format(
|
||||
path_no_ext, origin, "rle" if rle else "raw")
|
||||
|
||||
original_im = Image.open(tga_path)
|
||||
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)
|
||||
|
||||
# Generate a new test name every time so the
|
||||
# test will not fail with permission error
|
||||
# on Windows.
|
||||
test_file = self.tempfile("temp.tga")
|
||||
|
||||
original_im.save(test_file, rle=rle)
|
||||
saved_im = Image.open(test_file)
|
||||
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):
|
||||
# tga file with id field
|
||||
test_file = "Tests/images/tga_id_field.tga"
|
||||
|
@ -41,9 +107,6 @@ class TestFileTga(PillowTestCase):
|
|||
test_im = Image.open(test_file)
|
||||
self.assertEqual(test_im.size, (100, 100))
|
||||
|
||||
# Unsupported mode save
|
||||
self.assertRaises(IOError, lambda: im.convert("LA").save(test_file))
|
||||
|
||||
def test_save_rle(self):
|
||||
test_file = "Tests/images/rgb32rle.tga"
|
||||
im = Image.open(test_file)
|
||||
|
@ -60,8 +123,25 @@ class TestFileTga(PillowTestCase):
|
|||
test_im = Image.open(test_file)
|
||||
self.assertEqual(test_im.size, (199, 199))
|
||||
|
||||
# Unsupported mode save
|
||||
self.assertRaises(IOError, lambda: im.convert("LA").save(test_file))
|
||||
def test_save_l_transparency(self):
|
||||
# There are 559 transparent pixels in la.tga.
|
||||
num_transparent = 559
|
||||
|
||||
in_file = "Tests/images/la.tga"
|
||||
im = Image.open(in_file)
|
||||
self.assertEqual(im.mode, "LA")
|
||||
self.assertEqual(
|
||||
im.getchannel("A").getcolors()[0][0], num_transparent)
|
||||
|
||||
test_file = self.tempfile("temp.tga")
|
||||
im.save(test_file)
|
||||
|
||||
test_im = Image.open(test_file)
|
||||
self.assertEqual(test_im.mode, "LA")
|
||||
self.assertEqual(
|
||||
test_im.getchannel("A").getcolors()[0][0], num_transparent)
|
||||
|
||||
self.assert_image_equal(im, test_im)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -3,9 +3,10 @@ from io import BytesIO
|
|||
import struct
|
||||
import sys
|
||||
|
||||
from helper import unittest, PillowTestCase, hopper, py3
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image, TiffImagePlugin
|
||||
from PIL._util import py3
|
||||
from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION, RESOLUTION_UNIT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -62,8 +63,11 @@ class TestFileTiff(PillowTestCase):
|
|||
im.load()
|
||||
|
||||
def test_set_legacy_api(self):
|
||||
with self.assertRaises(Exception):
|
||||
ImageFileDirectory_v2.legacy_api = None
|
||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||
with self.assertRaises(Exception) as e:
|
||||
ifd.legacy_api = None
|
||||
self.assertEqual(str(e.exception),
|
||||
"Not allowing setting of legacy api")
|
||||
|
||||
def test_xyres_tiff(self):
|
||||
filename = "Tests/images/pil168.tif"
|
||||
|
@ -440,7 +444,8 @@ class TestFileTiff(PillowTestCase):
|
|||
for im in ims:
|
||||
yield im
|
||||
mp = io.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)
|
||||
reread = Image.open(mp)
|
||||
|
@ -501,7 +506,7 @@ class TestFileTiffW32(PillowTestCase):
|
|||
im = Image.open(tmpfile)
|
||||
fp = im.fp
|
||||
self.assertFalse(fp.closed)
|
||||
self.assertRaises(Exception, os.remove, tmpfile)
|
||||
self.assertRaises(WindowsError, os.remove, tmpfile)
|
||||
im.load()
|
||||
self.assertTrue(fp.closed)
|
||||
|
||||
|
|
23
Tests/test_file_wal.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from helper import unittest, PillowTestCase
|
||||
|
||||
from PIL import WalImageFile
|
||||
|
||||
|
||||
class TestFileWal(PillowTestCase):
|
||||
|
||||
def test_open(self):
|
||||
# Arrange
|
||||
TEST_FILE = "Tests/images/hopper.wal"
|
||||
|
||||
# Act
|
||||
im = WalImageFile.open(TEST_FILE)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(im.format, "WAL")
|
||||
self.assertEqual(im.format_description, "Quake2 Texture")
|
||||
self.assertEqual(im.mode, "P")
|
||||
self.assertEqual(im.size, (128, 128))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -83,7 +83,8 @@ class TestFileWebpAnimation(PillowTestCase):
|
|||
|
||||
temp_file1 = self.tempfile("temp.webp")
|
||||
frame1.copy().save(temp_file1,
|
||||
save_all=True, append_images=[frame2], lossless=True)
|
||||
save_all=True, append_images=[frame2],
|
||||
lossless=True)
|
||||
check(temp_file1)
|
||||
|
||||
# Tests appending using a generator
|
||||
|
@ -92,7 +93,8 @@ class TestFileWebpAnimation(PillowTestCase):
|
|||
yield im
|
||||
temp_file2 = self.tempfile("temp_generator.webp")
|
||||
frame1.copy().save(temp_file2,
|
||||
save_all=True, append_images=imGenerator([frame2]), lossless=True)
|
||||
save_all=True, append_images=imGenerator([frame2]),
|
||||
lossless=True)
|
||||
check(temp_file2)
|
||||
|
||||
def test_timestamp_and_duration(self):
|
||||
|
|
|
@ -16,7 +16,8 @@ class TestTTypeFontLeak(PillowLeakTestCase):
|
|||
self._test_leak(lambda: draw.text((0, 0), "some text "*1024, # ~10k
|
||||
font=font, fill="black"))
|
||||
|
||||
@unittest.skipIf(not features.check('freetype2'), "Test requires freetype2")
|
||||
@unittest.skipIf(not features.check('freetype2'),
|
||||
"Test requires freetype2")
|
||||
def test_leak(self):
|
||||
ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20)
|
||||
self._test_font(ttype)
|
||||
|
|
|
@ -2,6 +2,7 @@ from helper import unittest, PillowTestCase
|
|||
|
||||
from PIL import Image, FontFile, PcfFontFile
|
||||
from PIL import ImageFont, ImageDraw
|
||||
from PIL._util import py3
|
||||
|
||||
codecs = dir(Image.core)
|
||||
|
||||
|
@ -20,7 +21,7 @@ class TestFontPcf(PillowTestCase):
|
|||
with open(fontname, "rb") as test_file:
|
||||
font = PcfFontFile.PcfFontFile(test_file)
|
||||
self.assertIsInstance(font, FontFile.FontFile)
|
||||
#check the number of characters in the font
|
||||
# check the number of characters in the font
|
||||
self.assertEqual(len([_f for _f in font.glyph if _f]), 223)
|
||||
|
||||
tempname = self.tempfile("temp.pil")
|
||||
|
@ -66,7 +67,7 @@ class TestFontPcf(PillowTestCase):
|
|||
def _test_high_characters(self, message):
|
||||
tempname = self.save_font()
|
||||
font = ImageFont.load(tempname)
|
||||
im = Image.new("L", (750, 30) , "white")
|
||||
im = Image.new("L", (750, 30), "white")
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), message, "black", font=font)
|
||||
with Image.open('Tests/images/high_ascii_chars.png') as target:
|
||||
|
@ -76,7 +77,7 @@ class TestFontPcf(PillowTestCase):
|
|||
message = "".join(chr(i+1) for i in range(140, 232))
|
||||
self._test_high_characters(message)
|
||||
# accept bytes instances in Py3.
|
||||
if bytes is not str:
|
||||
if py3:
|
||||
self._test_high_characters(message.encode('latin1'))
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
|
||||
import colorsys
|
||||
import itertools
|
||||
|
@ -57,10 +58,10 @@ class TestFormatHSV(PillowTestCase):
|
|||
|
||||
(r, g, b) = im.split()
|
||||
|
||||
if bytes is str:
|
||||
conv_func = self.str_to_float
|
||||
else:
|
||||
if py3:
|
||||
conv_func = self.int_to_float
|
||||
else:
|
||||
conv_func = self.str_to_float
|
||||
|
||||
if hasattr(itertools, 'izip'):
|
||||
iter_helper = itertools.izip
|
||||
|
@ -72,11 +73,11 @@ class TestFormatHSV(PillowTestCase):
|
|||
for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(),
|
||||
b.tobytes())]
|
||||
|
||||
if str is bytes:
|
||||
new_bytes = b''.join(chr(h)+chr(s)+chr(v) for (
|
||||
if py3:
|
||||
new_bytes = b''.join(bytes(chr(h)+chr(s)+chr(v), 'latin-1') for (
|
||||
h, s, v) in converted)
|
||||
else:
|
||||
new_bytes = b''.join(bytes(chr(h)+chr(s)+chr(v), 'latin-1') for (
|
||||
new_bytes = b''.join(chr(h)+chr(s)+chr(v) for (
|
||||
h, s, v) in converted)
|
||||
|
||||
hsv = Image.frombytes(mode, r.size, new_bytes)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
from PIL._util import py3
|
||||
import os
|
||||
|
||||
|
||||
|
@ -60,12 +61,12 @@ class TestImage(PillowTestCase):
|
|||
self.assertEqual(im.height, 4)
|
||||
|
||||
def test_invalid_image(self):
|
||||
if str is bytes:
|
||||
import StringIO
|
||||
im = StringIO.StringIO('')
|
||||
else:
|
||||
if py3:
|
||||
import io
|
||||
im = io.BytesIO(b'')
|
||||
else:
|
||||
import StringIO
|
||||
im = StringIO.StringIO('')
|
||||
self.assertRaises(IOError, Image.open, im)
|
||||
|
||||
def test_bad_mode(self):
|
||||
|
@ -112,7 +113,6 @@ class TestImage(PillowTestCase):
|
|||
self.assertRaises(ValueError, im.save, temp_file)
|
||||
|
||||
def test_internals(self):
|
||||
|
||||
im = Image.new("L", (100, 100))
|
||||
im.readonly = 1
|
||||
im._copy()
|
||||
|
@ -122,8 +122,15 @@ class TestImage(PillowTestCase):
|
|||
im.paste(0, (0, 0, 100, 100))
|
||||
self.assertFalse(im.readonly)
|
||||
|
||||
test_file = self.tempfile("temp.ppm")
|
||||
im._dump(test_file)
|
||||
def test_dump(self):
|
||||
im = Image.new("L", (10, 10))
|
||||
im._dump(self.tempfile("temp_L.ppm"))
|
||||
|
||||
im = Image.new("RGB", (10, 10))
|
||||
im._dump(self.tempfile("temp_RGB.ppm"))
|
||||
|
||||
im = Image.new("HSV", (10, 10))
|
||||
self.assertRaises(ValueError, im._dump, self.tempfile("temp_HSV.ppm"))
|
||||
|
||||
def test_comparison_with_other_type(self):
|
||||
# Arrange
|
||||
|
|
|
@ -294,8 +294,9 @@ int main(int argc, char* argv[])
|
|||
compiler = ccompiler.new_compiler()
|
||||
compiler.add_include_dir(sysconfig.get_python_inc())
|
||||
|
||||
libdir = sysconfig.get_config_var('LIBDIR') or sysconfig.get_python_inc().replace('include', 'libs')
|
||||
print (libdir)
|
||||
libdir = (sysconfig.get_config_var('LIBDIR') or
|
||||
sysconfig.get_python_inc().replace('include', 'libs'))
|
||||
print(libdir)
|
||||
compiler.add_library_dir(libdir)
|
||||
objects = compiler.compile(['embed_pil.c'])
|
||||
compiler.link_executable(objects, 'embed_pil')
|
||||
|
|
|
@ -15,9 +15,11 @@ class TestImageArray(PillowTestCase):
|
|||
self.assertEqual(test("L"), (3, (100, 128), '|u1', 12800))
|
||||
|
||||
# FIXME: wrong?
|
||||
self.assertEqual(test("I"), (3, (100, 128), Image._ENDIAN + 'i4', 51200))
|
||||
self.assertEqual(test("I"), (3, (100, 128),
|
||||
Image._ENDIAN + 'i4', 51200))
|
||||
# FIXME: wrong?
|
||||
self.assertEqual(test("F"), (3, (100, 128), Image._ENDIAN + 'f4', 51200))
|
||||
self.assertEqual(test("F"), (3, (100, 128),
|
||||
Image._ENDIAN + 'f4', 51200))
|
||||
|
||||
self.assertEqual(test("LA"), (3, (100, 128, 2), '|u1', 25600))
|
||||
self.assertEqual(test("RGB"), (3, (100, 128, 3), '|u1', 38400))
|
||||
|
|
|
@ -67,13 +67,13 @@ class TestImageConvert(PillowTestCase):
|
|||
|
||||
f = self.tempfile('temp.png')
|
||||
|
||||
l = im.convert('L')
|
||||
self.assertEqual(l.info['transparency'], 0) # undone
|
||||
l.save(f)
|
||||
im_l = im.convert('L')
|
||||
self.assertEqual(im_l.info['transparency'], 0) # undone
|
||||
im_l.save(f)
|
||||
|
||||
rgb = im.convert('RGB')
|
||||
self.assertEqual(rgb.info['transparency'], (0, 0, 0)) # undone
|
||||
rgb.save(f)
|
||||
im_rgb = im.convert('RGB')
|
||||
self.assertEqual(im_rgb.info['transparency'], (0, 0, 0)) # undone
|
||||
im_rgb.save(f)
|
||||
|
||||
# ref https://github.com/python-pillow/Pillow/issues/664
|
||||
|
||||
|
@ -83,12 +83,12 @@ class TestImageConvert(PillowTestCase):
|
|||
im.info['transparency'] = 128
|
||||
|
||||
# Act
|
||||
rgba = im.convert('RGBA')
|
||||
im_rgba = im.convert('RGBA')
|
||||
|
||||
# Assert
|
||||
self.assertNotIn('transparency', rgba.info)
|
||||
self.assertNotIn('transparency', im_rgba.info)
|
||||
# https://github.com/python-pillow/Pillow/issues/2702
|
||||
self.assertEqual(rgba.palette, None)
|
||||
self.assertEqual(im_rgba.palette, None)
|
||||
|
||||
def test_trns_l(self):
|
||||
im = hopper('L')
|
||||
|
@ -96,19 +96,20 @@ class TestImageConvert(PillowTestCase):
|
|||
|
||||
f = self.tempfile('temp.png')
|
||||
|
||||
rgb = im.convert('RGB')
|
||||
self.assertEqual(rgb.info['transparency'], (128, 128, 128)) # undone
|
||||
rgb.save(f)
|
||||
im_rgb = im.convert('RGB')
|
||||
self.assertEqual(im_rgb.info['transparency'],
|
||||
(128, 128, 128)) # undone
|
||||
im_rgb.save(f)
|
||||
|
||||
p = im.convert('P')
|
||||
self.assertIn('transparency', p.info)
|
||||
p.save(f)
|
||||
im_p = im.convert('P')
|
||||
self.assertIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
p = self.assert_warning(
|
||||
im_p = self.assert_warning(
|
||||
UserWarning,
|
||||
im.convert, 'P', palette=Image.ADAPTIVE)
|
||||
self.assertNotIn('transparency', p.info)
|
||||
p.save(f)
|
||||
self.assertNotIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
def test_trns_RGB(self):
|
||||
im = hopper('RGB')
|
||||
|
@ -116,23 +117,35 @@ class TestImageConvert(PillowTestCase):
|
|||
|
||||
f = self.tempfile('temp.png')
|
||||
|
||||
l = im.convert('L')
|
||||
self.assertEqual(l.info['transparency'], l.getpixel((0, 0))) # undone
|
||||
l.save(f)
|
||||
im_l = im.convert('L')
|
||||
self.assertEqual(im_l.info['transparency'],
|
||||
im_l.getpixel((0, 0))) # undone
|
||||
im_l.save(f)
|
||||
|
||||
p = im.convert('P')
|
||||
self.assertIn('transparency', p.info)
|
||||
p.save(f)
|
||||
im_p = im.convert('P')
|
||||
self.assertIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
p = im.convert('RGBA')
|
||||
self.assertNotIn('transparency', p.info)
|
||||
p.save(f)
|
||||
im_rgba = im.convert('RGBA')
|
||||
self.assertNotIn('transparency', im_rgba.info)
|
||||
im_rgba.save(f)
|
||||
|
||||
p = self.assert_warning(
|
||||
im_p = self.assert_warning(
|
||||
UserWarning,
|
||||
im.convert, 'P', palette=Image.ADAPTIVE)
|
||||
self.assertNotIn('transparency', p.info)
|
||||
p.save(f)
|
||||
self.assertNotIn('transparency', im_p.info)
|
||||
im_p.save(f)
|
||||
|
||||
def test_gif_with_rgba_palette_to_p(self):
|
||||
# See https://github.com/python-pillow/Pillow/issues/2433
|
||||
im = Image.open('Tests/images/hopper.gif')
|
||||
im.info['transparency'] = 255
|
||||
im.load()
|
||||
self.assertEqual(im.palette.mode, 'RGBA')
|
||||
im_p = im.convert('P')
|
||||
|
||||
# Should not raise ValueError: unrecognized raw mode
|
||||
im_p.load()
|
||||
|
||||
def test_p_la(self):
|
||||
im = hopper('RGBA')
|
||||
|
@ -191,7 +204,8 @@ class TestImageConvert(PillowTestCase):
|
|||
if converted_im.mode == 'RGB':
|
||||
self.assert_image_similar(converted_im, target, 3)
|
||||
else:
|
||||
self.assert_image_similar(converted_im, target.getchannel(0), 1)
|
||||
self.assert_image_similar(converted_im,
|
||||
target.getchannel(0), 1)
|
||||
|
||||
matrix_convert('RGB')
|
||||
matrix_convert('L')
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from helper import unittest, PillowTestCase, hopper, py3
|
||||
from helper import unittest, PillowTestCase, hopper
|
||||
from PIL._util import py3
|
||||
|
||||
|
||||
class TestImageGetIm(PillowTestCase):
|
||||
|
|
|
@ -39,7 +39,7 @@ class TestImageQuantize(PillowTestCase):
|
|||
def test_rgba_quantize(self):
|
||||
image = hopper('RGBA')
|
||||
image.quantize()
|
||||
self.assertRaises(Exception, image.quantize, method=0)
|
||||
self.assertRaises(ValueError, image.quantize, method=0)
|
||||
|
||||
def test_quantize(self):
|
||||
image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB')
|
||||
|
|
|
@ -245,8 +245,8 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
for y in range(i.size[1]):
|
||||
used_colors = {px[x, y][0] for x in range(i.size[0])}
|
||||
self.assertEqual(256, len(used_colors),
|
||||
'All colors should present in resized image. '
|
||||
'Only {} on {} line.'.format(len(used_colors), y))
|
||||
'All colors should present in resized image. '
|
||||
'Only {} on {} line.'.format(len(used_colors), y))
|
||||
|
||||
@unittest.skip("current implementation isn't precise enough")
|
||||
def test_levels_rgba(self):
|
||||
|
@ -288,10 +288,14 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
|
|||
def test_dirty_pixels_rgba(self):
|
||||
case = self.make_dirty_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BOX), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BILINEAR),
|
||||
(255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.HAMMING),
|
||||
(255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.BICUBIC),
|
||||
(255, 255, 0))
|
||||
self.run_dirty_case(case.resize((20, 20), Image.LANCZOS),
|
||||
(255, 255, 0))
|
||||
|
||||
def test_dirty_pixels_la(self):
|
||||
case = self.make_dirty_case('LA', (255, 128), (0, 0))
|
||||
|
@ -367,40 +371,45 @@ class CoreResampleCoefficientsTest(PillowTestCase):
|
|||
im = Image.new('RGBA', (1280, 1280), (0x20, 0x40, 0x60, 0xff))
|
||||
histogram = im.resize((256, 256), Image.BICUBIC).histogram()
|
||||
|
||||
self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000) # first channel
|
||||
self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000) # second channel
|
||||
self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000) # third channel
|
||||
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) # fourth channel
|
||||
# first channel
|
||||
self.assertEqual(histogram[0x100 * 0 + 0x20], 0x10000)
|
||||
# second channel
|
||||
self.assertEqual(histogram[0x100 * 1 + 0x40], 0x10000)
|
||||
# third channel
|
||||
self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000)
|
||||
# fourth channel
|
||||
self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000)
|
||||
|
||||
|
||||
class CoreResampleBoxTest(PillowTestCase):
|
||||
def test_wrong_arguments(self):
|
||||
im = hopper()
|
||||
for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS):
|
||||
for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS):
|
||||
im.resize((32, 32), resample, (0, 0, im.width, im.height))
|
||||
im.resize((32, 32), resample, (20, 20, im.width, im.height))
|
||||
im.resize((32, 32), resample, (20, 20, 20, 100))
|
||||
im.resize((32, 32), resample, (20, 20, 100, 20))
|
||||
|
||||
with self.assertRaisesRegexp(TypeError, "must be sequence of length 4"):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
"must be sequence of length 4"):
|
||||
im.resize((32, 32), resample, (im.width, im.height))
|
||||
|
||||
with self.assertRaisesRegexp(ValueError, "can't be negative"):
|
||||
with self.assertRaisesRegex(ValueError, "can't be negative"):
|
||||
im.resize((32, 32), resample, (-20, 20, 100, 100))
|
||||
with self.assertRaisesRegexp(ValueError, "can't be negative"):
|
||||
with self.assertRaisesRegex(ValueError, "can't be negative"):
|
||||
im.resize((32, 32), resample, (20, -20, 100, 100))
|
||||
|
||||
with self.assertRaisesRegexp(ValueError, "can't be empty"):
|
||||
with self.assertRaisesRegex(ValueError, "can't be empty"):
|
||||
im.resize((32, 32), resample, (20.1, 20, 20, 100))
|
||||
with self.assertRaisesRegexp(ValueError, "can't be empty"):
|
||||
with self.assertRaisesRegex(ValueError, "can't be empty"):
|
||||
im.resize((32, 32), resample, (20, 20.1, 100, 20))
|
||||
with self.assertRaisesRegexp(ValueError, "can't be empty"):
|
||||
with self.assertRaisesRegex(ValueError, "can't be empty"):
|
||||
im.resize((32, 32), resample, (20.1, 20.1, 20, 20))
|
||||
|
||||
with self.assertRaisesRegexp(ValueError, "can't exceed"):
|
||||
with self.assertRaisesRegex(ValueError, "can't exceed"):
|
||||
im.resize((32, 32), resample, (0, 0, im.width + 1, im.height))
|
||||
with self.assertRaisesRegexp(ValueError, "can't exceed"):
|
||||
with self.assertRaisesRegex(ValueError, "can't exceed"):
|
||||
im.resize((32, 32), resample, (0, 0, im.width, im.height + 1))
|
||||
|
||||
def resize_tiled(self, im, dst_size, xtiles, ytiles):
|
||||
|
@ -447,7 +456,7 @@ class CoreResampleBoxTest(PillowTestCase):
|
|||
|
||||
# error with box should be much smaller than without
|
||||
self.assert_image_similar(reference, with_box, 6)
|
||||
with self.assertRaisesRegexp(AssertionError, "difference 29\."):
|
||||
with self.assertRaisesRegex(AssertionError, "difference 29\."):
|
||||
self.assert_image_similar(reference, without_box, 5)
|
||||
|
||||
def test_formats(self):
|
||||
|
@ -490,7 +499,7 @@ class CoreResampleBoxTest(PillowTestCase):
|
|||
try:
|
||||
res = im.resize(size, Image.LANCZOS, box)
|
||||
self.assertEqual(res.size, size)
|
||||
with self.assertRaisesRegexp(AssertionError, "difference \d"):
|
||||
with self.assertRaisesRegex(AssertionError, "difference \d"):
|
||||
# check that the difference at least that much
|
||||
self.assert_image_similar(res, im.crop(box), 20)
|
||||
except AssertionError:
|
||||
|
|
|
@ -39,15 +39,15 @@ class TestImagingCoreResize(PillowTestCase):
|
|||
self.assertEqual(r.im.bands, im.im.bands)
|
||||
|
||||
def test_reduce_filters(self):
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
r = self.resize(hopper("RGB"), (15, 12), f)
|
||||
self.assertEqual(r.mode, "RGB")
|
||||
self.assertEqual(r.size, (15, 12))
|
||||
|
||||
def test_enlarge_filters(self):
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
r = self.resize(hopper("RGB"), (212, 195), f)
|
||||
self.assertEqual(r.mode, "RGB")
|
||||
self.assertEqual(r.size, (212, 195))
|
||||
|
@ -66,8 +66,8 @@ class TestImagingCoreResize(PillowTestCase):
|
|||
}
|
||||
samples['dirty'].putpixel((1, 1), 128)
|
||||
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
# samples resized with current filter
|
||||
references = {
|
||||
name: self.resize(ch, (4, 4), f)
|
||||
|
@ -90,8 +90,8 @@ class TestImagingCoreResize(PillowTestCase):
|
|||
self.assert_image_equal(ch, references[channels[i]])
|
||||
|
||||
def test_enlarge_zero(self):
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,
|
||||
Image.BICUBIC, Image.LANCZOS]:
|
||||
for f in [Image.NEAREST, Image.BOX, Image.BILINEAR,
|
||||
Image.HAMMING, Image.BICUBIC, Image.LANCZOS]:
|
||||
r = self.resize(Image.new('RGB', (0, 0), "white"), (212, 195), f)
|
||||
self.assertEqual(r.mode, "RGB")
|
||||
self.assertEqual(r.size, (212, 195))
|
||||
|
|
|
@ -95,8 +95,34 @@ class TestImageRotate(PillowTestCase):
|
|||
im = hopper()
|
||||
self.rotate(im, im.mode, 45, center=(0, 0))
|
||||
self.rotate(im, im.mode, 45, translate=(im.size[0]/2, 0))
|
||||
self.rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0]/2, 0))
|
||||
self.rotate(im, im.mode, 45, center=(0, 0),
|
||||
translate=(im.size[0]/2, 0))
|
||||
|
||||
def test_rotate_no_fill(self):
|
||||
im = Image.new('RGB', (100, 100), 'green')
|
||||
target = Image.open('Tests/images/rotate_45_no_fill.png')
|
||||
im = im.rotate(45)
|
||||
self.assert_image_equal(im, target)
|
||||
|
||||
def test_rotate_with_fill(self):
|
||||
im = Image.new('RGB', (100, 100), 'green')
|
||||
target = Image.open('Tests/images/rotate_45_with_fill.png')
|
||||
im = im.rotate(45, fillcolor='white')
|
||||
self.assert_image_equal(im, target)
|
||||
|
||||
def test_alpha_rotate_no_fill(self):
|
||||
# Alpha images are handled differently internally
|
||||
im = Image.new('RGBA', (10, 10), 'green')
|
||||
im = im.rotate(45, expand=1)
|
||||
corner = im.getpixel((0,0))
|
||||
self.assertEqual(corner, (0, 0, 0, 0))
|
||||
|
||||
def test_alpha_rotate_with_fill(self):
|
||||
# Alpha images are handled differently internally
|
||||
im = Image.new('RGBA', (10, 10), 'green')
|
||||
im = im.rotate(45, expand=1, fillcolor=(255, 0, 0, 255))
|
||||
corner = im.getpixel((0,0))
|
||||
self.assertEqual(corner, (255, 0, 0, 255))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -53,15 +53,20 @@ class TestImageTransform(PillowTestCase):
|
|||
self.assert_image_equal(transformed, scaled)
|
||||
|
||||
def test_fill(self):
|
||||
im = hopper('RGB')
|
||||
(w, h) = im.size
|
||||
transformed = im.transform(im.size, Image.EXTENT,
|
||||
(0, 0,
|
||||
w*2, h*2),
|
||||
Image.BILINEAR,
|
||||
fillcolor = 'red')
|
||||
for mode, pixel in [
|
||||
['RGB', (255, 0, 0)],
|
||||
['RGBA', (255, 0, 0, 255)],
|
||||
['LA', (76, 0)]
|
||||
]:
|
||||
im = hopper(mode)
|
||||
(w, h) = im.size
|
||||
transformed = im.transform(im.size, Image.EXTENT,
|
||||
(0, 0,
|
||||
w*2, h*2),
|
||||
Image.BILINEAR,
|
||||
fillcolor='red')
|
||||
|
||||
self.assertEqual(transformed.getpixel((w-1, h-1)), (255, 0, 0))
|
||||
self.assertEqual(transformed.getpixel((w-1, h-1)), pixel)
|
||||
|
||||
def test_mesh(self):
|
||||
# this should be a checkerboard of halfsized hoppers in ul, lr
|
||||
|
|
|
@ -43,7 +43,7 @@ class TestImageCms(PillowTestCase):
|
|||
self.assertEqual(list(map(type, v)), [str, str, str, str])
|
||||
|
||||
# internal version number
|
||||
self.assertRegexpMatches(ImageCms.core.littlecms_version, r"\d+\.\d+$")
|
||||
self.assertRegex(ImageCms.core.littlecms_version, r"\d+\.\d+$")
|
||||
|
||||
self.skip_missing()
|
||||
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)
|
||||
|
@ -196,13 +196,13 @@ class TestImageCms(PillowTestCase):
|
|||
# not a linear luminance map. so L != 128:
|
||||
self.assertEqual(k, (137, 128, 128))
|
||||
|
||||
l = i_lab.getdata(0)
|
||||
a = i_lab.getdata(1)
|
||||
b = i_lab.getdata(2)
|
||||
l_data = i_lab.getdata(0)
|
||||
a_data = i_lab.getdata(1)
|
||||
b_data = i_lab.getdata(2)
|
||||
|
||||
self.assertEqual(list(l), [137] * 100)
|
||||
self.assertEqual(list(a), [128] * 100)
|
||||
self.assertEqual(list(b), [128] * 100)
|
||||
self.assertEqual(list(l_data), [137] * 100)
|
||||
self.assertEqual(list(a_data), [128] * 100)
|
||||
self.assertEqual(list(b_data), [128] * 100)
|
||||
|
||||
def test_lab_color(self):
|
||||
psRGB = ImageCms.createProfile("sRGB")
|
||||
|
@ -286,53 +286,104 @@ class TestImageCms(PillowTestCase):
|
|||
self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2))
|
||||
|
||||
self.assertEqual(p.attributes, 4294967296)
|
||||
assert_truncated_tuple_equal(p.blue_colorant, ((0.14306640625, 0.06060791015625, 0.7140960693359375), (0.1558847490315394, 0.06603820639433387, 0.06060791015625)))
|
||||
assert_truncated_tuple_equal(p.blue_primary, ((0.14306641366715667, 0.06060790921083026, 0.7140960805782015), (0.15588475410450106, 0.06603820408959558, 0.06060790921083026)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.blue_colorant,
|
||||
((0.14306640625, 0.06060791015625, 0.7140960693359375),
|
||||
(0.1558847490315394, 0.06603820639433387, 0.06060791015625)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.blue_primary,
|
||||
((0.14306641366715667, 0.06060790921083026, 0.7140960805782015),
|
||||
(0.15588475410450106, 0.06603820408959558, 0.06060790921083026)))
|
||||
assert_truncated_tuple_equal(p.chromatic_adaptation, (((1.04791259765625, 0.0229339599609375, -0.050201416015625), (0.02960205078125, 0.9904632568359375, -0.0170745849609375), (-0.009246826171875, 0.0150604248046875, 0.7517852783203125)), ((1.0267159024652783, 0.022470062342089134, 0.0229339599609375), (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875))))
|
||||
self.assertIsNone(p.chromaticity)
|
||||
self.assertEqual(p.clut, {0: (False, False, True), 1: (False, False, True), 2: (False, False, True), 3: (False, False, True)})
|
||||
self.assertEqual(p.clut, {
|
||||
0: (False, False, True),
|
||||
1: (False, False, True),
|
||||
2: (False, False, True),
|
||||
3: (False, False, True)
|
||||
})
|
||||
self.assertEqual(p.color_space, 'RGB')
|
||||
self.assertIsNone(p.colorant_table)
|
||||
self.assertIsNone(p.colorant_table_out)
|
||||
self.assertIsNone(p.colorimetric_intent)
|
||||
self.assertEqual(p.connection_space, 'XYZ ')
|
||||
self.assertEqual(p.copyright, 'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.creation_date, datetime.datetime(2009, 2, 27, 21, 36, 31))
|
||||
self.assertEqual(p.copyright,
|
||||
'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.creation_date,
|
||||
datetime.datetime(2009, 2, 27, 21, 36, 31))
|
||||
self.assertEqual(p.device_class, 'mntr')
|
||||
assert_truncated_tuple_equal(p.green_colorant, ((0.3851470947265625, 0.7168731689453125, 0.097076416015625), (0.32119769927720654, 0.5978443449048152, 0.7168731689453125)))
|
||||
assert_truncated_tuple_equal(p.green_primary, ((0.3851470888162112, 0.7168731974161346, 0.09707641738998518), (0.32119768793686687, 0.5978443567149709, 0.7168731974161346)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.green_colorant,
|
||||
((0.3851470947265625, 0.7168731689453125, 0.097076416015625),
|
||||
(0.32119769927720654, 0.5978443449048152, 0.7168731689453125)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.green_primary,
|
||||
((0.3851470888162112, 0.7168731974161346, 0.09707641738998518),
|
||||
(0.32119768793686687, 0.5978443567149709, 0.7168731974161346)))
|
||||
self.assertEqual(p.header_flags, 0)
|
||||
self.assertEqual(p.header_manufacturer, '\x00\x00\x00\x00')
|
||||
self.assertEqual(p.header_model, '\x00\x00\x00\x00')
|
||||
self.assertEqual(p.icc_measurement_condition, {'backing': (0.0, 0.0, 0.0), 'flare': 0.0, 'geo': 'unknown', 'observer': 1, 'illuminant_type': 'D65'})
|
||||
self.assertEqual(p.icc_measurement_condition, {
|
||||
'backing': (0.0, 0.0, 0.0),
|
||||
'flare': 0.0,
|
||||
'geo': 'unknown',
|
||||
'observer': 1,
|
||||
'illuminant_type': 'D65'
|
||||
})
|
||||
self.assertEqual(p.icc_version, 33554432)
|
||||
self.assertIsNone(p.icc_viewing_condition)
|
||||
self.assertEqual(p.intent_supported, {0: (True, True, True), 1: (True, True, True), 2: (True, True, True), 3: (True, True, True)})
|
||||
self.assertEqual(p.intent_supported, {
|
||||
0: (True, True, True),
|
||||
1: (True, True, True),
|
||||
2: (True, True, True),
|
||||
3: (True, True, True)
|
||||
})
|
||||
self.assertTrue(p.is_matrix_shaper)
|
||||
self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0)))
|
||||
self.assertIsNone(p.manufacturer)
|
||||
assert_truncated_tuple_equal(p.media_black_point, ((0.012054443359375, 0.0124969482421875, 0.01031494140625), (0.34573304157549234, 0.35842450765864337, 0.0124969482421875)))
|
||||
assert_truncated_tuple_equal(p.media_white_point, ((0.964202880859375, 1.0, 0.8249053955078125), (0.3457029219802284, 0.3585375327567059, 1.0)))
|
||||
assert_truncated_tuple_equal((p.media_white_point_temperature,), (5000.722328847392,))
|
||||
self.assertEqual(p.model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
assert_truncated_tuple_equal(
|
||||
p.media_black_point,
|
||||
((0.012054443359375, 0.0124969482421875, 0.01031494140625),
|
||||
(0.34573304157549234, 0.35842450765864337, 0.0124969482421875)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.media_white_point,
|
||||
((0.964202880859375, 1.0, 0.8249053955078125),
|
||||
(0.3457029219802284, 0.3585375327567059, 1.0)))
|
||||
assert_truncated_tuple_equal(
|
||||
(p.media_white_point_temperature,),
|
||||
(5000.722328847392,))
|
||||
self.assertEqual(p.model,
|
||||
'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(p.pcs, 'XYZ')
|
||||
self.assertIsNone(p.perceptual_rendering_intent_gamut)
|
||||
self.assertEqual(p.product_copyright, 'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.product_copyright,
|
||||
'Copyright International Color Consortium, 2009')
|
||||
self.assertEqual(p.product_desc, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_description,
|
||||
'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.product_manufacturer, '')
|
||||
self.assertEqual(p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(p.profile_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r')
|
||||
assert_truncated_tuple_equal(p.red_colorant, ((0.436065673828125, 0.2224884033203125, 0.013916015625), (0.6484536316398539, 0.3308524880306778, 0.2224884033203125)))
|
||||
assert_truncated_tuple_equal(p.red_primary, ((0.43606566581047446, 0.22248840582960838, 0.013916015621759925), (0.6484536250319214, 0.3308524944738204, 0.22248840582960838)))
|
||||
self.assertEqual(
|
||||
p.product_model, 'IEC 61966-2-1 Default RGB Colour Space - sRGB')
|
||||
self.assertEqual(
|
||||
p.profile_description, 'sRGB IEC61966-2-1 black scaled')
|
||||
self.assertEqual(
|
||||
p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r')
|
||||
assert_truncated_tuple_equal(
|
||||
p.red_colorant,
|
||||
((0.436065673828125, 0.2224884033203125, 0.013916015625),
|
||||
(0.6484536316398539, 0.3308524880306778, 0.2224884033203125)))
|
||||
assert_truncated_tuple_equal(
|
||||
p.red_primary,
|
||||
((0.43606566581047446, 0.22248840582960838, 0.013916015621759925),
|
||||
(0.6484536250319214, 0.3308524944738204, 0.22248840582960838)))
|
||||
self.assertEqual(p.rendering_intent, 0)
|
||||
self.assertIsNone(p.saturation_rendering_intent_gamut)
|
||||
self.assertIsNone(p.screening_description)
|
||||
self.assertIsNone(p.target)
|
||||
self.assertEqual(p.technology, 'CRT ')
|
||||
self.assertEqual(p.version, 2.0)
|
||||
self.assertEqual(p.viewing_condition, 'Reference Viewing Condition in IEC 61966-2-1')
|
||||
self.assertEqual(p.viewing_condition,
|
||||
'Reference Viewing Condition in IEC 61966-2-1')
|
||||
self.assertEqual(p.xcolor_space, 'RGB ')
|
||||
|
||||
def test_profile_typesafety(self):
|
||||
|
@ -346,7 +397,8 @@ class TestImageCms(PillowTestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
ImageCms.ImageCmsProfile(1).tobytes()
|
||||
|
||||
def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel):
|
||||
def assert_aux_channel_preserved(self, mode,
|
||||
transform_in_place, preserved_channel):
|
||||
def create_test_image():
|
||||
# set up test image with something interesting in the tested aux
|
||||
# channel.
|
||||
|
@ -379,29 +431,35 @@ class TestImageCms(PillowTestCase):
|
|||
# create some transform, it doesn't matter which one
|
||||
source_profile = ImageCms.createProfile("sRGB")
|
||||
destination_profile = ImageCms.createProfile("sRGB")
|
||||
t = ImageCms.buildTransform(source_profile, destination_profile, inMode=mode, outMode=mode)
|
||||
t = ImageCms.buildTransform(
|
||||
source_profile, destination_profile, inMode=mode, outMode=mode)
|
||||
|
||||
# apply transform
|
||||
if transform_in_place:
|
||||
ImageCms.applyTransform(source_image, t, inPlace=True)
|
||||
result_image = source_image
|
||||
else:
|
||||
result_image = ImageCms.applyTransform(source_image, t, inPlace=False)
|
||||
result_image = ImageCms.applyTransform(
|
||||
source_image, t, inPlace=False)
|
||||
result_image_aux = result_image.getchannel(preserved_channel)
|
||||
|
||||
self.assert_image_equal(source_image_aux, result_image_aux)
|
||||
|
||||
def test_preserve_auxiliary_channels_rgba(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=False, preserved_channel='A')
|
||||
self.assert_aux_channel_preserved(mode='RGBA',
|
||||
transform_in_place=False, preserved_channel='A')
|
||||
|
||||
def test_preserve_auxiliary_channels_rgba_in_place(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBA', transform_in_place=True, preserved_channel='A')
|
||||
self.assert_aux_channel_preserved(mode='RGBA',
|
||||
transform_in_place=True, preserved_channel='A')
|
||||
|
||||
def test_preserve_auxiliary_channels_rgbx(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=False, preserved_channel='X')
|
||||
self.assert_aux_channel_preserved(mode='RGBX',
|
||||
transform_in_place=False, preserved_channel='X')
|
||||
|
||||
def test_preserve_auxiliary_channels_rgbx_in_place(self):
|
||||
self.assert_aux_channel_preserved(mode='RGBX', transform_in_place=True, preserved_channel='X')
|
||||
self.assert_aux_channel_preserved(mode='RGBX',
|
||||
transform_in_place=True, preserved_channel='X')
|
||||
|
||||
def test_auxiliary_channels_isolated(self):
|
||||
# test data in aux channels does not affect non-aux channels
|
||||
|
@ -422,20 +480,29 @@ class TestImageCms(PillowTestCase):
|
|||
source_profile = ImageCms.createProfile(src_format[1])
|
||||
destination_profile = ImageCms.createProfile(dst_format[1])
|
||||
source_image = src_format[3]
|
||||
test_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[0], outMode=dst_format[0])
|
||||
test_transform = ImageCms.buildTransform(
|
||||
source_profile, destination_profile,
|
||||
inMode=src_format[0], outMode=dst_format[0])
|
||||
|
||||
# test conversion from aux-ful source
|
||||
if transform_in_place:
|
||||
test_image = source_image.copy()
|
||||
ImageCms.applyTransform(test_image, test_transform, inPlace=True)
|
||||
ImageCms.applyTransform(
|
||||
test_image, test_transform, inPlace=True)
|
||||
else:
|
||||
test_image = ImageCms.applyTransform(source_image, test_transform, inPlace=False)
|
||||
test_image = ImageCms.applyTransform(
|
||||
source_image, test_transform, inPlace=False)
|
||||
|
||||
# reference conversion from aux-less source
|
||||
reference_transform = ImageCms.buildTransform(source_profile, destination_profile, inMode=src_format[2], outMode=dst_format[2])
|
||||
reference_image = ImageCms.applyTransform(source_image.convert(src_format[2]), reference_transform)
|
||||
reference_transform = ImageCms.buildTransform(
|
||||
source_profile, destination_profile,
|
||||
inMode=src_format[2], outMode=dst_format[2])
|
||||
reference_image = ImageCms.applyTransform(
|
||||
source_image.convert(src_format[2]),
|
||||
reference_transform)
|
||||
|
||||
self.assert_image_equal(test_image.convert(dst_format[2]), reference_image)
|
||||
self.assert_image_equal(test_image.convert(dst_format[2]),
|
||||
reference_image)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -31,7 +31,8 @@ class TestImageColor(PillowTestCase):
|
|||
|
||||
# case insensitivity
|
||||
self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def"))
|
||||
self.assertEqual(ImageColor.getrgb("#CDEF"), ImageColor.getrgb("#cdef"))
|
||||
self.assertEqual(ImageColor.getrgb("#CDEF"),
|
||||
ImageColor.getrgb("#cdef"))
|
||||
self.assertEqual(ImageColor.getrgb("#DEFDEF"),
|
||||
ImageColor.getrgb("#defdef"))
|
||||
self.assertEqual(ImageColor.getrgb("#CDEFCDEF"),
|
||||
|
@ -78,6 +79,26 @@ class TestImageColor(PillowTestCase):
|
|||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(360,100%,50%)"))
|
||||
self.assertEqual((0, 255, 255), ImageColor.getrgb("hsl(180,100%,50%)"))
|
||||
|
||||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(0,100%,100%)"))
|
||||
self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360,100%,100%)"))
|
||||
self.assertEqual((0, 255, 255),
|
||||
ImageColor.getrgb("hsv(180,100%,100%)"))
|
||||
|
||||
# alternate format
|
||||
self.assertEqual(ImageColor.getrgb("hsb(0,100%,50%)"),
|
||||
ImageColor.getrgb("hsv(0,100%,50%)"))
|
||||
|
||||
# floats
|
||||
self.assertEqual((254, 3, 3),
|
||||
ImageColor.getrgb("hsl(0.1,99.2%,50.3%)"))
|
||||
self.assertEqual((255, 0, 0),
|
||||
ImageColor.getrgb("hsl(360.,100.0%,50%)"))
|
||||
|
||||
self.assertEqual((253, 2, 2),
|
||||
ImageColor.getrgb("hsv(0.1,99.2%,99.3%)"))
|
||||
self.assertEqual((255, 0, 0),
|
||||
ImageColor.getrgb("hsv(360.,100.0%,100%)"))
|
||||
|
||||
# case insensitivity
|
||||
self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"),
|
||||
ImageColor.getrgb("rgb(255,0,0)"))
|
||||
|
@ -87,6 +108,10 @@ class TestImageColor(PillowTestCase):
|
|||
ImageColor.getrgb("rgba(255,0,0,0)"))
|
||||
self.assertEqual(ImageColor.getrgb("HSL(0,100%,50%)"),
|
||||
ImageColor.getrgb("hsl(0,100%,50%)"))
|
||||
self.assertEqual(ImageColor.getrgb("HSV(0,100%,50%)"),
|
||||
ImageColor.getrgb("hsv(0,100%,50%)"))
|
||||
self.assertEqual(ImageColor.getrgb("HSB(0,100%,50%)"),
|
||||
ImageColor.getrgb("hsb(0,100%,50%)"))
|
||||
|
||||
# space agnosticism
|
||||
self.assertEqual((255, 0, 0),
|
||||
|
@ -97,6 +122,8 @@ class TestImageColor(PillowTestCase):
|
|||
ImageColor.getrgb("rgba( 255 , 0 , 0 , 0 )"))
|
||||
self.assertEqual((255, 0, 0),
|
||||
ImageColor.getrgb("hsl( 0 , 100% , 50% )"))
|
||||
self.assertEqual((255, 0, 0),
|
||||
ImageColor.getrgb("hsv( 0 , 100% , 100% )"))
|
||||
|
||||
# wrong number of components
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "rgb(255,0)")
|
||||
|
@ -116,6 +143,12 @@ class TestImageColor(PillowTestCase):
|
|||
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0,100,50%)")
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "hsl(0,100%,50)")
|
||||
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "hsv(0,100%)")
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "hsv(0,100%,0%,0%)")
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "hsv(0%,100%,50%)")
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "hsv(0,100,50%)")
|
||||
self.assertRaises(ValueError, ImageColor.getrgb, "hsv(0,100%,50)")
|
||||
|
||||
# look for rounding errors (based on code by Tim Hatch)
|
||||
def test_rounding_errors(self):
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
from helper import unittest, PillowTestCase, hopper
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageColor
|
||||
from PIL import ImageDraw
|
||||
import os.path
|
||||
|
||||
import sys
|
||||
from helper import PillowTestCase, hopper, unittest
|
||||
from PIL import Image, ImageColor, ImageDraw
|
||||
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
|
@ -173,6 +169,16 @@ class TestImageDraw(PillowTestCase):
|
|||
self.assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1)
|
||||
|
||||
def test_ellipse_symmetric(self):
|
||||
for bbox in [
|
||||
(25, 25, 76, 76),
|
||||
(25, 25, 75, 75)
|
||||
]:
|
||||
im = Image.new("RGB", (101, 101))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.ellipse(bbox, fill="green", outline="blue")
|
||||
self.assert_image_equal(im, im.transpose(Image.FLIP_LEFT_RIGHT))
|
||||
|
||||
def helper_line(self, points):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
|
@ -360,8 +366,6 @@ class TestImageDraw(PillowTestCase):
|
|||
ImageDraw.floodfill(im, (W, H), red)
|
||||
self.assert_image_equal(im, im_floodfill)
|
||||
|
||||
@unittest.skipIf(hasattr(sys, 'pypy_version_info'),
|
||||
"Causes fatal RPython error on PyPy")
|
||||
def test_floodfill_border(self):
|
||||
# floodfill() is experimental
|
||||
|
||||
|
@ -572,6 +576,47 @@ class TestImageDraw(PillowTestCase):
|
|||
draw.textsize("\n")
|
||||
draw.textsize("test\n")
|
||||
|
||||
def test_same_color_outline(self):
|
||||
# Prepare shape
|
||||
x0, y0 = 5, 5
|
||||
x1, y1 = 5, 50
|
||||
x2, y2 = 95, 50
|
||||
x3, y3 = 95, 5
|
||||
|
||||
s = ImageDraw.Outline()
|
||||
s.move(x0, y0)
|
||||
s.curve(x1, y1, x2, y2, x3, y3)
|
||||
s.line(x0, y0)
|
||||
|
||||
# Begin
|
||||
for mode in ["RGB", "L"]:
|
||||
for fill, outline in [
|
||||
["red", None],
|
||||
["red", "red"],
|
||||
["red", "#f00"]
|
||||
]:
|
||||
for operation, args in {
|
||||
'chord':[BBOX1, 0, 180],
|
||||
'ellipse':[BBOX1],
|
||||
'shape':[s],
|
||||
'pieslice':[BBOX1, -90, 45],
|
||||
'polygon':[[(18, 30), (85, 30), (60, 72)]],
|
||||
'rectangle':[BBOX1]
|
||||
}.items():
|
||||
# Arrange
|
||||
im = Image.new(mode, (W, H))
|
||||
draw = ImageDraw.Draw(im)
|
||||
|
||||
# Act
|
||||
draw_method = getattr(draw, operation)
|
||||
args += [fill, outline]
|
||||
draw_method(*args)
|
||||
|
||||
# Assert
|
||||
expected = ("Tests/images/imagedraw_outline"
|
||||
"_{}_{}.png".format(operation, mode))
|
||||
self.assert_image_similar(im, Image.open(expected), 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
229
Tests/test_imagedraw2.py
Normal file
|
@ -0,0 +1,229 @@
|
|||
import os.path
|
||||
|
||||
from helper import PillowTestCase, hopper, unittest
|
||||
from PIL import Image, ImageDraw2, features
|
||||
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
GRAY = (190, 190, 190)
|
||||
DEFAULT_MODE = "RGB"
|
||||
IMAGES_PATH = os.path.join("Tests", "images", "imagedraw")
|
||||
|
||||
# Image size
|
||||
W, H = 100, 100
|
||||
|
||||
# Bounding box points
|
||||
X0 = int(W / 4)
|
||||
X1 = int(X0 * 3)
|
||||
Y0 = int(H / 4)
|
||||
Y1 = int(X0 * 3)
|
||||
|
||||
# Two kinds of bounding box
|
||||
BBOX1 = [(X0, Y0), (X1, Y1)]
|
||||
BBOX2 = [X0, Y0, X1, Y1]
|
||||
|
||||
# Two kinds of coordinate sequences
|
||||
POINTS1 = [(10, 10), (20, 40), (30, 30)]
|
||||
POINTS2 = [10, 10, 20, 40, 30, 30]
|
||||
|
||||
KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)]
|
||||
|
||||
HAS_FREETYPE = features.check("freetype2")
|
||||
FONT_PATH = "Tests/fonts/FreeMono.ttf"
|
||||
|
||||
|
||||
class TestImageDraw(PillowTestCase):
|
||||
|
||||
def test_sanity(self):
|
||||
im = hopper("RGB").copy()
|
||||
|
||||
draw = ImageDraw2.Draw(im)
|
||||
pen = ImageDraw2.Pen("blue", width=7)
|
||||
draw.line(list(range(10)), pen)
|
||||
|
||||
from PIL import ImageDraw
|
||||
|
||||
draw, handler = ImageDraw.getdraw(im)
|
||||
pen = ImageDraw2.Pen("blue", width=7)
|
||||
draw.line(list(range(10)), pen)
|
||||
|
||||
def helper_ellipse(self, mode, bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
pen = ImageDraw2.Pen("blue", width=2)
|
||||
brush = ImageDraw2.Brush("green")
|
||||
expected = "Tests/images/imagedraw_ellipse_{}.png".format(mode)
|
||||
|
||||
# Act
|
||||
draw.ellipse(bbox, pen, brush)
|
||||
|
||||
# Assert
|
||||
self.assert_image_similar(im, Image.open(expected), 1)
|
||||
|
||||
def test_ellipse1(self):
|
||||
self.helper_ellipse("RGB", BBOX1)
|
||||
|
||||
def test_ellipse2(self):
|
||||
self.helper_ellipse("RGB", BBOX2)
|
||||
|
||||
def test_ellipse_edge(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
brush = ImageDraw2.Brush("white")
|
||||
|
||||
# Act
|
||||
draw.ellipse(((0, 0), (W-1, H)), brush)
|
||||
|
||||
# Assert
|
||||
self.assert_image_similar(
|
||||
im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1)
|
||||
|
||||
def helper_line(self, points):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
pen = ImageDraw2.Pen("yellow", width=2)
|
||||
|
||||
# Act
|
||||
draw.line(points, pen)
|
||||
|
||||
# Assert
|
||||
self.assert_image_equal(
|
||||
im, Image.open("Tests/images/imagedraw_line.png"))
|
||||
|
||||
def test_line1_pen(self):
|
||||
self.helper_line(POINTS1)
|
||||
|
||||
def test_line2_pen(self):
|
||||
self.helper_line(POINTS2)
|
||||
|
||||
def test_line_pen_as_brush(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
pen = None
|
||||
brush = ImageDraw2.Pen("yellow", width=2)
|
||||
|
||||
# Act
|
||||
# Pass in the pen as the brush parameter
|
||||
draw.line(POINTS1, pen, brush)
|
||||
|
||||
# Assert
|
||||
self.assert_image_equal(
|
||||
im, Image.open("Tests/images/imagedraw_line.png"))
|
||||
|
||||
def helper_polygon(self, points):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
pen = ImageDraw2.Pen("blue", width=2)
|
||||
brush = ImageDraw2.Brush("red")
|
||||
|
||||
# Act
|
||||
draw.polygon(points, pen, brush)
|
||||
|
||||
# Assert
|
||||
self.assert_image_equal(
|
||||
im, Image.open("Tests/images/imagedraw_polygon.png"))
|
||||
|
||||
def test_polygon1(self):
|
||||
self.helper_polygon(POINTS1)
|
||||
|
||||
def test_polygon2(self):
|
||||
self.helper_polygon(POINTS2)
|
||||
|
||||
def helper_rectangle(self, bbox):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
pen = ImageDraw2.Pen("green", width=2)
|
||||
brush = ImageDraw2.Brush("black")
|
||||
|
||||
# Act
|
||||
draw.rectangle(bbox, pen, brush)
|
||||
|
||||
# Assert
|
||||
self.assert_image_equal(
|
||||
im, Image.open("Tests/images/imagedraw_rectangle.png"))
|
||||
|
||||
def test_rectangle1(self):
|
||||
self.helper_rectangle(BBOX1)
|
||||
|
||||
def test_rectangle2(self):
|
||||
self.helper_rectangle(BBOX2)
|
||||
|
||||
def test_big_rectangle(self):
|
||||
# Test drawing a rectangle bigger than the image
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
bbox = [(-1, -1), (W + 1, H + 1)]
|
||||
brush = ImageDraw2.Brush("orange")
|
||||
draw = ImageDraw2.Draw(im)
|
||||
expected = "Tests/images/imagedraw_big_rectangle.png"
|
||||
|
||||
# Act
|
||||
draw.rectangle(bbox, brush)
|
||||
|
||||
# Assert
|
||||
self.assert_image_similar(im, Image.open(expected), 1)
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_text(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
font = ImageDraw2.Font("white", FONT_PATH)
|
||||
expected = "Tests/images/imagedraw2_text.png"
|
||||
|
||||
# Act
|
||||
draw.text((5, 5), "ImageDraw2", font)
|
||||
|
||||
# Assert
|
||||
self.assert_image_similar(im, Image.open(expected), 13)
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_textsize(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
font = ImageDraw2.Font("white", FONT_PATH)
|
||||
|
||||
# Act
|
||||
size = draw.textsize("ImageDraw2", font)
|
||||
|
||||
# Assert
|
||||
self.assertEqual(size[1], 12)
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_textsize_empty_string(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
font = ImageDraw2.Font("white", FONT_PATH)
|
||||
|
||||
# Act
|
||||
# Should not cause 'SystemError: <built-in method getsize of
|
||||
# ImagingFont object at 0x...> returned NULL without setting an error'
|
||||
draw.textsize("", font)
|
||||
draw.textsize("\n", font)
|
||||
draw.textsize("test\n", font)
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
def test_flush(self):
|
||||
# Arrange
|
||||
im = Image.new("RGB", (W, H))
|
||||
draw = ImageDraw2.Draw(im)
|
||||
font = ImageDraw2.Font("white", FONT_PATH)
|
||||
|
||||
# Act
|
||||
draw.text((5, 5), "ImageDraw2", font)
|
||||
im2 = draw.flush()
|
||||
|
||||
# Assert
|
||||
self.assert_image_equal(im, im2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -45,8 +45,9 @@ class TestImageEnhance(PillowTestCase):
|
|||
|
||||
for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']:
|
||||
for amount in [0, 0.5, 1.0]:
|
||||
self._check_alpha(getattr(ImageEnhance, op)(original).enhance(amount),
|
||||
original, op, amount)
|
||||
self._check_alpha(
|
||||
getattr(ImageEnhance, op)(original).enhance(amount),
|
||||
original, op, amount)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -215,14 +215,25 @@ class TestPyDecoder(PillowTestCase):
|
|||
buf = BytesIO(b'\x00'*255)
|
||||
|
||||
im = MockImageFile(buf)
|
||||
im.tile = [("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)]
|
||||
im.tile = [
|
||||
("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None)
|
||||
]
|
||||
d = self.get_decoder()
|
||||
|
||||
self.assertRaises(ValueError, im.load)
|
||||
|
||||
im.tile = [("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)]
|
||||
im.tile = [
|
||||
("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None)
|
||||
]
|
||||
self.assertRaises(ValueError, im.load)
|
||||
|
||||
def test_no_format(self):
|
||||
buf = BytesIO(b'\x00'*255)
|
||||
|
||||
im = MockImageFile(buf)
|
||||
self.assertIsNone(im.format)
|
||||
self.assertIsNone(im.get_format_mimetype())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -42,7 +42,7 @@ class SimplePatcher(object):
|
|||
delattr(self._parent_obj, self._attr_name)
|
||||
|
||||
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not Available")
|
||||
@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available")
|
||||
class TestImageFont(PillowTestCase):
|
||||
LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC
|
||||
|
||||
|
@ -67,16 +67,18 @@ class TestImageFont(PillowTestCase):
|
|||
}
|
||||
|
||||
def setUp(self):
|
||||
freetype_version = tuple(ImageFont.core.freetype2_version.split('.'))[:2]
|
||||
self.metrics = self.METRICS.get(freetype_version, self.METRICS['Default'])
|
||||
freetype_version = tuple(
|
||||
ImageFont.core.freetype2_version.split('.')
|
||||
)[:2]
|
||||
self.metrics = self.METRICS.get(freetype_version,
|
||||
self.METRICS['Default'])
|
||||
|
||||
def get_font(self):
|
||||
return ImageFont.truetype(FONT_PATH, FONT_SIZE,
|
||||
layout_engine=self.LAYOUT_ENGINE)
|
||||
|
||||
def test_sanity(self):
|
||||
self.assertRegexpMatches(
|
||||
ImageFont.core.freetype2_version, r"\d+\.\d+\.\d+$")
|
||||
self.assertRegex(ImageFont.core.freetype2_version, r"\d+\.\d+\.\d+$")
|
||||
|
||||
def test_font_properties(self):
|
||||
ttf = self.get_font()
|
||||
|
@ -203,7 +205,8 @@ class TestImageFont(PillowTestCase):
|
|||
target_img = Image.open(target)
|
||||
|
||||
# Epsilon ~.5 fails with FreeType 2.7
|
||||
self.assert_image_similar(im, target_img, self.metrics['multiline'])
|
||||
self.assert_image_similar(im, target_img,
|
||||
self.metrics['multiline'])
|
||||
|
||||
def test_unknown_align(self):
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
|
@ -211,10 +214,9 @@ class TestImageFont(PillowTestCase):
|
|||
ttf = self.get_font()
|
||||
|
||||
# Act/Assert
|
||||
self.assertRaises(AssertionError,
|
||||
draw.multiline_text, (0, 0), TEST_TEXT,
|
||||
font=ttf,
|
||||
align="unknown")
|
||||
self.assertRaises(
|
||||
AssertionError,
|
||||
draw.multiline_text, (0, 0), TEST_TEXT, font=ttf, align="unknown")
|
||||
|
||||
def test_draw_align(self):
|
||||
im = Image.new('RGB', (300, 100), 'white')
|
||||
|
@ -232,6 +234,11 @@ class TestImageFont(PillowTestCase):
|
|||
self.assertEqual(draw.textsize(TEST_TEXT, font=ttf),
|
||||
draw.multiline_textsize(TEST_TEXT, font=ttf))
|
||||
|
||||
# Test that multiline_textsize corresponds to ImageFont.textsize()
|
||||
# for single line text
|
||||
self.assertEqual(ttf.getsize('A'),
|
||||
draw.multiline_textsize('A', font=ttf))
|
||||
|
||||
# Test that textsize() can pass on additional arguments
|
||||
# to multiline_textsize()
|
||||
draw.textsize(TEST_TEXT, font=ttf, spacing=4)
|
||||
|
@ -409,7 +416,7 @@ class TestImageFont(PillowTestCase):
|
|||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
target = im.copy()
|
||||
draw = ImageDraw.Draw(im)
|
||||
#should not crash here.
|
||||
# should not crash here.
|
||||
draw.text((10, 10), '', font=font)
|
||||
self.assert_image_equal(im, target)
|
||||
|
||||
|
@ -424,7 +431,8 @@ class TestImageFont(PillowTestCase):
|
|||
# Make a copy of FreeTypeFont so we can patch the original
|
||||
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
|
||||
with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font):
|
||||
def loadable_font(filepath, size, index, encoding, *args, **kwargs):
|
||||
def loadable_font(filepath, size, index, encoding,
|
||||
*args, **kwargs):
|
||||
if filepath == path_to_fake:
|
||||
return ImageFont._FreeTypeFont(FONT_PATH, size, index,
|
||||
encoding, *args, **kwargs)
|
||||
|
@ -510,6 +518,12 @@ class TestImageFont(PillowTestCase):
|
|||
self.assertEqual(t.getsize('M'), self.metrics['getters'])
|
||||
self.assertEqual(t.getsize('y'), (12, 20))
|
||||
self.assertEqual(t.getsize('a'), (12, 16))
|
||||
self.assertEqual(t.getsize_multiline('A'), (12, 16))
|
||||
self.assertEqual(t.getsize_multiline('AB'), (24, 16))
|
||||
self.assertEqual(t.getsize_multiline('a'), (12, 16))
|
||||
self.assertEqual(t.getsize_multiline('ABC\n'), (36, 36))
|
||||
self.assertEqual(t.getsize_multiline('ABC\nA'), (36, 36))
|
||||
self.assertEqual(t.getsize_multiline('ABC\nAaaa'), (48, 36))
|
||||
|
||||
|
||||
@unittest.skipUnless(HAS_RAQM, "Raqm not Available")
|
||||
|
|
|
@ -19,7 +19,8 @@ class TestImageFontBitmap(PillowTestCase):
|
|||
font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24)
|
||||
size_outline = font_outline.getsize(text)
|
||||
size_bitmap = font_bitmap.getsize(text)
|
||||
size_final = max(size_outline[0], size_bitmap[0]), max(size_outline[1], size_bitmap[1])
|
||||
size_final = (max(size_outline[0], size_bitmap[0]),
|
||||
max(size_outline[1], size_bitmap[1]))
|
||||
im_bitmap = Image.new('RGB', size_final, (255, 255, 255))
|
||||
im_outline = im_bitmap.copy()
|
||||
draw_bitmap = ImageDraw.Draw(im_bitmap)
|
||||
|
|