Merge remote-tracking branch 'upstream/master' into hopper4

This commit is contained in:
Hugo 2014-09-22 10:34:14 +03:00
commit 0ccc445ebf
24 changed files with 398 additions and 168 deletions

View File

@ -16,14 +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"
- "pip install cffi"
- "pip install coverage nose"
- "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
@ -37,26 +37,24 @@ script:
- CFLAGS="-coverage" python setup.py build_ext --inplace
- coverage run --append --include=PIL/* selftest.py
# FIXME: re-add -x option to fail fast
# - coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py
- coverage run --append --include=PIL/* -m nose -v Tests/test_*.py
- coverage run --append --include=PIL/* -m nose -vx Tests/test_*.py
after_success:
# gather the coverage data
- sudo apt-get -qq install lcov
- 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
- gem install coveralls-lcov
- travis_retry gem install coveralls-lcov
- coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
- coverage report
- pip install coveralls-merge
- travis_retry pip install coveralls-merge
- coveralls-merge coverage.c.json
- pip install pep8 pyflakes
- travis_retry pip install pep8 pyflakes
- pep8 --statistics --count PIL/*.py
- pep8 --statistics --count Tests/*.py
- pyflakes PIL/*.py | tee >(wc -l)
@ -67,3 +65,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,10 +4,31 @@ Changelog (Pillow)
2.6.0 (unreleased)
------------------
- Fix for reading multipage TIFFs #885
[kostrom, wiredfool]
- On Windows, do not execute convert.exe without specifying path #912
[cgohlke]
- Correctly handle saving gray and CMYK JPEGs with quality-keep #857
- Fix msvc build error #911
[cgohlke]
- Fix for handling P + transparency -> RGBA conversions #904
[wiredfool]
- Retain alpha in ImageEnhance operations #909
[wiredfool]
- Jpeg2k Decode/encode memory leak fix #898
[joshware, wiredfool]
- EpsFilePlugin Speed improvements #886
[wiredfool, karstenw]
- Don't resize if already the right size #892
[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

View File

@ -7,7 +7,7 @@ Send a pull request. We'll generally want documentation and [tests](Tests/README
- Fork the repo
- Make a branch
- Add your changes + Tests
- Run the test suite. Try to run on both Python 2.x and 3.x, or you'll get tripped up. You can enable [Travis CI on your repo](https://travis-ci.org/profile/) to catch test failures prior to the pull request.
- Run the test suite. Try to run on both Python 2.x and 3.x, or you'll get tripped up. You can enable [Travis CI on your repo](https://travis-ci.org/profile/) to catch test failures prior to the pull request, and [Coveralls](https://coveralls.io/repos/new) to see if the changed code is covered by tests.
- Push to your fork, and make a pull request.
A few guidelines:

View File

@ -86,26 +86,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",
@ -136,49 +142,36 @@ def Ghostscript(tile, size, fp, scale=1):
finally:
try:
os.unlink(outfile)
os.unlink(infile)
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
@ -193,36 +186,27 @@ 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)
# 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)
# 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")
# go to offset - start of "%!PS"
# go to offset - start of "%!PS"
fp.seek(offset)
box = None
self.mode = "RGB"
@ -231,18 +215,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:
@ -264,9 +242,7 @@ class EpsImageFile(ImageFile.ImageFile):
pass
else:
m = field.match(s)
if m:
k = m.group(1)
@ -276,16 +252,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
@ -297,63 +273,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

@ -530,7 +530,7 @@ class Image:
"""
Closes the file pointer, if possible.
This operation will destroy the image core and release it's memory.
This operation will destroy the image core and release its memory.
The image data will be unusable afterward.
This function is only required to close images that have not
@ -867,7 +867,17 @@ class Image:
trns = trns_im.getpixel((0,0))
elif self.mode == 'P' and mode == 'RGBA':
t = self.info['transparency']
delete_trns = True
if isinstance(t, bytes):
self.im.putpalettealphas(t)
elif isinstance(t, int):
self.im.putpalettealpha(t,0)
else:
raise ValueError("Transparency for P mode should" +
" be bytes or int")
if mode == "P" and palette == ADAPTIVE:
im = self.im.quantize(colors)
@ -1514,6 +1524,9 @@ class Image:
self.load()
if self.size == size:
return self._new(self.im)
if self.mode in ("1", "P"):
resample = NEAREST

View File

@ -47,8 +47,11 @@ class Color(_Enhance):
"""
def __init__(self, image):
self.image = image
self.degenerate = image.convert("L").convert(image.mode)
self.intermediate_mode = 'L'
if 'A' in image.getbands():
self.intermediate_mode = 'LA'
self.degenerate = image.convert(self.intermediate_mode).convert(image.mode)
class Contrast(_Enhance):
"""Adjust image contrast.
@ -62,6 +65,9 @@ class Contrast(_Enhance):
mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5)
self.degenerate = Image.new("L", image.size, mean).convert(image.mode)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
class Brightness(_Enhance):
"""Adjust image brightness.
@ -74,6 +80,9 @@ class Brightness(_Enhance):
self.image = image
self.degenerate = Image.new(image.mode, image.size, 0)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
class Sharpness(_Enhance):
"""Adjust image sharpness.
@ -85,3 +94,6 @@ class Sharpness(_Enhance):
def __init__(self, image):
self.image = image
self.degenerate = image.filter(ImageFilter.SMOOTH)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])

View File

@ -227,6 +227,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
@ -471,6 +473,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:
@ -481,6 +484,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: pass

View File

@ -17,6 +17,9 @@
from PIL import Image
import sys
if sys.platform != "win32":
raise ImportError("ImageGrab is Windows only")
try:
# built-in driver (1.1.3 and later)
@ -40,7 +43,7 @@ def grab(bbox=None):
def grabclipboard():
debug = 0 # temporary interface
debug = 0 # temporary interface
data = Image.core.grabclipboard(debug)
if isinstance(data, bytes):
from PIL import BmpImagePlugin

View File

@ -19,4 +19,3 @@ 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

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

@ -155,7 +155,7 @@ class PillowTestCase(unittest.TestCase):
raise IOError()
outfile = self.tempfile("temp.png")
if command_succeeds(['convert', f, outfile]):
if command_succeeds([IMCONVERT, f, outfile]):
from PIL import Image
return Image.open(outfile)
raise IOError()
@ -251,6 +251,14 @@ def netpbm_available():
def imagemagick_available():
return command_succeeds(['convert', '-version'])
return IMCONVERT and command_succeeds([IMCONVERT, '-version'])
if sys.platform == 'win32':
IMCONVERT = os.environ.get('MAGICK_HOME', '')
if IMCONVERT:
IMCONVERT = os.path.join(IMCONVERT, 'convert.exe')
else:
IMCONVERT = 'convert'
# End of file

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

@ -347,8 +347,8 @@ class TestFilePng(PillowTestCase):
im2 = Image.open(f)
self.assertIn('transparency', im2.info)
self.assert_image_similar(im2.convert('RGBA'), im.convert('RGBA'),
16)
self.assert_image_equal(im2.convert('RGBA'),
im.convert('RGBA'))
def test_save_icc_profile_none(self):
# check saving files with an ICC profile set to None (omit profile)

View File

@ -2,13 +2,13 @@ from helper import unittest, PillowTestCase, fromstring, tostring
from PIL import Image
codecs = dir(Image.core)
fihopperme = "Tests/images/hopper.jpg"
data = tostring(Image.open(fihopperme).resize((512, 512)), "JPEG")
CODECS = dir(Image.core)
FILENAME = "Tests/images/hopper.jpg"
DATA = tostring(Image.open(FILENAME).resize((512, 512)), "JPEG")
def draft(mode, size):
im = fromstring(data)
im = fromstring(DATA)
im.draft(mode, size)
return im
@ -16,7 +16,7 @@ def draft(mode, size):
class TestImageDraft(PillowTestCase):
def setUp(self):
if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
if "jpeg_encoder" not in CODECS or "jpeg_decoder" not in CODECS:
self.skipTest("jpeg support not available")
def test_size(self):

View File

@ -22,6 +22,34 @@ class TestImageEnhance(PillowTestCase):
ImageEnhance.Sharpness(im).enhance(0.5)
def _half_transparent_image(self):
# returns an image, half transparent, half solid
im = hopper('RGB')
transparent = Image.new('L', im.size, 0)
solid = Image.new('L', (im.size[0]//2, im.size[1]), 255)
transparent.paste(solid, (0,0))
im.putalpha(transparent)
return im
def _check_alpha(self,im, original, op, amount):
self.assertEqual(im.getbands(), original.getbands())
self.assert_image_equal(im.split()[-1], original.split()[-1],
"Diff on %s: %s" % (op, amount))
def test_alpha(self):
# Issue https://github.com/python-pillow/Pillow/issues/899
# Is alpha preserved through image enhancement?
original = self._half_transparent_image()
for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']:
for amount in [0,0.5,1.0]:
self._check_alpha(getattr(ImageEnhance,op)(original).enhance(amount),
original, op, amount)
if __name__ == '__main__':
unittest.main()

View File

@ -1,5 +1,7 @@
from helper import unittest, PillowTestCase
import sys
try:
from PIL import ImageGrab
@ -19,6 +21,27 @@ except ImportError:
self.skipTest("ImportError")
class TestImageGrabImport(PillowTestCase):
def test_import(self):
# Arrange
exception = None
# Act
try:
from PIL import ImageGrab
ImageGrab.__name__ # dummy to prevent Pyflakes warning
except Exception as e:
exception = e
# Assert
if sys.platform == 'win32':
self.assertIsNone(exception, None)
else:
self.assertIsInstance(exception, ImportError)
self.assertEqual(str(exception), "ImageGrab is Windows only")
if __name__ == '__main__':
unittest.main()

View File

@ -39,10 +39,10 @@ class TestModeI16(PillowTestCase):
imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h))
self.verify(imOut) # transform
fihopperme = self.tempfile("temp.im")
imIn.save(fihopperme)
filename = self.tempfile("temp.im")
imIn.save(filename)
imOut = Image.open(fihopperme)
imOut = Image.open(filename)
self.verify(imIn)
self.verify(imOut)

View File

@ -6,13 +6,14 @@ from PIL import Image
class TestPickle(PillowTestCase):
def helper_pickle_file(self, pickle, protocol=0):
# Arrange
im = Image.open('Tests/images/hopper.jpg')
fihopperme = self.tempfile('temp.pkl')
filename = self.tempfile('temp.pkl')
# Act
with open(fihopperme, 'wb') as f:
with open(filename, 'wb') as f:
pickle.dump(im, f, protocol)
with open(fihopperme, 'rb') as f:
with open(filename, 'rb') as f:
loaded_im = pickle.load(f)
# Assert

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);

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 */

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

@ -221,9 +221,10 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int
}
if (clientstate->ifd){
unsigned int ifdoffset = clientstate->ifd;
int rv;
unsigned int ifdoffset = clientstate->ifd;
TRACE(("reading tiff ifd %d\n", ifdoffset));
int rv = TIFFSetSubDirectory(tiff, ifdoffset);
rv = TIFFSetSubDirectory(tiff, ifdoffset);
if (!rv){
TRACE(("error in TIFFSetSubDirectory"));
return -1;