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 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 cffi"
- "travis_retry pip install coverage nose" - "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 - 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) 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 - Setting transparency value to 0 when the tRNS contains only null byte(s) #1239
[juztin] [juztin]
@ -31,6 +43,12 @@ Changelog (Pillow)
- Tiff: allow writing floating point tag values #1113 - Tiff: allow writing floating point tag values #1113
[bpedersen2] [bpedersen2]
2.8.2 (2015-06-06)
------------------
- Bug fix: Fixed Tiff handling of bad EXIF data
[radarhere]
2.8.1 (2015-04-02) 2.8.1 (2015-04-02)
------------------ ------------------

105
Makefile
View File

@ -1,28 +1,5 @@
.PHONY: pre clean install test inplace coverage test-dep help docs livedocs # 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
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
clean: clean:
python setup.py clean python setup.py clean
@ -30,46 +7,70 @@ clean:
rm -r build || true rm -r build || true
find . -name __pycache__ | xargs rm -r || 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: coverage:
# requires nose-cov
coverage erase coverage erase
coverage run --parallel-mode --include=PIL/* selftest.py coverage run --parallel-mode --include=PIL/* selftest.py
nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py nosetests --with-cov --cov='PIL/' --cov-report=html Tests/test_*.py
# doesn't combine properly before report, # Doesn't combine properly before report, writing report instead of displaying invalid report.
# writing report instead of displaying invalid report
rm -r htmlcov || true rm -r htmlcov || true
coverage combine coverage combine
coverage report coverage report
test-dep: doc:
pip install coveralls nose nose-cov pep8 pyflakes
docs:
$(MAKE) -C docs html $(MAKE) -C docs html
docserver: docserve:
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null&
# Test sdist upload via test.pythonpackages.com, no creds required help:
# .pypirc: @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=gztar,zip
test:
python test-installed.py
# https://docs.python.org/2/distutils/packageindex.html#the-pypirc-file
upload-test:
# [test] # [test]
# username: # username:
# password: # password:
# repository = http://test.pythonpackages.com # repository = http://test.pythonpackages.com
sdisttest: python setup.py sdist --format=gztar,zip upload -r test
python setup.py sdist --format=zip upload -r test
sdistup: upload:
python setup.py sdist --format=zip upload python setup.py sdist --format=gztar,zip upload
python setup.py sdist upload
sdist:
python setup.py sdist --format=zip

View File

@ -62,6 +62,10 @@ class DcxImageFile(PcxImageFile):
self.__fp = self.fp self.__fp = self.fp
self.seek(0) self.seek(0)
@property
def n_frames(self):
return len(self._offset)
def seek(self, frame): def seek(self, frame):
if frame >= len(self._offset): if frame >= len(self._offset):
raise EOFError("attempt to seek outside DCX directory") 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)) self.palette = ImagePalette.raw("RGB", b"".join(palette))
# set things up to decode first frame # set things up to decode first frame
self.frame = -1 self.__frame = -1
self.__fp = self.fp self.__fp = self.fp
self.__rewind = self.fp.tell()
self._n_frames = None
self.seek(0) self.seek(0)
def _palette(self, palette, shift): def _palette(self, palette, shift):
@ -109,11 +110,35 @@ class FliImageFile(ImageFile.ImageFile):
palette[i] = (r, g, b) palette[i] = (r, g, b)
i += 1 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) raise ValueError("cannot seek to frame %d" % frame)
self.frame = frame self.__frame = frame
# move to next frame # move to next frame
self.fp = self.__fp self.fp = self.__fp
@ -128,11 +153,10 @@ class FliImageFile(ImageFile.ImageFile):
self.decodermaxblock = framesize self.decodermaxblock = framesize
self.tile = [("fli", (0, 0)+self.size, self.__offset, None)] self.tile = [("fli", (0, 0)+self.size, self.__offset, None)]
self.__offset = self.__offset + framesize self.__offset += framesize
def tell(self): def tell(self):
return self.__frame
return self.frame
# #
# registry # registry

View File

@ -87,9 +87,30 @@ class GifImageFile(ImageFile.ImageFile):
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell() 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): 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: if frame == 0:
# rewind # rewind
@ -292,57 +313,10 @@ def _save(im, fp, filename):
for s in header: for s in header:
fp.write(s) 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 # local image header
fp.write(b"," + get_local_header(fp, im)
o16(0) + o16(0) + # bounding box
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(flags) + # flags
o8(8)) # bits
im_out.encoderconfig = (8, interlace) im_out.encoderconfig = (8, get_interlace(im))
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0, ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])]) RAWMODE[im_out.mode])])
@ -356,6 +330,85 @@ def _save(im, fp, filename):
pass 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): def _save_netpbm(im, fp, filename):
# #
@ -405,11 +458,26 @@ def _save_netpbm(im, fp, filename):
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# GIF utilities # 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): def getheader(im, palette=None, info=None):
"""Return a list of strings representing a GIF header""" """Return a list of strings representing a GIF header"""
optimize = info and info.get("optimize", 0)
# Header Block # Header Block
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp # http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
header = [ header = [
@ -431,15 +499,8 @@ def getheader(im, palette=None, info=None):
used_palette_colors = palette_bytes = None used_palette_colors = palette_bytes = None
if im.mode in ("P", "L") and optimize: if _get_optimize(im, info):
used_palette_colors = [] used_palette_colors = _get_used_palette_colors(im)
# check which colors are used
i = 0
for count in im.histogram():
if count:
used_palette_colors.append(i)
i += 1
# create the new palette if not every color is used # create the new palette if not every color is used
if len(used_palette_colors) < 256: if len(used_palette_colors) < 256:
@ -510,13 +571,7 @@ def getdata(im, offset=(0, 0), **params):
im.encoderinfo = params im.encoderinfo = params
# local image header # local image header
fp.write(b"," + get_local_header(fp, im, offset)
o16(offset[0]) + # offset
o16(offset[1]) +
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(0) + # flags
o8(8)) # bits
ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])]) 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 width, height = im.size
filter(lambda x: False if (x[0] > width or x[1] > height or filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 255 or x[1] > 255) else True, sizes) 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 offset = fp.tell() + len(sizes)*16
for size in sizes: for size in sizes:
width, height = size width, height = size
@ -57,7 +57,7 @@ def _save(im, fp, filename):
fp.write(b"\0") # bColorCount(1) fp.write(b"\0") # bColorCount(1)
fp.write(b"\0") # bReserved(1) fp.write(b"\0") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2) 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() image_io = BytesIO()
tmp = im.copy() tmp = im.copy()
@ -66,8 +66,8 @@ def _save(im, fp, filename):
image_io.seek(0) image_io.seek(0)
image_bytes = image_io.read() image_bytes = image_io.read()
bytes_len = len(image_bytes) bytes_len = len(image_bytes)
fp.write(struct.pack("I", bytes_len)) # dwBytesInRes(4) fp.write(struct.pack("<I", bytes_len)) # dwBytesInRes(4)
fp.write(struct.pack("I", offset)) # dwImageOffset(4) fp.write(struct.pack("<I", offset)) # dwImageOffset(4)
current = fp.tell() current = fp.tell()
fp.seek(offset) fp.seek(offset)
fp.write(image_bytes) fp.write(image_bytes)

View File

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

View File

@ -1810,7 +1810,7 @@ class Image(object):
self.readonly = 0 self.readonly = 0
self.pyaccess = None 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. # instead of bloating the method docs, add a separate chapter.
def transform(self, size, method, data=None, resample=NEAREST, fill=1): 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 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() potential CObjects... Python won't let you use type()
on them, and raises a TypeError (stupid, if you ask on them, and raises a TypeError (stupid, if you ask
me!) me!)
@ -123,8 +123,8 @@ FLAGS = {
"NOTCACHE": 64, # Inhibit 1-pixel cache "NOTCACHE": 64, # Inhibit 1-pixel cache
"NOTPRECALC": 256, "NOTPRECALC": 256,
"NULLTRANSFORM": 512, # Don't transform anyway "NULLTRANSFORM": 512, # Don't transform anyway
"HIGHRESPRECALC": 1024, # Use more memory to give better accurancy "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy
"LOWRESPRECALC": 2048, # Use less memory to minimize resouces "LOWRESPRECALC": 2048, # Use less memory to minimize resources
"WHITEBLACKCOMPENSATION": 8192, "WHITEBLACKCOMPENSATION": 8192,
"BLACKPOINTCOMPENSATION": 8192, "BLACKPOINTCOMPENSATION": 8192,
"GAMUTCHECK": 4096, # Out of Gamut alarm "GAMUTCHECK": 4096, # Out of Gamut alarm
@ -573,7 +573,7 @@ def applyTransform(im, transform, inPlace=0):
This function applies a pre-calculated transform (from This function applies a pre-calculated transform (from
ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
to an image. The transform can be used for multiple images, saving 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 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 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 If an error occurs while trying to obtain the default intent, a
PyCMSError is raised. 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 intent for this profile. Most profiles support multiple
rendering intents, but are intended mostly for one type of conversion. rendering intents, but are intended mostly for one type of conversion.
If you wish to use a different intent than returned, use 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 see the pyCMS documentation for details on rendering intents and what
they do. 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 output, or proof
INPUT = 0 (or use ImageCms.DIRECTION_INPUT) INPUT = 0 (or use ImageCms.DIRECTION_INPUT)

View File

@ -73,7 +73,7 @@ class Contrast(_Enhance):
class Brightness(_Enhance): class Brightness(_Enhance):
"""Adjust image brightness. """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 enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the
original image. original image.
""" """

View File

@ -4,7 +4,7 @@
# #
# JPEG (JFIF) file handling # 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) # Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1)
# #
# History: # History:

View File

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

View File

@ -62,6 +62,10 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
def load_seek(self, pos): def load_seek(self, pos):
self.__fp.seek(pos) self.__fp.seek(pos)
@property
def n_frames(self):
return self.__framecount
def seek(self, frame): def seek(self, frame):
if frame < 0 or frame >= self.__framecount: if frame < 0 or frame >= self.__framecount:
raise EOFError("no more images in MPO file") 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._fp = self.fp
self.frame = 0 self.frame = 0
@property
def n_frames(self):
return len(self.layers)
def seek(self, layer): def seek(self, layer):
# seek to given layer (1..max) # seek to given layer (1..max)
if layer == self.frame: if layer == self.frame:

View File

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

View File

@ -294,7 +294,7 @@ class ImageFileDirectory(collections.MutableMapping):
def named(self): 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 from PIL import TiffTags
result = {} result = {}
@ -426,6 +426,11 @@ class ImageFileDirectory(collections.MutableMapping):
for i in range(i16(fp.read(2))): for i in range(i16(fp.read(2))):
ifd = fp.read(12) 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) tag, typ = i16(ifd), i16(ifd, 2)
@ -476,7 +481,14 @@ class ImageFileDirectory(collections.MutableMapping):
else: else:
print("- value:", self[tag]) 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 # save primitives
@ -636,6 +648,8 @@ class TiffImageFile(ImageFile.ImageFile):
self.__first = self.__next = self.ifd.i32(ifh, 4) self.__first = self.__next = self.ifd.i32(ifh, 4)
self.__frame = -1 self.__frame = -1
self.__fp = self.fp self.__fp = self.fp
self._frame_pos = []
self._n_frames = None
if Image.DEBUG: if Image.DEBUG:
print("*** TiffImageFile._open ***") print("*** TiffImageFile._open ***")
@ -645,28 +659,30 @@ class TiffImageFile(ImageFile.ImageFile):
# and load the first frame # and load the first frame
self._seek(0) 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): def seek(self, frame):
"Select a given frame as current image" "Select a given frame as current image"
if frame < 0: self._seek(max(frame, 0)) # Questionable backwards compatibility.
frame = 0
self._seek(frame)
# Create a new core image object on second and # Create a new core image object on second and
# subsequent frames in the image. Image may be # subsequent frames in the image. Image may be
# different size/mode. # different size/mode.
Image._decompression_bomb_check(self.size) Image._decompression_bomb_check(self.size)
self.im = Image.core.new(self.mode, 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): def _seek(self, frame):
self.fp = self.__fp self.fp = self.__fp
if frame < self.__frame: while len(self._frame_pos) <= frame:
# rewind file
self.__frame = -1
self.__next = self.__first
while self.__frame < frame:
if not self.__next: if not self.__next:
raise EOFError("no more images in TIFF file") raise EOFError("no more images in TIFF file")
if Image.DEBUG: if Image.DEBUG:
@ -676,14 +692,19 @@ class TiffImageFile(ImageFile.ImageFile):
# was passed to libtiff, invalidating the buffer # was passed to libtiff, invalidating the buffer
self.fp.tell() self.fp.tell()
self.fp.seek(self.__next) self.fp.seek(self.__next)
self._frame_pos.append(self.__next)
if Image.DEBUG: if Image.DEBUG:
print("Loading tags, location: %s" % self.fp.tell()) print("Loading tags, location: %s" % self.fp.tell())
self.tag.load(self.fp) self.tag.load(self.fp)
self.__next = self.tag.next self.__next = self.tag.next
self.__frame += 1 self.__frame += 1
self.fp.seek(self._frame_pos[frame])
self.tag.load(self.fp)
self.__frame = frame
self._setup() self._setup()
def _tell(self): def tell(self):
"Return the current frame number"
return self.__frame return self.__frame
def _decoder(self, rawmode, layer, tile=None): 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 # Rearranging for supporting byteio items, since they have a fileno
# that returns an IOError if there's no underlying fp. Easier to # 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: if Image.DEBUG:
print("have getvalue. just sending in a string from getvalue") print("have getvalue. just sending in a string from getvalue")
n, err = decoder.decode(self.fp.getvalue()) n, err = decoder.decode(self.fp.getvalue())
@ -985,7 +1006,7 @@ class TiffImageFile(ImageFile.ImageFile):
# Write TIFF files # Write TIFF files
# little endian is default except for image modes with # little endian is default except for image modes with
# explict big endian byte-order # explicit big endian byte-order
SAVE_INFO = { SAVE_INFO = {
# mode => rawmode, byteorder, photometrics, # 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. 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 (Linux) :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 :target: https://travis-ci.org/python-pillow/pillow-wheels
:alt: Travis CI build status (OS X) :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/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Latest PyPI version :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/ :target: https://pypi.python.org/pypi/Pillow/
:alt: Number of PyPI downloads :alt: Number of PyPI downloads

View File

@ -75,8 +75,8 @@ def makedelta(fp, sequence):
for im in sequence: for im in sequence:
# # To specify duration, add the time in milliseconds to getdata(),
# FIXME: write graphics control block before each frame # e.g. getdata(im, duration=1000)
if not previous: 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 # Assert
self.assertEqual(frame, 0) 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): def test_seek_too_far(self):
# Arrange # Arrange
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)

View File

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

View File

@ -134,6 +134,10 @@ class TestFileGif(PillowTestCase):
except EOFError: except EOFError:
self.assertEqual(framecount, 5) 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): def test_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif") img = Image.open("Tests/images/dispose_none.gif")
try: try:
@ -169,6 +173,33 @@ class TestFileGif(PillowTestCase):
# first frame # first frame
self.assertEqual(img.histogram()[img.info['transparency']], 0) 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__': if __name__ == '__main__':
unittest.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) im.seek(0)
self.assertEqual(im.tell(), 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): def test_image_grab(self):
for test_file in test_files: for test_file in test_files:
im = Image.open(test_file) im = Image.open(test_file)

View File

@ -16,6 +16,13 @@ class TestImagePsd(PillowTestCase):
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PSD") 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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -42,6 +42,10 @@ class TestImageSpider(PillowTestCase):
# Assert # Assert
self.assertEqual(index, 0) self.assertEqual(index, 0)
def test_n_frames(self):
im = Image.open(TEST_FILE)
self.assertEqual(im.n_frames, 1)
def test_loadImageSeries(self): def test_loadImageSeries(self):
# Arrange # Arrange
not_spider_file = "Tests/images/hopper.ppm" 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 from PIL import Image, TiffImagePlugin
import struct
class TestFileTiff(PillowTestCase): class TestFileTiff(PillowTestCase):
@ -77,6 +79,12 @@ class TestFileTiff(PillowTestCase):
im._setup() im._setup()
self.assertEqual(im.info['dpi'], (72., 72.)) 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): def test_little_endian(self):
im = Image.open('Tests/images/16bit.cropped.tif') im = Image.open('Tests/images/16bit.cropped.tif')
self.assertEqual(im.getpixel((0, 0)), 480) self.assertEqual(im.getpixel((0, 0)), 480)
@ -142,6 +150,13 @@ class TestFileTiff(PillowTestCase):
self.assertEqual( self.assertEqual(
im.getextrema(), (-3.140936851501465, 3.140684127807617)) 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): def test_multipage(self):
# issue #862 # issue #862
im = Image.open('Tests/images/multipage.tiff') 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. # Make an image with one colored pixel, in one channel.
# When resized, that channel should be the same as a GS image. # When resized, that channel should be the same as a GS image.
# Other channels should be unaffected. # 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. # an endianness issues.
samples = { samples = {

View File

@ -300,32 +300,32 @@ class TestImageDraw(PillowTestCase):
img, draw = self.create_base_image_draw((20, 20)) img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 5), BLACK, 2) draw.line((5, 5, 14, 5), BLACK, 2)
self.assert_image_equal( 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_horizontal_w2px_inverted.png')) 'line_horizontal_w2px_inverted.png'))
expected.load() expected.load()
img, draw = self.create_base_image_draw((20, 20)) img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 5), BLACK, 2) draw.line((14, 5, 5, 5), BLACK, 2)
self.assert_image_equal( 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_horizontal_w3px.png')) 'line_horizontal_w3px.png'))
expected.load() expected.load()
img, draw = self.create_base_image_draw((20, 20)) img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 14, 5), BLACK, 3) draw.line((5, 5, 14, 5), BLACK, 3)
self.assert_image_equal( 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)) img, draw = self.create_base_image_draw((20, 20))
draw.line((14, 5, 5, 5), BLACK, 3) draw.line((14, 5, 5, 5), BLACK, 3)
self.assert_image_equal( 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_horizontal_w101px.png')) 'line_horizontal_w101px.png'))
expected.load() expected.load()
img, draw = self.create_base_image_draw((200, 110)) img, draw = self.create_base_image_draw((200, 110))
draw.line((5, 55, 195, 55), BLACK, 101) draw.line((5, 55, 195, 55), BLACK, 101)
self.assert_image_equal( 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): def test_line_h_s1_w2(self):
self.skipTest('failing') self.skipTest('failing')
@ -344,32 +344,32 @@ class TestImageDraw(PillowTestCase):
img, draw = self.create_base_image_draw((20, 20)) img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 5, 14), BLACK, 2) draw.line((5, 5, 5, 14), BLACK, 2)
self.assert_image_equal( 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_w2px_inverted.png')) 'line_vertical_w2px_inverted.png'))
expected.load() expected.load()
img, draw = self.create_base_image_draw((20, 20)) img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 5, 5), BLACK, 2) draw.line((5, 14, 5, 5), BLACK, 2)
self.assert_image_equal( 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_w3px.png')) 'line_vertical_w3px.png'))
expected.load() expected.load()
img, draw = self.create_base_image_draw((20, 20)) img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 5, 5, 14), BLACK, 3) draw.line((5, 5, 5, 14), BLACK, 3)
self.assert_image_equal( 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)) img, draw = self.create_base_image_draw((20, 20))
draw.line((5, 14, 5, 5), BLACK, 3) draw.line((5, 14, 5, 5), BLACK, 3)
self.assert_image_equal( 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_w101px.png')) 'line_vertical_w101px.png'))
expected.load() expected.load()
img, draw = self.create_base_image_draw((110, 200)) img, draw = self.create_base_image_draw((110, 200))
draw.line((55, 5, 55, 195), BLACK, 101) draw.line((55, 5, 55, 195), BLACK, 101)
self.assert_image_equal(img, expected, 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, expected = Image.open(os.path.join(IMAGES_PATH,
'line_vertical_slope1px_w2px.png')) 'line_vertical_slope1px_w2px.png'))
expected.load() expected.load()

View File

@ -278,7 +278,7 @@ findLCMStype(char* PILmode)
return TYPE_YCbCr_8; return TYPE_YCbCr_8;
} }
else if (strcmp(PILmode, "LAB") == 0) { 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)); 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); PyType_Ready(&Font_Type);
if (FT_Init_FreeType(&library)) if (FT_Init_FreeType(&library))
return 0; /* leave it uninitalized */ return 0; /* leave it uninitialized */
FT_Library_Version(library, &major, &minor, &patch); FT_Library_Version(library, &major, &minor, &patch);

View File

@ -737,7 +737,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
return NULL; 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)) { // while (PyDict_Next(dir, &pos, &key, &value)) {
for (pos=0;pos<d_size;pos++){ for (pos=0;pos<d_size;pos++){
key = PyList_GetItem(keys,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 Create two color cubes, one fine grained with 8x16x8=1024
colors buckets and a coarse with 4x4x4=64 color buckets. 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). the whole color range (assuming nQuantPixels > 64).
For a quantization to 256 colors all 64 coarse colors will be used 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 fine colors to the lookup cube */
add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors); 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); qp = malloc(sizeof(Pixel)*nPixels);
if (!qp) goto error; if (!qp) goto error;
map_image_pixels(pixelData, nPixels, lookupCube, qp); map_image_pixels(pixelData, nPixels, lookupCube, qp);

View File

@ -2,7 +2,7 @@
* The Python Imaging Library * The Python Imaging Library
* $Id$ * $Id$
* *
* Pillow image resamling support * Pillow image resampling support
* *
* history: * history:
* 2002-03-09 fl Created (for PIL 1.1.3) * 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. // back in. Can't use read encoded stripe.
// This thing pretty much requires that I have the whole image in one shot. // 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){ while(state->y < state->ysize){
if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) { if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) {
TRACE(("Decode Error, row %d\n", state->y)); 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 FillOrder = 2 should be used only when BitsPerSample = 1 and
the data is either uncompressed or compressed using CCITT 1D 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. Yeah. I thought so. We'll see how well people read the spec.
We've got several fillorder=2 modes in TiffImagePlugin.py We've got several fillorder=2 modes in TiffImagePlugin.py

View File

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