Merge branch 'master' into flake8

This commit is contained in:
hugovk 2014-09-14 10:23:51 +03:00
commit 1de128d6b6
74 changed files with 878 additions and 541 deletions

View File

@ -16,15 +16,14 @@ python:
- 3.4
install:
- "sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick lcov"
- "pip install cffi"
- "pip install coveralls nose coveralls-merge"
- "gem install coveralls-lcov"
- "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"
# Pyroma installation is slow on Py3, so just do it for Py2.
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then travis_retry pip install pyroma; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then pip install unittest2; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi
# webp
- pushd depends && ./install_webp.sh && popd
@ -42,18 +41,20 @@ script:
after_success:
# gather the coverage data
- travis_retry sudo apt-get -qq install lcov
- lcov --capture --directory . -b . --output-file coverage.info
# filter to remove system headers
- lcov --remove coverage.info '/usr/*' -o coverage.filtered.info
# convert to json
- travis_retry gem install coveralls-lcov
- coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
- coverage report
- travis_retry pip install coveralls-merge
- coveralls-merge coverage.c.json
- pip install pep8 pyflakes
- pep8 --statistics --count *.py
- travis_retry pip install pep8 pyflakes
- pep8 --statistics --count PIL/*.py
- pep8 --statistics --count Tests/*.py
- pyflakes *.py | tee >(wc -l)
@ -65,3 +66,5 @@ after_success:
# (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-run.sh; fi
matrix:
fast_finish: true

View File

@ -4,6 +4,27 @@ Changelog (Pillow)
2.6.0 (unreleased)
------------------
- Jpeg2k Decode/Encode Memory Leak Fix #898
[joshware, wiredfool]
- EpsFilePlugin Speed improvements #886
[wiredfool, karstenw]
- Don't resize if already the right size.
[radarhere]
- Fix for reading multipage TIFFs #885
[kostrom, wiredfool]
- Correctly handle saving gray and CMYK JPEGs with quality=keep #857
[etienned]
- Correct duplicate Tiff Metadata and Exif tag values
[hugovk]
- Windows fixes #871
[wiredfool]
- Fix TGA files with image ID field #856
[megabuz]
@ -55,7 +76,7 @@ Changelog (Pillow)
- Added docs for ExifTags
[Wintermute3]
- More tests for CurImagePlugin, DcxImagePlugin, ImageFont, ImageMath, ImagePalette, IptcImagePlugin, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util
- More tests for CurImagePlugin, DcxImagePlugin, Effects.c, GimpGradientFile, ImageFont, ImageMath, ImagePalette, IptcImagePlugin, SpiderImagePlugin, SgiImagePlugin, XpmImagePlugin and _util
[hugovk]
- Fix return value of FreeTypeFont.textsize() does not include font offsets

View File

@ -91,26 +91,32 @@ def Ghostscript(tile, size, fp, scale=1):
out_fd, outfile = tempfile.mkstemp()
os.close(out_fd)
in_fd, infile = tempfile.mkstemp()
os.close(in_fd)
# ignore length and offset!
# ghostscript can read it
# copy whole file to read in ghostscript
with open(infile, 'wb') as f:
# fetch length of fp
fp.seek(0, 2)
fsize = fp.tell()
# ensure start position
# go back
fp.seek(0)
lengthfile = fsize
while lengthfile > 0:
s = fp.read(min(lengthfile, 100*1024))
if not s:
break
length -= len(s)
f.write(s)
infile_temp = None
if hasattr(fp, 'name') and os.path.exists(fp.name):
infile = fp.name
else:
in_fd, infile_temp = tempfile.mkstemp()
os.close(in_fd)
infile = infile_temp
# ignore length and offset!
# ghostscript can read it
# copy whole file to read in ghostscript
with open(infile_temp, 'wb') as f:
# fetch length of fp
fp.seek(0, 2)
fsize = fp.tell()
# ensure start position
# go back
fp.seek(0)
lengthfile = fsize
while lengthfile > 0:
s = fp.read(min(lengthfile, 100*1024))
if not s:
break
lengthfile -= len(s)
f.write(s)
# Build ghostscript command
command = ["gs",
@ -143,56 +149,36 @@ def Ghostscript(tile, size, fp, scale=1):
finally:
try:
os.unlink(outfile)
os.unlink(infile)
except:
pass
if infile_temo:
os.unlink(infile_temp)
except: pass
return im
class PSFile:
"""Wrapper that treats either CR or LF as end of line."""
"""Wrapper for bytesio object that treats either CR or LF as end of line."""
def __init__(self, fp):
self.fp = fp
self.char = None
def __getattr__(self, id):
v = getattr(self.fp, id)
setattr(self, id, v)
return v
def seek(self, offset, whence=0):
self.char = None
self.fp.seek(offset, whence)
def read(self, count):
return self.fp.read(count).decode('latin-1')
def readbinary(self, count):
return self.fp.read(count)
def tell(self):
pos = self.fp.tell()
if self.char:
pos -= 1
return pos
def readline(self):
s = b""
if self.char:
c = self.char
self.char = None
else:
c = self.fp.read(1)
s = self.char or b""
self.char = None
c = self.fp.read(1)
while c not in b"\r\n":
s = s + c
c = self.fp.read(1)
if c == b"\r":
self.char = self.fp.read(1)
if self.char == b"\n":
self.char = None
return s.decode('latin-1') + "\n"
self.char = self.fp.read(1)
# line endings can be 1 or 2 of \r \n, in either order
if self.char in b"\r\n":
self.char = None
return s.decode('latin-1')
def _accept(prefix):
return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5
@ -208,33 +194,23 @@ class EpsImageFile(ImageFile.ImageFile):
format = "EPS"
format_description = "Encapsulated Postscript"
mode_map = { 1:"L", 2:"LAB", 3:"RGB" }
def _open(self):
(length, offset) = self._find_offset(self.fp)
fp = PSFile(self.fp)
# FIX for: Some EPS file not handled correctly / issue #302
# EPS can contain binary data
# or start directly with latin coding
# read header in both ways to handle both
# file types
# more info see:
# http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
# for HEAD without binary preview
s = fp.read(4)
# for HEAD with binary preview
fp.seek(0)
sb = fp.readbinary(160)
if s[:4] == "%!PS":
fp.seek(0, 2)
length = fp.tell()
offset = 0
elif i32(sb[0:4]) == 0xC6D3D0C5:
offset = i32(sb[4:8])
length = i32(sb[8:12])
else:
raise SyntaxError("not an EPS file")
# Rewrap the open file pointer in something that will
# convert line endings and decode to latin-1.
try:
if bytes is str:
# Python2, no encoding conversion necessary
fp = open(self.fp.name, "Ur")
else:
# Python3, can use bare open command.
fp = open(self.fp.name, "Ur", encoding='latin-1')
except Exception as msg:
# Expect this for bytesio/stringio
fp = PSFile(self.fp)
# go to offset - start of "%!PS"
fp.seek(offset)
@ -247,18 +223,12 @@ class EpsImageFile(ImageFile.ImageFile):
#
# Load EPS header
s = fp.readline()
s = fp.readline().strip('\r\n')
while s:
if len(s) > 255:
raise SyntaxError("not an EPS file")
if s[-2:] == '\r\n':
s = s[:-2]
elif s[-1:] == '\n':
s = s[:-1]
try:
m = split.match(s)
except re.error as v:
@ -280,9 +250,7 @@ class EpsImageFile(ImageFile.ImageFile):
pass
else:
m = field.match(s)
if m:
k = m.group(1)
@ -292,16 +260,16 @@ class EpsImageFile(ImageFile.ImageFile):
self.info[k[:8]] = k[9:]
else:
self.info[k] = ""
elif s[0:1] == '%':
elif s[0] == '%':
# handle non-DSC Postscript comments that some
# tools mistakenly put in the Comments section
pass
else:
raise IOError("bad EPS header")
s = fp.readline()
s = fp.readline().strip('\r\n')
if s[:1] != "%":
if s[0] != "%":
break
#
@ -312,64 +280,48 @@ class EpsImageFile(ImageFile.ImageFile):
if len(s) > 255:
raise SyntaxError("not an EPS file")
if s[-2:] == '\r\n':
s = s[:-2]
elif s[-1:] == '\n':
s = s[:-1]
if s[:11] == "%ImageData:":
# Encoded bitmapped image.
[x, y, bi, mo, z3, z4, en, id] = s[11:].split(None, 7)
[x, y, bi, mo, z3, z4, en, id] =\
s[11:].split(None, 7)
x = int(x)
y = int(y)
bi = int(bi)
mo = int(mo)
en = int(en)
if en == 1:
decoder = "eps_binary"
elif en == 2:
decoder = "eps_hex"
else:
if int(bi) != 8:
break
if bi != 8:
try:
self.mode = self.mode_map[int(mo)]
except:
break
if mo == 1:
self.mode = "L"
elif mo == 2:
self.mode = "LAB"
elif mo == 3:
self.mode = "RGB"
else:
break
if id[:1] == id[-1:] == '"':
id = id[1:-1]
# Scan forward to the actual image data
while True:
s = fp.readline()
if not s:
break
if s[:len(id)] == id:
self.size = x, y
self.tile2 = [(decoder,
(0, 0, x, y),
fp.tell(),
0)]
return
s = fp.readline()
self.size = int(x), int(y)
return
s = fp.readline().strip('\r\n')
if not s:
break
if not box:
raise IOError("cannot determine EPS bounding box")
def _find_offset(self, fp):
s = fp.read(160)
if s[:4] == b"%!PS":
# for HEAD without binary preview
fp.seek(0, 2)
length = fp.tell()
offset = 0
elif i32(s[0:4]) == 0xC6D3D0C5:
# FIX for: Some EPS file not handled correctly / issue #302
# EPS can contain binary data
# or start directly with latin coding
# more info see http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
offset = i32(s[4:8])
length = i32(s[8:12])
else:
raise SyntaxError("not an EPS file")
return (length, offset)
def load(self, scale=1):
# Load EPS via Ghostscript
if not self.tile:

View File

@ -67,8 +67,8 @@ TAGS = {
0x0213: "YCbCrPositioning",
0x0214: "ReferenceBlackWhite",
0x1000: "RelatedImageFileFormat",
0x1001: "RelatedImageLength", # FIXME / Dictionary contains duplicate keys
0x1001: "RelatedImageWidth", # FIXME \ Dictionary contains duplicate keys
0x1001: "RelatedImageWidth",
0x1002: "RelatedImageLength",
0x828d: "CFARepeatPatternDim",
0x828e: "CFAPattern",
0x828f: "BatteryLevel",

View File

@ -24,6 +24,7 @@ from PIL._binary import o8
EPSILON = 1e-10
def linear(middle, pos):
if pos <= middle:
if middle < EPSILON:
@ -38,25 +39,30 @@ def linear(middle, pos):
else:
return 0.5 + 0.5 * pos / middle
def curved(middle, pos):
return pos ** (log(0.5) / log(max(middle, EPSILON)))
def sine(middle, pos):
return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0
def sphere_increasing(middle, pos):
return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2)
def sphere_decreasing(middle, pos):
return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2)
SEGMENTS = [ linear, curved, sine, sphere_increasing, sphere_decreasing ]
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
class GradientFile:
gradient = None
def getpalette(self, entries = 256):
def getpalette(self, entries=256):
palette = []
@ -89,6 +95,7 @@ class GradientFile:
return b"".join(palette), "RGBA"
##
# File handler for GIMP's gradient format.
@ -99,7 +106,13 @@ class GimpGradientFile(GradientFile):
if fp.readline()[:13] != b"GIMP Gradient":
raise SyntaxError("not a GIMP gradient file")
count = int(fp.readline())
line = fp.readline()
# GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do
if line.startswith(b"Name: "):
line = fp.readline().strip()
count = int(line)
gradient = []
@ -108,13 +121,13 @@ class GimpGradientFile(GradientFile):
s = fp.readline().split()
w = [float(x) for x in s[:11]]
x0, x1 = w[0], w[2]
xm = w[1]
rgb0 = w[3:7]
rgb1 = w[7:11]
x0, x1 = w[0], w[2]
xm = w[1]
rgb0 = w[3:7]
rgb1 = w[7:11]
segment = SEGMENTS[int(s[11])]
cspace = int(s[12])
cspace = int(s[12])
if cspace != 0:
raise IOError("cannot handle HSV colour space")

View File

@ -1516,6 +1516,9 @@ class Image:
self.load()
if self.size == size:
return self._new(self.im)
if self.mode in ("1", "P"):
resample = NEAREST
@ -1912,6 +1915,16 @@ class Image:
im = self.im.transpose(method)
return self._new(im)
def effect_spread(self, distance):
"""
Randomly spread pixels in an image.
:param distance: Distance to spread pixels.
"""
self.load()
im = self.im.effect_spread(distance)
return self._new(im)
# --------------------------------------------------------------------
# Lazy operations
@ -2421,3 +2434,32 @@ def _show(image, **options):
def _showxv(image, title=None, **options):
from PIL import ImageShow
ImageShow.show(image, title, **options)
# --------------------------------------------------------------------
# Effects
def effect_mandelbrot(size, extent, quality):
"""
Generate a Mandelbrot set covering the given extent.
:param size: The requested size in pixels, as a 2-tuple:
(width, height).
:param extent: The extent to cover, as a 4-tuple:
(x0, y0, x1, y2).
:param quality: Quality.
"""
return Image()._new(core.effect_mandelbrot(size, extent, quality))
def effect_noise(size, sigma):
"""
Generate Gaussian noise centered around 128.
:param size: The requested size in pixels, as a 2-tuple:
(width, height).
:param sigma: Standard deviation of noise.
"""
return Image()._new(core.effect_noise(size, sigma))
# End of file

View File

@ -234,6 +234,8 @@ class ImageFile(Image.Image):
break
b = b[n:]
t = t + n
# Need to cleanup here to prevent leaks in PyPy
d.cleanup()
self.tile = []
self.readonly = readonly
@ -479,6 +481,7 @@ def _save(im, fp, tile, bufsize=0):
break
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
e.cleanup()
else:
# slight speedup: compress to real file object
for e, b, o, a in tile:
@ -489,6 +492,7 @@ def _save(im, fp, tile, bufsize=0):
s = e.encode_to_file(fh, bufsize)
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
e.cleanup()
try:
fp.flush()
except:

View File

@ -550,6 +550,15 @@ def convert_dict_qtables(qtables):
def get_sampling(im):
# There's no subsampling when image have only 1 layer
# (grayscale images) or when they are CMYK (4 layers),
# so set subsampling to default value.
#
# NOTE: currently Pillow can't encode JPEG to YCCK format.
# If YCCK support is added in the future, subsampling code will have
# to be updated (here and in JpegEncode.c) to deal with 4 layers.
if not hasattr(im, 'layers') or im.layers in (1, 4):
return -1
sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
return samplings.get(sampling, -1)

View File

@ -281,6 +281,7 @@ class ImageFileDirectory(collections.MutableMapping):
self.tagdata = {}
self.tagtype = {} # added 2008-06-05 by Florian Hoech
self.next = None
self.offset = None
def __str__(self):
return str(self.as_dict())
@ -415,6 +416,7 @@ class ImageFileDirectory(collections.MutableMapping):
# load tag dictionary
self.reset()
self.offset = fp.tell()
i16 = self.i16
i32 = self.i32
@ -446,7 +448,11 @@ class ImageFileDirectory(collections.MutableMapping):
# Get and expand tag value
if size > 4:
here = fp.tell()
if Image.DEBUG:
print ("Tag Location: %s" %here)
fp.seek(i32(ifd, 8))
if Image.DEBUG:
print ("Data Location: %s" %fp.tell())
data = ImageFile._safe_read(fp, size)
fp.seek(here)
else:
@ -630,18 +636,20 @@ class TiffImageFile(ImageFile.ImageFile):
def seek(self, frame):
"Select a given frame as current image"
if frame < 0:
frame = 0
self._seek(frame)
# 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
@ -650,14 +658,21 @@ class TiffImageFile(ImageFile.ImageFile):
while self.__frame < frame:
if not self.__next:
raise EOFError("no more images in TIFF file")
if Image.DEBUG:
print("Seeking to frame %s, on frame %s, __next %s, location: %s"%
(frame, self.__frame, self.__next, self.fp.tell()))
# reset python3 buffered io handle in case fp
# was passed to libtiff, invalidating the buffer
self.fp.tell()
self.fp.seek(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._setup()
def _tell(self):
return self.__frame
def _decoder(self, rawmode, layer, tile=None):
@ -706,6 +721,7 @@ class TiffImageFile(ImageFile.ImageFile):
# (self._compression, (extents tuple),
# 0, (rawmode, self._compression, fp))
ignored, extents, ignored_2, args = self.tile[0]
args = args + (self.ifd.offset,)
decoder = Image._getdecoder(self.mode, 'libtiff', args,
self.decoderconfig)
try:
@ -744,7 +760,8 @@ class TiffImageFile(ImageFile.ImageFile):
self.readonly = 0
# libtiff closed the fp in a, we need to close self.fp, if possible
if hasattr(self.fp, 'close'):
self.fp.close()
if not self.__next:
self.fp.close()
self.fp = None # might be shared
if err < 0:

View File

@ -46,8 +46,8 @@ TAGS = {
(262, 5): "CMYK",
(262, 6): "YCbCr",
(262, 8): "CieLAB",
(262, 32803): "CFA", # TIFF/EP, Adobe DNG
(262, 32892): "LinearRaw", # Adobe DNG
(262, 32803): "CFA", # TIFF/EP, Adobe DNG
(262, 32892): "LinearRaw", # Adobe DNG
263: "Thresholding",
264: "CellWidth",
@ -240,7 +240,7 @@ TAGS = {
45579: "YawAngle",
45580: "PitchAngle",
45581: "RollAngle",
# Adobe DNG
50706: "DNGVersion",
50707: "DNGBackwardVersion",
@ -255,7 +255,6 @@ TAGS = {
50716: "BlackLevelDeltaV",
50717: "WhiteLevel",
50718: "DefaultScale",
50741: "BestQualityScale", # FIXME! Dictionary contains duplicate keys 50741
50719: "DefaultCropOrigin",
50720: "DefaultCropSize",
50778: "CalibrationIlluminant1",
@ -279,11 +278,12 @@ TAGS = {
50737: "ChromaBlurRadius",
50738: "AntiAliasStrength",
50740: "DNGPrivateData",
50741: "MakerNoteSafety", # FIXME! Dictionary contains duplicate keys 50741
50741: "MakerNoteSafety",
50780: "BestQualityScale",
#ImageJ
50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe
50839: "ImageJMetaData", # private tag registered with Adobe
# ImageJ
50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe
50839: "ImageJMetaData", # private tag registered with Adobe
}
##

View File

@ -20,3 +20,6 @@ Pillow is the "friendly" PIL fork by `Alex Clark and Contributors <https://githu
.. image:: https://coveralls.io/repos/python-pillow/Pillow/badge.png?branch=master
:target: https://coveralls.io/r/python-pillow/Pillow?branch=master
.. image:: https://landscape.io/github/python-pillow/Pillow/master/landscape.png
:target: https://landscape.io/github/python-pillow/Pillow/master
:alt: Code Health

View File

@ -10,6 +10,9 @@ Install::
pip install coverage nose
If you're using Python 2.6, there's one additional dependency::
pip install unittest2
Execution
---------

42
Tests/check_j2k_leaks.py Executable file
View File

@ -0,0 +1,42 @@
from helper import unittest, PillowTestCase
import sys
from PIL import Image
from io import BytesIO
# Limits for testing the leak
mem_limit = 1024*1048576
stack_size = 8*1048576
iterations = int((mem_limit/stack_size)*2)
codecs = dir(Image.core)
test_file = "Tests/images/rgb_trns_ycbc.jp2"
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
class TestJpegLeaks(PillowTestCase):
def setUp(self):
if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs:
self.skipTest('JPEG 2000 support not available')
def test_leak_load(self):
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for count in range(iterations):
with Image.open(test_file) as im:
im.load()
def test_leak_save(self):
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for count in range(iterations):
with Image.open(test_file) as im:
im.load()
test_output = BytesIO()
im.save(test_output, "JPEG2000")
test_output.seek(0)
output = test_output.read()
if __name__ == '__main__':
unittest.main()

View File

@ -182,6 +182,26 @@ def tostring(im, format, **options):
return out.getvalue()
def hopper(mode="RGB", cache={}):
from PIL import Image
im = None
# FIXME: Implement caching to reduce reading from disk but so an original
# copy is returned each time and the cached image isn't modified by tests
# (for fast, isolated, repeatable tests).
# im = cache.get(mode)
if im is None:
if mode == "RGB":
im = Image.open("Tests/images/hopper.ppm")
elif mode == "F":
im = lena("L").convert(mode)
elif mode[:4] == "I;16":
im = lena("I").convert(mode)
else:
im = lena("RGB").convert(mode)
# cache[mode] = im
return im
def lena(mode="RGB", cache={}):
from PIL import Image
im = None

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,6 @@
GIMP Gradient
4
0.000000 0.351923 0.534893 1.000000 1.000000 1.000000 0.910000 0.730303 0.510606 1.000000 0.480000 1 0
0.501504 0.611002 0.637730 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1 0
0.931891 0.951264 1.000000 0.531255 0.316078 1.031173 1.000000 0.000000 0.000000 0.000000 1.000000 0 0
0.810551 0.881217 0.921883 0.717576 0.441331 0.081217 1.000000 0.751576 0.410331 0.081217 1.000000 0 0

View File

@ -0,0 +1,7 @@
GIMP Gradient
Name: A GIMP 1.3 gradient file
4
0.000000 0.351923 0.534893 1.000000 1.000000 1.000000 0.910000 0.730303 0.510606 1.000000 0.480000 1 0
0.501504 0.611002 0.637730 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 1.000000 1 0
0.931891 0.951264 1.000000 0.531255 0.316078 1.031173 1.000000 0.000000 0.000000 0.000000 1.000000 0 0
0.810551 0.881217 0.921883 0.717576 0.441331 0.081217 1.000000 0.751576 0.410331 0.081217 1.000000 0 0

BIN
Tests/images/hopper.bw Normal file

Binary file not shown.

BIN
Tests/images/hopper.dcx Normal file

Binary file not shown.

BIN
Tests/images/hopper.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
Tests/images/hopper.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
Tests/images/hopper.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
Tests/images/hopper.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
Tests/images/hopper.ppm Normal file

Binary file not shown.

BIN
Tests/images/hopper.ras Normal file

Binary file not shown.

BIN
Tests/images/hopper.rgb Normal file

Binary file not shown.

BIN
Tests/images/hopper.spider Normal file

Binary file not shown.

BIN
Tests/images/hopper.tar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Tests/images/lena_gray.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

BIN
Tests/images/multipage.tiff Normal file

Binary file not shown.

View File

@ -1,9 +1,9 @@
from helper import unittest, PillowTestCase, lena
from helper import unittest, PillowTestCase, hopper
from PIL import Image, DcxImagePlugin
# Created with ImageMagick: convert lena.ppm lena.dcx
TEST_FILE = "Tests/images/lena.dcx"
# Created with ImageMagick: convert hopper.ppm hopper.dcx
TEST_FILE = "Tests/images/hopper.dcx"
class TestFileDcx(PillowTestCase):
@ -17,7 +17,7 @@ class TestFileDcx(PillowTestCase):
# Assert
self.assertEqual(im.size, (128, 128))
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
orig = lena()
orig = hopper()
self.assert_image_equal(im, orig)
def test_tell(self):

View File

@ -63,6 +63,17 @@ class TestFileEps(PillowTestCase):
with io.open(self.tempfile('temp_iobase.eps'), 'wb') as fh:
image1.save(fh, 'EPS')
def test_bytesio_object(self):
with open(file1, 'rb') as f:
img_bytes = io.BytesIO(f.read())
img = Image.open(img_bytes)
img.load()
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
image1_scale1_compare.load()
self.assert_image_similar(img, image1_scale1_compare, 5)
def test_render_scale1(self):
# We need png support for these render test
codecs = dir(Image.core)
@ -137,6 +148,83 @@ class TestFileEps(PillowTestCase):
# open image with binary preview
Image.open(file3)
def _test_readline(self,t, ending):
ending = "Failure with line ending: %s" %("".join("%s" %ord(s) for s in ending))
self.assertEqual(t.readline().strip('\r\n'), 'something', ending)
self.assertEqual(t.readline().strip('\r\n'), 'else', ending)
self.assertEqual(t.readline().strip('\r\n'), 'baz', ending)
self.assertEqual(t.readline().strip('\r\n'), 'bif', ending)
def _test_readline_stringio(self, test_string, ending):
# check all the freaking line endings possible
try:
import StringIO
except:
# don't skip, it skips everything in the parent test
return
t = StringIO.StringIO(test_string)
self._test_readline(t, ending)
def _test_readline_io(self, test_string, ending):
import io
if str is bytes:
t = io.StringIO(unicode(test_string))
else:
t = io.StringIO(test_string)
self._test_readline(t, ending)
def _test_readline_file_universal(self, test_string, ending):
f = self.tempfile('temp.txt')
with open(f,'wb') as w:
if str is bytes:
w.write(test_string)
else:
w.write(test_string.encode('UTF-8'))
with open(f,'rU') as t:
self._test_readline(t, ending)
def _test_readline_file_psfile(self, test_string, ending):
f = self.tempfile('temp.txt')
with open(f,'wb') as w:
if str is bytes:
w.write(test_string)
else:
w.write(test_string.encode('UTF-8'))
with open(f,'rb') as r:
t = EpsImagePlugin.PSFile(r)
self._test_readline(t, ending)
def test_readline(self):
# check all the freaking line endings possible from the spec
#test_string = u'something\r\nelse\n\rbaz\rbif\n'
line_endings = ['\r\n', '\n']
not_working_endings = ['\n\r', '\r']
strings = ['something', 'else', 'baz', 'bif']
for ending in line_endings:
s = ending.join(strings)
# Native python versions will pass these endings.
#self._test_readline_stringio(s, ending)
#self._test_readline_io(s, ending)
#self._test_readline_file_universal(s, ending)
self._test_readline_file_psfile(s, ending)
for ending in not_working_endings:
# these only work with the PSFile, while they're in spec,
# they're not likely to be used
s = ending.join(strings)
# Native python versions may fail on these endings.
#self._test_readline_stringio(s, ending)
#self._test_readline_io(s, ending)
#self._test_readline_file_universal(s, ending)
self._test_readline_file_psfile(s, ending)
if __name__ == '__main__':
unittest.main()

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase, lena, netpbm_available
from helper import unittest, PillowTestCase, hopper, netpbm_available
from PIL import Image
from PIL import GifImagePlugin
@ -6,8 +6,9 @@ from PIL import GifImagePlugin
codecs = dir(Image.core)
# sample gif stream
file = "Tests/images/lena.gif"
with open(file, "rb") as f:
TEST_GIF = "Tests/images/hopper.gif"
with open(TEST_GIF, "rb") as f:
data = f.read()
@ -18,7 +19,7 @@ class TestFileGif(PillowTestCase):
self.skipTest("gif support not available") # can this happen?
def test_sanity(self):
im = Image.open(file)
im = Image.open(TEST_GIF)
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
@ -45,7 +46,7 @@ class TestFileGif(PillowTestCase):
def test_roundtrip(self):
out = self.tempfile('temp.gif')
im = lena()
im = hopper()
im.save(out)
reread = Image.open(out)
@ -54,17 +55,17 @@ class TestFileGif(PillowTestCase):
def test_roundtrip2(self):
# see https://github.com/python-pillow/Pillow/issues/403
out = self.tempfile('temp.gif')
im = Image.open('Tests/images/lena.gif')
im = Image.open(TEST_GIF)
im2 = im.copy()
im2.save(out)
reread = Image.open(out)
self.assert_image_similar(reread.convert('RGB'), lena(), 50)
self.assert_image_similar(reread.convert('RGB'), hopper(), 50)
def test_palette_handling(self):
# see https://github.com/python-pillow/Pillow/issues/513
im = Image.open('Tests/images/lena.gif')
im = Image.open(TEST_GIF)
im = im.convert('RGB')
im = im.resize((100, 100), Image.ANTIALIAS)
@ -100,7 +101,7 @@ class TestFileGif(PillowTestCase):
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_bmp_mode(self):
img = Image.open(file).convert("RGB")
img = Image.open(TEST_GIF).convert("RGB")
tempfile = self.tempfile("temp.gif")
GifImagePlugin._save_netpbm(img, 0, tempfile)
@ -108,7 +109,7 @@ class TestFileGif(PillowTestCase):
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_l_mode(self):
img = Image.open(file).convert("L")
img = Image.open(TEST_GIF).convert("L")
tempfile = self.tempfile("temp.gif")
GifImagePlugin._save_netpbm(img, 0, tempfile)

View File

@ -0,0 +1,127 @@
from helper import unittest, PillowTestCase
from PIL import GimpGradientFile
class TestImage(PillowTestCase):
def test_linear_pos_le_middle(self):
# Arrange
middle = 0.5
pos = 0.25
# Act
ret = GimpGradientFile.linear(middle, pos)
# Assert
self.assertEqual(ret, 0.25)
def test_linear_pos_le_small_middle(self):
# Arrange
middle = 1e-11
pos = 1e-12
# Act
ret = GimpGradientFile.linear(middle, pos)
# Assert
self.assertEqual(ret, 0.0)
def test_linear_pos_gt_middle(self):
# Arrange
middle = 0.5
pos = 0.75
# Act
ret = GimpGradientFile.linear(middle, pos)
# Assert
self.assertEqual(ret, 0.75)
def test_linear_pos_gt_small_middle(self):
# Arrange
middle = 1 - 1e-11
pos = 1 - 1e-12
# Act
ret = GimpGradientFile.linear(middle, pos)
# Assert
self.assertEqual(ret, 1.0)
def test_curved(self):
# Arrange
middle = 0.5
pos = 0.75
# Act
ret = GimpGradientFile.curved(middle, pos)
# Assert
self.assertEqual(ret, 0.75)
def test_sine(self):
# Arrange
middle = 0.5
pos = 0.75
# Act
ret = GimpGradientFile.sine(middle, pos)
# Assert
self.assertEqual(ret, 0.8535533905932737)
def test_sphere_increasing(self):
# Arrange
middle = 0.5
pos = 0.75
# Act
ret = GimpGradientFile.sphere_increasing(middle, pos)
# Assert
self.assertEqual(ret, 0.9682458365518543)
def test_sphere_decreasing(self):
# Arrange
middle = 0.5
pos = 0.75
# Act
ret = GimpGradientFile.sphere_decreasing(middle, pos)
# Assert
self.assertEqual(ret, 0.3385621722338523)
def test_load_via_imagepalette(self):
# Arrange
from PIL import ImagePalette
test_file = "Tests/images/gimp_gradient.ggr"
# Act
palette = ImagePalette.load(test_file)
# Assert
# load returns raw palette information
self.assertEqual(len(palette[0]), 1024)
self.assertEqual(palette[1], "RGBA")
def test_load_1_3_via_imagepalette(self):
# Arrange
from PIL import ImagePalette
# GIMP 1.3 gradient files contain a name field
test_file = "Tests/images/gimp_gradient_with_name.ggr"
# Act
palette = ImagePalette.load(test_file)
# Assert
# load returns raw palette information
self.assertEqual(len(palette[0]), 1024)
self.assertEqual(palette[1], "RGBA")
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -3,14 +3,14 @@ from helper import unittest, PillowTestCase
from PIL import Image
# sample ppm stream
file = "Tests/images/lena.ico"
data = open(file, "rb").read()
TEST_ICO_FILE = "Tests/images/hopper.ico"
TEST_DATA = open(TEST_ICO_FILE, "rb").read()
class TestFileIco(PillowTestCase):
def test_sanity(self):
im = Image.open(file)
im = Image.open(TEST_ICO_FILE)
im.load()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (16, 16))

View File

@ -60,8 +60,8 @@ class TestFileJpeg(PillowTestCase):
self.assertGreater(y, 0.8)
self.assertEqual(k, 0.0)
# the opposite corner is black
c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1,
im.size[1]-1))]
c, m, y, k = [x / 255.0 for x in im.getpixel((
im.size[0]-1, im.size[1]-1))]
self.assertGreater(k, 0.9)
# roundtrip, and check again
im = self.roundtrip(im)
@ -70,8 +70,8 @@ class TestFileJpeg(PillowTestCase):
self.assertGreater(m, 0.8)
self.assertGreater(y, 0.8)
self.assertEqual(k, 0.0)
c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1,
im.size[1]-1))]
c, m, y, k = [x / 255.0 for x in im.getpixel((
im.size[0]-1, im.size[1]-1))]
self.assertGreater(k, 0.9)
def test_dpi(self):
@ -152,8 +152,8 @@ class TestFileJpeg(PillowTestCase):
if py3:
a = bytes(random.randint(0, 255) for _ in range(256 * 256 * 3))
else:
a = b''.join(chr(random.randint(0, 255)) for _ in
range(256 * 256 * 3))
a = b''.join(chr(random.randint(0, 255)) for _ in range(
256 * 256 * 3))
im = Image.frombuffer("RGB", (256, 256), a, "raw", "RGB", 0, 1)
# this requires more bytes than pixels in the image
im.save(f, format="JPEG", progressive=True, quality=100)
@ -224,9 +224,18 @@ class TestFileJpeg(PillowTestCase):
self.assertIsNone(im._getmp())
def test_quality_keep(self):
# RGB
im = Image.open("Tests/images/lena.jpg")
f = self.tempfile('temp.jpg')
im.save(f, quality='keep')
# Grayscale
im = Image.open("Tests/images/lena_gray.jpg")
f = self.tempfile('temp.jpg')
im.save(f, quality='keep')
# CMYK
im = Image.open("Tests/images/pil_sample_cmyk.jpg")
f = self.tempfile('temp.jpg')
im.save(f, quality='keep')
def test_junk_jpeg_header(self):
# https://github.com/python-pillow/Pillow/issues/630
@ -279,11 +288,11 @@ class TestFileJpeg(PillowTestCase):
30)
# dict of qtable lists
self.assert_image_similar(
im, self.roundtrip(
im, qtables={
0: standard_l_qtable, 1: standard_chrominance_qtable}),
30)
self.assert_image_similar(im,
self.roundtrip(im,
qtables={0: standard_l_qtable,
1: standard_chrominance_qtable}),
30)
@unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg(self):
@ -300,6 +309,15 @@ class TestFileJpeg(PillowTestCase):
# Default save quality is 75%, so a tiny bit of difference is alright
self.assert_image_similar(img, Image.open(tempfile), 1)
def test_no_duplicate_0x1001_tag(self):
# Arrange
from PIL import ExifTags
tag_ids = dict(zip(ExifTags.TAGS.values(), ExifTags.TAGS.keys()))
# Assert
self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001)
self.assertEqual(tag_ids['RelatedImageLength'], 0x1002)
if __name__ == '__main__':
unittest.main()

View File

@ -4,8 +4,7 @@ import os
from PIL import Image, TiffImagePlugin
class TestFileLibTiff(PillowTestCase):
class LibTiffTestCase(PillowTestCase):
def setUp(self):
codecs = dir(Image.core)
@ -32,6 +31,8 @@ class TestFileLibTiff(PillowTestCase):
out = self.tempfile("temp.png")
im.save(out)
class TestFileLibTiff(LibTiffTestCase):
def test_g4_tiff(self):
"""Test the ordinary file path load path"""
@ -311,6 +312,35 @@ class TestFileLibTiff(PillowTestCase):
self.assertRaises(OSError, lambda: os.fstat(fn))
self.assertRaises(OSError, lambda: os.close(fn))
def test_multipage(self):
# issue #862
TiffImagePlugin.READ_LIBTIFF = True
im = Image.open('Tests/images/multipage.tiff')
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
im.seek(0)
self.assertEqual(im.size, (10,10))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,128,0))
self.assertTrue(im.tag.next)
im.seek(1)
self.assertEqual(im.size, (10,10))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (255,0,0))
self.assertTrue(im.tag.next)
im.seek(2)
self.assertFalse(im.tag.next)
self.assertEqual(im.size, (20,20))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,0,255))
TiffImagePlugin.READ_LIBTIFF = False
def test__next(self):
TiffImagePlugin.READ_LIBTIFF = True
im = Image.open('Tests/images/lena.tif')
self.assertFalse(im.tag.next)
im.load()
self.assertFalse(im.tag.next)
if __name__ == '__main__':
unittest.main()

View File

@ -2,12 +2,10 @@ from helper import unittest
from PIL import Image
from test_file_libtiff import TestFileLibTiff
from test_file_libtiff import LibTiffTestCase
class TestFileLibTiffSmall(TestFileLibTiff):
# Inherits TestFileLibTiff's setUp() and self._assert_noerr()
class TestFileLibTiffSmall(LibTiffTestCase):
""" The small lena image was failing on open in the libtiff
decoder because the file pointer was set to the wrong place

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase, lena
from helper import unittest, PillowTestCase, hopper
from io import BytesIO
@ -10,8 +10,8 @@ codecs = dir(Image.core)
# sample png stream
file = "Tests/images/lena.png"
data = open(file, "rb").read()
TEST_PNG_FILE = "Tests/images/hopper.png"
TEST_DATA = open(TEST_PNG_FILE, "rb").read()
# stuff to create inline PNG images
@ -58,7 +58,7 @@ class TestFilePng(PillowTestCase):
file = self.tempfile("temp.png")
lena("RGB").save(file)
hopper("RGB").save(file)
im = Image.open(file)
im.load()
@ -66,19 +66,19 @@ class TestFilePng(PillowTestCase):
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PNG")
lena("1").save(file)
hopper("1").save(file)
im = Image.open(file)
lena("L").save(file)
hopper("L").save(file)
im = Image.open(file)
lena("P").save(file)
hopper("P").save(file)
im = Image.open(file)
lena("RGB").save(file)
hopper("RGB").save(file)
im = Image.open(file)
lena("I").save(file)
hopper("I").save(file)
im = Image.open(file)
def test_broken(self):
@ -240,17 +240,17 @@ class TestFilePng(PillowTestCase):
def test_load_verify(self):
# Check open/load/verify exception (@PIL150)
im = Image.open("Tests/images/lena.png")
im = Image.open(TEST_PNG_FILE)
im.verify()
im = Image.open("Tests/images/lena.png")
im = Image.open(TEST_PNG_FILE)
im.load()
self.assertRaises(RuntimeError, lambda: im.verify())
def test_roundtrip_dpi(self):
# Check dpi roundtripping
im = Image.open(file)
im = Image.open(TEST_PNG_FILE)
im = roundtrip(im, dpi=(100, 100))
self.assertEqual(im.info["dpi"], (100, 100))
@ -258,7 +258,7 @@ class TestFilePng(PillowTestCase):
def test_roundtrip_text(self):
# Check text roundtripping
im = Image.open(file)
im = Image.open(TEST_PNG_FILE)
info = PngImagePlugin.PngInfo()
info.add_text("TXT", "VALUE")
@ -342,7 +342,7 @@ class TestFilePng(PillowTestCase):
def test_trns_p(self):
# Check writing a transparency of 0, issue #528
im = lena('P')
im = hopper('P')
im.info['transparency'] = 0
f = self.tempfile("temp.png")
@ -364,7 +364,7 @@ class TestFilePng(PillowTestCase):
def test_roundtrip_icc_profile(self):
# check that we can roundtrip the icc profile
im = lena('RGB')
im = hopper('RGB')
jpeg_image = Image.open('Tests/images/flower2.jpg')
expected_icc = jpeg_image.info['icc_profile']

View File

@ -8,8 +8,8 @@ class TestFileSgi(PillowTestCase):
def test_rgb(self):
# Arrange
# Created with ImageMagick then renamed:
# convert lena.ppm lena.sgi
test_file = "Tests/images/lena.rgb"
# convert hopper.ppm hopper.sgi
test_file = "Tests/images/hopper.rgb"
# Act / Assert
self.assertRaises(ValueError, lambda: Image.open(test_file))
@ -17,8 +17,8 @@ class TestFileSgi(PillowTestCase):
def test_l(self):
# Arrange
# Created with ImageMagick then renamed:
# convert lena.ppm -monochrome lena.sgi
test_file = "Tests/images/lena.bw"
# convert hopper.ppm -monochrome hopper.sgi
test_file = "Tests/images/hopper.bw"
# Act / Assert
self.assertRaises(ValueError, lambda: Image.open(test_file))

View File

@ -1,9 +1,9 @@
from helper import unittest, PillowTestCase, lena
from helper import unittest, PillowTestCase, hopper
from PIL import Image
from PIL import SpiderImagePlugin
TEST_FILE = "Tests/images/lena.spider"
TEST_FILE = "Tests/images/hopper.spider"
class TestImageSpider(PillowTestCase):
@ -18,7 +18,7 @@ class TestImageSpider(PillowTestCase):
def test_save(self):
# Arrange
temp = self.tempfile('temp.spider')
im = lena()
im = hopper()
# Act
im.save(temp, "SPIDER")
@ -44,7 +44,7 @@ class TestImageSpider(PillowTestCase):
def test_loadImageSeries(self):
# Arrange
not_spider_file = "Tests/images/lena.ppm"
not_spider_file = "Tests/images/hopper.ppm"
file_list = [TEST_FILE, not_spider_file, "path/not_found.ext"]
# Act

View File

@ -7,8 +7,8 @@ class TestFileSun(PillowTestCase):
def test_sanity(self):
# Arrange
# Created with ImageMagick: convert lena.ppm lena.ras
test_file = "Tests/images/lena.ras"
# Created with ImageMagick: convert hopper.jpg hopper.ras
test_file = "Tests/images/hopper.ras"
# Act
im = Image.open(test_file)

View File

@ -4,8 +4,8 @@ from PIL import Image, TarIO
codecs = dir(Image.core)
# sample ppm stream
tarfile = "Tests/images/lena.tar"
# Sample tar archive
TEST_TAR_FILE = "Tests/images/hopper.tar"
class TestFileTar(PillowTestCase):
@ -16,7 +16,7 @@ class TestFileTar(PillowTestCase):
def test_sanity(self):
if "zip_decoder" in codecs:
tar = TarIO.TarIO(tarfile, 'lena.png')
tar = TarIO.TarIO(TEST_TAR_FILE, 'hopper.png')
im = Image.open(tar)
im.load()
self.assertEqual(im.mode, "RGB")
@ -24,7 +24,7 @@ class TestFileTar(PillowTestCase):
self.assertEqual(im.format, "PNG")
if "jpeg_decoder" in codecs:
tar = TarIO.TarIO(tarfile, 'lena.jpg')
tar = TarIO.TarIO(TEST_TAR_FILE, 'hopper.jpg')
im = Image.open(tar)
im.load()
self.assertEqual(im.mode, "RGB")

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, lena, py3
from PIL import Image
from PIL import Image, TiffImagePlugin
class TestFileTiff(PillowTestCase):
@ -141,6 +141,32 @@ class TestFileTiff(PillowTestCase):
self.assertEqual(
im.getextrema(), (-3.140936851501465, 3.140684127807617))
def test_multipage(self):
# issue #862
im = Image.open('Tests/images/multipage.tiff')
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
im.seek(0)
self.assertEqual(im.size, (10,10))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,128,0))
im.seek(1)
im.load()
self.assertEqual(im.size, (10,10))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (255,0,0))
im.seek(2)
im.load()
self.assertEqual(im.size, (20,20))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,0,255))
def test_multipage_last_frame(self):
im = Image.open('Tests/images/multipage-lastframe.tif')
im.load()
self.assertEqual(im.size, (20,20))
self.assertEqual(im.convert('RGB').getpixel((0,0)), (0,0,255))
def test___str__(self):
# Arrange
file = "Tests/images/pil136.tiff"

View File

@ -8,7 +8,7 @@ tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys()))
class TestFileTiffMetadata(PillowTestCase):
def test_rt_metadata(self):
""" Test writing arbitray metadata into the tiff image directory
""" Test writing arbitrary metadata into the tiff image directory
Use case is ImageJ private tags, one numeric, one arbitrary
data. https://github.com/python-pillow/Pillow/issues/291
"""
@ -87,6 +87,10 @@ class TestFileTiffMetadata(PillowTestCase):
self.assertEqual(
value, reloaded[tag], "%s didn't roundtrip" % tag)
def test_no_duplicate_50741_tag(self):
self.assertEqual(tag_ids['MakerNoteSafety'], 50741)
self.assertEqual(tag_ids['BestQualityScale'], 50780)
if __name__ == '__main__':
unittest.main()

View File

@ -1,6 +1,7 @@
from helper import unittest, PillowTestCase, lena
from helper import unittest, PillowTestCase, hopper
from PIL import Image
import sys
class TestImage(PillowTestCase):
@ -57,7 +58,7 @@ class TestImage(PillowTestCase):
def test_expand_x(self):
# Arrange
im = lena()
im = hopper()
orig_size = im.size
xmargin = 5
@ -70,7 +71,7 @@ class TestImage(PillowTestCase):
def test_expand_xy(self):
# Arrange
im = lena()
im = hopper()
orig_size = im.size
xmargin = 5
ymargin = 3
@ -84,7 +85,7 @@ class TestImage(PillowTestCase):
def test_getbands(self):
# Arrange
im = lena()
im = hopper()
# Act
bands = im.getbands()
@ -94,7 +95,7 @@ class TestImage(PillowTestCase):
def test_getbbox(self):
# Arrange
im = lena()
im = hopper()
# Act
bbox = im.getbbox()
@ -140,6 +141,60 @@ class TestImage(PillowTestCase):
img_colors = sorted(img.getcolors())
self.assertEqual(img_colors, expected_colors)
def test_effect_mandelbrot(self):
# Arrange
size = (512, 512)
extent = (-3, -2.5, 2, 2.5)
quality = 100
# Act
im = Image.effect_mandelbrot(size, extent, quality)
# Assert
self.assertEqual(im.size, (512, 512))
im2 = Image.open('Tests/images/effect_mandelbrot.png')
self.assert_image_equal(im, im2)
def test_effect_mandelbrot_bad_arguments(self):
# Arrange
size = (512, 512)
# Get coordinates the wrong way round:
extent = (+3, +2.5, -2, -2.5)
# Quality < 2:
quality = 1
# Act/Assert
self.assertRaises(
ValueError,
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):
# Arrange
size = (100, 100)
sigma = 128
# Act
im = Image.effect_noise(size, sigma)
# Assert
self.assertEqual(im.size, (100, 100))
self.assertEqual(im.getpixel((0, 0)), 60)
self.assertEqual(im.getpixel((0, 1)), 28)
def test_effect_spread(self):
# Arrange
im = hopper()
distance = 10
# Act
im2 = im.effect_spread(distance)
# Assert
self.assertEqual(im.size, (128, 128))
im3 = Image.open('Tests/images/effect_spread.png')
self.assert_image_similar(im2, im3, 110)
if __name__ == '__main__':
unittest.main()

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase, lena
from helper import unittest, PillowTestCase, hopper
from PIL import Image
@ -9,21 +9,21 @@ class TestImageLoad(PillowTestCase):
def test_sanity(self):
im = lena()
im = hopper()
pix = im.load()
self.assertEqual(pix[0, 0], (223, 162, 133))
self.assertEqual(pix[0, 0], (20, 20, 70))
def test_close(self):
im = Image.open("Tests/images/lena.gif")
im = Image.open("Tests/images/hopper.gif")
im.close()
self.assertRaises(ValueError, lambda: im.load())
self.assertRaises(ValueError, lambda: im.getpixel((0, 0)))
def test_contextmanager(self):
fn = None
with Image.open("Tests/images/lena.gif") as im:
with Image.open("Tests/images/hopper.gif") as im:
fn = im.fp.fileno()
os.fstat(fn)

View File

@ -1,6 +1,6 @@
from helper import unittest, PillowTestCase, lena
from PIL import ImageSequence
from PIL import Image, ImageSequence, TiffImagePlugin
class TestImageSequence(PillowTestCase):
@ -22,7 +22,31 @@ class TestImageSequence(PillowTestCase):
self.assertEqual(index, 1)
def _test_multipage_tiff(self, dbg=False):
# debug had side effect of calling fp.tell.
Image.DEBUG=dbg
im = Image.open('Tests/images/multipage.tiff')
for index, frame in enumerate(ImageSequence.Iterator(im)):
frame.load()
self.assertEqual(index, im.tell())
frame.convert('RGB')
Image.DEBUG=False
def test_tiff(self):
#self._test_multipage_tiff(True)
self._test_multipage_tiff(False)
def test_libtiff(self):
codecs = dir(Image.core)
if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs:
self.skipTest("tiff support not available")
TiffImagePlugin.READ_LIBTIFF = True
#self._test_multipage_tiff(True)
self._test_multipage_tiff(False)
TiffImagePlugin.READ_LIBTIFF = False
if __name__ == '__main__':
unittest.main()

View File

@ -6,8 +6,8 @@ import shutil
from PIL import Image, JpegImagePlugin, GifImagePlugin
test_jpg = "Tests/images/lena.jpg"
test_gif = "Tests/images/lena.gif"
TEST_JPG = "Tests/images/hopper.jpg"
TEST_GIF = "Tests/images/hopper.gif"
test_filenames = (
"temp_';",
@ -32,24 +32,24 @@ class TestShellInjection(PillowTestCase):
def test_load_djpeg_filename(self):
for filename in test_filenames:
src_file = self.tempfile(filename)
shutil.copy(test_jpg, src_file)
shutil.copy(TEST_JPG, src_file)
im = Image.open(src_file)
im.load_djpeg()
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
def test_save_cjpeg_filename(self):
im = Image.open(test_jpg)
im = Image.open(TEST_JPG)
self.assert_save_filename_check(im, JpegImagePlugin._save_cjpeg)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_filename_bmp_mode(self):
im = Image.open(test_gif).convert("RGB")
im = Image.open(TEST_GIF).convert("RGB")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_filename_l_mode(self):
im = Image.open(test_gif).convert("L")
im = Image.open(TEST_GIF).convert("L")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)

View File

@ -103,6 +103,8 @@ PyImaging_DecoderNew(int contextsize)
static void
_dealloc(ImagingDecoderObject* decoder)
{
if (decoder->cleanup)
decoder->cleanup(&decoder->state);
free(decoder->state.buffer);
free(decoder->state.context);
Py_XDECREF(decoder->lock);
@ -442,8 +444,9 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
char* rawmode;
char* compname;
int fp;
int ifdoffset;
if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp))
if (! PyArg_ParseTuple(args, "sssii", &mode, &rawmode, &compname, &fp, &ifdoffset))
return NULL;
TRACE(("new tiff decoder %s\n", compname));
@ -455,7 +458,7 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
if (get_unpacker(decoder, mode, rawmode) < 0)
return NULL;
if (! ImagingLibTiffInit(&decoder->state, fp)) {
if (! ImagingLibTiffInit(&decoder->state, fp, ifdoffset)) {
Py_DECREF(decoder);
PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
return NULL;

View File

@ -11,9 +11,9 @@ rm -r openjpeg-2.1.0
tar -xvzf openjpeg-2.1.0.tar.gz
pushd openjpeg-2.1.0
pushd openjpeg-2.1.0
cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install
cmake -DCMAKE_INSTALL_PREFIX=/usr . && make -j4 && sudo make -j4 install
popd

View File

@ -1,18 +1,15 @@
#!/bin/bash
# install webp
if [ ! -f libwebp-0.4.0.tar.gz ]; then
wget 'https://webp.googlecode.com/files/libwebp-0.4.0.tar.gz'
if [ ! -f libwebp-0.4.1.tar.gz ]; then
wget 'http://downloads.webmproject.org/releases/webp/libwebp-0.4.1.tar.gz'
fi
rm -r libwebp-0.4.0
tar -xvzf libwebp-0.4.0.tar.gz
rm -r libwebp-0.4.1
tar -xvzf libwebp-0.4.1.tar.gz
pushd libwebp-0.4.1
pushd libwebp-0.4.0
./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make && sudo make install
./configure --prefix=/usr --enable-libwebpmux --enable-libwebpdemux && make -j4 && sudo make -j4 install
popd

View File

@ -670,6 +670,7 @@ files, using either JPEG or HEX encoding depending on the image mode (and
whether JPEG support is available or not).
PIXAR (read only)
^^^^
PIL provides limited support for PIXAR raster files. The library can identify
and read “dumped” RGB files.

View File

@ -99,6 +99,18 @@ _dealloc(ImagingEncoderObject* encoder)
PyObject_Del(encoder);
}
static PyObject*
_encode_cleanup(ImagingEncoderObject* encoder, PyObject* args)
{
int status = 0;
if (encoder->cleanup){
status = encoder->cleanup(&encoder->state);
}
return Py_BuildValue("i", status);
}
static PyObject*
_encode(ImagingEncoderObject* encoder, PyObject* args)
{
@ -239,6 +251,7 @@ _setimage(ImagingEncoderObject* encoder, PyObject* args)
static struct PyMethodDef methods[] = {
{"encode", (PyCFunction)_encode, 1},
{"cleanup", (PyCFunction)_encode_cleanup, 1},
{"encode_to_file", (PyCFunction)_encode_to_file, 1},
{"setimage", (PyCFunction)_setimage, 1},
{NULL, NULL} /* sentinel */
@ -540,8 +553,8 @@ static unsigned int** get_qtables_arrays(PyObject* qtables) {
tables = PySequence_Fast(qtables, "expected a sequence");
num_tables = PySequence_Size(qtables);
if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) {
PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4.");
if (num_tables < 1 || num_tables > NUM_QUANT_TBLS) {
PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 1 and 4.");
return NULL;
}
qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int*));
@ -760,7 +773,7 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
(ttag_t) PyInt_AsLong(key),
intav);
free(intav);
}
}
} else {
TRACE((" %d elements, setting as floats \n", len));
floatav = malloc(sizeof(float)*len);
@ -903,7 +916,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
j2k_decode_coord_tuple(tile_offset,
&context->tile_offset_x,
&context->tile_offset_y);
j2k_decode_coord_tuple(tile_size,
j2k_decode_coord_tuple(tile_size,
&context->tile_size_x,
&context->tile_size_y);
@ -918,7 +931,7 @@ PyImaging_Jpeg2KEncoderNew(PyObject *self, PyObject *args)
if (context->tile_offset_x > context->offset_x
|| context->tile_offset_y > context->offset_y) {
PyErr_SetString(PyExc_ValueError,
PyErr_SetString(PyExc_ValueError,
"JPEG 2000 tile offset too large to cover image area");
Py_DECREF(encoder);
return NULL;

View File

@ -49,6 +49,12 @@
#define L(rgb)\
((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114)
#ifndef round
double round(double x) {
return floor(x+0.5);
}
#endif
/* ------------------- */
/* 1 (bit) conversions */
/* ------------------- */

View File

@ -48,25 +48,25 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality)
for (y = 0; y < ysize; y++) {
UINT8* buf = im->image8[y];
for (x = 0; x < xsize; x++) {
x1 = y1 = xi2 = yi2 = 0.0;
cr = x*dr + extent[0];
ci = y*di + extent[1];
for (k = 1;; k++) {
y1 = 2*x1*y1 + ci;
x1 = xi2 - yi2 + cr;
xi2 = x1*x1;
yi2 = y1*y1;
if ((xi2 + yi2) > radius) {
buf[x] = k*255/quality;
break;
}
if (k > quality) {
buf[x] = 0;
break;
}
}
}
for (x = 0; x < xsize; x++) {
x1 = y1 = xi2 = yi2 = 0.0;
cr = x*dr + extent[0];
ci = y*di + extent[1];
for (k = 1;; k++) {
y1 = 2*x1*y1 + ci;
x1 = xi2 - yi2 + cr;
xi2 = x1*x1;
yi2 = y1*y1;
if ((xi2 + yi2) > radius) {
buf[x] = k*255/quality;
break;
}
if (k > quality) {
buf[x] = 0;
break;
}
}
}
}
return im;
}
@ -74,7 +74,7 @@ ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality)
Imaging
ImagingEffectNoise(int xsize, int ysize, float sigma)
{
/* Generate gaussian noise centered around 128 */
/* Generate Gaussian noise centered around 128 */
Imaging imOut;
int x, y;
@ -83,19 +83,19 @@ ImagingEffectNoise(int xsize, int ysize, float sigma)
imOut = ImagingNew("L", xsize, ysize);
if (!imOut)
return NULL;
return NULL;
next = 0.0;
nextok = 0;
for (y = 0; y < imOut->ysize; y++) {
UINT8* out = imOut->image8[y];
for (x = 0; x < imOut->xsize; x++) {
for (x = 0; x < imOut->xsize; x++) {
if (nextok) {
this = next;
nextok = 0;
} else {
/* after numerical recepies */
/* after numerical recipes */
double v1, v2, radius, factor;
do {
v1 = rand()*(2.0/32767.0) - 1.0;
@ -113,14 +113,6 @@ ImagingEffectNoise(int xsize, int ysize, float sigma)
return imOut;
}
Imaging
ImagingEffectPerlinTurbulence(int xsize, int ysize)
{
/* Perlin turbulence (In progress) */
return NULL;
}
Imaging
ImagingEffectSpread(Imaging imIn, int distance)
{
@ -132,11 +124,11 @@ ImagingEffectSpread(Imaging imIn, int distance)
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
if (!imOut)
return NULL;
return NULL;
#define SPREAD(type, image)\
#define SPREAD(type, image)\
for (y = 0; y < imIn->ysize; y++)\
for (x = 0; x < imIn->xsize; x++) {\
for (x = 0; x < imIn->xsize; x++) {\
int xx = x + (rand() % distance) - distance/2;\
int yy = y + (rand() % distance) - distance/2;\
if (xx >= 0 && xx < imIn->xsize && yy >= 0 && yy < imIn->ysize) {\
@ -147,9 +139,9 @@ ImagingEffectSpread(Imaging imIn, int distance)
}
if (imIn->image8) {
SPREAD(UINT8, image8);
SPREAD(UINT8, image8);
} else {
SPREAD(INT32, image32);
SPREAD(INT32, image32);
}
ImagingCopyInfo(imOut, imIn);
@ -157,217 +149,4 @@ ImagingEffectSpread(Imaging imIn, int distance)
return imOut;
}
/* -------------------------------------------------------------------- */
/* Taken from the "C" code in the W3C SVG specification. Translated
to C89 by Fredrik Lundh */
#if 0
/* Produces results in the range [1, 2**31 - 2].
Algorithm is: r = (a * r) mod m
where a = 16807 and m = 2**31 - 1 = 2147483647
See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
To test: the algorithm should produce the result 1043618065
as the 10,000th generated number if the original seed is 1.
*/
#define RAND_m 2147483647 /* 2**31 - 1 */
#define RAND_a 16807 /* 7**5; primitive root of m */
#define RAND_q 127773 /* m / a */
#define RAND_r 2836 /* m % a */
static long
perlin_setup_seed(long lSeed)
{
if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1;
if (lSeed > RAND_m - 1) lSeed = RAND_m - 1;
return lSeed;
}
static long
perlin_random(long lSeed)
{
long result;
result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q);
if (result <= 0) result += RAND_m;
return result;
}
#define BSize 0x100
#define BM 0xff
#define PerlinN 0x1000
#define NP 12 /* 2^PerlinN */
#define NM 0xfff
static int perlin_uLatticeSelector[BSize + BSize + 2];
static double perlin_fGradient[4][BSize + BSize + 2][2];
typedef struct
{
int nWidth; /* How much to subtract to wrap for stitching. */
int nHeight;
int nWrapX; /* Minimum value to wrap. */
int nWrapY;
} StitchInfo;
static void
perlin_init(long lSeed)
{
double s;
int i, j, k;
lSeed = perlin_setup_seed(lSeed);
for(k = 0; k < 4; k++)
{
for(i = 0; i < BSize; i++)
{
perlin_uLatticeSelector[i] = i;
for (j = 0; j < 2; j++)
perlin_fGradient[k][i][j] = (double)(((lSeed = perlin_random(lSeed)) % (BSize + BSize)) - BSize) / BSize;
s = (double) (sqrt(perlin_fGradient[k][i][0] * perlin_fGradient[k][i][0] + perlin_fGradient[k][i][1] * perlin_fGradient[k][i][1]));
perlin_fGradient[k][i][0] /= s;
perlin_fGradient[k][i][1] /= s;
}
}
while(--i)
{
k = perlin_uLatticeSelector[i];
perlin_uLatticeSelector[i] = perlin_uLatticeSelector[j = (lSeed = perlin_random(lSeed)) % BSize];
perlin_uLatticeSelector[j] = k;
}
for(i = 0; i < BSize + 2; i++)
{
perlin_uLatticeSelector[BSize + i] = perlin_uLatticeSelector[i];
for(k = 0; k < 4; k++)
for(j = 0; j < 2; j++)
perlin_fGradient[k][BSize + i][j] = perlin_fGradient[k][i][j];
}
}
#define s_curve(t) ( t * t * (3. - 2. * t) )
#define lerp(t, a, b) ( a + t * (b - a) )
static double
perlin_noise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo)
{
int bx0, bx1, by0, by1, b00, b10, b01, b11;
double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
register int i, j;
t = vec[0] + (double) PerlinN;
bx0 = (int)t;
bx1 = bx0+1;
rx0 = t - (int)t;
rx1 = rx0 - 1.0f;
t = vec[1] + (double) PerlinN;
by0 = (int)t;
by1 = by0+1;
ry0 = t - (int)t;
ry1 = ry0 - 1.0f;
/* If stitching, adjust lattice points accordingly. */
if(pStitchInfo != NULL)
{
if(bx0 >= pStitchInfo->nWrapX)
bx0 -= pStitchInfo->nWidth;
if(bx1 >= pStitchInfo->nWrapX)
bx1 -= pStitchInfo->nWidth;
if(by0 >= pStitchInfo->nWrapY)
by0 -= pStitchInfo->nHeight;
if(by1 >= pStitchInfo->nWrapY)
by1 -= pStitchInfo->nHeight;
}
bx0 &= BM;
bx1 &= BM;
by0 &= BM;
by1 &= BM;
i = perlin_uLatticeSelector[bx0];
j = perlin_uLatticeSelector[bx1];
b00 = perlin_uLatticeSelector[i + by0];
b10 = perlin_uLatticeSelector[j + by0];
b01 = perlin_uLatticeSelector[i + by1];
b11 = perlin_uLatticeSelector[j + by1];
sx = (double) (s_curve(rx0));
sy = (double) (s_curve(ry0));
q = perlin_fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1];
q = perlin_fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1];
a = lerp(sx, u, v);
q = perlin_fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1];
q = perlin_fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1];
b = lerp(sx, u, v);
return lerp(sy, a, b);
}
double
perlin_turbulence(
int nColorChannel, double *point, double fBaseFreqX, double fBaseFreqY,
int nNumOctaves, int bFractalSum, int bDoStitching,
double fTileX, double fTileY, double fTileWidth, double fTileHeight)
{
StitchInfo stitch;
StitchInfo *pStitchInfo = NULL; /* Not stitching when NULL. */
double fSum = 0.0f;
double vec[2];
double ratio = 1;
int nOctave;
vec[0] = point[0] * fBaseFreqX;
vec[1] = point[1] * fBaseFreqY;
/* Adjust the base frequencies if necessary for stitching. */
if(bDoStitching)
{
/* When stitching tiled turbulence, the frequencies must be adjusted */
/* so that the tile borders will be continuous. */
if(fBaseFreqX != 0.0)
{
double fLoFreq = (double) (floor(fTileWidth * fBaseFreqX)) / fTileWidth;
double fHiFreq = (double) (ceil(fTileWidth * fBaseFreqX)) / fTileWidth;
if(fBaseFreqX / fLoFreq < fHiFreq / fBaseFreqX)
fBaseFreqX = fLoFreq;
else
fBaseFreqX = fHiFreq;
}
if(fBaseFreqY != 0.0)
{
double fLoFreq = (double) (floor(fTileHeight * fBaseFreqY)) / fTileHeight;
double fHiFreq = (double) (ceil(fTileHeight * fBaseFreqY)) / fTileHeight;
if(fBaseFreqY / fLoFreq < fHiFreq / fBaseFreqY)
fBaseFreqY = fLoFreq;
else
fBaseFreqY = fHiFreq;
}
/* Set up initial stitch values. */
pStitchInfo = &stitch;
stitch.nWidth = (int) (fTileWidth * fBaseFreqX + 0.5f);
stitch.nWrapX = (int) (fTileX * fBaseFreqX + PerlinN + stitch.nWidth);
stitch.nHeight = (int) (fTileHeight * fBaseFreqY + 0.5f);
stitch.nWrapY = (int) (fTileY * fBaseFreqY + PerlinN + stitch.nHeight);
}
for(nOctave = 0; nOctave < nNumOctaves; nOctave++)
{
if(bFractalSum)
fSum += (double) (perlin_noise2(nColorChannel, vec, pStitchInfo) / ratio);
else
fSum += (double) (fabs(perlin_noise2(nColorChannel, vec, pStitchInfo)) / ratio);
vec[0] *= 2;
vec[1] *= 2;
ratio *= 2;
if(pStitchInfo != NULL)
{
/* Update stitch values. Subtracting PerlinN before the multiplication and */
/* adding it afterward simplifies to subtracting it once. */
stitch.nWidth *= 2;
stitch.nWrapX = 2 * stitch.nWrapX - PerlinN;
stitch.nHeight *= 2;
stitch.nWrapY = 2 * stitch.nWrapY - PerlinN;
}
}
return fSum;
}
#endif
// End of file

View File

@ -800,6 +800,11 @@ ImagingJpeg2KDecodeCleanup(ImagingCodecState state) {
if (context->decoder)
ImagingIncrementalCodecDestroy(context->decoder);
context->error_msg = NULL;
/* Prevent multiple calls to ImagingIncrementalCodecDestroy */
context->decoder = NULL;
return -1;
}

7
libImaging/Jpeg2KEncode.c Normal file → Executable file
View File

@ -576,15 +576,20 @@ int
ImagingJpeg2KEncodeCleanup(ImagingCodecState state) {
JPEG2KENCODESTATE *context = (JPEG2KENCODESTATE *)state->context;
if (context->quality_layers)
if (context->quality_layers && context->encoder)
Py_DECREF(context->quality_layers);
if (context->error_msg)
free ((void *)context->error_msg);
context->error_msg = NULL;
if (context->encoder)
ImagingIncrementalCodecDestroy(context->encoder);
/* Prevent multiple calls to ImagingIncrementalCodecDestroy */
context->encoder = NULL;
return -1;
}

View File

@ -21,8 +21,8 @@
#include "TiffDecode.h"
void dump_state(const TIFFSTATE *state){
TRACE(("State: Location %u size %d eof %d data: %p \n", (uint)state->loc,
(int)state->size, (uint)state->eof, state->data));
TRACE(("State: Location %u size %d eof %d data: %p ifd: %d\n", (uint)state->loc,
(int)state->size, (uint)state->eof, state->data, state->ifd));
}
/*
@ -142,7 +142,7 @@ void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) {
(void) hdata; (void) base; (void) size;
}
int ImagingLibTiffInit(ImagingCodecState state, int fp) {
int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset) {
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
TRACE(("initing libtiff\n"));
@ -158,6 +158,7 @@ int ImagingLibTiffInit(ImagingCodecState state, int fp) {
clientstate->size = 0;
clientstate->data = 0;
clientstate->fp = fp;
clientstate->ifd = offset;
clientstate->eof = 0;
return 1;
@ -195,7 +196,6 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
clientstate->loc = 0;
clientstate->data = (tdata_t)buffer;
clientstate->flrealloc = 0;
dump_state(clientstate);
TIFFSetWarningHandler(NULL);
@ -220,6 +220,16 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
return -1;
}
if (clientstate->ifd){
unsigned int ifdoffset = clientstate->ifd;
TRACE(("reading tiff ifd %d\n", ifdoffset));
int rv = TIFFSetSubDirectory(tiff, ifdoffset);
if (!rv){
TRACE(("error in TIFFSetSubDirectory"));
return -1;
}
}
size = TIFFScanlineSize(tiff);
TRACE(("ScanlineSize: %d \n", size));
if (size > state->bytes) {

View File

@ -26,6 +26,7 @@ typedef struct {
toff_t loc; /* toff_t == uint32 */
tsize_t size; /* tsize_t == int32 */
int fp;
int ifd; /* offset of the ifd, used for multipage */
TIFF *tiff; /* Used in write */
toff_t eof;
int flrealloc; /* may we realloc */
@ -33,7 +34,7 @@ typedef struct {
extern int ImagingLibTiffInit(ImagingCodecState state, int fp);
extern int ImagingLibTiffInit(ImagingCodecState state, int fp, int offset);
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
@ -50,5 +51,4 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
#define TRACE(args)
#endif

View File

@ -3,7 +3,7 @@
from multiprocessing import Pool, cpu_count
from distutils.ccompiler import CCompiler
import os
import os, sys
try:
MAX_PROCS = int(os.environ.get('MAX_CONCURRENCY', cpu_count()))
@ -50,7 +50,7 @@ def _mp_compile(self, sources, output_dir=None, macros=None,
return objects
# explicitly don't enable if environment says 1 processor
if MAX_PROCS != 1:
if MAX_PROCS != 1 and not sys.platform.startswith('win'):
try:
# bug, only enable if we can make a Pool. see issue #790 and
# http://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing

View File

@ -49,13 +49,13 @@ def testimage():
Or open existing files:
>>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.gif"))
>>> im = Image.open(os.path.join(ROOT, "Tests/images/hopper.gif"))
>>> _info(im)
('GIF', 'P', (128, 128))
>>> _info(Image.open(os.path.join(ROOT, "Tests/images/lena.ppm")))
>>> _info(Image.open(os.path.join(ROOT, "Tests/images/hopper.ppm")))
('PPM', 'RGB', (128, 128))
>>> try:
... _info(Image.open(os.path.join(ROOT, "Tests/images/lena.jpg")))
... _info(Image.open(os.path.join(ROOT, "Tests/images/hopper.jpg")))
... except IOError as v:
... print(v)
('JPEG', 'RGB', (128, 128))
@ -63,7 +63,7 @@ def testimage():
PIL doesn't actually load the image data until it's needed,
or you call the "load" method:
>>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm"))
>>> im = Image.open(os.path.join(ROOT, "Tests/images/hopper.ppm"))
>>> print(im.im) # internal image attribute
None
>>> a = im.load()
@ -73,7 +73,7 @@ def testimage():
You can apply many different operations on images. Most
operations return a new image:
>>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm"))
>>> im = Image.open(os.path.join(ROOT, "Tests/images/hopper.ppm"))
>>> _info(im.convert("L"))
(None, 'L', (128, 128))
>>> _info(im.copy())
@ -89,9 +89,9 @@ def testimage():
>>> len(im.getdata())
16384
>>> im.getextrema()
((61, 255), (26, 234), (44, 223))
((0, 255), (0, 255), (0, 255))
>>> im.getpixel((0, 0))
(223, 162, 133)
(20, 20, 70)
>>> len(im.getprojection())
2
>>> len(im.histogram())

View File

@ -487,6 +487,8 @@ class pil_build_ext(build_ext):
# In Google's precompiled zip it is call "libwebp":
if _find_library_file(self, "webp"):
feature.webp = "webp"
elif _find_library_file(self, "libwebp"):
feature.webp = "libwebp"
if feature.want('webpmux'):
if (_find_include_file(self, "webp/mux.h") and
@ -494,6 +496,9 @@ class pil_build_ext(build_ext):
if (_find_library_file(self, "webpmux") and
_find_library_file(self, "webpdemux")):
feature.webpmux = "webpmux"
if (_find_library_file(self, "libwebpmux") and
_find_library_file(self, "libwebpdemux")):
feature.webpmux = "libwebpmux"
for f in feature:
if not getattr(feature, f) and feature.require(f):
@ -559,13 +564,13 @@ class pil_build_ext(build_ext):
libraries=["lcms2"] + extra))
if os.path.isfile("_webp.c") and feature.webp:
libs = ["webp"]
libs = [feature.webp]
defs = []
if feature.webpmux:
defs.append(("HAVE_WEBPMUX", None))
libs.append("webpmux")
libs.append("webpdemux")
libs.append(feature.webpmux)
libs.append(feature.webpmux.replace('pmux','pdemux'))
exts.append(Extension(
"PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs))