Merge branch 'master' into flake8
17
.travis.yml
|
@ -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
|
||||
|
|
23
CHANGES.rst
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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")
|
||||
|
|
42
PIL/Image.py
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
##
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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()
|
|
@ -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
|
||||
|
|
BIN
Tests/images/effect_mandelbrot.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
Tests/images/effect_spread.png
Normal file
After Width: | Height: | Size: 40 KiB |
6
Tests/images/gimp_gradient.ggr
Normal 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
|
7
Tests/images/gimp_gradient_with_name.ggr
Normal 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
BIN
Tests/images/hopper.dcx
Normal file
BIN
Tests/images/hopper.gif
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
Tests/images/hopper.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Tests/images/hopper.jpg
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
Tests/images/hopper.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
Tests/images/hopper.ppm
Normal file
BIN
Tests/images/hopper.ras
Normal file
BIN
Tests/images/hopper.rgb
Normal file
BIN
Tests/images/hopper.spider
Normal file
BIN
Tests/images/hopper.tar
Normal file
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 37 KiB |
BIN
Tests/images/lena_gray.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
Tests/images/multipage-lastframe.tif
Normal file
BIN
Tests/images/multipage.tiff
Normal 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):
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
127
Tests/test_file_gimpgradient.py
Normal 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
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
7
decode.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
23
encode.c
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
/* ------------------- */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
14
selftest.py
|
@ -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())
|
||||
|
|
11
setup.py
|
@ -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))
|
||||
|
|