Merge branch 'master' into gitignore-review

This commit is contained in:
Andrew Murray 2018-08-25 10:27:18 +10:00 committed by GitHub
commit 1e68afbada
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
222 changed files with 4314 additions and 1476 deletions

View File

@ -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

View File

@ -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="

View File

@ -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

View File

@ -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

View File

@ -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]
@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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/

View File

@ -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')))

View File

@ -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')))

View File

@ -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):

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

BIN
Tests/images/hopper.gd Normal file

Binary file not shown.

BIN
Tests/images/hopper.wal Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

BIN
Tests/images/la.tga Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

View 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"

View File

@ -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
View 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()

View File

@ -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
View 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()

View File

@ -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')

View File

@ -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)

View File

@ -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.

View 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')

View File

@ -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")

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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__':

View File

@ -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
View 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()

View File

@ -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):

View File

@ -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)

View File

@ -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'))

View File

@ -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)

View File

@ -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

View File

@ -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')

View File

@ -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))

View File

@ -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')

View File

@ -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):

View File

@ -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')

View File

@ -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:

View File

@ -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))

View File

@ -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()

View File

@ -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

View File

@ -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__':

View File

@ -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):

View File

@ -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
View 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()

View File

@ -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__':

View File

@ -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()

View File

@ -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")

View File

@ -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)

Some files were not shown because too many files have changed in this diff Show More