Merge pull request #11 from python-pillow/master

Merge with master
This commit is contained in:
Hugo 2015-03-26 12:34:50 +02:00
commit ea72529a21
42 changed files with 488 additions and 161 deletions

View File

@ -54,7 +54,6 @@ after_success:
- travis_retry pip install coveralls-merge - travis_retry pip install coveralls-merge
- coveralls-merge coverage.c.json - coveralls-merge coverage.c.json
- travis_retry pip install pep8 pyflakes - travis_retry pip install pep8 pyflakes
- pep8 --statistics --count PIL/*.py - pep8 --statistics --count PIL/*.py
- pep8 --statistics --count Tests/*.py - pep8 --statistics --count Tests/*.py
@ -62,10 +61,53 @@ after_success:
- pyflakes PIL/*.py | tee >(wc -l) - pyflakes PIL/*.py | tee >(wc -l)
- pyflakes Tests/*.py | tee >(wc -l) - pyflakes Tests/*.py | tee >(wc -l)
# Coverage and quality reports on just the latest diff. # Coverage and quality reports on just the latest diff.
# (Installation is very slow on Py3, so just do it for Py2.) # (Installation is very slow on Py3, so just do it for Py2.)
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-install.sh; fi - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-install.sh; fi
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/diffcover-run.sh; fi - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then Scripts/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 succeded! Triggering OS X build..."
# Trigger an OS X build at the pillow-wheels repo
./build_children.sh
else
echo "Some jobs failed"
fi
fi
fi
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
matrix: matrix:
fast_finish: true fast_finish: true
env:
global:
# travis encrypt AUTH_TOKEN=
secure: "Vzm7aG1Qv0SDQcqiPzZMedNLn5ZmpL7IzF0DYnqcD+/l+zmKU22SnJBcX0uVXumo+r7eZfpsShpqfcdsZvMlvmQnwz+Y6AGKQru9tCKZbTMnuRjWKKXekC+tr8Xt9CKvRVtte5PyXW31paxUI3/e+fQGBwoFjEEC+6EpEOjeRfE="

View File

@ -1,13 +1,55 @@
Changelog (Pillow) Changelog (Pillow)
================== ==================
2.8.0 (unreleased)
------------------
- Improve reference docs for PIL.ImageDraw.Draw.pieslice() #1145
[audreyr]
- Added copy method font_variant() and accessible properties to truetype() #1123
[radarhere]
- Fix ImagingEffectNoise #1128
[hugovk]
- Remove unreachable code
[hugovk]
- Let Python do the endian stuff + tests #1121
[amoibos, radarhere]
- Fix webp decode memory leak #1114
[benoit-pierre]
- Fast path for opaque pixels in RGBa unpacker #1088
[bgilbert]
- Enable basic support for 'RGBa' raw encoding/decoding #1096
[immerrr]
- Fix pickling L mode images with no palette, #1095
[hugovk]
- iPython display hook #1091
[wiredfool]
- Adjust buffer size when quality=keep, fixes #148 (again)
[wiredfool]
- Fix for corrupted bitmaps embedded in truetype fonts. #1072
[jackyyf, wiredfool]
2.7.0 (2015-01-01) 2.7.0 (2015-01-01)
------------------ ------------------
- Split Sane into a separate repo: https://github.com/python-pillow/Sane
[hugovk]
- Look for OSX and Linux fonts in common places. #1054 - Look for OSX and Linux fonts in common places. #1054
[charleslaw] [charleslaw]
- Fix potential PNG decompression DOS #1060 - Fix CVE-2014-9601, potential PNG decompression DOS #1060
[wiredfool] [wiredfool]
- Use underscores, not spaces, in TIFF tag kwargs. #1044, #1058 - Use underscores, not spaces, in TIFF tag kwargs. #1044, #1058
@ -22,13 +64,13 @@ Changelog (Pillow)
- Fix MSVC compiler error: Use Py_ssize_t instead of ssize_t #1051 - Fix MSVC compiler error: Use Py_ssize_t instead of ssize_t #1051
[cgohlke] [cgohlke]
- Fix compiler error: MSVC needs varaibles defined at the start of the block #1048 - Fix compiler error: MSVC needs variables defined at the start of the block #1048
[cgohlke] [cgohlke]
- The GIF Palette optimization algorithm is only applicable to mode='P' or 'L' #993 - The GIF Palette optimization algorithm is only applicable to mode='P' or 'L' #993
[moriyoshi] [moriyoshi]
- Use PySide as an alernative to PyQt4/5. - Use PySide as an alternative to PyQt4/5.
[holg] [holg]
- Replace affine-based im.resize implementation with convolution-based im.stretch #997 - Replace affine-based im.resize implementation with convolution-based im.stretch #997
@ -82,7 +124,7 @@ Changelog (Pillow)
2.6.2 (2015-01-01) 2.6.2 (2015-01-01)
------------------ ------------------
- Fix potential PNG decompression DOS #1060 - Fix CVE-2014-9601, potential PNG decompression DOS #1060
[wiredfool] [wiredfool]
- Fix Regression in PyPy 2.4 in streamio #958 - Fix Regression in PyPy 2.4 in streamio #958
@ -1757,7 +1799,7 @@ Changelog (Pillow)
(1.1.2c1 and 1.1.2 final released) (1.1.2c1 and 1.1.2 final released)
+ Adapted to Python 2.1. Among other things, all uses of the + Adapted to Python 2.1. Among other things, all uses of the
"regex" module has been repleased with "re". "regex" module have been replaced with "re".
+ Fixed attribute error when reading large PNG files (this bug + Fixed attribute error when reading large PNG files (this bug
was introduced in maintenance code released after the 1.1.1 was introduced in maintenance code released after the 1.1.1
@ -2381,7 +2423,7 @@ Changelog (Pillow)
the default value is 75. the default value is 75.
JPEG smooth smooth dithered images. value JPEG smooth smooth dithered images. value
is strengh (1-100). default is is strength (1-100). default is
off (0). off (0).
PNG optimize minimize output file at the PNG optimize minimize output file at the

View File

@ -2,6 +2,7 @@ include *.c
include *.h include *.h
include *.md include *.md
include *.py include *.py
include *.sh
include *.rst include *.rst
include *.txt include *.txt
include *.yaml include *.yaml

View File

@ -273,7 +273,7 @@ class IcoImageFile(ImageFile.ImageFile):
self.size = im.size self.size = im.size
def load_seek(self): def load_seek(self):
# Flage the ImageFile.Parser so that it # Flag the ImageFile.Parser so that it
# just does all the decode at the end. # just does all the decode at the end.
pass pass
# #

View File

@ -598,6 +598,16 @@ class Image:
id(self) id(self)
) )
def _repr_png_(self):
""" iPython display hook support
:returns: png version of the image as bytes
"""
from io import BytesIO
b = BytesIO()
self.save(b, 'PNG')
return b.getvalue()
def __getattr__(self, name): def __getattr__(self, name):
if name == "__array_interface__": if name == "__array_interface__":
# numpy array interface support # numpy array interface support
@ -625,7 +635,7 @@ class Image:
self.mode = mode self.mode = mode
self.size = size self.size = size
self.im = core.new(mode, size) self.im = core.new(mode, size)
if mode in ("L", "P"): if mode in ("L", "P") and palette:
self.putpalette(palette) self.putpalette(palette)
self.frombytes(data) self.frombytes(data)
@ -2024,7 +2034,7 @@ def frombytes(mode, size, data, decoder_name="raw", *args):
You can also use any pixel decoder supported by PIL. For more You can also use any pixel decoder supported by PIL. For more
information on available decoders, see the section information on available decoders, see the section
**Writing Your Own File Decoder**. :ref:`Writing Your Own File Decoder <file-decoders>`.
Note that this function decodes pixel data only, not entire images. Note that this function decodes pixel data only, not entire images.
If you have an entire image in a string, wrap it in a If you have an entire image in a string, wrap it in a

View File

@ -133,6 +133,11 @@ class FreeTypeFont:
DeprecationWarning) DeprecationWarning)
font = file font = file
self.path = font
self.size = size
self.index = index
self.encoding = encoding
if isPath(font): if isPath(font):
self.font = core.getfont(font, size, index, encoding) self.font = core.getfont(font, size, index, encoding)
else: else:
@ -162,6 +167,22 @@ class FreeTypeFont:
self.font.render(text, im.id, mode == "1") self.font.render(text, im.id, mode == "1")
return im, offset return im, offset
def font_variant(self, font=None, size=None, index=None, encoding=None):
"""
Create a copy of this FreeTypeFont object,
using any specified arguments to override the settings.
Parameters are identical to the parameters used to initialize this
object, minus the deprecated 'file' argument.
:return: A FreeTypeFont object.
"""
return FreeTypeFont(font=self.path if font is None else font,
size=self.size if size is None else size,
index=self.index if index is None else index,
encoding=self.encoding if encoding is None else
encoding)
## ##
# Wrapper that creates a transposed font from any existing font # Wrapper that creates a transposed font from any existing font
# object. # object.
@ -214,7 +235,7 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None):
This function requires the _imagingft service. This function requires the _imagingft service.
:param filename: A truetype font file. Under Windows, if the file :param font: A truetype font file. Under Windows, if the file
is not found in this filename, the loader also looks in is not found in this filename, the loader also looks in
Windows :file:`fonts/` directory. Windows :file:`fonts/` directory.
:param size: The requested size, in points. :param size: The requested size, in points.
@ -224,6 +245,7 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None):
Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert),
and "armn" (Apple Roman). See the FreeType documentation and "armn" (Apple Roman). See the FreeType documentation
for more information. for more information.
:param filename: Deprecated. Please use font instead.
:return: A font object. :return: A font object.
:exception IOError: If the file could not be read. :exception IOError: If the file could not be read.
""" """
@ -254,8 +276,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None):
elif sys.platform in ('linux', 'linux2'): elif sys.platform in ('linux', 'linux2'):
lindirs = os.environ.get("XDG_DATA_DIRS", "") lindirs = os.environ.get("XDG_DATA_DIRS", "")
if not lindirs: if not lindirs:
#According to the freedesktop spec, XDG_DATA_DIRS should # According to the freedesktop spec, XDG_DATA_DIRS should
#default to /usr/share # default to /usr/share
lindirs = '/usr/share' lindirs = '/usr/share'
lindirs = lindirs.split(":") lindirs = lindirs.split(":")
for lindir in lindirs: for lindir in lindirs:
@ -265,7 +287,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None):
filepath = os.path.join(walkroot, ttf_filename) filepath = os.path.join(walkroot, ttf_filename)
return FreeTypeFont(filepath, size, index, encoding) return FreeTypeFont(filepath, size, index, encoding)
elif sys.platform == 'darwin': elif sys.platform == 'darwin':
macdirs = ['/Library/Fonts/', '/System/Library/Fonts/', os.path.expanduser('~/Library/Fonts/')] macdirs = ['/Library/Fonts/', '/System/Library/Fonts/',
os.path.expanduser('~/Library/Fonts/')]
for macdir in macdirs: for macdir in macdirs:
filepath = os.path.join(macdir, ttf_filename) filepath = os.path.join(macdir, ttf_filename)
if os.path.exists(filepath): if os.path.exists(filepath):

View File

@ -684,7 +684,8 @@ def _save(im, fp, filename):
# https://github.com/jdriscoll/django-imagekit/issues/50 # https://github.com/jdriscoll/django-imagekit/issues/50
bufsize = 0 bufsize = 0
if "optimize" in info or "progressive" in info or "progression" in info: if "optimize" in info or "progressive" in info or "progression" in info:
if quality >= 95: # keep sets quality to 0, but the actual value may be high.
if quality >= 95 or quality == 0:
bufsize = 2 * im.size[0] * im.size[1] bufsize = 2 * im.size[0] * im.size[1]
else: else:
bufsize = im.size[0] * im.size[1] bufsize = im.size[0] * im.size[1]

View File

@ -175,7 +175,7 @@ class iTXt(str):
:param lang: language code :param lang: language code
:param tkey: UTF-8 version of the key name :param tkey: UTF-8 version of the key name
""" """
self = str.__new__(cls, text) self = str.__new__(cls, text)
self.lang = lang self.lang = lang
self.tkey = tkey self.tkey = tkey
@ -198,7 +198,7 @@ class PngInfo:
:param data: a byte string of the encoded data :param data: a byte string of the encoded data
""" """
self.chunks.append((cid, data)) self.chunks.append((cid, data))
def add_itxt(self, key, value, lang="", tkey="", zip=False): def add_itxt(self, key, value, lang="", tkey="", zip=False):
@ -211,7 +211,7 @@ class PngInfo:
:param zip: compression flag :param zip: compression flag
""" """
if not isinstance(key, bytes): if not isinstance(key, bytes):
key = key.encode("latin-1", "strict") key = key.encode("latin-1", "strict")
if not isinstance(value, bytes): if not isinstance(value, bytes):
@ -235,7 +235,7 @@ class PngInfo:
:param value: value for this key, text or an :param value: value for this key, text or an
:py:class:`PIL.PngImagePlugin.iTXt` instance :py:class:`PIL.PngImagePlugin.iTXt` instance
:param zip: compression flag :param zip: compression flag
""" """
if isinstance(value, iTXt): if isinstance(value, iTXt):
return self.add_itxt(key, value, value.lang, value.tkey, bool(zip)) return self.add_itxt(key, value, value.lang, value.tkey, bool(zip))
@ -462,7 +462,7 @@ class PngStream(ChunkStream):
self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
self.check_text_memory(len(v)) self.check_text_memory(len(v))
return s return s
@ -729,10 +729,6 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
alpha_bytes = 2**bits alpha_bytes = 2**bits
chunk(fp, b"tRNS", alpha[:alpha_bytes]) chunk(fp, b"tRNS", alpha[:alpha_bytes])
if 0:
# FIXME: to be supported some day
chunk(fp, b"gAMA", o32(int(gamma * 100000.0)))
dpi = im.encoderinfo.get("dpi") dpi = im.encoderinfo.get("dpi")
if dpi: if dpi:
chunk(fp, b"pHYs", chunk(fp, b"pHYs",

View File

@ -11,6 +11,8 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from struct import unpack, pack
if bytes is str: if bytes is str:
def i8(c): def i8(c):
return ord(c) return ord(c)
@ -34,7 +36,7 @@ def i16le(c, o=0):
c: string containing bytes to convert c: string containing bytes to convert
o: offset of bytes to convert in string o: offset of bytes to convert in string
""" """
return i8(c[o]) | (i8(c[o+1]) << 8) return unpack("<H", c[o:o+2])[0]
def i32le(c, o=0): def i32le(c, o=0):
@ -44,33 +46,31 @@ def i32le(c, o=0):
c: string containing bytes to convert c: string containing bytes to convert
o: offset of bytes to convert in string o: offset of bytes to convert in string
""" """
return (i8(c[o]) | (i8(c[o+1]) << 8) | (i8(c[o+2]) << 16) | return unpack("<I", c[o:o+4])[0]
(i8(c[o+3]) << 24))
def i16be(c, o=0): def i16be(c, o=0):
return (i8(c[o]) << 8) | i8(c[o+1]) return unpack(">H", c[o:o+2])[0]
def i32be(c, o=0): def i32be(c, o=0):
return ((i8(c[o]) << 24) | (i8(c[o+1]) << 16) | return unpack(">I", c[o:o+4])[0]
(i8(c[o+2]) << 8) | i8(c[o+3]))
# Output, le = little endian, be = big endian # Output, le = little endian, be = big endian
def o16le(i): def o16le(i):
return o8(i) + o8(i >> 8) return pack("<H", i)
def o32le(i): def o32le(i):
return o8(i) + o8(i >> 8) + o8(i >> 16) + o8(i >> 24) return pack("<I", i)
def o16be(i): def o16be(i):
return o8(i >> 8) + o8(i) return pack(">H", i)
def o32be(i): def o32be(i):
return o8(i >> 24) + o8(i >> 16) + o8(i >> 8) + o8(i) return pack(">I", i)
# End of file # End of file

View File

@ -1,14 +1,19 @@
Pillow Pillow
====== ======
*Python Imaging Library (Fork)* Python Imaging Library (Fork)
-----------------------------
Pillow is the "friendly" PIL fork by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. For more information, please `read the documentation <http://pillow.readthedocs.org/>`_, `check the changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_ and `find out how to contribute <https://github.com/python-pillow/Pillow/blob/master/CONTRIBUTING.md>`_. Pillow is the "friendly PIL fork" by `Alex Clark and Contributors <https://github.com/python-pillow/Pillow/graphs/contributors>`_. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
.. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master .. image:: https://travis-ci.org/python-pillow/Pillow.svg?branch=master
:target: https://travis-ci.org/python-pillow/Pillow :target: https://travis-ci.org/python-pillow/Pillow
:alt: Travis CI build status :alt: Travis CI build status (Linux)
.. image:: https://travis-ci.org/python-pillow/pillow-wheels.svg?branch=latest
:target: https://travis-ci.org/python-pillow/pillow-wheels
:alt: Travis CI build status (OS X)
.. image:: https://pypip.in/v/Pillow/badge.png .. image:: https://pypip.in/v/Pillow/badge.png
:target: https://pypi.python.org/pypi/Pillow/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Latest PyPI version :alt: Latest PyPI version
@ -24,3 +29,15 @@ Pillow is the "friendly" PIL fork by `Alex Clark and Contributors <https://githu
.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png .. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png
:target: https://landscape.io/github/python-pillow/Pillow/master :target: https://landscape.io/github/python-pillow/Pillow/master
:alt: Code health :alt: Code health
More Information
----------------
- `Changelog <https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst>`_
- `Contribute <https://github.com/python-pillow/Pillow/blob/master/CONTRIBUTING.md>`_
- `Documentation <http://pillow.readthedocs.org/>`_
- `About <http://pillow.readthedocs.org/about.html>`_
- `Guides <http://pillow.readthedocs.org/guides.html>`_
- `Installation <http://pillow.readthedocs.org/installation.html>`_
- `Reference <http://pillow.readthedocs.org/reference/index.html>`_

View File

@ -78,7 +78,7 @@ def makedelta(fp, sequence):
if not previous: if not previous:
# global header # global header
for s in getheader(im) + getdata(im): for s in getheader(im)[0] + getdata(im):
fp.write(s) fp.write(s)
else: else:

View File

@ -3,7 +3,7 @@ Pillow Tests
Test scripts are named ``test_xxx.py`` and use the ``unittest`` module. A base class and helper functions can be found in ``helper.py``. Test scripts are named ``test_xxx.py`` and use the ``unittest`` module. A base class and helper functions can be found in ``helper.py``.
Depedencies Dependencies
----------- -----------
Install:: Install::

37
Tests/check_webp_leaks.py Normal file
View File

@ -0,0 +1,37 @@
from helper import unittest, PillowTestCase
import sys
from PIL import Image
from io import BytesIO
# Limits for testing the leak
mem_limit = 16 # max increase in MB
iterations = 5000
test_file = "Tests/images/hopper.webp"
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
class TestWebPLeaks(PillowTestCase):
def setUp(self):
try:
from PIL import _webp
except ImportError:
self.skipTest('WebP support not installed')
def _get_mem_usage(self):
from resource import getpagesize, getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss
return mem * getpagesize() / 1024 / 1024
def test_leak_load(self):
with open(test_file, 'rb') as f:
im_data = f.read()
start_mem = self._get_mem_usage()
for count in range(iterations):
with Image.open(BytesIO(im_data)) as im:
im.load()
mem = (self._get_mem_usage() - start_mem)
self.assertLess(mem, mem_limit, msg='memory usage limit exceeded')
if __name__ == '__main__':
unittest.main()

Binary file not shown.

BIN
Tests/fonts/DejaVuSans.ttf Normal file

Binary file not shown.

28
Tests/test_binary.py Normal file
View File

@ -0,0 +1,28 @@
from helper import unittest, PillowTestCase
from PIL import _binary
class TestBinary(PillowTestCase):
def test_standard(self):
self.assertEqual(_binary.i8(b'*'), 42)
self.assertEqual(_binary.o8(42), b'*')
def test_little_endian(self):
self.assertEqual(_binary.i16le(b'\xff\xff\x00\x00'), 65535)
self.assertEqual(_binary.i32le(b'\xff\xff\x00\x00'), 65535)
self.assertEqual(_binary.o16le(65535), b'\xff\xff')
self.assertEqual(_binary.o32le(65535), b'\xff\xff\x00\x00')
def test_big_endian(self):
self.assertEqual(_binary.i16be(b'\x00\x00\xff\xff'), 0)
self.assertEqual(_binary.i32be(b'\x00\x00\xff\xff'), 65535)
self.assertEqual(_binary.o16be(65535), b'\xff\xff')
self.assertEqual(_binary.o32be(65535), b'\x00\x00\xff\xff')
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -3,6 +3,7 @@ from helper import djpeg_available, cjpeg_available
import random import random
from io import BytesIO from io import BytesIO
import os
from PIL import Image from PIL import Image
from PIL import ImageFile from PIL import ImageFile
@ -334,6 +335,24 @@ class TestFileJpeg(PillowTestCase):
self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001) self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)
self.assertEqual(tag_ids['RelatedImageLength'], 0x1002) self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)
def test_MAXBLOCK_scaling(self):
def gen_random_image(size):
""" Generates a very hard to compress file
:param size: tuple
"""
return Image.frombytes('RGB',size, os.urandom(size[0]*size[1] *3))
im = gen_random_image((512,512))
f = self.tempfile("temp.jpeg")
im.save(f, quality=100, optimize=True)
reloaded = Image.open(f)
# none of these should crash
reloaded.save(f, quality='keep')
reloaded.save(f, quality='keep', progressive=True)
reloaded.save(f, quality='keep', optimize=True)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -374,6 +374,14 @@ class TestFilePng(PillowTestCase):
im = roundtrip(im) im = roundtrip(im)
self.assertEqual(im.info['icc_profile'], expected_icc) self.assertEqual(im.info['icc_profile'], expected_icc)
def test_repr_png(self):
im = hopper()
repr_png = Image.open(BytesIO(im._repr_png_()))
self.assertEqual(repr_png.format, 'PNG')
self.assert_image_equal(im, repr_png)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -176,12 +176,23 @@ class TestFileTiff(PillowTestCase):
# Assert # Assert
self.assertIsInstance(ret, str) self.assertIsInstance(ret, str)
def test_as_dict(self):
# Arrange
filename = "Tests/images/pil136.tiff"
im = Image.open(filename)
# Act
ret = im.ifd.as_dict()
# Assert
self.assertIsInstance(ret, dict)
self.assertEqual( self.assertEqual(
ret, ret, {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,),
'{256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), ' 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,),
'262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), ' 279: (9460,), 282: ((720000, 10000),),
'279: (9460,), 282: ((720000, 10000),), ' 283: ((720000, 10000),), 284: (1,)})
'283: ((720000, 10000),), 284: (1,)}')
def test__delitem__(self): def test__delitem__(self):
# Arrange # Arrange

View File

@ -83,7 +83,11 @@ class TestFileWebpAlpha(PillowTestCase):
image.load() image.load()
image.getdata() image.getdata()
self.assert_image_similar(image, pil_image, 1.0) # early versions of webp are known to produce higher deviations: deal with it
if _webp.WebPDecoderVersion(self) <= 0x201:
self.assert_image_similar(image, pil_image, 3.0)
else:
self.assert_image_similar(image, pil_image, 1.0)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -168,8 +168,6 @@ class TestImage(PillowTestCase):
ValueError, ValueError,
lambda: Image.effect_mandelbrot(size, extent, quality)) lambda: Image.effect_mandelbrot(size, extent, quality))
@unittest.skipUnless(sys.platform.startswith('win32'),
"Stalls on Travis CI, passes on Windows")
def test_effect_noise(self): def test_effect_noise(self):
# Arrange # Arrange
size = (100, 100) size = (100, 100)
@ -180,8 +178,8 @@ class TestImage(PillowTestCase):
# Assert # Assert
self.assertEqual(im.size, (100, 100)) self.assertEqual(im.size, (100, 100))
self.assertEqual(im.getpixel((0, 0)), 60) self.assertEqual(im.mode, "L")
self.assertEqual(im.getpixel((0, 1)), 28) self.assertNotEqual(im.getpixel((0, 0)), im.getpixel((0, 1)))
def test_effect_spread(self): def test_effect_spread(self):
# Arrange # Arrange

View File

@ -44,6 +44,22 @@ try:
self.assertRegexpMatches( self.assertRegexpMatches(
ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$")
def test_font_properties(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
self.assertEqual(ttf.path, FONT_PATH)
self.assertEqual(ttf.size, FONT_SIZE)
ttf_copy = ttf.font_variant()
self.assertEqual(ttf_copy.path, FONT_PATH)
self.assertEqual(ttf_copy.size, FONT_SIZE)
ttf_copy = ttf.font_variant(size=FONT_SIZE+1)
self.assertEqual(ttf_copy.size, FONT_SIZE+1)
second_font_path = "Tests/fonts/DejaVuSans.ttf"
ttf_copy = ttf.font_variant(font=second_font_path)
self.assertEqual(ttf_copy.path, second_font_path)
def test_font_with_name(self): def test_font_with_name(self):
ImageFont.truetype(FONT_PATH, FONT_SIZE) ImageFont.truetype(FONT_PATH, FONT_SIZE)
self._render(FONT_PATH) self._render(FONT_PATH)

View File

@ -0,0 +1,24 @@
from helper import unittest, PillowTestCase
from PIL import Image, ImageFont, ImageDraw
class TestImageFontBitmap(PillowTestCase):
def test_similar(self):
text = 'EmbeddedBitmap'
font_outline = ImageFont.truetype(font='Tests/fonts/DejaVuSans.ttf', size=24)
font_bitmap = ImageFont.truetype(font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24)
size_outline, size_bitmap = font_outline.getsize(text), font_bitmap.getsize(text)
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, draw_outline = ImageDraw.Draw(im_bitmap), ImageDraw.Draw(im_outline)
# Metrics are different on the bitmap and ttf fonts, more so on some platforms
# and versions of freetype than others. Mac has a 1px difference, linux doesn't.
draw_bitmap.text((0, size_final[1] - size_bitmap[1]),
text, fill=(0, 0, 0), font=font_bitmap)
draw_outline.text((0, size_final[1] - size_outline[1]),
text, fill=(0, 0, 0), font=font_outline)
self.assert_image_similar(im_bitmap, im_outline, 20)
if __name__ == '__main__':
unittest.main()

View File

@ -48,6 +48,10 @@ class TestLibPack(PillowTestCase):
self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4]) self.assertEqual(pack("RGBA", "RGBA"), [1, 2, 3, 4])
self.assertEqual(pack("RGBa", "RGBa"), [1, 2, 3, 4])
self.assertEqual(pack("RGBa", "BGRa"), [3, 2, 1, 4])
self.assertEqual(pack("RGBa", "aBGR"), [4, 3, 2, 1])
self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4]) self.assertEqual(pack("CMYK", "CMYK"), [1, 2, 3, 4])
self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3]) self.assertEqual(pack("YCbCr", "YCbCr"), [1, 2, 3])
@ -125,6 +129,11 @@ class TestLibPack(PillowTestCase):
self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) self.assertEqual(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0))
self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) self.assertEqual(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0))
self.assertEqual(unpack("RGBa", "RGBa", 4), (1, 2, 3, 4))
self.assertEqual(unpack("RGBa", "BGRa", 4), (3, 2, 1, 4))
self.assertEqual(unpack("RGBa", "aRGB", 4), (2, 3, 4, 1))
self.assertEqual(unpack("RGBa", "aBGR", 4), (4, 3, 2, 1))
self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? self.assertEqual(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255?
self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) self.assertEqual(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255))
self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) self.assertEqual(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255))

View File

@ -5,10 +5,12 @@ from PIL import Image
class TestPickle(PillowTestCase): class TestPickle(PillowTestCase):
def helper_pickle_file(self, pickle, protocol=0): def helper_pickle_file(self, pickle, protocol=0, mode=None):
# Arrange # Arrange
im = Image.open('Tests/images/hopper.jpg') im = Image.open('Tests/images/hopper.jpg')
filename = self.tempfile('temp.pkl') filename = self.tempfile('temp.pkl')
if mode:
im = im.convert(mode)
# Act # Act
with open(filename, 'wb') as f: with open(filename, 'wb') as f:
@ -19,9 +21,11 @@ class TestPickle(PillowTestCase):
# Assert # Assert
self.assertEqual(im, loaded_im) self.assertEqual(im, loaded_im)
def helper_pickle_string( def helper_pickle_string(self, pickle, protocol=0,
self, pickle, protocol=0, file='Tests/images/hopper.jpg'): file='Tests/images/hopper.jpg', mode=None):
im = Image.open(file) im = Image.open(file)
if mode:
im = im.convert(mode)
# Act # Act
dumped_string = pickle.dumps(im, protocol) dumped_string = pickle.dumps(im, protocol)
@ -67,6 +71,26 @@ class TestPickle(PillowTestCase):
]: ]:
self.helper_pickle_string(pickle, file=file) self.helper_pickle_string(pickle, file=file)
def test_pickle_l_mode(self):
# Arrange
import pickle
# Act / Assert
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(pickle, protocol, mode="L")
self.helper_pickle_file(pickle, protocol, mode="L")
def test_cpickle_l_mode(self):
# Arrange
try:
import cPickle
except ImportError:
return
# Act / Assert
for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1):
self.helper_pickle_string(cPickle, protocol, mode="L")
self.helper_pickle_file(cPickle, protocol, mode="L")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -243,7 +243,11 @@ font_getsize(FontObject* self, PyObject* args)
&delta); &delta);
x += delta.x; x += delta.x;
} }
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960
* Yifu Yu<root@jackyyf.com>, 2014-10-15
*/
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP);
if (error) if (error)
return geterror(error); return geterror(error);
if (i == 0) if (i == 0)
@ -316,7 +320,8 @@ font_getabc(FontObject* self, PyObject* args)
int index, error; int index, error;
face = self->face; face = self->face;
index = FT_Get_Char_Index(face, ch); index = FT_Get_Char_Index(face, ch);
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP);
if (error) if (error)
return geterror(error); return geterror(error);
a = face->glyph->metrics.horiBearingX / 64.0; a = face->glyph->metrics.horiBearingX / 64.0;
@ -363,8 +368,8 @@ font_render(FontObject* self, PyObject* args)
} }
im = (Imaging) id; im = (Imaging) id;
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
load_flags = FT_LOAD_RENDER; load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP;
if (mask) if (mask)
load_flags |= FT_LOAD_TARGET_MONO; load_flags |= FT_LOAD_TARGET_MONO;

46
_webp.c
View File

@ -136,9 +136,9 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
{ {
PyBytesObject *webp_string; PyBytesObject *webp_string;
uint8_t *webp; const uint8_t *webp;
Py_ssize_t size; Py_ssize_t size;
PyObject *ret, *bytes, *pymode, *icc_profile = Py_None, *exif = Py_None; PyObject *ret = Py_None, *bytes = NULL, *pymode = NULL, *icc_profile = NULL, *exif = NULL;
WebPDecoderConfig config; WebPDecoderConfig config;
VP8StatusCode vp8_status_code = VP8_STATUS_OK; VP8StatusCode vp8_status_code = VP8_STATUS_OK;
char* mode = "RGB"; char* mode = "RGB";
@ -173,31 +173,34 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
WebPData exif_data = {0}; WebPData exif_data = {0};
WebPMux* mux = WebPMuxCreate(&data, copy_data); WebPMux* mux = WebPMuxCreate(&data, copy_data);
WebPMuxGetFrame(mux, 1, &image); if (NULL == mux)
webp = (uint8_t*)image.bitstream.bytes; goto end;
if (WEBP_MUX_OK != WebPMuxGetFrame(mux, 1, &image))
{
WebPMuxDelete(mux);
goto end;
}
webp = image.bitstream.bytes;
size = image.bitstream.size; size = image.bitstream.size;
vp8_status_code = WebPDecode(webp, size, &config); vp8_status_code = WebPDecode(webp, size, &config);
WebPMuxGetChunk(mux, "ICCP", &icc_profile_data); if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "ICCP", &icc_profile_data))
if (icc_profile_data.size > 0) {
icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size); icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size);
}
WebPMuxGetChunk(mux, "EXIF", &exif_data); if (WEBP_MUX_OK == WebPMuxGetChunk(mux, "EXIF", &exif_data))
if (exif_data.size > 0) {
exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size); exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size);
}
WebPDataClear(&image.bitstream);
WebPMuxDelete(mux); WebPMuxDelete(mux);
} }
#endif #endif
} }
if (vp8_status_code != VP8_STATUS_OK) { if (vp8_status_code != VP8_STATUS_OK)
WebPFreeDecBuffer(&config.output); goto end;
Py_RETURN_NONE;
}
if (config.output.colorspace < MODE_YUV) { if (config.output.colorspace < MODE_YUV) {
bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba,
@ -215,8 +218,21 @@ PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
pymode = PyString_FromString(mode); pymode = PyString_FromString(mode);
#endif #endif
ret = Py_BuildValue("SiiSSS", bytes, config.output.width, ret = Py_BuildValue("SiiSSS", bytes, config.output.width,
config.output.height, pymode, icc_profile, exif); config.output.height, pymode,
NULL == icc_profile ? Py_None : icc_profile,
NULL == exif ? Py_None : exif);
end:
WebPFreeDecBuffer(&config.output); WebPFreeDecBuffer(&config.output);
Py_XDECREF(bytes);
Py_XDECREF(pymode);
Py_XDECREF(icc_profile);
Py_XDECREF(exif);
if (Py_None == ret)
Py_RETURN_NONE;
return ret; return ret;
} }

7
build_children.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# Get last child project build number from branch named "latest"
BUILD_NUM=$(curl -s 'https://api.travis-ci.org/repos/python-pillow/pillow-wheels/branches/latest' | grep -o '^{"branch":{"id":[0-9]*,' | grep -o '[0-9]' | tr -d '\n')
# Restart last child project build
curl -X POST https://api.travis-ci.org/builds/$BUILD_NUM/restart --header "Authorization: token "$AUTH_TOKEN

View File

@ -125,7 +125,7 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following
not present. not present.
**jfif_version** **jfif_version**
A tuple representing the jfif version, (major version, minor version). A tuple representing the jfif version, (major version, minor version).
**jfif_density** **jfif_density**
A tuple representing the pixel density of the image, in units specified A tuple representing the pixel density of the image, in units specified
@ -139,8 +139,8 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following
* 2 - Pixels per Centimeter * 2 - Pixels per Centimeter
**dpi** **dpi**
A tuple representing the reported pixel density in pixels per inch, if A tuple representing the reported pixel density in pixels per inch, if
the file is a jfif file and the units are in inches. the file is a jfif file and the units are in inches.
**adobe** **adobe**
Adobe application marker found. If the file is not an Adobe JPEG file, this Adobe application marker found. If the file is not an Adobe JPEG file, this
@ -153,10 +153,10 @@ The :py:meth:`~PIL.Image.Image.open` method may set the following
Indicates that this is a progressive JPEG file. Indicates that this is a progressive JPEG file.
**icc-profile** **icc-profile**
The ICC color profile for the image. The ICC color profile for the image.
**exif** **exif**
Raw EXIF data from the image. Raw EXIF data from the image.
The :py:meth:`~PIL.Image.Image.save` method supports the following options: The :py:meth:`~PIL.Image.Image.save` method supports the following options:
@ -178,7 +178,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**dpi** **dpi**
A tuple of integers representing the pixel density, ``(x,y)``. A tuple of integers representing the pixel density, ``(x,y)``.
**icc-profile** **icc-profile**
If present, the image is stored with the provided ICC profile. If If present, the image is stored with the provided ICC profile. If
this parameter is not provided, the image will be saved with no this parameter is not provided, the image will be saved with no
profile attached. To preserve the existing profile:: profile attached. To preserve the existing profile::
@ -186,11 +186,11 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
im.save(filename, 'jpeg', icc_profile=im.info.get('icc_profile')) im.save(filename, 'jpeg', icc_profile=im.info.get('icc_profile'))
**exif** **exif**
If present, the image will be stored with the provided raw EXIF data. If present, the image will be stored with the provided raw EXIF data.
**subsampling** **subsampling**
If present, sets the subsampling for the encoder. If present, sets the subsampling for the encoder.
* ``keep``: Only valid for JPEG files, will retain the original image setting. * ``keep``: Only valid for JPEG files, will retain the original image setting.
* ``4:4:4``, ``4:2:2``, ``4:1:1``: Specific sampling values * ``4:4:4``, ``4:2:2``, ``4:1:1``: Specific sampling values
* ``-1``: equivalent to ``keep`` * ``-1``: equivalent to ``keep``
@ -206,7 +206,7 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
* a string, naming a preset, e.g. ``keep``, ``web_low``, or ``web_high`` * a string, naming a preset, e.g. ``keep``, ``web_low``, or ``web_high``
* a list, tuple, or dictionary (with integer keys = * a list, tuple, or dictionary (with integer keys =
range(len(keys))) of lists of 64 integers. There must be range(len(keys))) of lists of 64 integers. There must be
between 2 and 4 tables. between 2 and 4 tables.
.. versionadded:: 2.5.0 .. versionadded:: 2.5.0
@ -347,16 +347,22 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
possible. This includes extra processing in order to find optimal encoder possible. This includes extra processing in order to find optimal encoder
settings. settings.
**transparency** **transparency**
For ``P``, ``L``, and ``RGB`` images, this option controls what For ``P``, ``L``, and ``RGB`` images, this option controls what
color image to mark as transparent. color image to mark as transparent.
**dpi** **dpi**
A tuple of two numbers corresponding to the desired dpi in each direction. A tuple of two numbers corresponding to the desired dpi in each direction.
**pnginfo** **pnginfo**
A :py:class:`PIL.PngImagePlugin.PngInfo` instance containing text tags. A :py:class:`PIL.PngImagePlugin.PngInfo` instance containing text tags.
**compress_level**
ZLIB compression level, a number between 0 and 9: 1 gives best speed,
9 gives best compression, 0 gives no compression at all. Default is 6.
When ``optimize`` option is True ``compress_level`` has no effect
(it is set to 9 regardless of a value passed).
**bits (experimental)** **bits (experimental)**
For ``P`` images, this option controls how many bits to store. If omitted, For ``P`` images, this option controls how many bits to store. If omitted,
the PNG writer uses 8 bits (256 colors). the PNG writer uses 8 bits (256 colors).
@ -450,7 +456,7 @@ Saving Tiff Images
The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments: The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:
**tiffinfo** **tiffinfo**
A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict
object containing tiff tags and values. The TIFF field type is object containing tiff tags and values. The TIFF field type is
autodetected for Numeric and string values, any other types autodetected for Numeric and string values, any other types
@ -459,7 +465,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
:py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with
the appropriate numerical value from the appropriate numerical value from
``TiffTags.TYPES``. ``TiffTags.TYPES``.
.. versionadded:: 2.3.0 .. versionadded:: 2.3.0
**compression** **compression**
@ -471,7 +477,7 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo. These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo.
**description** **description**
**software** **software**
@ -483,7 +489,7 @@ These arguments to set the tiff header fields are an alternative to using the ge
Strings Strings
**resolution_unit** **resolution_unit**
A string of "inch", "centimeter" or "cm" A string of "inch", "centimeter" or "cm"
**resolution** **resolution**

View File

@ -1,4 +1,6 @@
Writing your own file decoder .. _file-decoders:
Writing Your Own File Decoder
============================= =============================
The Python Imaging Library uses a plug-in model which allows you to The Python Imaging Library uses a plug-in model which allows you to
@ -7,7 +9,7 @@ library itself. Such plug-ins usually have names like
:file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name :file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name
(usually an abbreviation). (usually an abbreviation).
.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually. .. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually.
A decoder plug-in should contain a decoder class, based on the A decoder plug-in should contain a decoder class, based on the
:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an :py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
@ -68,7 +70,7 @@ true color.
] ]
Image.register_open("SPAM", SpamImageFile) Image.register_open("SPAM", SpamImageFile)
Image.register_extension("SPAM", ".spam") Image.register_extension("SPAM", ".spam")
Image.register_extension("SPAM", ".spa") # dos version Image.register_extension("SPAM", ".spa") # dos version
@ -116,7 +118,8 @@ The fields are used as follows:
Note that the :py:attr:`tile` attribute contains a list of tile descriptors, Note that the :py:attr:`tile` attribute contains a list of tile descriptors,
not just a single descriptor. not just a single descriptor.
The ``raw`` decoder The raw decoder
---------------
The ``raw`` decoder is used to read uncompressed data from an image file. It The ``raw`` decoder is used to read uncompressed data from an image file. It
can be used with most uncompressed file formats, such as PPM, BMP, uncompressed can be used with most uncompressed file formats, such as PPM, BMP, uncompressed
@ -124,7 +127,7 @@ TIFF, and many others. To use the raw decoder with the
:py:func:`PIL.Image.fromstring` function, use the following syntax:: :py:func:`PIL.Image.fromstring` function, use the following syntax::
image = Image.fromstring( image = Image.fromstring(
mode, size, data, "raw", mode, size, data, "raw",
raw mode, stride, orientation raw mode, stride, orientation
) )

View File

@ -12,7 +12,7 @@ Installation
.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. .. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7.
Simple installation Simple Installation
------------------- -------------------
.. note:: .. note::
@ -121,7 +121,7 @@ Sample Usage::
$ MAX_CONCURRENCY=1 python setup.py build-ext --enable-[feature] install $ MAX_CONCURRENCY=1 python setup.py build-ext --enable-[feature] install
Linux installation Linux Installation
------------------ ------------------
.. note:: .. note::
@ -160,10 +160,10 @@ Prerequisites are installed on **Fedora 20** with::
lcms2-devel libwebp-devel tcl-devel tk-devel lcms2-devel libwebp-devel tcl-devel tk-devel
Mac OS X installation OS X Installation
--------------------- -----------------
We provide binaries for OS X in the form of `Python Wheels <http://wheel.readthedocs.org/en/latest/index.html>`_. Alternatively you can compile Pillow with with XCode. We provide binaries for OS X in the form of `Python Wheels <http://wheel.readthedocs.org/en/latest/index.html>`_. Alternatively you can compile Pillow from soure with XCode.
The easiest way to install external libraries is via `Homebrew <http://mxcl.github.com/homebrew/>`_. After you install Homebrew, run:: The easiest way to install external libraries is via `Homebrew <http://mxcl.github.com/homebrew/>`_. After you install Homebrew, run::
@ -173,7 +173,7 @@ Install Pillow with::
$ pip install Pillow $ pip install Pillow
Windows installation Windows Installation
-------------------- --------------------
We provide binaries for Windows in the form of Python Eggs and `Python Wheels We provide binaries for Windows in the form of Python Eggs and `Python Wheels
@ -194,11 +194,11 @@ Python Eggs
Python Wheels Python Wheels
^^^^^^^^^^^^^ ^^^^^^^^^^^^^
.. Note:: Experimental. Requires setuptools >=0.8 and pip >=1.4.1 .. Note:: Requires setuptools >=0.8 and pip >=1.4.1. Some older versions of pip required the ``--use-wheel`` flag.
:: ::
$ pip install --use-wheel Pillow $ pip install Pillow
If the above does not work, it's likely because we haven't uploaded a If the above does not work, it's likely because we haven't uploaded a
wheel for the latest version of Pillow. In that case, try pinning it wheel for the latest version of Pillow. In that case, try pinning it
@ -206,10 +206,10 @@ to a specific version:
:: ::
$ pip install --use-wheel Pillow==2.6.1 $ pip install Pillow==2.6.1
FreeBSD installation FreeBSD Installation
--------------------- --------------------
.. Note:: Only FreeBSD 10 tested .. Note:: Only FreeBSD 10 tested

View File

@ -184,8 +184,11 @@ Methods
:param xy: Four points to define the bounding box. Sequence of :param xy: Four points to define the bounding box. Sequence of
``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``.
:param outline: Color to use for the outline. :param start: Starting angle, in degrees. Angles are measured from
3 o'clock, increasing clockwise.
:param end: Ending angle, in degrees.
:param fill: Color to use for the fill. :param fill: Color to use for the fill.
:param outline: Color to use for the outline.
.. py:method:: PIL.ImageDraw.Draw.point(xy, fill=None) .. py:method:: PIL.ImageDraw.Draw.point(xy, fill=None)

View File

@ -13,7 +13,7 @@ Example: Using the :py:mod:`~PIL.ImageMath` module
.. code-block:: python .. code-block:: python
import Image, ImageMath from PIL import Image, ImageMath
im1 = Image.open("image1.jpg") im1 = Image.open("image1.jpg")
im2 = Image.open("image2.jpg") im2 = Image.open("image2.jpg")

View File

@ -53,7 +53,7 @@ vector data. Path objects can be passed to the methods on the
Converts the path to a Python list [(x, y), …]. Converts the path to a Python list [(x, y), …].
:param flat: By default, this function returns a list of 2-tuples :param flat: By default, this function returns a list of 2-tuples
[(x, y), ...]. If this argument is :keyword:`True`, it [(x, y), ...]. If this argument is `True`, it
returns a flat list [x, y, ...] instead. returns a flat list [x, y, ...] instead.
:return: A list of coordinates. See **flat**. :return: A list of coordinates. See **flat**.

View File

@ -1,6 +1,13 @@
Pillow 2.7.0 Pillow 2.7.0
============ ============
Sane Plugin
-----------
The Sane plugin has now been split into its own repo:
https://github.com/python-pillow/Sane .
Png text chunk size limits Png text chunk size limits
-------------------------- --------------------------
@ -157,7 +164,7 @@ so the quality was worse compared to other Gaussian blur software.
The new implementation does not have this drawback. The new implementation does not have this drawback.
TFF Parameter Changes TIFF Parameter Changes
---------------------- ----------------------
Several kwarg parameters for saving TIFF images were previously Several kwarg parameters for saving TIFF images were previously

View File

@ -204,19 +204,6 @@ ImagingNewDIB(const char *mode, int xsize, int ysize)
#endif #endif
#if 0
{
/* DEBUG: dump palette to file */
FILE *err = fopen("dib.pal", "w");
for (i = 0; i < 256; i++)
fprintf(err, "%d: %d/%d/%d\n", i,
pal->palPalEntry[i].peRed,
pal->palPalEntry[i].peGreen,
pal->palPalEntry[i].peBlue);
fclose(err);
}
#endif
dib->palette = CreatePalette(pal); dib->palette = CreatePalette(pal);
} }

View File

@ -98,8 +98,8 @@ ImagingEffectNoise(int xsize, int ysize, float sigma)
/* after numerical recipes */ /* after numerical recipes */
double v1, v2, radius, factor; double v1, v2, radius, factor;
do { do {
v1 = rand()*(2.0/32767.0) - 1.0; v1 = rand()*(2.0/RAND_MAX) - 1.0;
v2 = rand()*(2.0/32767.0) - 1.0; v2 = rand()*(2.0/RAND_MAX) - 1.0;
radius= v1*v1 + v2*v2; radius= v1*v1 + v2*v2;
} while (radius >= 1.0); } while (radius >= 1.0);
factor = sqrt(-2.0*log(radius)/radius); factor = sqrt(-2.0*log(radius)/radius);

View File

@ -304,23 +304,6 @@ perspective_transform(double* xin, double* yin, int x, int y, void* data)
return 1; return 1;
} }
#if 0
static int
quadratic_transform(double* xin, double* yin, int x, int y, void* data)
{
double* a = (double*) data;
double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3];
double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7];
double a8 = a[8]; double a9 = a[9]; double a10 = a[10]; double a11 = a[11];
xin[0] = a0 + a1*x + a2*y + a3*x*x + a4*x*y + a5*y*y;
yin[0] = a6 + a7*x + a8*y + a9*x*x + a10*x*y + a11*y*y;
return 1;
}
#endif
static int static int
quad_transform(double* xin, double* yin, int x, int y, void* data) quad_transform(double* xin, double* yin, int x, int y, void* data)
{ {

View File

@ -530,6 +530,11 @@ static struct {
{"RGBA", "B", 8, band2}, {"RGBA", "B", 8, band2},
{"RGBA", "A", 8, band3}, {"RGBA", "A", 8, band3},
/* true colour w. alpha premultiplied */
{"RGBa", "RGBa", 32, copy4},
{"RGBa", "BGRa", 32, ImagingPackBGRA},
{"RGBa", "aBGR", 32, ImagingPackABGR},
/* true colour w. padding */ /* true colour w. padding */
{"RGBX", "RGBX", 32, copy4}, {"RGBX", "RGBX", 32, copy4},
{"RGBX", "RGBX;L", 32, packRGBXL}, {"RGBX", "RGBX;L", 32, packRGBXL},

View File

@ -44,9 +44,6 @@ struct _HashTable {
#define RESIZE_FACTOR 3 #define RESIZE_FACTOR 3
static int _hashtable_insert_node(HashTable *,HashNode *,int,int,CollisionFunc); static int _hashtable_insert_node(HashTable *,HashNode *,int,int,CollisionFunc);
#if 0
static int _hashtable_test(HashTable *);
#endif
HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) { HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) {
HashTable *h; HashTable *h;
@ -132,22 +129,6 @@ static void _hashtable_resize(HashTable *h) {
} }
} }
#if 0
static int _hashtable_test(HashTable *h) {
uint32_t i;
int j;
HashNode *n;
for (i=0;i<h->length;i++) {
for (n=h->table[i];n&&n->next;n=n->next) {
j=h->cmpFunc(h,n->key,n->next->key);
printf ("%c",j?(j<0?'-':'+'):'=');
}
printf ("\n");
}
return 0;
}
#endif
static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int update,CollisionFunc cf) { static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int update,CollisionFunc cf) {
uint32_t hash=h->hashFunc(h,node->key)%h->length; uint32_t hash=h->hashFunc(h,node->key)%h->length;
HashNode **n,*nv; HashNode **n,*nv;

View File

@ -638,7 +638,12 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels)
int a = in[3]; int a = in[3];
if (!a) if (!a)
out[R] = out[G] = out[B] = out[A] = 0; out[R] = out[G] = out[B] = out[A] = 0;
else { else if (a == 255) {
out[R] = in[0];
out[G] = in[1];
out[B] = in[2];
out[A] = a;
} else {
out[R] = CLIP(in[0] * 255 / a); out[R] = CLIP(in[0] * 255 / a);
out[G] = CLIP(in[1] * 255 / a); out[G] = CLIP(in[1] * 255 / a);
out[B] = CLIP(in[2] * 255 / a); out[B] = CLIP(in[2] * 255 / a);
@ -1114,6 +1119,12 @@ static struct {
{"RGBA", "B", 8, band2}, {"RGBA", "B", 8, band2},
{"RGBA", "A", 8, band3}, {"RGBA", "A", 8, band3},
/* true colour w. alpha premultiplied */
{"RGBa", "RGBa", 32, copy4},
{"RGBa", "BGRa", 32, unpackBGRA},
{"RGBa", "aRGB", 32, unpackARGB},
{"RGBa", "aBGR", 32, unpackABGR},
/* true colour w. padding */ /* true colour w. padding */
{"RGBX", "RGB", 24, ImagingUnpackRGB}, {"RGBX", "RGB", 24, ImagingUnpackRGB},
{"RGBX", "RGB;L", 24, unpackRGBL}, {"RGBX", "RGB;L", 24, unpackRGBL},

View File

@ -722,6 +722,9 @@ class pil_build_ext(build_ext):
os.unlink(tmpfile) os.unlink(tmpfile)
def debug_build():
return hasattr(sys, 'gettotalrefcount')
setup( setup(
name=NAME, name=NAME,
version=PILLOW_VERSION, version=PILLOW_VERSION,
@ -754,6 +757,6 @@ setup(
test_suite='PIL.tests', test_suite='PIL.tests',
keywords=["Imaging", ], keywords=["Imaging", ],
license='Standard PIL License', license='Standard PIL License',
zip_safe=True, zip_safe= not debug_build(),
) )
# End of file # End of file