This commit is contained in:
hugovk 2015-06-08 14:37:19 +03:00
commit d9cd57a5f7
41 changed files with 1003 additions and 496 deletions

View File

@ -20,7 +20,8 @@ install:
- "travis_retry sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick"
- "travis_retry pip install cffi"
- "travis_retry pip install coverage nose"
- "travis_retry pip install pyroma"
# Pyroma tests sometimes hang on PyPy; skip for PyPy
- if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi

View File

@ -4,6 +4,18 @@ Changelog (Pillow)
2.9.0 (Unreleased)
------------------
- Provide n_frames attribute to multi-frame formats #1261
[anntzer, radarhere]
- Add duration and loop set to GifImagePlugin #1172
[radarhere]
- Ico files are little endian #1232
[wiredfool]
- Upgrade olefile from 0.30 to 0.42b #1226
[radarhere, decalage2]
- Setting transparency value to 0 when the tRNS contains only null byte(s) #1239
[juztin]
@ -31,6 +43,12 @@ Changelog (Pillow)
- Tiff: allow writing floating point tag values #1113
[bpedersen2]
2.8.2 (2015-06-06)
------------------
- Bug fix: Fixed Tiff handling of bad EXIF data
[radarhere]
2.8.1 (2015-04-02)
------------------

111
Makefile
View File

@ -1,28 +1,5 @@
.PHONY: pre clean install test inplace coverage test-dep help docs livedocs
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " clean remove build products"
@echo " install make and install"
@echo " test run tests on installed pillow"
@echo " inplace make inplace extension"
@echo " coverage run coverage test (in progress)"
@echo " docs make html docs"
@echo " docserver run an http server on the docs directory"
@echo " test-dep install coveraget and test dependencies"
pre:
virtualenv .
bin/pip install -r requirements.txt
bin/python setup.py develop
bin/python selftest.py
bin/nosetests Tests/test_*.py
bin/python setup.py install
bin/python test-installed.py
check-manifest
pyroma .
viewdoc
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: clean coverage doc docserve help inplace install install-req release-test sdist test upload upload-test
clean:
python setup.py clean
@ -30,46 +7,70 @@ clean:
rm -r build || true
find . -name __pycache__ | xargs rm -r || true
install:
python setup.py install
python selftest.py --installed
test:
python test-installed.py
inplace: clean
python setup.py build_ext --inplace
coverage:
# requires nose-cov
coverage erase
coverage run --parallel-mode --include=PIL/* selftest.py
nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py
# doesn't combine properly before report,
# writing report instead of displaying invalid report
# Doesn't combine properly before report, writing report instead of displaying invalid report.
rm -r htmlcov || true
coverage combine
coverage report
test-dep:
pip install coveralls nose nose-cov pep8 pyflakes
docs:
doc:
$(MAKE) -C docs html
docserver:
docserve:
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null&
# Test sdist upload via test.pythonpackages.com, no creds required
# .pypirc:
# [test]
# username:
# password:
# repository = http://test.pythonpackages.com
sdisttest:
python setup.py sdist --format=zip upload -r test
sdistup:
python setup.py sdist --format=zip upload
python setup.py sdist upload
help:
@echo "Welcome to Pillow development. Please use \`make <target>' where <target> is one of"
@echo " clean remove build products"
@echo " coverage run coverage test (in progress)"
@echo " doc make html docs"
@echo " docserve run an http server on the docs directory"
@echo " html to make standalone HTML files"
@echo " inplace make inplace extension"
@echo " install make and install"
@echo " install-req install documentation and test dependencies"
@echo " release-test run code and package tests before release"
@echo " test run tests on installed pillow"
@echo " upload build and upload sdists to PyPI"
@echo " upload-test build and upload sdists to test.pythonpackages.com"
inplace: clean
python setup.py build_ext --inplace
install:
python setup.py install
python selftest.py --installed
install-req:
pip install -r requirements.txt
release-test:
$(MAKE) install-req
python setup.py develop
python selftest.py
nosetests Tests/test_*.py
python setup.py install
python test-installed.py
check-manifest
pyroma .
viewdoc
sdist:
python setup.py sdist --format=zip
python setup.py sdist --format=gztar,zip
test:
python test-installed.py
# https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file
upload-test:
# [test]
# username:
# password:
# repository = http://test.pythonpackages.com
python setup.py sdist --format=gztar,zip upload -r test
upload:
python setup.py sdist --format=gztar,zip upload

View File

@ -62,6 +62,10 @@ class DcxImageFile(PcxImageFile):
self.__fp = self.fp
self.seek(0)
@property
def n_frames(self):
return len(self._offset)
def seek(self, frame):
if frame >= len(self._offset):
raise EOFError("attempt to seek outside DCX directory")

View File

@ -86,9 +86,10 @@ class FliImageFile(ImageFile.ImageFile):
self.palette = ImagePalette.raw("RGB", b"".join(palette))
# set things up to decode first frame
self.frame = -1
self.__frame = -1
self.__fp = self.fp
self.__rewind = self.fp.tell()
self._n_frames = None
self.seek(0)
def _palette(self, palette, shift):
@ -109,11 +110,35 @@ class FliImageFile(ImageFile.ImageFile):
palette[i] = (r, g, b)
i += 1
def seek(self, frame):
@property
def n_frames(self):
if self._n_frames is None:
current = self.tell()
try:
while True:
self.seek(self.tell() + 1)
except EOFError:
self._n_frames = self.tell() + 1
self.seek(current)
return self._n_frames
if frame != self.frame + 1:
def seek(self, frame):
if frame == self.__frame:
return
if frame < self.__frame:
self._seek(0)
for f in range(self.__frame + 1, frame + 1):
self._seek(f)
def _seek(self, frame):
if frame == 0:
self.__frame = -1
self.__fp.seek(self.__rewind)
self.__offset = 128
if frame != self.__frame + 1:
raise ValueError("cannot seek to frame %d" % frame)
self.frame = frame
self.__frame = frame
# move to next frame
self.fp = self.__fp
@ -128,11 +153,10 @@ class FliImageFile(ImageFile.ImageFile):
self.decodermaxblock = framesize
self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
self.__offset = self.__offset + framesize
self.__offset += framesize
def tell(self):
return self.frame
return self.__frame
#
# registry

View File

@ -87,9 +87,30 @@ class GifImageFile(ImageFile.ImageFile):
self.__fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell()
self.seek(0) # get ready to read first frame
self._n_frames = None
self._seek(0) # get ready to read first frame
@property
def n_frames(self):
if self._n_frames is None:
current = self.tell()
try:
while True:
self.seek(self.tell() + 1)
except EOFError:
self._n_frames = self.tell() + 1
self.seek(current)
return self._n_frames
def seek(self, frame):
if frame == self.__frame:
return
if frame < self.__frame:
self._seek(0)
for f in range(self.__frame + 1, frame + 1):
self._seek(f)
def _seek(self, frame):
if frame == 0:
# rewind
@ -292,57 +313,10 @@ def _save(im, fp, filename):
for s in header:
fp.write(s)
flags = 0
try:
interlace = im.encoderinfo["interlace"]
except KeyError:
interlace = 1
# workaround for @PIL153
if min(im.size) < 16:
interlace = 0
if interlace:
flags = flags | 64
try:
transparency = im.encoderinfo["transparency"]
except KeyError:
pass
else:
transparency = int(transparency)
# optimize the block away if transparent color is not used
transparent_color_exists = True
# adjust the transparency index after optimize
if used_palette_colors is not None and len(used_palette_colors) < 256:
for i in range(len(used_palette_colors)):
if used_palette_colors[i] == transparency:
transparency = i
transparent_color_exists = True
break
else:
transparent_color_exists = False
# transparency extension block
if transparent_color_exists:
fp.write(b"!" +
o8(249) + # extension intro
o8(4) + # length
o8(1) + # transparency info present
o16(0) + # duration
o8(transparency) + # transparency index
o8(0))
# local image header
fp.write(b"," +
o16(0) + o16(0) + # bounding box
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(flags) + # flags
o8(8)) # bits
get_local_header(fp, im)
im_out.encoderconfig = (8, interlace)
im_out.encoderconfig = (8, get_interlace(im))
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])])
@ -356,6 +330,85 @@ def _save(im, fp, filename):
pass
def get_interlace(im):
try:
interlace = im.encoderinfo["interlace"]
except KeyError:
interlace = 1
# workaround for @PIL153
if min(im.size) < 16:
interlace = 0
return interlace
def get_local_header(fp, im, offset=(0, 0)):
transparent_color_exists = False
try:
transparency = im.encoderinfo["transparency"]
except KeyError:
pass
else:
transparency = int(transparency)
# optimize the block away if transparent color is not used
transparent_color_exists = True
if _get_optimize(im, im.encoderinfo):
used_palette_colors = _get_used_palette_colors(im)
# adjust the transparency index after optimize
if len(used_palette_colors) < 256:
for i in range(len(used_palette_colors)):
if used_palette_colors[i] == transparency:
transparency = i
transparent_color_exists = True
break
else:
transparent_color_exists = False
if "duration" in im.encoderinfo:
duration = int(im.encoderinfo["duration"] / 10)
else:
duration = 0
if transparent_color_exists or duration != 0:
transparency_flag = 1 if transparent_color_exists else 0
if not transparent_color_exists:
transparency = 0
fp.write(b"!" +
o8(249) + # extension intro
o8(4) + # length
o8(transparency_flag) + # transparency info present
o16(duration) + # duration
o8(transparency) + # transparency index
o8(0))
if "loop" in im.encoderinfo:
number_of_loops = im.encoderinfo["loop"]
fp.write(b"!" +
o8(255) + # extension intro
o8(11) +
b"NETSCAPE2.0" +
o8(3) +
o8(1) +
o16(number_of_loops) + # number of loops
o8(0))
flags = 0
if get_interlace(im):
flags = flags | 64
fp.write(b"," +
o16(offset[0]) + # offset
o16(offset[1]) +
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(flags) + # flags
o8(8)) # bits
def _save_netpbm(im, fp, filename):
#
@ -405,11 +458,26 @@ def _save_netpbm(im, fp, filename):
# --------------------------------------------------------------------
# GIF utilities
def _get_optimize(im, info):
return im.mode in ("P", "L") and info and info.get("optimize", 0)
def _get_used_palette_colors(im):
used_palette_colors = []
# check which colors are used
i = 0
for count in im.histogram():
if count:
used_palette_colors.append(i)
i += 1
return used_palette_colors
def getheader(im, palette=None, info=None):
"""Return a list of strings representing a GIF header"""
optimize = info and info.get("optimize", 0)
# Header Block
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
header = [
@ -431,15 +499,8 @@ def getheader(im, palette=None, info=None):
used_palette_colors = palette_bytes = None
if im.mode in ("P", "L") and optimize:
used_palette_colors = []
# check which colors are used
i = 0
for count in im.histogram():
if count:
used_palette_colors.append(i)
i += 1
if _get_optimize(im, info):
used_palette_colors = _get_used_palette_colors(im)
# create the new palette if not every color is used
if len(used_palette_colors) < 256:
@ -510,13 +571,7 @@ def getdata(im, offset=(0, 0), **params):
im.encoderinfo = params
# local image header
fp.write(b"," +
o16(offset[0]) + # offset
o16(offset[1]) +
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(0) + # flags
o8(8)) # bits
get_local_header(fp, im, offset)
ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])])

View File

@ -48,7 +48,7 @@ def _save(im, fp, filename):
width, height = im.size
filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 255 or x[1] > 255) else True, sizes)
fp.write(struct.pack("H", len(sizes))) # idCount(2)
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes)*16
for size in sizes:
width, height = size
@ -57,7 +57,7 @@ def _save(im, fp, filename):
fp.write(b"\0") # bColorCount(1)
fp.write(b"\0") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2)
fp.write(struct.pack("H", 32)) # wBitCount(2)
fp.write(struct.pack("<H", 32)) # wBitCount(2)
image_io = BytesIO()
tmp = im.copy()
@ -66,8 +66,8 @@ def _save(im, fp, filename):
image_io.seek(0)
image_bytes = image_io.read()
bytes_len = len(image_bytes)
fp.write(struct.pack("I", bytes_len)) # dwBytesInRes(4)
fp.write(struct.pack("I", offset)) # dwImageOffset(4)
fp.write(struct.pack("<I", bytes_len)) # dwBytesInRes(4)
fp.write(struct.pack("<I", offset)) # dwImageOffset(4)
current = fp.tell()
fp.seek(offset)
fp.write(image_bytes)

View File

@ -260,6 +260,10 @@ class ImImageFile(ImageFile.ImageFile):
self.tile = [("raw", (0, 0)+self.size, offs,
(self.rawmode, 0, -1))]
@property
def n_frames(self):
return self.info[FRAMES]
def seek(self, frame):
if frame < 0 or frame >= self.info[FRAMES]:

View File

@ -1810,7 +1810,7 @@ class Image(object):
self.readonly = 0
self.pyaccess = None
# FIXME: the different tranform methods need further explanation
# FIXME: the different transform methods need further explanation
# instead of bloating the method docs, add a separate chapter.
def transform(self, size, method, data=None, resample=NEAREST, fill=1):
"""

View File

@ -64,7 +64,7 @@ pyCMS
0.0.2 alpha Jan 6, 2002
Added try/except statements arount type() checks of
Added try/except statements around type() checks of
potential CObjects... Python won't let you use type()
on them, and raises a TypeError (stupid, if you ask
me!)
@ -123,8 +123,8 @@ FLAGS = {
"NOTCACHE": 64, # Inhibit 1-pixel cache
"NOTPRECALC": 256,
"NULLTRANSFORM": 512, # Don't transform anyway
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces
"HIGHRESPRECALC": 1024, # Use more memory to give better accuracy
"LOWRESPRECALC": 2048, # Use less memory to minimize resources
"WHITEBLACKCOMPENSATION": 8192,
"BLACKPOINTCOMPENSATION": 8192,
"GAMUTCHECK": 4096, # Out of Gamut alarm
@ -573,7 +573,7 @@ def applyTransform(im, transform, inPlace=0):
This function applies a pre-calculated transform (from
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
to an image. The transform can be used for multiple images, saving
considerable calcuation time if doing the same conversion multiple times.
considerable calculation time if doing the same conversion multiple times.
If you want to modify im in-place instead of receiving a new image as
the return value, set inPlace to TRUE. This can only be done if
@ -858,7 +858,7 @@ def getDefaultIntent(profile):
If an error occurs while trying to obtain the default intent, a
PyCMSError is raised.
Use this function to determine the default (and usually best optomized)
Use this function to determine the default (and usually best optimized)
rendering intent for this profile. Most profiles support multiple
rendering intents, but are intended mostly for one type of conversion.
If you wish to use a different intent than returned, use
@ -914,7 +914,7 @@ def isIntentSupported(profile, intent, direction):
see the pyCMS documentation for details on rendering intents and what
they do.
:param direction: Integer specifing if the profile is to be used for input,
:param direction: Integer specifying if the profile is to be used for input,
output, or proof
INPUT = 0 (or use ImageCms.DIRECTION_INPUT)

View File

@ -73,7 +73,7 @@ class Contrast(_Enhance):
class Brightness(_Enhance):
"""Adjust image brightness.
This class can be used to control the brighntess of an image. An
This class can be used to control the brightness of an image. An
enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
original image.
"""

View File

@ -4,7 +4,7 @@
#
# JPEG (JFIF) file handling
#
# See "Digital Compression and Coding of Continous-Tone Still Images,
# See "Digital Compression and Coding of Continuous-Tone Still Images,
# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1)
#
# History:

View File

@ -71,6 +71,10 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
self.seek(0)
@property
def n_frames(self):
return len(self.images)
def seek(self, frame):
try:

View File

@ -62,6 +62,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
def load_seek(self, pos):
self.__fp.seek(pos)
@property
def n_frames(self):
return self.__framecount
def seek(self, frame):
if frame < 0 or frame >= self.__framecount:
raise EOFError("no more images in MPO file")

File diff suppressed because it is too large Load Diff

View File

@ -132,6 +132,10 @@ class PsdImageFile(ImageFile.ImageFile):
self._fp = self.fp
self.frame = 0
@property
def n_frames(self):
return len(self.layers)
def seek(self, layer):
# seek to given layer (1..max)
if layer == self.frame:

View File

@ -127,12 +127,12 @@ class SpiderImageFile(ImageFile.ImageFile):
if self.istack == 0 and self.imgnumber == 0:
# stk=0, img=0: a regular 2D image
offset = hdrlen
self.nimages = 1
self._nimages = 1
elif self.istack > 0 and self.imgnumber == 0:
# stk>0, img=0: Opening the stack for the first time
self.imgbytes = int(h[12]) * int(h[2]) * 4
self.hdrlen = hdrlen
self.nimages = int(h[26])
self._nimages = int(h[26])
# Point to the first image in the stack
offset = hdrlen * 2
self.imgnumber = 1
@ -154,6 +154,10 @@ class SpiderImageFile(ImageFile.ImageFile):
(self.rawmode, 0, 1))]
self.__fp = self.fp # FIXME: hack
@property
def n_frames(self):
return self._nimages
# 1st image index is zero (although SPIDER imgnumber starts at 1)
def tell(self):
if self.imgnumber < 1:
@ -164,7 +168,7 @@ class SpiderImageFile(ImageFile.ImageFile):
def seek(self, frame):
if self.istack == 0:
return
if frame >= self.nimages:
if frame >= self._nimages:
raise EOFError("attempt to seek past end of file")
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
self.fp = self.__fp

View File

@ -294,7 +294,7 @@ class ImageFileDirectory(collections.MutableMapping):
def named(self):
"""
Returns the complete tag dictionary, with named tags where posible.
Returns the complete tag dictionary, with named tags where possible.
"""
from PIL import TiffTags
result = {}
@ -426,6 +426,11 @@ class ImageFileDirectory(collections.MutableMapping):
for i in range(i16(fp.read(2))):
ifd = fp.read(12)
if len(ifd) != 12:
warnings.warn("Possibly corrupt EXIF data. "
"Expecting to read 12 bytes but only got %d."
% (len(ifd)))
continue
tag, typ = i16(ifd), i16(ifd, 2)
@ -476,7 +481,14 @@ class ImageFileDirectory(collections.MutableMapping):
else:
print("- value:", self[tag])
self.next = i32(fp.read(4))
ifd = fp.read(4)
if len(ifd) != 4:
warnings.warn("Possibly corrupt EXIF data. "
"Expecting to read 4 bytes but only got %d."
% (len(ifd)))
return
self.next = i32(ifd)
# save primitives
@ -636,6 +648,8 @@ class TiffImageFile(ImageFile.ImageFile):
self.__first = self.__next = self.ifd.i32(ifh, 4)
self.__frame = -1
self.__fp = self.fp
self._frame_pos = []
self._n_frames = None
if Image.DEBUG:
print("*** TiffImageFile._open ***")
@ -645,28 +659,30 @@ class TiffImageFile(ImageFile.ImageFile):
# and load the first frame
self._seek(0)
@property
def n_frames(self):
if self._n_frames is None:
current = self.tell()
try:
while True:
self._seek(self.tell() + 1)
except EOFError:
self._n_frames = self.tell() + 1
self.seek(current)
return self._n_frames
def seek(self, frame):
"Select a given frame as current image"
if frame < 0:
frame = 0
self._seek(frame)
self._seek(max(frame, 0)) # Questionable backwards compatibility.
# Create a new core image object on second and
# subsequent frames in the image. Image may be
# different size/mode.
Image._decompression_bomb_check(self.size)
self.im = Image.core.new(self.mode, self.size)
def tell(self):
"Return the current frame number"
return self._tell()
def _seek(self, frame):
self.fp = self.__fp
if frame < self.__frame:
# rewind file
self.__frame = -1
self.__next = self.__first
while self.__frame < frame:
while len(self._frame_pos) <= frame:
if not self.__next:
raise EOFError("no more images in TIFF file")
if Image.DEBUG:
@ -676,14 +692,19 @@ class TiffImageFile(ImageFile.ImageFile):
# was passed to libtiff, invalidating the buffer
self.fp.tell()
self.fp.seek(self.__next)
self._frame_pos.append(self.__next)
if Image.DEBUG:
print("Loading tags, location: %s" % self.fp.tell())
self.tag.load(self.fp)
self.__next = self.tag.next
self.__frame += 1
self.fp.seek(self._frame_pos[frame])
self.tag.load(self.fp)
self.__frame = frame
self._setup()
def _tell(self):
def tell(self):
"Return the current frame number"
return self.__frame
def _decoder(self, rawmode, layer, tile=None):
@ -749,7 +770,7 @@ class TiffImageFile(ImageFile.ImageFile):
#
# Rearranging for supporting byteio items, since they have a fileno
# that returns an IOError if there's no underlying fp. Easier to
# dea. with here by reordering.
# deal with here by reordering.
if Image.DEBUG:
print("have getvalue. just sending in a string from getvalue")
n, err = decoder.decode(self.fp.getvalue())
@ -985,7 +1006,7 @@ class TiffImageFile(ImageFile.ImageFile):
# Write TIFF files
# little endian is default except for image modes with
# explict big endian byte-order
# explicit big endian byte-order
SAVE_INFO = {
# mode => rawmode, byteorder, photometrics,

View File

@ -6,19 +6,23 @@ 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.
.. 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
:alt: Travis CI build status (Linux)
.. image:: https://travis-ci.org/python-pillow/pillow-wheels.svg?branch=latest
..
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://img.shields.io/pypi/v/pillow.svg
..
image:: https://img.shields.io/pypi/v/pillow.svg
:target: https://pypi.python.org/pypi/Pillow/
:alt: Latest PyPI version
.. image:: https://img.shields.io/pypi/dm/pillow.svg
..
image:: https://img.shields.io/pypi/dm/pillow.svg
:target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads

View File

@ -75,8 +75,8 @@ def makedelta(fp, sequence):
for im in sequence:
#
# FIXME: write graphics control block before each frame
# To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im, duration=1000)
if not previous:

BIN
Tests/images/hopper.im Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

View File

@ -30,6 +30,10 @@ class TestFileDcx(PillowTestCase):
# Assert
self.assertEqual(frame, 0)
def test_n_frames(self):
im = Image.open(TEST_FILE)
self.assertEqual(im.n_frames, 1)
def test_seek_too_far(self):
# Arrange
im = Image.open(TEST_FILE)

View File

@ -18,6 +18,10 @@ class TestFileFli(PillowTestCase):
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "FLI")
def test_n_frames(self):
im = Image.open(test_file)
self.assertEqual(im.n_frames, 2)
if __name__ == '__main__':
unittest.main()

View File

@ -134,6 +134,10 @@ class TestFileGif(PillowTestCase):
except EOFError:
self.assertEqual(framecount, 5)
def test_n_frames(self):
im = Image.open("Tests/images/iss634.gif")
self.assertEqual(im.n_frames, 43)
def test_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif")
try:
@ -169,6 +173,33 @@ class TestFileGif(PillowTestCase):
# first frame
self.assertEqual(img.histogram()[img.info['transparency']], 0)
def test_duration(self):
duration = 1000
out = self.tempfile('temp.gif')
fp = open(out, "wb")
im = Image.new('L', (100, 100), '#000')
for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, duration=duration):
fp.write(s)
fp.write(b";")
fp.close()
reread = Image.open(out)
self.assertEqual(reread.info['duration'], duration)
def test_number_of_loops(self):
number_of_loops = 2
out = self.tempfile('temp.gif')
fp = open(out, "wb")
im = Image.new('L', (100, 100), '#000')
for s in GifImagePlugin.getheader(im)[0] + GifImagePlugin.getdata(im, loop=number_of_loops):
fp.write(s)
fp.write(b";")
fp.close()
reread = Image.open(out)
self.assertEqual(reread.info['loop'], number_of_loops)
if __name__ == '__main__':
unittest.main()

33
Tests/test_file_im.py Normal file
View File

@ -0,0 +1,33 @@
from helper import unittest, PillowTestCase, hopper
from PIL import Image
# sample im
TEST_IM = "Tests/images/hopper.im"
class TestFileIm(PillowTestCase):
def test_sanity(self):
im = Image.open(TEST_IM)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "IM")
def test_n_frames(self):
im = Image.open(TEST_IM)
self.assertEqual(im.n_frames, 1)
def test_roundtrip(self):
out = self.tempfile('temp.im')
im = hopper()
im.save(out)
reread = Image.open(out)
self.assert_image_equal(reread, im)
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -95,6 +95,10 @@ class TestFileMpo(PillowTestCase):
im.seek(0)
self.assertEqual(im.tell(), 0)
def test_n_frames(self):
im = Image.open("Tests/images/sugarshack.mpo")
self.assertEqual(im.n_frames, 2)
def test_image_grab(self):
for test_file in test_files:
im = Image.open(test_file)

View File

@ -16,6 +16,13 @@ class TestImagePsd(PillowTestCase):
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PSD")
def test_n_frames(self):
im = Image.open("Tests/images/hopper_merged.psd")
self.assertEqual(im.n_frames, 1)
im = Image.open(test_file)
self.assertEqual(im.n_frames, 2)
if __name__ == '__main__':
unittest.main()

View File

@ -42,6 +42,10 @@ class TestImageSpider(PillowTestCase):
# Assert
self.assertEqual(index, 0)
def test_n_frames(self):
im = Image.open(TEST_FILE)
self.assertEqual(im.n_frames, 1)
def test_loadImageSeries(self):
# Arrange
not_spider_file = "Tests/images/hopper.ppm"

View File

@ -3,6 +3,8 @@ from helper import unittest, PillowTestCase, hopper, py3
from PIL import Image, TiffImagePlugin
import struct
class TestFileTiff(PillowTestCase):
@ -77,6 +79,12 @@ class TestFileTiff(PillowTestCase):
im._setup()
self.assertEqual(im.info['dpi'], (72., 72.))
def test_bad_exif(self):
try:
Image.open('Tests/images/hopper_bad_exif.jpg')._getexif()
except struct.error:
self.fail("Bad EXIF data should not pass incorrect values to _binary unpack")
def test_little_endian(self):
im = Image.open('Tests/images/16bit.cropped.tif')
self.assertEqual(im.getpixel((0, 0)), 480)
@ -142,6 +150,13 @@ class TestFileTiff(PillowTestCase):
self.assertEqual(
im.getextrema(), (-3.140936851501465, 3.140684127807617))
def test_n_frames(self):
im = Image.open('Tests/images/multipage-lastframe.tif')
self.assertEqual(im.n_frames, 1)
im = Image.open('Tests/images/multipage.tiff')
self.assertEqual(im.n_frames, 3)
def test_multipage(self):
# issue #862
im = Image.open('Tests/images/multipage.tiff')

View File

@ -54,7 +54,7 @@ class TestImagingCoreResize(PillowTestCase):
# Make an image with one colored pixel, in one channel.
# When resized, that channel should be the same as a GS image.
# Other channels should be unaffected.
# The R and A channels should not swap, which is indicitive of
# The R and A channels should not swap, which is indicative of
# an endianness issues.
samples = {

View File

@ -300,32 +300,32 @@ class TestImageDraw(PillowTestCase):
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 5), BLACK, 2)
self.assert_image_equal(
img, expected, 'line straigth horizontal normal 2px wide failed')
img, expected, 'line straight horizontal normal 2px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_horizontal_w2px_inverted.png'))
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 5), BLACK, 2)
self.assert_image_equal(
img, expected, 'line straigth horizontal inverted 2px wide failed')
img, expected, 'line straight horizontal inverted 2px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_horizontal_w3px.png'))
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 5), BLACK, 3)
self.assert_image_equal(
img, expected, 'line straigth horizontal normal 3px wide failed')
img, expected, 'line straight horizontal normal 3px wide failed')
img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 5), BLACK, 3)
self.assert_image_equal(
img, expected, 'line straigth horizontal inverted 3px wide failed')
img, expected, 'line straight horizontal inverted 3px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_horizontal_w101px.png'))
expected.load()
img, draw = self.create_base_image_draw((200, 110))
draw.line((5, 55, 195, 55), BLACK, 101)
self.assert_image_equal(
img, expected, 'line straigth horizontal 101px wide failed')
img, expected, 'line straight horizontal 101px wide failed')
def test_line_h_s1_w2(self):
self.skipTest('failing')
@ -344,32 +344,32 @@ class TestImageDraw(PillowTestCase):
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 5, 14), BLACK, 2)
self.assert_image_equal(
img, expected, 'line straigth vertical normal 2px wide failed')
img, expected, 'line straight vertical normal 2px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_w2px_inverted.png'))
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 5, 5), BLACK, 2)
self.assert_image_equal(
img, expected, 'line straigth vertical inverted 2px wide failed')
img, expected, 'line straight vertical inverted 2px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_w3px.png'))
expected.load()
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 5, 14), BLACK, 3)
self.assert_image_equal(
img, expected, 'line straigth vertical normal 3px wide failed')
img, expected, 'line straight vertical normal 3px wide failed')
img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 5, 5), BLACK, 3)
self.assert_image_equal(
img, expected, 'line straigth vertical inverted 3px wide failed')
img, expected, 'line straight vertical inverted 3px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_w101px.png'))
expected.load()
img, draw = self.create_base_image_draw((110, 200))
draw.line((55, 5, 55, 195), BLACK, 101)
self.assert_image_equal(img, expected,
'line straigth vertical 101px wide failed')
'line straight vertical 101px wide failed')
expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_slope1px_w2px.png'))
expected.load()

View File

@ -278,7 +278,7 @@ findLCMStype(char* PILmode)
return TYPE_YCbCr_8;
}
else if (strcmp(PILmode, "LAB") == 0) {
// LabX equvalent like ALab, but not reversed -- no #define in lcms2
// LabX equivalent like ALab, but not reversed -- no #define in lcms2
return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1));
}

View File

@ -572,7 +572,7 @@ setup_module(PyObject* m) {
PyType_Ready(&Font_Type);
if (FT_Init_FreeType(&library))
return 0; /* leave it uninitalized */
return 0; /* leave it uninitialized */
FT_Library_Version(library, &major, &minor, &patch);

View File

@ -737,7 +737,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
return NULL;
}
// While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t
// While fails on 64 bit machines, complains that pos is an int instead of a Py_ssize_t
// while (PyDict_Next(dir, &pos, &key, &value)) {
for (pos=0;pos<d_size;pos++){
key = PyList_GetItem(keys,pos);

View File

@ -345,7 +345,7 @@ int quantize_octree(Pixel *pixelData,
/*
Create two color cubes, one fine grained with 8x16x8=1024
colors buckets and a coarse with 4x4x4=64 color buckets.
The coarse one guarantes that there are color buckets available for
The coarse one guarantees that there are color buckets available for
the whole color range (assuming nQuantPixels > 64).
For a quantization to 256 colors all 64 coarse colors will be used
@ -421,7 +421,7 @@ int quantize_octree(Pixel *pixelData,
/* add fine colors to the lookup cube */
add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors);
/* create result pixles and map palatte indices */
/* create result pixels and map palette indices */
qp = malloc(sizeof(Pixel)*nPixels);
if (!qp) goto error;
map_image_pixels(pixelData, nPixels, lookupCube, qp);

View File

@ -2,7 +2,7 @@
* The Python Imaging Library
* $Id$
*
* Pillow image resamling support
* Pillow image resampling support
*
* history:
* 2002-03-09 fl Created (for PIL 1.1.3)

View File

@ -245,7 +245,7 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
// back in. Can't use read encoded stripe.
// This thing pretty much requires that I have the whole image in one shot.
// Prehaps a stub version would work better???
// Perhaps a stub version would work better???
while(state->y < state->ysize){
if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) {
TRACE(("Decode Error, row %d\n", state->y));

View File

@ -808,7 +808,7 @@ unpackI12_I16(UINT8* out, const UINT8* in, int pixels){
FillOrder = 2 should be used only when BitsPerSample = 1 and
the data is either uncompressed or compressed using CCITT 1D
or 2D compression, to avoid potentially ambigous situations.
or 2D compression, to avoid potentially ambiguous situations.
Yeah. I thought so. We'll see how well people read the spec.
We've got several fillorder=2 modes in TiffImagePlugin.py

View File

@ -1,3 +1,8 @@
# Testing reqs
# Testing and documentation requirements
-e .
-r docs/requirements.txt
coveralls
nose
nose-cov
pep8
pyflakes