Merge branch 'master' into alpha-composite-box

This commit is contained in:
Alexander 2017-09-11 23:34:45 +03:00
commit 5bc72dbc10
89 changed files with 967 additions and 548 deletions

View File

@ -10,8 +10,8 @@ notifications:
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
- python: "pypy-5.7.1" - python: "pypy"
- python: "pypy3.3-5.2-alpha1" - python: "pypy3"
- python: '3.6' - python: '3.6'
- python: '2.7' - python: '2.7'
- python: "2.7_with_system_site_packages" # For PyQt4 - python: "2.7_with_system_site_packages" # For PyQt4

View File

@ -3,6 +3,46 @@ Changelog (Pillow)
4.3.0 (unreleased) 4.3.0 (unreleased)
------------------ ------------------
- Faster filter operations for Kernel, Gaussian, and Unsharp Mask filters #2679
[homm]
- EPS: Add showpage to force rendering of some EPS images #2636
[kaplun]
- DOC: Fix type of palette parameter in Image.quantize. #2703
[kkopachev]
- DOC: Fix Ico docs to match code #2712
[hugovk]
- Added file pointer save to SpiderImagePlugin #2647
[radarhere]
- Add targa version 2 footer #2713
[jhultgre]
- Removed redundant lines #2714
[radarhere]
- Travis CI: Use default pypy/pypy3 #2721
[hugovk]
- Fix for SystemError when rendering an empty string, added in 4.2.0 #2706
[wiredfool]
- Fix for memory leaks in font handling added in 4.2.0 #2634
[wiredfool]
- Tests: cleanup, more tests. Fixed WMF save handler #2689
[radarhere]
- Removed debugging interface for Image.core.grabclipboard #2708
[radarhere]
- Doc syntax fix #2710
[radarhere]
- Faster packing and unpacking for RGB, LA, and related storage modes #2693 - Faster packing and unpacking for RGB, LA, and related storage modes #2693
[homm] [homm]

View File

@ -129,6 +129,7 @@ def Ghostscript(tile, size, fp, scale=1):
"-c", "%d %d translate" % (-bbox[0], -bbox[1]), "-c", "%d %d translate" % (-bbox[0], -bbox[1]),
# adjust for image origin # adjust for image origin
"-f", infile, # input file "-f", infile, # input file
"-c", "showpage", # showpage (see: https://bugs.ghostscript.com/show_bug.cgi?id=698272)
] ]
if gs_windows_binary is not None: if gs_windows_binary is not None:

View File

@ -992,7 +992,7 @@ class Image(object):
2 = fast octree 2 = fast octree
3 = libimagequant 3 = libimagequant
:param kmeans: Integer :param kmeans: Integer
:param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette. :param palette: Quantize to the palette of given :py:class:`PIL.Image.Image`.
:returns: A new image :returns: A new image
""" """
@ -1114,6 +1114,8 @@ class Image(object):
:param filter: Filter kernel. :param filter: Filter kernel.
:returns: An :py:class:`~PIL.Image.Image` object. """ :returns: An :py:class:`~PIL.Image.Image` object. """
from . import ImageFilter
self.load() self.load()
if isinstance(filter, collections.Callable): if isinstance(filter, collections.Callable):
@ -1122,9 +1124,10 @@ class Image(object):
raise TypeError("filter argument should be ImageFilter.Filter " + raise TypeError("filter argument should be ImageFilter.Filter " +
"instance or class") "instance or class")
if self.im.bands == 1: multiband = isinstance(filter, ImageFilter.MultibandFilter)
if self.im.bands == 1 or multiband:
return self._new(filter.filter(self.im)) return self._new(filter.filter(self.im))
# fix to handle multiband images since _imaging doesn't
ims = [] ims = []
for c in range(self.im.bands): for c in range(self.im.bands):
ims.append(self._new(filter.filter(self.im.getband(c)))) ims.append(self._new(filter.filter(self.im.getband(c))))
@ -1494,14 +1497,13 @@ class Image(object):
mode = getmodebase(self.mode) + "A" mode = getmodebase(self.mode) + "A"
try: try:
self.im.setmode(mode) self.im.setmode(mode)
self.pyaccess = None
except (AttributeError, ValueError): except (AttributeError, ValueError):
# do things the hard way # do things the hard way
im = self.im.convert(mode) im = self.im.convert(mode)
if im.mode not in ("LA", "RGBA"): if im.mode not in ("LA", "RGBA"):
raise ValueError # sanity check raise ValueError # sanity check
self.im = im self.im = im
self.pyaccess = None self.pyaccess = None
self.mode = self.im.mode self.mode = self.im.mode
except (KeyError, ValueError): except (KeyError, ValueError):
raise ValueError("illegal image mode") raise ValueError("illegal image mode")
@ -1602,7 +1604,6 @@ class Image(object):
self.load() self.load()
if self.readonly: if self.readonly:
self._copy() self._copy()
self.pyaccess = None
self.load() self.load()
if self.pyaccess: if self.pyaccess:

View File

@ -22,7 +22,11 @@ class Filter(object):
pass pass
class Kernel(Filter): class MultibandFilter(Filter):
pass
class Kernel(MultibandFilter):
""" """
Create a convolution kernel. The current version only Create a convolution kernel. The current version only
supports 3x3 and 5x5 integer and floating point kernels. supports 3x3 and 5x5 integer and floating point kernels.
@ -142,7 +146,7 @@ class ModeFilter(Filter):
return image.modefilter(self.size) return image.modefilter(self.size)
class GaussianBlur(Filter): class GaussianBlur(MultibandFilter):
"""Gaussian blur filter. """Gaussian blur filter.
:param radius: Blur radius. :param radius: Blur radius.
@ -156,7 +160,7 @@ class GaussianBlur(Filter):
return image.gaussian_blur(self.radius) return image.gaussian_blur(self.radius)
class UnsharpMask(Filter): class UnsharpMask(MultibandFilter):
"""Unsharp mask filter. """Unsharp mask filter.
See Wikipedia's entry on `digital unsharp masking`_ for an explanation of See Wikipedia's entry on `digital unsharp masking`_ for an explanation of

View File

@ -72,8 +72,7 @@ def grabclipboard():
os.unlink(filepath) os.unlink(filepath)
return im return im
else: else:
debug = 0 # temporary interface data = Image.core.grabclipboard()
data = Image.core.grabclipboard(debug)
if isinstance(data, bytes): if isinstance(data, bytes):
from . import BmpImagePlugin from . import BmpImagePlugin
import io import io

View File

@ -267,17 +267,11 @@ def _save(im, fp, filename):
raise IOError("Error creating Spider header") raise IOError("Error creating Spider header")
# write the SPIDER header # write the SPIDER header
try:
fp = open(filename, 'wb')
except:
raise IOError("Unable to open %s for writing" % filename)
fp.writelines(hdr) fp.writelines(hdr)
rawmode = "F;32NF" # 32-bit native floating point rawmode = "F;32NF" # 32-bit native floating point
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))]) ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
fp.close()
def _save_spider(im, fp, filename): def _save_spider(im, fp, filename):
# get the filename extension and register it with Image # get the filename extension and register it with Image

View File

@ -139,16 +139,13 @@ SAVE = {
} }
def _save(im, fp, filename, check=0): def _save(im, fp, filename):
try: try:
rawmode, bits, colormaptype, imagetype = SAVE[im.mode] rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
except KeyError: except KeyError:
raise IOError("cannot write mode %s as TGA" % im.mode) raise IOError("cannot write mode %s as TGA" % im.mode)
if check:
return check
if colormaptype: if colormaptype:
colormapfirst, colormaplength, colormapentry = 0, 256, 24 colormapfirst, colormaplength, colormapentry = 0, 256, 24
else: else:
@ -182,6 +179,9 @@ def _save(im, fp, filename, check=0):
ImageFile._save( ImageFile._save(
im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))]) im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))])
# write targa version 2 footer
fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000")
# #
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# Registry # Registry

View File

@ -153,7 +153,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename): def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"): if _handler is None or not hasattr(_handler, "save"):
raise IOError("WMF save handler not installed") raise IOError("WMF save handler not installed")
_handler.save(im, fp, filename) _handler.save(im, fp, filename)

View File

@ -170,6 +170,41 @@ class PillowTestCase(unittest.TestCase):
return Image.open(outfile) return Image.open(outfile)
raise IOError() raise IOError()
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
class PillowLeakTestCase(PillowTestCase):
# requires unix/osx
iterations = 100 # count
mem_limit = 512 # k
def _get_mem_usage(self):
"""
Gets the RUSAGE memory usage, returns in K. Encapsulates the difference
between OSX and Linux rss reporting
:returns; memory usage in kilobytes
"""
from resource import getpagesize, getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == 'darwin':
# man 2 getrusage:
# ru_maxrss the maximum resident set size utilized (in bytes).
return mem / 1024 # Kb
else:
# linux
# man 2 getrusage
# ru_maxrss (since Linux 2.6.32)
# This is the maximum resident set size used (in kilobytes).
return mem # Kb
def _test_leak(self, core):
start_mem = self._get_mem_usage()
for cycle in range(self.iterations):
core()
mem = (self._get_mem_usage() - start_mem)
self.assertLess(mem, self.mem_limit,
msg='memory usage limit exceeded in iteration %d' % cycle)
# helpers # helpers

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -36,7 +36,7 @@ class TestDecompressionBomb(PillowTestCase):
# Act / Assert # Act / Assert
self.assert_warning(Image.DecompressionBombWarning, self.assert_warning(Image.DecompressionBombWarning,
lambda: Image.open(TEST_FILE)) Image.open, TEST_FILE)
class TestDecompressionCrop(PillowTestCase): class TestDecompressionCrop(PillowTestCase):
@ -52,7 +52,7 @@ class TestDecompressionCrop(PillowTestCase):
# same decompression bomb warnings on them. # same decompression bomb warnings on them.
box = (0, 0, self.src.width * 2, self.src.height * 2) box = (0, 0, self.src.width * 2, self.src.height * 2)
self.assert_warning(Image.DecompressionBombWarning, self.assert_warning(Image.DecompressionBombWarning,
lambda: self.src.crop(box)) self.src.crop, box)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -51,13 +51,13 @@ class TestFeatures(PillowTestCase):
# Arrange # Arrange
codec = "unsupported_codec" codec = "unsupported_codec"
# Act / Assert # Act / Assert
self.assertRaises(ValueError, lambda: features.check_codec(codec)) self.assertRaises(ValueError, features.check_codec, codec)
def test_unsupported_module(self): def test_unsupported_module(self):
# Arrange # Arrange
module = "unsupported_module" module = "unsupported_module"
# Act / Assert # Act / Assert
self.assertRaises(ValueError, lambda: features.check_module(module)) self.assertRaises(ValueError, features.check_module, module)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -28,7 +28,7 @@ class TestFileBmp(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: BmpImagePlugin.BmpImageFile(fp)) BmpImagePlugin.BmpImageFile, fp)
def test_save_to_bytes(self): def test_save_to_bytes(self):
output = io.BytesIO() output = io.BytesIO()

View File

@ -24,8 +24,7 @@ class TestFileBufrStub(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: BufrStubImagePlugin.BufrStubImageFile, invalid_file)
BufrStubImagePlugin.BufrStubImageFile(invalid_file))
def test_load(self): def test_load(self):
# Arrange # Arrange
@ -40,7 +39,7 @@ class TestFileBufrStub(PillowTestCase):
tmpfile = self.tempfile("temp.bufr") tmpfile = self.tempfile("temp.bufr")
# Act / Assert: stub cannot save without an implemented handler # Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, lambda: im.save(tmpfile)) self.assertRaises(IOError, im.save, tmpfile)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -21,7 +21,7 @@ class TestFileCur(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: CurImagePlugin.CurImageFile(invalid_file)) CurImagePlugin.CurImageFile, invalid_file)
no_cursors_file = "Tests/images/no_cursors.cur" no_cursors_file = "Tests/images/no_cursors.cur"

View File

@ -23,7 +23,7 @@ class TestFileDcx(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: DcxImagePlugin.DcxImageFile(fp)) DcxImagePlugin.DcxImageFile, fp)
def test_tell(self): def test_tell(self):
# Arrange # Arrange
@ -58,7 +58,7 @@ class TestFileDcx(PillowTestCase):
frame = 999 # too big on purpose frame = 999 # too big on purpose
# Act / Assert # Act / Assert
self.assertRaises(EOFError, lambda: im.seek(frame)) self.assertRaises(EOFError, im.seek, frame)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -55,7 +55,7 @@ class TestFileEps(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: EpsImagePlugin.EpsImageFile(invalid_file)) EpsImagePlugin.EpsImageFile, invalid_file)
def test_cmyk(self): def test_cmyk(self):
cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps") cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps")
@ -71,6 +71,16 @@ class TestFileEps(PillowTestCase):
target = Image.open('Tests/images/pil_sample_rgb.jpg') target = Image.open('Tests/images/pil_sample_rgb.jpg')
self.assert_image_similar(cmyk_image, target, 10) self.assert_image_similar(cmyk_image, target, 10)
def test_showpage(self):
# See https://github.com/python-pillow/Pillow/issues/2615
plot_image = Image.open("Tests/images/reqd_showpage.eps")
target = Image.open("Tests/images/reqd_showpage.png")
#should not crash/hang
plot_image.load()
# fonts could be slightly different
self.assert_image_similar(plot_image, target, 2)
def test_file_object(self): def test_file_object(self):
# issue 479 # issue 479
image1 = Image.open(file1) image1 = Image.open(file1)
@ -97,7 +107,7 @@ class TestFileEps(PillowTestCase):
def test_image_mode_not_supported(self): def test_image_mode_not_supported(self):
im = hopper("RGBA") im = hopper("RGBA")
tmpfile = self.tempfile('temp.eps') tmpfile = self.tempfile('temp.eps')
self.assertRaises(ValueError, lambda: im.save(tmpfile)) self.assertRaises(ValueError, im.save, tmpfile)
def test_render_scale1(self): def test_render_scale1(self):
# We need png support for these render test # We need png support for these render test

View File

@ -24,8 +24,7 @@ class TestFileFitsStub(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: FitsStubImagePlugin.FITSStubImageFile, invalid_file)
FitsStubImagePlugin.FITSStubImageFile(invalid_file))
def test_load(self): def test_load(self):
# Arrange # Arrange
@ -41,10 +40,10 @@ class TestFileFitsStub(PillowTestCase):
dummy_filename = "dummy.filename" dummy_filename = "dummy.filename"
# Act / Assert: stub cannot save without an implemented handler # Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, lambda: im.save(dummy_filename)) self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises( self.assertRaises(
IOError, IOError,
lambda: FitsStubImagePlugin._save(im, dummy_fp, dummy_filename)) FitsStubImagePlugin._save, im, dummy_fp, dummy_filename)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -41,7 +41,7 @@ class TestFileFli(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: FliImagePlugin.FliImageFile(invalid_file)) FliImagePlugin.FliImageFile, invalid_file)
def test_n_frames(self): def test_n_frames(self):
im = Image.open(static_test_file) im = Image.open(static_test_file)
@ -71,7 +71,7 @@ class TestFileFli(PillowTestCase):
self.assertEqual(im.tell(), 0) self.assertEqual(im.tell(), 0)
# Test seek past end of file # Test seek past end of file
self.assertRaises(EOFError, lambda: im.seek(2)) self.assertRaises(EOFError, im.seek, 2)
def test_seek_tell(self): def test_seek_tell(self):
im = Image.open(animated_test_file) im = Image.open(animated_test_file)

View File

@ -9,12 +9,12 @@ class TestFileFpx(PillowTestCase):
# Test an invalid OLE file # Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: FpxImagePlugin.FpxImageFile(invalid_file)) FpxImagePlugin.FpxImageFile, invalid_file)
# Test a valid OLE file, but not an FPX file # Test a valid OLE file, but not an FPX file
ole_file = "Tests/images/test-ole-file.doc" ole_file = "Tests/images/test-ole-file.doc"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: FpxImagePlugin.FpxImageFile(ole_file)) FpxImagePlugin.FpxImageFile, ole_file)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -9,7 +9,7 @@ class TestFileGbr(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: GbrImagePlugin.GbrImageFile(invalid_file)) GbrImagePlugin.GbrImageFile, invalid_file)
def test_gbr_file(self): def test_gbr_file(self):
im = Image.open('Tests/images/gbr.gbr') im = Image.open('Tests/images/gbr.gbr')

View File

@ -31,7 +31,7 @@ class TestFileGif(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: GifImagePlugin.GifImageFile(invalid_file)) GifImagePlugin.GifImageFile, invalid_file)
def test_optimize(self): def test_optimize(self):
def test_grayscale(optimize): def test_grayscale(optimize):
@ -375,29 +375,31 @@ class TestFileGif(PillowTestCase):
def test_version(self): def test_version(self):
out = self.tempfile('temp.gif') out = self.tempfile('temp.gif')
def assertVersionAfterSave(im, version):
im.save(out)
reread = Image.open(out)
self.assertEqual(reread.info["version"], version)
# Test that GIF87a is used by default # Test that GIF87a is used by default
im = Image.new('L', (100, 100), '#000') im = Image.new('L', (100, 100), '#000')
im.save(out) assertVersionAfterSave(im, b"GIF87a")
reread = Image.open(out)
self.assertEqual(reread.info["version"], b"GIF87a") # Test setting the version to 89a
im = Image.new('L', (100, 100), '#000')
im.info["version"] = b"89a"
assertVersionAfterSave(im, b"GIF89a")
# Test that adding a GIF89a feature changes the version # Test that adding a GIF89a feature changes the version
im.info["transparency"] = 1 im.info["transparency"] = 1
im.save(out) assertVersionAfterSave(im, b"GIF89a")
reread = Image.open(out)
self.assertEqual(reread.info["version"], b"GIF89a")
# Test that a GIF87a image is also saved in that format # Test that a GIF87a image is also saved in that format
im = Image.open("Tests/images/test.colors.gif") im = Image.open("Tests/images/test.colors.gif")
im.save(out) assertVersionAfterSave(im, b"GIF87a")
reread = Image.open(out)
self.assertEqual(reread.info["version"], b"GIF87a")
# Test that a GIF89a image is also saved in that format # Test that a GIF89a image is also saved in that format
im.info["version"] = b"GIF89a" im.info["version"] = b"GIF89a"
im.save(out) assertVersionAfterSave(im, b"GIF87a")
reread = Image.open(out)
self.assertEqual(reread.info["version"], b"GIF87a")
def test_append_images(self): def test_append_images(self):
out = self.tempfile('temp.gif') out = self.tempfile('temp.gif')

View File

@ -10,13 +10,13 @@ class TestImage(PillowTestCase):
GimpPaletteFile(fp) GimpPaletteFile(fp)
with open('Tests/images/hopper.jpg', 'rb') as fp: with open('Tests/images/hopper.jpg', 'rb') as fp:
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp)) self.assertRaises(SyntaxError, GimpPaletteFile, fp)
with open('Tests/images/bad_palette_file.gpl', 'rb') as fp: with open('Tests/images/bad_palette_file.gpl', 'rb') as fp:
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp)) self.assertRaises(SyntaxError, GimpPaletteFile, fp)
with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp: with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp:
self.assertRaises(ValueError, lambda: GimpPaletteFile(fp)) self.assertRaises(ValueError, GimpPaletteFile, fp)
def test_get_palette(self): def test_get_palette(self):
# Arrange # Arrange

View File

@ -24,8 +24,7 @@ class TestFileGribStub(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: GribStubImagePlugin.GribStubImageFile, invalid_file)
GribStubImagePlugin.GribStubImageFile(invalid_file))
def test_load(self): def test_load(self):
# Arrange # Arrange
@ -40,7 +39,7 @@ class TestFileGribStub(PillowTestCase):
tmpfile = self.tempfile("temp.grib") tmpfile = self.tempfile("temp.grib")
# Act / Assert: stub cannot save without an implemented handler # Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, lambda: im.save(tmpfile)) self.assertRaises(IOError, im.save, tmpfile)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -24,8 +24,7 @@ class TestFileHdf5Stub(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: Hdf5StubImagePlugin.HDF5StubImageFile, invalid_file)
Hdf5StubImagePlugin.HDF5StubImageFile(invalid_file))
def test_load(self): def test_load(self):
# Arrange # Arrange
@ -41,10 +40,10 @@ class TestFileHdf5Stub(PillowTestCase):
dummy_filename = "dummy.filename" dummy_filename = "dummy.filename"
# Act / Assert: stub cannot save without an implemented handler # Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, lambda: im.save(dummy_filename)) self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises( self.assertRaises(
IOError, IOError,
lambda: Hdf5StubImagePlugin._save(im, dummy_fp, dummy_filename)) Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,7 +1,8 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, IcnsImagePlugin
import io
import sys import sys
# sample icon file # sample icon file
@ -82,6 +83,23 @@ class TestFileIcns(PillowTestCase):
self.assertEqual(im2.mode, 'RGBA') self.assertEqual(im2.mode, 'RGBA')
self.assertEqual(im2.size, (wr, hr)) self.assertEqual(im2.size, (wr, hr))
def test_getimage(self):
with open(TEST_FILE, 'rb') as fp:
icns_file = IcnsImagePlugin.IcnsFile(fp)
im = icns_file.getimage()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (1024, 1024))
im = icns_file.getimage((512, 512))
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (512, 512))
def test_not_an_icns_file(self):
with io.BytesIO(b'invalid\n') as fp:
self.assertRaises(SyntaxError,
IcnsImagePlugin.IcnsFile, fp)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -18,7 +18,7 @@ class TestFileIco(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: IcoImagePlugin.IcoImageFile(fp)) IcoImagePlugin.IcoImageFile, fp)
def test_save_to_bytes(self): def test_save_to_bytes(self):
output = io.BytesIO() output = io.BytesIO()

View File

@ -43,18 +43,27 @@ class TestFileIm(PillowTestCase):
self.assertLess(im.tell(), n_frames) self.assertLess(im.tell(), n_frames)
def test_roundtrip(self): def test_roundtrip(self):
out = self.tempfile('temp.im') for mode in ["RGB", "P"]:
im = hopper() out = self.tempfile('temp.im')
im.save(out) im = hopper(mode)
reread = Image.open(out) im.save(out)
reread = Image.open(out)
self.assert_image_equal(reread, im) self.assert_image_equal(reread, im)
def test_save_unsupported_mode(self):
out = self.tempfile('temp.im')
im = hopper("HSV")
self.assertRaises(ValueError, im.save, out)
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: ImImagePlugin.ImImageFile(invalid_file)) ImImagePlugin.ImImageFile, invalid_file)
def test_number(self):
self.assertEqual(1.2, ImImagePlugin.number("1.2"))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -314,7 +314,7 @@ class TestFileJpeg(PillowTestCase):
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
self.assertRaises( self.assertRaises(
TypeError, lambda: self.roundtrip(hopper(), subsampling="1:1:1")) TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
def test_exif(self): def test_exif(self):
im = Image.open("Tests/images/pil_sample_rgb.jpg") im = Image.open("Tests/images/pil_sample_rgb.jpg")
@ -423,18 +423,18 @@ class TestFileJpeg(PillowTestCase):
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg") self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
# not a sequence # not a sequence
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a')) self.assertRaises(Exception, self.roundtrip, im, qtables='a')
# sequence wrong length # sequence wrong length
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[])) self.assertRaises(Exception, self.roundtrip, im, qtables=[])
# sequence wrong length # sequence wrong length
self.assertRaises(Exception, self.assertRaises(Exception,
lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5])) self.roundtrip, im, qtables=[1, 2, 3, 4, 5])
# qtable entry not a sequence # qtable entry not a sequence
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1])) self.assertRaises(Exception, self.roundtrip, im, qtables=[1])
# qtable entry has wrong number of items # qtable entry has wrong number of items
self.assertRaises(Exception, self.assertRaises(Exception,
lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]])) self.roundtrip, im, qtables=[[1, 2, 3, 4]])
@unittest.skipUnless(djpeg_available(), "djpeg not available") @unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg(self): def test_load_djpeg(self):
@ -479,7 +479,7 @@ class TestFileJpeg(PillowTestCase):
# Act # Act
# Shouldn't raise error # Shouldn't raise error
fn = "Tests/images/sugarshack_bad_mpo_header.jpg" fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
im = self.assert_warning(UserWarning, lambda: Image.open(fn)) im = self.assert_warning(UserWarning, Image.open, fn)
# Assert # Assert
self.assertEqual(im.format, "JPEG") self.assertEqual(im.format, "JPEG")
@ -584,7 +584,7 @@ class TestFileCloseW32(PillowTestCase):
im = Image.open(tmpfile) im = Image.open(tmpfile)
fp = im.fp fp = im.fp
self.assertFalse(fp.closed) self.assertFalse(fp.closed)
self.assertRaises(Exception, lambda: os.remove(tmpfile)) self.assertRaises(Exception, os.remove, tmpfile)
im.load() im.load()
self.assertTrue(fp.closed) self.assertTrue(fp.closed)
# this should not fail, as load should have closed the file. # this should not fail, as load should have closed the file.

View File

@ -44,8 +44,7 @@ class TestFileJpeg2k(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: Jpeg2KImagePlugin.Jpeg2KImageFile, invalid_file)
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file))
def test_bytesio(self): def test_bytesio(self):
with open('Tests/images/test-card-lossless.jp2', 'rb') as f: with open('Tests/images/test-card-lossless.jp2', 'rb') as f:

View File

@ -362,10 +362,9 @@ class TestFileLibTiff(LibTiffTestCase):
im = hopper('RGB') im = hopper('RGB')
out = self.tempfile('temp.tif') out = self.tempfile('temp.tif')
self.assertRaises( self.assertRaises(IOError, im.save, out, compression='tiff_ccitt')
IOError, lambda: im.save(out, compression='tiff_ccitt')) self.assertRaises(IOError, im.save, out, compression='group3')
self.assertRaises(IOError, lambda: im.save(out, compression='group3')) self.assertRaises(IOError, im.save, out, compression='group4')
self.assertRaises(IOError, lambda: im.save(out, compression='group4'))
def test_fp_leak(self): def test_fp_leak(self):
im = Image.open("Tests/images/hopper_g4_500.tif") im = Image.open("Tests/images/hopper_g4_500.tif")
@ -373,10 +372,10 @@ class TestFileLibTiff(LibTiffTestCase):
os.fstat(fn) os.fstat(fn)
im.load() # this should close it. im.load() # this should close it.
self.assertRaises(OSError, lambda: os.fstat(fn)) self.assertRaises(OSError, os.fstat, fn)
im = None # this should force even more closed. im = None # this should force even more closed.
self.assertRaises(OSError, lambda: os.fstat(fn)) self.assertRaises(OSError, os.fstat, fn)
self.assertRaises(OSError, lambda: os.close(fn)) self.assertRaises(OSError, os.close, fn)
def test_multipage(self): def test_multipage(self):
# issue #862 # issue #862

View File

@ -9,8 +9,7 @@ class TestFileMcIdas(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: McIdasImagePlugin.McIdasImageFile, invalid_file)
McIdasImagePlugin.McIdasImageFile(invalid_file))
def test_valid_file(self): def test_valid_file(self):
# Arrange # Arrange

View File

@ -42,19 +42,19 @@ class TestFileMic(PillowTestCase):
im.seek(0) im.seek(0)
self.assertEqual(im.tell(), 0) self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, lambda: im.seek(99)) self.assertRaises(EOFError, im.seek, 99)
self.assertEqual(im.tell(), 0) self.assertEqual(im.tell(), 0)
def test_invalid_file(self): def test_invalid_file(self):
# Test an invalid OLE file # Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: MicImagePlugin.MicImageFile(invalid_file)) MicImagePlugin.MicImageFile, invalid_file)
# Test a valid OLE file, but not a MIC file # Test a valid OLE file, but not a MIC file
ole_file = "Tests/images/test-ole-file.doc" ole_file = "Tests/images/test-ole-file.doc"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: MicImagePlugin.MicImageFile(ole_file)) MicImagePlugin.MicImageFile, ole_file)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -26,7 +26,7 @@ class TestFileMsp(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: MspImagePlugin.MspImageFile(invalid_file)) MspImagePlugin.MspImageFile, invalid_file)
def test_bad_checksum(self): def test_bad_checksum(self):
# Arrange # Arrange
@ -35,7 +35,7 @@ class TestFileMsp(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: MspImagePlugin.MspImageFile(bad_checksum)) MspImagePlugin.MspImageFile, bad_checksum)
def test_open_windows_v1(self): def test_open_windows_v1(self):
# Arrange # Arrange
@ -77,7 +77,7 @@ class TestFileMsp(PillowTestCase):
filename = self.tempfile("temp.msp") filename = self.tempfile("temp.msp")
# Act/Assert # Act/Assert
self.assertRaises(IOError, lambda: im.save(filename)) self.assertRaises(IOError, im.save, filename)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -51,7 +51,7 @@ class TestFilePalm(PillowTestCase):
mode = "RGB" mode = "RGB"
# Act / Assert # Act / Assert
self.assertRaises(IOError, lambda: self.helper_save_as_palm(mode)) self.assertRaises(IOError, self.helper_save_as_palm, mode)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -21,13 +21,14 @@ class TestFilePcx(PillowTestCase):
# Test an unsupported mode # Test an unsupported mode
f = self.tempfile("temp.pcx") f = self.tempfile("temp.pcx")
self.assertRaises(ValueError, lambda: hopper("RGBA").save(f)) im = hopper("RGBA")
self.assertRaises(ValueError, im.save, f)
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: PcxImagePlugin.PcxImageFile(invalid_file)) PcxImagePlugin.PcxImageFile, invalid_file)
def test_odd(self): def test_odd(self):
# see issue #523, odd sized images should have a stride that's even. # see issue #523, odd sized images should have a stride that's even.

View File

@ -59,7 +59,7 @@ class TestFilePdf(PillowTestCase):
im = hopper("LA") im = hopper("LA")
outfile = self.tempfile("temp_LA.pdf") outfile = self.tempfile("temp_LA.pdf")
self.assertRaises(ValueError, lambda: im.save(outfile)) self.assertRaises(ValueError, im.save, outfile)
def test_save_all(self): def test_save_all(self):
# Single frame image # Single frame image

View File

@ -22,7 +22,7 @@ class TestFilePixar(PillowTestCase):
self.assertRaises( self.assertRaises(
SyntaxError, SyntaxError,
lambda: PixarImagePlugin.PixarImageFile(invalid_file)) PixarImagePlugin.PixarImageFile, invalid_file)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, PillowLeakTestCase, hopper
from PIL import Image, ImageFile, PngImagePlugin from PIL import Image, ImageFile, PngImagePlugin
from io import BytesIO from io import BytesIO
@ -7,9 +7,6 @@ import sys
codecs = dir(Image.core) codecs = dir(Image.core)
# For Truncated phng memory leak
MEM_LIMIT = 2 # max increase in MB
ITERATIONS = 100 # Leak is 56k/iteration, this will leak 5.6megs
# sample png stream # sample png stream
@ -87,14 +84,14 @@ class TestFilePng(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: PngImagePlugin.PngImageFile(invalid_file)) PngImagePlugin.PngImageFile, invalid_file)
def test_broken(self): def test_broken(self):
# Check reading of totally broken files. In this case, the test # Check reading of totally broken files. In this case, the test
# file was checked into Subversion as a text file. # file was checked into Subversion as a text file.
test_file = "Tests/images/broken.png" test_file = "Tests/images/broken.png"
self.assertRaises(IOError, lambda: Image.open(test_file)) self.assertRaises(IOError, Image.open, test_file)
def test_bad_text(self): def test_bad_text(self):
# Make sure PIL can read malformed tEXt chunks (@PIL152) # Make sure PIL can read malformed tEXt chunks (@PIL152)
@ -424,7 +421,7 @@ class TestFilePng(PillowTestCase):
data = b'\x89' + fd.read() data = b'\x89' + fd.read()
pngfile = BytesIO(data) pngfile = BytesIO(data)
self.assertRaises(IOError, lambda: Image.open(pngfile)) self.assertRaises(IOError, Image.open, pngfile)
def test_trns_rgb(self): def test_trns_rgb(self):
# Check writing and reading of tRNS chunks for RGB images. # Check writing and reading of tRNS chunks for RGB images.
@ -505,17 +502,17 @@ class TestFilePng(PillowTestCase):
im.convert("P").save(test_file, dpi=(100, 100)) im.convert("P").save(test_file, dpi=(100, 100))
chunks = [] chunks = []
fp = open(test_file, "rb") with open(test_file, "rb") as fp:
fp.read(8) fp.read(8)
png = PngImagePlugin.PngStream(fp) png = PngImagePlugin.PngStream(fp)
while True: while True:
cid, pos, length = png.read() cid, pos, length = png.read()
chunks.append(cid) chunks.append(cid)
try: try:
s = png.call(cid, pos, length) s = png.call(cid, pos, length)
except EOFError: except EOFError:
break break
png.crc(cid, s) png.crc(cid, s)
# https://www.w3.org/TR/PNG/#5ChunkOrdering # https://www.w3.org/TR/PNG/#5ChunkOrdering
# IHDR - shall be first # IHDR - shall be first
@ -531,28 +528,22 @@ class TestFilePng(PillowTestCase):
# pHYs - before IDAT # pHYs - before IDAT
self.assertLess(chunks.index(b"pHYs"), chunks.index(b"IDAT")) self.assertLess(chunks.index(b"pHYs"), chunks.index(b"IDAT"))
def test_getchunks(self):
im = hopper()
chunks = PngImagePlugin.getchunks(im)
self.assertEqual(len(chunks), 3)
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS") @unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
class TestTruncatedPngPLeaks(PillowTestCase): class TestTruncatedPngPLeaks(PillowLeakTestCase):
mem_limit = 2*1024 # max increase in K
iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs
def setUp(self): def setUp(self):
if "zip_encoder" not in codecs or "zip_decoder" not in codecs: if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
self.skipTest("zip/deflate support not available") self.skipTest("zip/deflate support not available")
def _get_mem_usage(self):
from resource import getpagesize, getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == 'darwin':
# man 2 getrusage:
# ru_maxrss the maximum resident set size utilized (in bytes).
return mem / 1024 / 1024 # megs
else:
# linux
# man 2 getrusage
# ru_maxrss (since Linux 2.6.32)
# This is the maximum resident set size used (in kilobytes).
return mem / 1024 # megs
def test_leak_load(self): def test_leak_load(self):
with open('Tests/images/hopper.png', 'rb') as f: with open('Tests/images/hopper.png', 'rb') as f:
DATA = BytesIO(f.read(16 * 1024)) DATA = BytesIO(f.read(16 * 1024))
@ -560,13 +551,13 @@ class TestTruncatedPngPLeaks(PillowTestCase):
ImageFile.LOAD_TRUNCATED_IMAGES = True ImageFile.LOAD_TRUNCATED_IMAGES = True
with Image.open(DATA) as im: with Image.open(DATA) as im:
im.load() im.load()
start_mem = self._get_mem_usage()
def core():
with Image.open(DATA) as im:
im.load()
try: try:
for _ in range(ITERATIONS): self._test_leak(core)
with Image.open(DATA) as im:
im.load()
mem = (self._get_mem_usage() - start_mem)
self.assertLess(mem, MEM_LIMIT, msg='memory usage limit exceeded')
finally: finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False ImageFile.LOAD_TRUNCATED_IMAGES = False

View File

@ -39,7 +39,7 @@ class TestFilePpm(PillowTestCase):
with open(path, 'w') as f: with open(path, 'w') as f:
f.write('P6') f.write('P6')
self.assertRaises(ValueError, lambda: Image.open(path)) self.assertRaises(ValueError, Image.open, path)
def test_neg_ppm(self): def test_neg_ppm(self):
# Storage.c accepted negative values for xsize, ysize. the # Storage.c accepted negative values for xsize, ysize. the

View File

@ -21,7 +21,7 @@ class TestImagePsd(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: PsdImagePlugin.PsdImageFile(invalid_file)) PsdImagePlugin.PsdImageFile, invalid_file)
def test_n_frames(self): def test_n_frames(self):
im = Image.open("Tests/images/hopper_merged.psd") im = Image.open("Tests/images/hopper_merged.psd")
@ -66,7 +66,7 @@ class TestImagePsd(PillowTestCase):
def test_seek_eoferror(self): def test_seek_eoferror(self):
im = Image.open(test_file) im = Image.open(test_file)
self.assertRaises(EOFError, lambda: im.seek(-1)) self.assertRaises(EOFError, im.seek, -1)
def test_icc_profile(self): def test_icc_profile(self):
im = Image.open(test_file) im = Image.open(test_file)

View File

@ -42,8 +42,7 @@ class TestFileSgi(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(ValueError, self.assertRaises(ValueError,
lambda: SgiImagePlugin.SgiImageFile, invalid_file)
SgiImagePlugin.SgiImageFile(invalid_file))
def test_write(self): def test_write(self):
def roundtrip(img): def roundtrip(img):
@ -62,14 +61,14 @@ class TestFileSgi(PillowTestCase):
im = hopper('LA') im = hopper('LA')
out = self.tempfile('temp.sgi') out = self.tempfile('temp.sgi')
self.assertRaises(ValueError, lambda: im.save(out, format='sgi')) self.assertRaises(ValueError, im.save, out, format='sgi')
def test_incorrect_number_of_bands(self): def test_incorrect_number_of_bands(self):
im = hopper('YCbCr') im = hopper('YCbCr')
im.mode = 'RGB' im.mode = 'RGB'
out = self.tempfile('temp.sgi') out = self.tempfile('temp.sgi')
self.assertRaises(ValueError, lambda: im.save(out, format='sgi')) self.assertRaises(ValueError, im.save, out, format='sgi')
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -4,6 +4,8 @@ from PIL import Image
from PIL import ImageSequence from PIL import ImageSequence
from PIL import SpiderImagePlugin from PIL import SpiderImagePlugin
import tempfile
TEST_FILE = "Tests/images/hopper.spider" TEST_FILE = "Tests/images/hopper.spider"
@ -30,6 +32,21 @@ class TestImageSpider(PillowTestCase):
self.assertEqual(im2.size, (128, 128)) self.assertEqual(im2.size, (128, 128))
self.assertEqual(im2.format, "SPIDER") self.assertEqual(im2.format, "SPIDER")
def test_tempfile(self):
# Arrange
im = hopper()
# Act
with tempfile.TemporaryFile() as fp:
im.save(fp, "SPIDER")
# Assert
fp.seek(0)
reloaded = Image.open(fp)
self.assertEqual(reloaded.mode, "F")
self.assertEqual(reloaded.size, (128, 128))
self.assertEqual(reloaded.format, "SPIDER")
def test_isSpiderImage(self): def test_isSpiderImage(self):
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE)) self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
@ -84,12 +101,12 @@ class TestImageSpider(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/invalid.spider" invalid_file = "Tests/images/invalid.spider"
self.assertRaises(IOError, lambda: Image.open(invalid_file)) self.assertRaises(IOError, Image.open, invalid_file)
def test_nonstack_file(self): def test_nonstack_file(self):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
self.assertRaises(EOFError, lambda: im.seek(0)) self.assertRaises(EOFError, im.seek, 0)
def test_nonstack_dos(self): def test_nonstack_dos(self):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)

View File

@ -24,7 +24,7 @@ class TestFileSun(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: SunImagePlugin.SunImageFile(invalid_file)) SunImagePlugin.SunImageFile, invalid_file)
def test_im1(self): def test_im1(self):
im = Image.open('Tests/images/sunraster.im1') im = Image.open('Tests/images/sunraster.im1')

View File

@ -85,6 +85,10 @@ class TestFileTiff(PillowTestCase):
im = Image.open("Tests/images/copyleft.tiff") im = Image.open("Tests/images/copyleft.tiff")
self.assertEqual(im.mode, 'RGB') self.assertEqual(im.mode, 'RGB')
def test_set_legacy_api(self):
with self.assertRaises(Exception):
ImageFileDirectory_v2.legacy_api = None
def test_xyres_tiff(self): def test_xyres_tiff(self):
filename = "Tests/images/pil168.tif" filename = "Tests/images/pil168.tif"
im = Image.open(filename) im = Image.open(filename)
@ -140,11 +144,11 @@ class TestFileTiff(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: TiffImagePlugin.TiffImageFile(invalid_file)) TiffImagePlugin.TiffImageFile, invalid_file)
TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0") TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0")
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: TiffImagePlugin.TiffImageFile(invalid_file)) TiffImagePlugin.TiffImageFile, invalid_file)
TiffImagePlugin.PREFIXES.pop() TiffImagePlugin.PREFIXES.pop()
def test_bad_exif(self): def test_bad_exif(self):
@ -163,7 +167,7 @@ class TestFileTiff(PillowTestCase):
def test_save_unsupported_mode(self): def test_save_unsupported_mode(self):
im = hopper("HSV") im = hopper("HSV")
outfile = self.tempfile("temp.tif") outfile = self.tempfile("temp.tif")
self.assertRaises(IOError, lambda: im.save(outfile)) self.assertRaises(IOError, im.save, outfile)
def test_little_endian(self): def test_little_endian(self):
im = Image.open('Tests/images/16bit.cropped.tif') im = Image.open('Tests/images/16bit.cropped.tif')
@ -340,7 +344,7 @@ class TestFileTiff(PillowTestCase):
filename = "Tests/images/pil136.tiff" filename = "Tests/images/pil136.tiff"
im = Image.open(filename) im = Image.open(filename)
self.assertEqual(im.tell(), 0) self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, lambda: im.seek(1)) self.assertRaises(EOFError, im.seek, 1)
def test__limit_rational_int(self): def test__limit_rational_int(self):
from PIL.TiffImagePlugin import _limit_rational from PIL.TiffImagePlugin import _limit_rational
@ -495,12 +499,12 @@ class TestFileTiff(PillowTestCase):
with Image.open("Tests/images/uint16_1_4660.tif") as im: with Image.open("Tests/images/uint16_1_4660.tif") as im:
im.save(tmpfile) im.save(tmpfile)
f = open(tmpfile, 'rb') with open(tmpfile, 'rb') as f:
im = Image.open(f) im = Image.open(f)
fp = im.fp fp = im.fp
self.assertFalse(fp.closed) self.assertFalse(fp.closed)
im.load() im.load()
self.assertFalse(fp.closed) self.assertFalse(fp.closed)
@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only") @unittest.skipUnless(sys.platform.startswith('win32'), "Windows only")
class TestFileTiffW32(PillowTestCase): class TestFileTiffW32(PillowTestCase):
@ -515,7 +519,7 @@ class TestFileTiffW32(PillowTestCase):
im = Image.open(tmpfile) im = Image.open(tmpfile)
fp = im.fp fp = im.fp
self.assertFalse(fp.closed) self.assertFalse(fp.closed)
self.assertRaises(Exception, lambda: os.remove(tmpfile)) self.assertRaises(Exception, os.remove, tmpfile)
im.load() im.load()
self.assertTrue(fp.closed) self.assertTrue(fp.closed)

View File

@ -161,7 +161,7 @@ class TestFileTiffMetadata(PillowTestCase):
head = f.read(8) head = f.read(8)
info = TiffImagePlugin.ImageFileDirectory(head) info = TiffImagePlugin.ImageFileDirectory(head)
try: try:
self.assert_warning(UserWarning, lambda: info.load(f)) self.assert_warning(UserWarning, info.load, f)
except struct.error: except struct.error:
self.fail("Should not be struct errors there.") self.fail("Should not be struct errors there.")

View File

@ -75,7 +75,8 @@ class TestFileWebp(PillowTestCase):
def test_write_unsupported_mode(self): def test_write_unsupported_mode(self):
temp_file = self.tempfile("temp.webp") temp_file = self.tempfile("temp.webp")
self.assertRaises(IOError, lambda: hopper("L").save(temp_file)) im = hopper("L")
self.assertRaises(IOError, im.save, temp_file)
def test_WebPEncode_with_invalid_args(self): def test_WebPEncode_with_invalid_args(self):
self.assertRaises(TypeError, _webp.WebPEncode) self.assertRaises(TypeError, _webp.WebPEncode)

View File

@ -1,5 +1,7 @@
from helper import unittest, PillowTestCase, hopper from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image
from PIL import WmfImagePlugin
class TestFileWmf(PillowTestCase): class TestFileWmf(PillowTestCase):
@ -26,12 +28,29 @@ class TestFileWmf(PillowTestCase):
imref.load() imref.load()
self.assert_image_similar(im, imref, 2.0) self.assert_image_similar(im, imref, 2.0)
def test_register_handler(self):
class TestHandler:
methodCalled = False
def save(self, im, fp, filename):
self.methodCalled = True
handler = TestHandler()
WmfImagePlugin.register_handler(handler)
im = hopper()
tmpfile = self.tempfile("temp.wmf")
im.save(tmpfile)
self.assertTrue(handler.methodCalled)
# Restore the state before this test
WmfImagePlugin.register_handler(None)
def test_save(self): def test_save(self):
im = hopper() im = hopper()
for ext in [".wmf", ".emf"]: for ext in [".wmf", ".emf"]:
tmpfile = self.tempfile("temp"+ext) tmpfile = self.tempfile("temp"+ext)
self.assertRaises(IOError, lambda: im.save(tmpfile)) self.assertRaises(IOError, im.save, tmpfile)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -21,7 +21,7 @@ class TestFileXpm(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: XpmImagePlugin.XpmImageFile(invalid_file)) XpmImagePlugin.XpmImageFile, invalid_file)
def test_load_read(self): def test_load_read(self):
# Arrange # Arrange

View File

@ -25,8 +25,7 @@ class TestFileXVThumb(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: XVThumbImagePlugin.XVThumbImageFile, bad_file)
XVThumbImagePlugin.XVThumbImageFile(bad_file))
def test_invalid_file(self): def test_invalid_file(self):
# Arrange # Arrange
@ -34,8 +33,7 @@ class TestFileXVThumb(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError,
lambda: XVThumbImagePlugin.XVThumbImageFile, invalid_file)
XVThumbImagePlugin.XVThumbImageFile(invalid_file))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -9,15 +9,15 @@ class TestFontBdf(PillowTestCase):
def test_sanity(self): def test_sanity(self):
test_file = open(filename, "rb") with open(filename, "rb") as test_file:
font = BdfFontFile.BdfFontFile(test_file) font = BdfFontFile.BdfFontFile(test_file)
self.assertIsInstance(font, FontFile.FontFile) self.assertIsInstance(font, FontFile.FontFile)
self.assertEqual(len([_f for _f in font.glyph if _f]), 190) self.assertEqual(len([_f for _f in font.glyph if _f]), 190)
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, lambda: BdfFontFile.BdfFontFile(fp)) self.assertRaises(SyntaxError, BdfFontFile.BdfFontFile, fp)
if __name__ == '__main__': if __name__ == '__main__':

34
Tests/test_font_leaks.py Normal file
View File

@ -0,0 +1,34 @@
from __future__ import division
from helper import unittest, PillowLeakTestCase
import sys
from PIL import Image, features, ImageDraw, ImageFont
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
class TestTTypeFontLeak(PillowLeakTestCase):
# fails at iteration 3 in master
iterations = 10
mem_limit = 4096 #k
def _test_font(self, font):
im = Image.new('RGB', (255,255), 'white')
draw = ImageDraw.ImageDraw(im)
self._test_leak(lambda: draw.text((0, 0), "some text "*1024, #~10k
font=font, fill="black"))
@unittest.skipIf(not features.check('freetype2'), "Test requires freetype2")
def test_leak(self):
ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20)
self._test_font(ttype)
class TestDefaultFontLeak(TestTTypeFontLeak):
# fails at iteration 37 in master
iterations = 100
mem_limit = 1024 #k
def test_leak(self):
default_font = ImageFont.load_default()
self._test_font(default_font)
if __name__ == '__main__':
unittest.main()

View File

@ -17,8 +17,8 @@ class TestFontPcf(PillowTestCase):
self.skipTest("zlib support not available") self.skipTest("zlib support not available")
def save_font(self): def save_font(self):
test_file = open(fontname, "rb") with open(fontname, "rb") as test_file:
font = PcfFontFile.PcfFontFile(test_file) font = PcfFontFile.PcfFontFile(test_file)
self.assertIsInstance(font, FontFile.FontFile) self.assertIsInstance(font, FontFile.FontFile)
self.assertEqual(len([_f for _f in font.glyph if _f]), 192) self.assertEqual(len([_f for _f in font.glyph if _f]), 192)
@ -32,7 +32,7 @@ class TestFontPcf(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, lambda: PcfFontFile.PcfFontFile(fp)) self.assertRaises(SyntaxError, PcfFontFile.PcfFontFile, fp)
def xtest_draw(self): def xtest_draw(self):

View File

@ -47,10 +47,9 @@ class TestImage(PillowTestCase):
self.assertEqual(im2.getcolors(), [(10000, 0)]) self.assertEqual(im2.getcolors(), [(10000, 0)])
self.assertEqual(im3.getcolors(), [(10000, 0)]) self.assertEqual(im3.getcolors(), [(10000, 0)])
self.assertRaises(ValueError, lambda: Image.new("X", (100, 100))) self.assertRaises(ValueError, Image.new, "X", (100, 100))
self.assertRaises(ValueError, lambda: Image.new("", (100, 100))) self.assertRaises(ValueError, Image.new, "", (100, 100))
# self.assertRaises( # self.assertRaises(MemoryError, Image.new, "L", (1000000, 1000000))
# MemoryError, lambda: Image.new("L", (1000000, 1000000)))
def test_width_height(self): def test_width_height(self):
im = Image.new("RGB", (1, 2)) im = Image.new("RGB", (1, 2))
@ -68,7 +67,10 @@ class TestImage(PillowTestCase):
else: else:
import io import io
im = io.BytesIO(b'') im = io.BytesIO(b'')
self.assertRaises(IOError, lambda: Image.open(im)) self.assertRaises(IOError, Image.open, im)
def test_bad_mode(self):
self.assertRaises(ValueError, Image.open, "filename", "bad mode")
@unittest.skipIf(sys.version_info < (3, 4), @unittest.skipIf(sys.version_info < (3, 4),
"pathlib only available in Python 3.4 or later") "pathlib only available in Python 3.4 or later")
@ -109,7 +111,7 @@ class TestImage(PillowTestCase):
def test_unknown_extension(self): def test_unknown_extension(self):
im = hopper() im = hopper()
temp_file = self.tempfile("temp.unknown") temp_file = self.tempfile("temp.unknown")
self.assertRaises(ValueError, lambda: im.save(temp_file)) self.assertRaises(ValueError, im.save, temp_file)
def test_internals(self): def test_internals(self):
@ -273,6 +275,20 @@ class TestImage(PillowTestCase):
target.crop((32, 32, 96, 96))) target.crop((32, 32, 96, 96)))
self.assertEqual(source.size, (128, 128)) self.assertEqual(source.size, (128, 128))
# errors
self.assertRaises(ValueError,
source.alpha_composite, over, "invalid source")
self.assertRaises(ValueError,
source.alpha_composite, over, (0, 0), "invalid destination")
self.assertRaises(ValueError,
source.alpha_composite, over, (0))
self.assertRaises(ValueError,
source.alpha_composite, over, (0, 0), (0))
self.assertRaises(ValueError,
source.alpha_composite, over, (0, -1))
self.assertRaises(ValueError,
source.alpha_composite, over, (0, 0), (0, -1))
def test_registered_extensions_uninitialized(self): def test_registered_extensions_uninitialized(self):
# Arrange # Arrange
Image._initialized = 0 Image._initialized = 0
@ -327,7 +343,7 @@ class TestImage(PillowTestCase):
# Act/Assert # Act/Assert
self.assertRaises( self.assertRaises(
ValueError, ValueError,
lambda: Image.effect_mandelbrot(size, extent, quality)) Image.effect_mandelbrot, size, extent, quality)
def test_effect_noise(self): def test_effect_noise(self):
# Arrange # Arrange
@ -388,7 +404,7 @@ class TestImage(PillowTestCase):
im = hopper() im = hopper()
# Act / Assert # Act / Assert
self.assertRaises(NotImplementedError, lambda: im.offset(None)) self.assertRaises(NotImplementedError, im.offset, None)
def test_fromstring(self): def test_fromstring(self):
self.assertRaises(NotImplementedError, Image.fromstring) self.assertRaises(NotImplementedError, Image.fromstring)
@ -399,8 +415,7 @@ class TestImage(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(ValueError, self.assertRaises(ValueError,
lambda: Image.linear_gradient(wrong_mode)) Image.linear_gradient, wrong_mode)
return
def test_linear_gradient(self): def test_linear_gradient(self):
@ -425,8 +440,7 @@ class TestImage(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(ValueError, self.assertRaises(ValueError,
lambda: Image.radial_gradient(wrong_mode)) Image.radial_gradient, wrong_mode)
return
def test_radial_gradient(self): def test_radial_gradient(self):
@ -445,6 +459,11 @@ class TestImage(PillowTestCase):
target = Image.open(target_file).convert(mode) target = Image.open(target_file).convert(mode)
self.assert_image_equal(im, target) self.assert_image_equal(im, target)
def test_remap_palette(self):
# Test illegal image mode
im = hopper()
self.assertRaises(ValueError, im.remap_palette, None)
def test__new(self): def test__new(self):
from PIL import ImagePalette from PIL import ImagePalette
@ -496,10 +515,10 @@ class TestRegistry(PillowTestCase):
self.assertEqual(enc.args, ('RGB', 'args', 'extra')) self.assertEqual(enc.args, ('RGB', 'args', 'extra'))
def test_encode_registry_fail(self): def test_encode_registry_fail(self):
self.assertRaises(IOError, lambda: Image._getencoder('RGB', self.assertRaises(IOError, Image._getencoder, 'RGB',
'DoesNotExist', 'DoesNotExist',
('args',), ('args',),
extra=('extra',))) extra=('extra',))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -107,7 +107,7 @@ class TestImageConvert(PillowTestCase):
p = self.assert_warning( p = self.assert_warning(
UserWarning, UserWarning,
lambda: im.convert('P', palette=Image.ADAPTIVE)) im.convert, 'P', palette=Image.ADAPTIVE)
self.assertNotIn('transparency', p.info) self.assertNotIn('transparency', p.info)
p.save(f) p.save(f)
@ -131,7 +131,7 @@ class TestImageConvert(PillowTestCase):
p = self.assert_warning( p = self.assert_warning(
UserWarning, UserWarning,
lambda: im.convert('P', palette=Image.ADAPTIVE)) im.convert, 'P', palette=Image.ADAPTIVE)
self.assertNotIn('transparency', p.info) self.assertNotIn('transparency', p.info)
p.save(f) p.save(f)
@ -155,7 +155,7 @@ class TestImageConvert(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(ValueError, self.assertRaises(ValueError,
lambda: im.convert(mode='CMYK', matrix=matrix)) im.convert, mode='CMYK', matrix=matrix)
def test_matrix_wrong_mode(self): def test_matrix_wrong_mode(self):
# Arrange # Arrange
@ -168,7 +168,7 @@ class TestImageConvert(PillowTestCase):
# Act / Assert # Act / Assert
self.assertRaises(ValueError, self.assertRaises(ValueError,
lambda: im.convert(mode='L', matrix=matrix)) im.convert, mode='L', matrix=matrix)
def test_matrix_xyz(self): def test_matrix_xyz(self):

View File

@ -34,7 +34,7 @@ class TestImageFilter(PillowTestCase):
filter(ImageFilter.UnsharpMask) filter(ImageFilter.UnsharpMask)
filter(ImageFilter.UnsharpMask(10)) filter(ImageFilter.UnsharpMask(10))
self.assertRaises(TypeError, lambda: filter("hello")) self.assertRaises(TypeError, filter, "hello")
def test_crash(self): def test_crash(self):
@ -83,7 +83,7 @@ class TestImageFilter(PillowTestCase):
self.assertEqual(rankfilter("1"), (0, 4, 8)) self.assertEqual(rankfilter("1"), (0, 4, 8))
self.assertEqual(rankfilter("L"), (0, 4, 8)) self.assertEqual(rankfilter("L"), (0, 4, 8))
self.assertRaises(ValueError, lambda: rankfilter("P")) self.assertRaises(ValueError, rankfilter, "P")
self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0))) self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0)))
self.assertEqual(rankfilter("I"), (0, 4, 8)) self.assertEqual(rankfilter("I"), (0, 4, 8))
self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0)) self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0))
@ -95,26 +95,38 @@ class TestImageFilter(PillowTestCase):
self.assertEqual(rankfilter.rank, 2) self.assertEqual(rankfilter.rank, 2)
def test_consistency_3x3(self): def test_consistency_3x3(self):
im = Image.open("Tests/images/hopper.bmp") source = Image.open("Tests/images/hopper.bmp")
emboss = im.filter(ImageFilter.Kernel((3, 3), reference = Image.open("Tests/images/hopper_emboss.bmp")
(-1, -1, 0, kernel = ImageFilter.Kernel((3, 3),
-1, 0, 1, (-1, -1, 0,
0, 1, 1), .3)) -1, 0, 1,
0, 1, 1), .3)
source = source.split() * 2
reference = reference.split() * 2
self.assert_image_equal( for mode in ['L', 'LA', 'RGB', 'CMYK']:
emboss, Image.open("Tests/images/hopper_emboss.bmp")) self.assert_image_equal(
Image.merge(mode, source[:len(mode)]).filter(kernel),
Image.merge(mode, reference[:len(mode)]),
)
def test_consistency_5x5(self): def test_consistency_5x5(self):
im = Image.open("Tests/images/hopper.bmp") source = Image.open("Tests/images/hopper.bmp")
emboss = im.filter(ImageFilter.Kernel((5, 5), reference = Image.open("Tests/images/hopper_emboss_more.bmp")
(-1, -1, -1, -1, 0, kernel = ImageFilter.Kernel((5, 5),
-1, -1, -1, 0, 1, (-1, -1, -1, -1, 0,
-1, -1, 0, 1, 1, -1, -1, -1, 0, 1,
-1, 0, 1, 1, 1, -1, -1, 0, 1, 1,
0, 1, 1, 1, 1), 0.3)) -1, 0, 1, 1, 1,
0, 1, 1, 1, 1), 0.3)
source = source.split() * 2
reference = reference.split() * 2
self.assert_image_equal( for mode in ['L', 'LA', 'RGB', 'CMYK']:
emboss, Image.open("Tests/images/hopper_emboss_more.bmp")) self.assert_image_equal(
Image.merge(mode, source[:len(mode)]).filter(kernel),
Image.merge(mode, reference[:len(mode)]),
)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -11,6 +11,9 @@ class TestImageFromBytes(PillowTestCase):
self.assert_image_equal(im1, im2) self.assert_image_equal(im1, im2)
def test_not_implemented(self):
self.assertRaises(NotImplementedError, Image.fromstring)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -19,7 +19,7 @@ class TestImageLoad(PillowTestCase):
im = Image.open("Tests/images/hopper.gif") im = Image.open("Tests/images/hopper.gif")
im.close() im.close()
self.assertRaises(ValueError, im.load) self.assertRaises(ValueError, im.load)
self.assertRaises(ValueError, lambda: im.getpixel((0, 0))) self.assertRaises(ValueError, im.getpixel, (0, 0))
def test_contextmanager(self): def test_contextmanager(self):
fn = None fn = None
@ -27,7 +27,7 @@ class TestImageLoad(PillowTestCase):
fn = im.fp.fileno() fn = im.fp.fileno()
os.fstat(fn) os.fstat(fn)
self.assertRaises(OSError, lambda: os.fstat(fn)) self.assertRaises(OSError, os.fstat, fn)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -6,17 +6,17 @@ class TestImagePoint(PillowTestCase):
def test_sanity(self): def test_sanity(self):
im = hopper() im = hopper()
self.assertRaises(ValueError, lambda: im.point(list(range(256)))) self.assertRaises(ValueError, im.point, list(range(256)))
im.point(list(range(256))*3) im.point(list(range(256))*3)
im.point(lambda x: x) im.point(lambda x: x)
im = im.convert("I") im = im.convert("I")
self.assertRaises(ValueError, lambda: im.point(list(range(256)))) self.assertRaises(ValueError, im.point, list(range(256)))
im.point(lambda x: x*1) im.point(lambda x: x*1)
im.point(lambda x: x+1) im.point(lambda x: x+1)
im.point(lambda x: x*1+1) im.point(lambda x: x*1+1)
self.assertRaises(TypeError, lambda: im.point(lambda x: x-1)) self.assertRaises(TypeError, im.point, lambda x: x-1)
self.assertRaises(TypeError, lambda: im.point(lambda x: x/1)) self.assertRaises(TypeError, im.point, lambda x: x/1)
def test_16bit_lut(self): def test_16bit_lut(self):
""" Tests for 16 bit -> 8 bit lut for converting I->L images """ Tests for 16 bit -> 8 bit lut for converting I->L images
@ -36,7 +36,8 @@ class TestImagePoint(PillowTestCase):
self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L')) self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L'))
def test_f_mode(self): def test_f_mode(self):
self.assertRaises(ValueError, lambda: hopper('F').point(None)) im = hopper('F')
self.assertRaises(ValueError, im.point, None)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -13,14 +13,14 @@ class TestImagePutPalette(PillowTestCase):
if p: if p:
return im.mode, p[:10] return im.mode, p[:10]
return im.mode return im.mode
self.assertRaises(ValueError, lambda: palette("1")) self.assertRaises(ValueError, palette, "1")
self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
self.assertRaises(ValueError, lambda: palette("I")) self.assertRaises(ValueError, palette, "I")
self.assertRaises(ValueError, lambda: palette("F")) self.assertRaises(ValueError, palette, "F")
self.assertRaises(ValueError, lambda: palette("RGB")) self.assertRaises(ValueError, palette, "RGB")
self.assertRaises(ValueError, lambda: palette("RGBA")) self.assertRaises(ValueError, palette, "RGBA")
self.assertRaises(ValueError, lambda: palette("YCbCr")) self.assertRaises(ValueError, palette, "YCbCr")
def test_imagepalette(self): def test_imagepalette(self):
im = hopper("P") im = hopper("P")

View File

@ -39,7 +39,7 @@ class TestImageQuantize(PillowTestCase):
def test_rgba_quantize(self): def test_rgba_quantize(self):
image = hopper('RGBA') image = hopper('RGBA')
image.quantize() image.quantize()
self.assertRaises(Exception, lambda: image.quantize(method=0)) self.assertRaises(Exception, image.quantize, method=0)
def test_quantize(self): def test_quantize(self):
image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB') image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB')

View File

@ -460,7 +460,7 @@ class CoreResampleBoxTest(PillowTestCase):
self.assert_image_similar(cropped, with_box, 0.4) self.assert_image_similar(cropped, with_box, 0.4)
def test_passthrough(self): def test_passthrough(self):
"When no resize is required" # When no resize is required
im = hopper() im = hopper()
for size, box in [ for size, box in [
@ -478,7 +478,7 @@ class CoreResampleBoxTest(PillowTestCase):
raise raise
def test_no_passthrough(self): def test_no_passthrough(self):
"When resize is required" # When resize is required
im = hopper() im = hopper()
for size, box in [ for size, box in [
@ -498,7 +498,7 @@ class CoreResampleBoxTest(PillowTestCase):
raise raise
def test_skip_horizontal(self): def test_skip_horizontal(self):
"Can skip resize in one dimension" # Can skip resize for one dimension
im = hopper() im = hopper()
for flt in [Image.NEAREST, Image.BICUBIC]: for flt in [Image.NEAREST, Image.BICUBIC]:
@ -519,7 +519,7 @@ class CoreResampleBoxTest(PillowTestCase):
raise raise
def test_skip_vertical(self): def test_skip_vertical(self):
"Can skip resize in one dimension" # Can skip resize for one dimension
im = hopper() im = hopper()
for flt in [Image.NEAREST, Image.BICUBIC]: for flt in [Image.NEAREST, Image.BICUBIC]:

View File

@ -112,6 +112,10 @@ class TestImageResize(PillowTestCase):
resize(mode, (112, 103)) resize(mode, (112, 103))
resize(mode, (188, 214)) resize(mode, (188, 214))
# Test unknown resampling filter
im = hopper()
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -141,8 +141,8 @@ class TestImageTransform(PillowTestCase):
self.test_mesh() self.test_mesh()
def test_missing_method_data(self): def test_missing_method_data(self):
self.assertRaises(ValueError, lambda: im = hopper()
hopper().transform((100, 100), None)) self.assertRaises(ValueError, im.transform, (100, 100), None)
class TestImageTransformAffine(PillowTestCase): class TestImageTransformAffine(PillowTestCase):

View File

@ -144,20 +144,26 @@ class TestImageCms(PillowTestCase):
'IEC 61966-2.1 Default RGB colour space - sRGB') 'IEC 61966-2.1 Default RGB colour space - sRGB')
def test_exceptions(self): def test_exceptions(self):
# Test mode mismatch
psRGB = ImageCms.createProfile("sRGB")
pLab = ImageCms.createProfile("LAB")
t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB")
self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA"))
# the procedural pyCMS API uses PyCMSError for all sorts of errors # the procedural pyCMS API uses PyCMSError for all sorts of errors
self.assertRaises( self.assertRaises(
ImageCms.PyCMSError, ImageCms.PyCMSError,
lambda: ImageCms.profileToProfile(hopper(), "foo", "bar")) ImageCms.profileToProfile, hopper(), "foo", "bar")
self.assertRaises( self.assertRaises(
ImageCms.PyCMSError, ImageCms.PyCMSError,
lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) ImageCms.buildTransform, "foo", "bar", "RGB", "RGB")
self.assertRaises( self.assertRaises(
ImageCms.PyCMSError, ImageCms.PyCMSError,
lambda: ImageCms.getProfileName(None)) ImageCms.getProfileName, None)
self.skip_missing() self.skip_missing()
self.assertRaises( self.assertRaises(
ImageCms.PyCMSError, ImageCms.PyCMSError,
lambda: ImageCms.isIntentSupported(SRGB, None, None)) ImageCms.isIntentSupported, SRGB, None, None)
def test_display_profile(self): def test_display_profile(self):
# try fetching the profile for the current display device # try fetching the profile for the current display device
@ -167,6 +173,14 @@ class TestImageCms(PillowTestCase):
ImageCms.createProfile("LAB", 5000) ImageCms.createProfile("LAB", 5000)
ImageCms.createProfile("LAB", 6500) ImageCms.createProfile("LAB", 6500)
def test_unsupported_color_space(self):
self.assertRaises(ImageCms.PyCMSError,
ImageCms.createProfile, "unsupported")
def test_invalid_color_temperature(self):
self.assertRaises(ImageCms.PyCMSError,
ImageCms.createProfile, "LAB", "invalid")
def test_simple_lab(self): def test_simple_lab(self):
i = Image.new('RGB', (10, 10), (128, 128, 128)) i = Image.new('RGB', (10, 10), (128, 128, 128))

View File

@ -51,13 +51,11 @@ class TestImageDraw(PillowTestCase):
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.line(((0, 0)), fill=(0, 0, 0)) draw.line(((0, 0)), fill=(0, 0, 0))
del draw
def test_mode_mismatch(self): def test_mode_mismatch(self):
im = hopper("RGB").copy() im = hopper("RGB").copy()
self.assertRaises(ValueError, self.assertRaises(ValueError, ImageDraw.ImageDraw, im, mode="L")
lambda: ImageDraw.ImageDraw(im, mode="L"))
def helper_arc(self, bbox, start, end): def helper_arc(self, bbox, start, end):
# Arrange # Arrange
@ -66,7 +64,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.arc(bbox, start, end) draw.arc(bbox, start, end)
del draw
# Assert # Assert
self.assert_image_similar( self.assert_image_similar(
@ -89,7 +86,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.arc(BBOX1, start=start, end=end) draw.arc(BBOX1, start=start, end=end)
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -105,7 +101,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.arc(BBOX1, start=start, end=end) draw.arc(BBOX1, start=start, end=end)
del draw
# Assert # Assert
self.assert_image_similar( self.assert_image_similar(
@ -119,7 +114,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.bitmap((10, 10), small) draw.bitmap((10, 10), small)
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -133,7 +127,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.chord(bbox, start, end, fill="red", outline="yellow") draw.chord(bbox, start, end, fill="red", outline="yellow")
del draw
# Assert # Assert
self.assert_image_similar(im, Image.open(expected), 1) self.assert_image_similar(im, Image.open(expected), 1)
@ -156,7 +149,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.ellipse(bbox, fill="green", outline="blue") draw.ellipse(bbox, fill="green", outline="blue")
del draw
# Assert # Assert
self.assert_image_similar(im, Image.open(expected), 1) self.assert_image_similar(im, Image.open(expected), 1)
@ -176,7 +168,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.ellipse(((0, 0), (W-1, H)), fill="white") draw.ellipse(((0, 0), (W-1, H)), fill="white")
del draw
# Assert # Assert
self.assert_image_similar( self.assert_image_similar(
@ -189,7 +180,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.line(points, fill="yellow", width=2) draw.line(points, fill="yellow", width=2)
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -217,7 +207,6 @@ class TestImageDraw(PillowTestCase):
s.line(x0, y0) s.line(x0, y0)
draw.shape(s, fill=1) draw.shape(s, fill=1)
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -239,7 +228,6 @@ class TestImageDraw(PillowTestCase):
s.line(x0, y0) s.line(x0, y0)
draw.shape(s, outline="blue") draw.shape(s, outline="blue")
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -252,7 +240,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.pieslice(bbox, start, end, fill="white", outline="blue") draw.pieslice(bbox, start, end, fill="white", outline="blue")
del draw
# Assert # Assert
self.assert_image_similar( self.assert_image_similar(
@ -273,7 +260,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.point(points, fill="yellow") draw.point(points, fill="yellow")
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -292,7 +278,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.polygon(points, fill="red", outline="blue") draw.polygon(points, fill="red", outline="blue")
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -316,7 +301,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.polygon(KITE_POINTS, fill="blue", outline="yellow") draw.polygon(KITE_POINTS, fill="blue", outline="yellow")
del draw
# Assert # Assert
self.assert_image_equal(im, Image.open(expected)) self.assert_image_equal(im, Image.open(expected))
@ -328,7 +312,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.rectangle(bbox, fill="black", outline="green") draw.rectangle(bbox, fill="black", outline="green")
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -350,7 +333,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.rectangle(bbox, fill="orange") draw.rectangle(bbox, fill="orange")
del draw
# Assert # Assert
self.assert_image_similar(im, Image.open(expected), 1) self.assert_image_similar(im, Image.open(expected), 1)
@ -377,7 +359,6 @@ class TestImageDraw(PillowTestCase):
# Test that filling outside the image does not change the image # Test that filling outside the image does not change the image
ImageDraw.floodfill(im, (W, H), red) ImageDraw.floodfill(im, (W, H), red)
self.assert_image_equal(im, im_floodfill) self.assert_image_equal(im, im_floodfill)
del draw
@unittest.skipIf(hasattr(sys, 'pypy_version_info'), @unittest.skipIf(hasattr(sys, 'pypy_version_info'),
"Causes fatal RPython error on PyPy") "Causes fatal RPython error on PyPy")
@ -394,7 +375,6 @@ class TestImageDraw(PillowTestCase):
ImageDraw.floodfill( ImageDraw.floodfill(
im, centre_point, ImageColor.getrgb("red"), im, centre_point, ImageColor.getrgb("red"),
border=ImageColor.getrgb("black")) border=ImageColor.getrgb("black"))
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -414,7 +394,6 @@ class TestImageDraw(PillowTestCase):
ImageDraw.floodfill( ImageDraw.floodfill(
im, centre_point, ImageColor.getrgb("red"), im, centre_point, ImageColor.getrgb("red"),
thresh=30) thresh=30)
del draw
# Assert # Assert
self.assert_image_equal( self.assert_image_equal(
@ -577,7 +556,6 @@ class TestImageDraw(PillowTestCase):
# Act # Act
draw.line([(50, 50), (50, 50)], width=3) draw.line([(50, 50), (50, 50)], width=3)
del draw
# Assert # Assert
self.assert_image_similar(im, Image.open(expected), 1) self.assert_image_similar(im, Image.open(expected), 1)

View File

@ -69,7 +69,7 @@ class TestImageFile(PillowTestCase):
im1, im2 = roundtrip("JPEG") # lossy compression im1, im2 = roundtrip("JPEG") # lossy compression
self.assert_image(im1, im2.mode, im2.size) self.assert_image(im1, im2.mode, im2.size)
self.assertRaises(IOError, lambda: roundtrip("PDF")) self.assertRaises(IOError, roundtrip, "PDF")
def test_ico(self): def test_ico(self):
with open('Tests/images/python.ico', 'rb') as f: with open('Tests/images/python.ico', 'rb') as f:
@ -93,7 +93,7 @@ class TestImageFile(PillowTestCase):
self.assert_image_equal(im1, im2) self.assert_image_equal(im1, im2)
def test_raise_ioerror(self): def test_raise_ioerror(self):
self.assertRaises(IOError, lambda: ImageFile.raise_ioerror(1)) self.assertRaises(IOError, ImageFile.raise_ioerror, 1)
def test_raise_typeerror(self): def test_raise_typeerror(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
@ -182,7 +182,7 @@ class TestPyDecoder(PillowTestCase):
self.assertEqual(d.state.xsize, xsize) self.assertEqual(d.state.xsize, xsize)
self.assertEqual(d.state.ysize, ysize) self.assertEqual(d.state.ysize, ysize)
self.assertRaises(ValueError, lambda: d.set_as_raw(b'\x00')) self.assertRaises(ValueError, d.set_as_raw, b'\x00')
def test_extents_none(self): def test_extents_none(self):
buf = BytesIO(b'\x00'*255) buf = BytesIO(b'\x00'*255)

View File

@ -106,7 +106,7 @@ class TestImageFont(PillowTestCase):
# Usage note: making two fonts from the same buffer fails. # Usage note: making two fonts from the same buffer fails.
# shared_bytes = self._font_as_bytes() # shared_bytes = self._font_as_bytes()
# self._render(shared_bytes) # self._render(shared_bytes)
# self.assertRaises(Exception, lambda: _render(shared_bytes)) # self.assertRaises(Exception, _render, shared_bytes)
def test_font_with_open_file(self): def test_font_with_open_file(self):
with open(FONT_PATH, 'rb') as f: with open(FONT_PATH, 'rb') as f:
@ -141,7 +141,6 @@ class TestImageFont(PillowTestCase):
size = draw.textsize(txt, ttf) size = draw.textsize(txt, ttf)
draw.text((10, 10), txt, font=ttf) draw.text((10, 10), txt, font=ttf)
draw.rectangle((10, 10, 10 + size[0], 10 + size[1])) draw.rectangle((10, 10, 10 + size[0], 10 + size[1]))
del draw
target = 'Tests/images/rectangle_surrounding_text.png' target = 'Tests/images/rectangle_surrounding_text.png'
target_img = Image.open(target) target_img = Image.open(target)
@ -188,7 +187,6 @@ class TestImageFont(PillowTestCase):
draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None, draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None,
spacing=4, align="left") spacing=4, align="left")
draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left") draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left")
del draw
# Test align center and right # Test align center and right
for align, ext in {"center": "_center", for align, ext in {"center": "_center",
@ -196,7 +194,6 @@ class TestImageFont(PillowTestCase):
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align)
del draw
target = 'Tests/images/multiline_text'+ext+'.png' target = 'Tests/images/multiline_text'+ext+'.png'
target_img = Image.open(target) target_img = Image.open(target)
@ -211,9 +208,9 @@ class TestImageFont(PillowTestCase):
# Act/Assert # Act/Assert
self.assertRaises(AssertionError, self.assertRaises(AssertionError,
lambda: draw.multiline_text((0, 0), TEST_TEXT, draw.multiline_text, (0, 0), TEST_TEXT,
font=ttf, font=ttf,
align="unknown")) align="unknown")
def test_draw_align(self): def test_draw_align(self):
im = Image.new('RGB', (300, 100), 'white') im = Image.new('RGB', (300, 100), 'white')
@ -221,7 +218,6 @@ class TestImageFont(PillowTestCase):
ttf = self.get_font() ttf = self.get_font()
line = "some text" line = "some text"
draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left') draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left')
del draw
def test_multiline_size(self): def test_multiline_size(self):
ttf = self.get_font() ttf = self.get_font()
@ -236,7 +232,6 @@ class TestImageFont(PillowTestCase):
# to multiline_textsize() # to multiline_textsize()
draw.textsize(TEST_TEXT, font=ttf, spacing=4) draw.textsize(TEST_TEXT, font=ttf, spacing=4)
draw.textsize(TEST_TEXT, ttf, 4) draw.textsize(TEST_TEXT, ttf, 4)
del draw
def test_multiline_width(self): def test_multiline_width(self):
ttf = self.get_font() ttf = self.get_font()
@ -246,7 +241,6 @@ class TestImageFont(PillowTestCase):
self.assertEqual(draw.textsize("longest line", font=ttf)[0], self.assertEqual(draw.textsize("longest line", font=ttf)[0],
draw.multiline_textsize("longest line\nline", draw.multiline_textsize("longest line\nline",
font=ttf)[0]) font=ttf)[0])
del draw
def test_multiline_spacing(self): def test_multiline_spacing(self):
ttf = self.get_font() ttf = self.get_font()
@ -254,7 +248,6 @@ class TestImageFont(PillowTestCase):
im = Image.new(mode='RGB', size=(300, 100)) im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im) draw = ImageDraw.Draw(im)
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10)
del draw
target = 'Tests/images/multiline_text_spacing.png' target = 'Tests/images/multiline_text_spacing.png'
target_img = Image.open(target) target_img = Image.open(target)
@ -279,7 +272,6 @@ class TestImageFont(PillowTestCase):
# Rotated font # Rotated font
draw.font = transposed_font draw.font = transposed_font
box_size_b = draw.textsize(word) box_size_b = draw.textsize(word)
del draw
# Check (w,h) of box a is (h,w) of box b # Check (w,h) of box a is (h,w) of box b
self.assertEqual(box_size_a[0], box_size_b[1]) self.assertEqual(box_size_a[0], box_size_b[1])
@ -302,7 +294,6 @@ class TestImageFont(PillowTestCase):
# Rotated font # Rotated font
draw.font = transposed_font draw.font = transposed_font
box_size_b = draw.textsize(word) box_size_b = draw.textsize(word)
del draw
# Check boxes a and b are same size # Check boxes a and b are same size
self.assertEqual(box_size_a, box_size_b) self.assertEqual(box_size_a, box_size_b)
@ -384,7 +375,7 @@ class TestImageFont(PillowTestCase):
filename = "somefilenamethatdoesntexist.ttf" filename = "somefilenamethatdoesntexist.ttf"
# Act/Assert # Act/Assert
self.assertRaises(IOError, lambda: ImageFont.load_path(filename)) self.assertRaises(IOError, ImageFont.load_path, filename)
def test_default_font(self): def test_default_font(self):
# Arrange # Arrange
@ -398,16 +389,29 @@ class TestImageFont(PillowTestCase):
# Act # Act
default_font = ImageFont.load_default() default_font = ImageFont.load_default()
draw.text((10, 10), txt, font=default_font) draw.text((10, 10), txt, font=default_font)
del draw
# Assert # Assert
self.assert_image_equal(im, target_img) self.assert_image_equal(im, target_img)
def test_getsize_empty(self): def test_getsize_empty(self):
# issue #2614
font = self.get_font() font = self.get_font()
# should not crash. # should not crash.
self.assertEqual((0, 0), font.getsize('')) self.assertEqual((0, 0), font.getsize(''))
def test_render_empty(self):
# issue 2666
font = self.get_font()
im = Image.new(mode='RGB', size=(300, 100))
target = im.copy()
draw = ImageDraw.Draw(im)
#should not crash here.
draw.text((10, 10), '', font=font)
self.assert_image_equal(im, target)
def _test_fake_loading_font(self, path_to_fake, fontname): def _test_fake_loading_font(self, path_to_fake, fontname):
# Make a copy of FreeTypeFont so we can patch the original # Make a copy of FreeTypeFont so we can patch the original
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)

View File

@ -81,9 +81,9 @@ class MorphTests(PillowTestCase):
def test_no_operator_loaded(self): def test_no_operator_loaded(self):
mop = ImageMorph.MorphOp() mop = ImageMorph.MorphOp()
self.assertRaises(Exception, lambda: mop.apply(None)) self.assertRaises(Exception, mop.apply, None)
self.assertRaises(Exception, lambda: mop.match(None)) self.assertRaises(Exception, mop.match, None)
self.assertRaises(Exception, lambda: mop.save_lut(None)) self.assertRaises(Exception, mop.save_lut, None)
# Test the named patterns # Test the named patterns
def test_erosion8(self): def test_erosion8(self):
@ -214,9 +214,9 @@ class MorphTests(PillowTestCase):
im = hopper('RGB') im = hopper('RGB')
mop = ImageMorph.MorphOp(op_name="erosion8") mop = ImageMorph.MorphOp(op_name="erosion8")
self.assertRaises(Exception, lambda: mop.apply(im)) self.assertRaises(Exception, mop.apply, im)
self.assertRaises(Exception, lambda: mop.match(im)) self.assertRaises(Exception, mop.match, im)
self.assertRaises(Exception, lambda: mop.get_on_pixels(im)) self.assertRaises(Exception, mop.get_on_pixels, im)
def test_add_patterns(self): def test_add_patterns(self):
# Arrange # Arrange
@ -240,7 +240,7 @@ class MorphTests(PillowTestCase):
def test_unknown_pattern(self): def test_unknown_pattern(self):
self.assertRaises( self.assertRaises(
Exception, Exception,
lambda: ImageMorph.LutBuilder(op_name='unknown')) ImageMorph.LutBuilder, op_name='unknown')
def test_pattern_syntax_error(self): def test_pattern_syntax_error(self):
# Arrange # Arrange
@ -249,9 +249,7 @@ class MorphTests(PillowTestCase):
lb.add_patterns(new_patterns) lb.add_patterns(new_patterns)
# Act / Assert # Act / Assert
self.assertRaises( self.assertRaises(Exception, lb.build_lut)
Exception,
lambda: lb.build_lut())
def test_load_invalid_mrl(self): def test_load_invalid_mrl(self):
# Arrange # Arrange
@ -259,7 +257,7 @@ class MorphTests(PillowTestCase):
mop = ImageMorph.MorphOp() mop = ImageMorph.MorphOp()
# Act / Assert # Act / Assert
self.assertRaises(Exception, lambda: mop.load_lut(invalid_mrl)) self.assertRaises(Exception, mop.load_lut, invalid_mrl)
def test_roundtrip_mrl(self): def test_roundtrip_mrl(self):
# Arrange # Arrange

View File

@ -37,26 +37,26 @@ class TestImageOpsUsm(PillowTestCase):
def test_usm_formats(self): def test_usm_formats(self):
usm = ImageOps.unsharp_mask usm = ImageOps.unsharp_mask
self.assertRaises(ValueError, lambda: usm(im.convert("1"))) self.assertRaises(ValueError, usm, im.convert("1"))
usm(im.convert("L")) usm(im.convert("L"))
self.assertRaises(ValueError, lambda: usm(im.convert("I"))) self.assertRaises(ValueError, usm, im.convert("I"))
self.assertRaises(ValueError, lambda: usm(im.convert("F"))) self.assertRaises(ValueError, usm, im.convert("F"))
usm(im.convert("RGB")) usm(im.convert("RGB"))
usm(im.convert("RGBA")) usm(im.convert("RGBA"))
usm(im.convert("CMYK")) usm(im.convert("CMYK"))
self.assertRaises(ValueError, lambda: usm(im.convert("YCbCr"))) self.assertRaises(ValueError, usm, im.convert("YCbCr"))
def test_blur_formats(self): def test_blur_formats(self):
blur = ImageOps.gaussian_blur blur = ImageOps.gaussian_blur
self.assertRaises(ValueError, lambda: blur(im.convert("1"))) self.assertRaises(ValueError, blur, im.convert("1"))
blur(im.convert("L")) blur(im.convert("L"))
self.assertRaises(ValueError, lambda: blur(im.convert("I"))) self.assertRaises(ValueError, blur, im.convert("I"))
self.assertRaises(ValueError, lambda: blur(im.convert("F"))) self.assertRaises(ValueError, blur, im.convert("F"))
blur(im.convert("RGB")) blur(im.convert("RGB"))
blur(im.convert("RGBA")) blur(im.convert("RGBA"))
blur(im.convert("CMYK")) blur(im.convert("CMYK"))
self.assertRaises(ValueError, lambda: blur(im.convert("YCbCr"))) self.assertRaises(ValueError, blur, im.convert("YCbCr"))
def test_usm_accuracy(self): def test_usm_accuracy(self):

View File

@ -2,56 +2,54 @@ from helper import unittest, PillowTestCase
from PIL import ImagePalette, Image from PIL import ImagePalette, Image
ImagePalette = ImagePalette.ImagePalette
class TestImagePalette(PillowTestCase): class TestImagePalette(PillowTestCase):
def test_sanity(self): def test_sanity(self):
ImagePalette("RGB", list(range(256))*3) ImagePalette.ImagePalette("RGB", list(range(256))*3)
self.assertRaises( self.assertRaises(ValueError,
ValueError, lambda: ImagePalette("RGB", list(range(256))*2)) ImagePalette.ImagePalette, "RGB", list(range(256))*2)
def test_getcolor(self): def test_getcolor(self):
palette = ImagePalette() palette = ImagePalette.ImagePalette()
test_map = {} test_map = {}
for i in range(256): for i in range(256):
test_map[palette.getcolor((i, i, i))] = i test_map[palette.getcolor((i, i, i))] = i
self.assertEqual(len(test_map), 256) self.assertEqual(len(test_map), 256)
self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3))) self.assertRaises(ValueError, palette.getcolor, (1, 2, 3))
# Test unknown color specifier
self.assertRaises(ValueError, palette.getcolor, "unknown")
def test_file(self): def test_file(self):
palette = ImagePalette("RGB", list(range(256))*3) palette = ImagePalette.ImagePalette("RGB", list(range(256))*3)
f = self.tempfile("temp.lut") f = self.tempfile("temp.lut")
palette.save(f) palette.save(f)
from PIL.ImagePalette import load, raw p = ImagePalette.load(f)
p = load(f)
# load returns raw palette information # load returns raw palette information
self.assertEqual(len(p[0]), 768) self.assertEqual(len(p[0]), 768)
self.assertEqual(p[1], "RGB") self.assertEqual(p[1], "RGB")
p = raw(p[1], p[0]) p = ImagePalette.raw(p[1], p[0])
self.assertIsInstance(p, ImagePalette) self.assertIsInstance(p, ImagePalette.ImagePalette)
self.assertEqual(p.palette, palette.tobytes()) self.assertEqual(p.palette, palette.tobytes())
def test_make_linear_lut(self): def test_make_linear_lut(self):
# Arrange # Arrange
from PIL.ImagePalette import make_linear_lut
black = 0 black = 0
white = 255 white = 255
# Act # Act
lut = make_linear_lut(black, white) lut = ImagePalette.make_linear_lut(black, white)
# Assert # Assert
self.assertIsInstance(lut, list) self.assertIsInstance(lut, list)
@ -63,22 +61,19 @@ class TestImagePalette(PillowTestCase):
def test_make_linear_lut_not_yet_implemented(self): def test_make_linear_lut_not_yet_implemented(self):
# Update after FIXME # Update after FIXME
# Arrange # Arrange
from PIL.ImagePalette import make_linear_lut
black = 1 black = 1
white = 255 white = 255
# Act # Act
self.assertRaises( self.assertRaises(NotImplementedError,
NotImplementedError, ImagePalette.make_linear_lut, black, white)
lambda: make_linear_lut(black, white))
def test_make_gamma_lut(self): def test_make_gamma_lut(self):
# Arrange # Arrange
from PIL.ImagePalette import make_gamma_lut
exp = 5 exp = 5
# Act # Act
lut = make_gamma_lut(exp) lut = ImagePalette.make_gamma_lut(exp)
# Assert # Assert
self.assertIsInstance(lut, list) self.assertIsInstance(lut, list)
@ -92,19 +87,18 @@ class TestImagePalette(PillowTestCase):
def test_rawmode_valueerrors(self): def test_rawmode_valueerrors(self):
# Arrange # Arrange
from PIL.ImagePalette import raw palette = ImagePalette.raw("RGB", list(range(256))*3)
palette = raw("RGB", list(range(256))*3)
# Act / Assert # Act / Assert
self.assertRaises(ValueError, palette.tobytes) self.assertRaises(ValueError, palette.tobytes)
self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3))) self.assertRaises(ValueError, palette.getcolor, (1, 2, 3))
f = self.tempfile("temp.lut") f = self.tempfile("temp.lut")
self.assertRaises(ValueError, lambda: palette.save(f)) self.assertRaises(ValueError, palette.save, f)
def test_getdata(self): def test_getdata(self):
# Arrange # Arrange
data_in = list(range(256))*3 data_in = list(range(256))*3
palette = ImagePalette("RGB", data_in) palette = ImagePalette.ImagePalette("RGB", data_in)
# Act # Act
mode, data_out = palette.getdata() mode, data_out = palette.getdata()
@ -114,9 +108,8 @@ class TestImagePalette(PillowTestCase):
def test_rawmode_getdata(self): def test_rawmode_getdata(self):
# Arrange # Arrange
from PIL.ImagePalette import raw
data_in = list(range(256))*3 data_in = list(range(256))*3
palette = raw("RGB", data_in) palette = ImagePalette.raw("RGB", data_in)
# Act # Act
rawmode, data_out = palette.getdata() rawmode, data_out = palette.getdata()
@ -138,6 +131,10 @@ class TestImagePalette(PillowTestCase):
self.assert_image_equal(img, reloaded) self.assert_image_equal(img, reloaded)
def test_invalid_palette(self):
self.assertRaises(IOError,
ImagePalette.load, "Tests/images/hopper.jpg")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -22,7 +22,7 @@ class TestImageSequence(PillowTestCase):
self.assertEqual(index, 1) self.assertEqual(index, 1)
self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0)) self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
def test_iterator(self): def test_iterator(self):
im = Image.open('Tests/images/multipage.tiff') im = Image.open('Tests/images/multipage.tiff')
@ -30,7 +30,7 @@ class TestImageSequence(PillowTestCase):
for index in range(0, im.n_frames): for index in range(0, im.n_frames):
self.assertEqual(i[index], next(i)) self.assertEqual(i[index], next(i))
self.assertRaises(IndexError, lambda: i[index+1]) self.assertRaises(IndexError, lambda: i[index+1])
self.assertRaises(StopIteration, lambda: next(i)) self.assertRaises(StopIteration, next, i)
def _test_multipage_tiff(self): def _test_multipage_tiff(self):
im = Image.open('Tests/images/multipage.tiff') im = Image.open('Tests/images/multipage.tiff')

View File

@ -1,4 +1,4 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase, hopper
from PIL import Image from PIL import Image
from PIL import ImageShow from PIL import ImageShow
@ -10,12 +10,30 @@ class TestImageShow(PillowTestCase):
dir(Image) dir(Image)
dir(ImageShow) dir(ImageShow)
def test_register(self):
# Test registering a viewer that is not a class
ImageShow.register("not a class")
def test_show(self):
class TestViewer:
methodCalled = False
def show(self, image, title=None, **options):
self.methodCalled = True
return True
viewer = TestViewer()
ImageShow.register(viewer, -1)
im = hopper()
self.assertTrue(ImageShow.show(im))
self.assertTrue(viewer.methodCalled)
def test_viewer(self): def test_viewer(self):
viewer = ImageShow.Viewer() viewer = ImageShow.Viewer()
self.assertIsNone(viewer.get_format(None)) self.assertIsNone(viewer.get_format(None))
self.assertRaises(NotImplementedError, lambda: viewer.get_command(None)) self.assertRaises(NotImplementedError, viewer.get_command, None)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -26,7 +26,7 @@ class TestImageStat(PillowTestCase):
self.assertRaises(AttributeError, lambda: st.spam) self.assertRaises(AttributeError, lambda: st.spam)
self.assertRaises(TypeError, lambda: ImageStat.Stat(1)) self.assertRaises(TypeError, ImageStat.Stat, 1)
def test_hopper(self): def test_hopper(self):

View File

@ -29,8 +29,8 @@ class TestLibImage(PillowTestCase):
im.im.setmode("RGB") im.im.setmode("RGB")
self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3)) self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3))
self.assertRaises(ValueError, lambda: im.im.setmode("L")) self.assertRaises(ValueError, im.im.setmode, "L")
self.assertRaises(ValueError, lambda: im.im.setmode("RGBABCDE")) self.assertRaises(ValueError, im.im.setmode, "RGBABCDE")
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -86,12 +86,12 @@ class TestNumpy(PillowTestCase):
self.assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE) self.assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE)
# Check 64-bit integer formats # Check 64-bit integer formats
self.assertRaises(TypeError, lambda: to_image(numpy.uint64)) self.assertRaises(TypeError, to_image, numpy.uint64)
self.assertRaises(TypeError, lambda: to_image(numpy.int64)) self.assertRaises(TypeError, to_image, numpy.int64)
# Check floating-point formats # Check floating-point formats
self.assert_image(to_image(numpy.float), "F", TEST_IMAGE_SIZE) self.assert_image(to_image(numpy.float), "F", TEST_IMAGE_SIZE)
self.assertRaises(TypeError, lambda: to_image(numpy.float16)) self.assertRaises(TypeError, to_image, numpy.float16)
self.assert_image(to_image(numpy.float32), "F", TEST_IMAGE_SIZE) self.assert_image(to_image(numpy.float32), "F", TEST_IMAGE_SIZE)
self.assert_image(to_image(numpy.float64), "F", TEST_IMAGE_SIZE) self.assert_image(to_image(numpy.float64), "F", TEST_IMAGE_SIZE)

View File

@ -819,7 +819,7 @@ _filter(ImagingObject* self, PyObject* args)
Py_ssize_t kernelsize; Py_ssize_t kernelsize;
FLOAT32* kerneldata; FLOAT32* kerneldata;
int xsize, ysize; int xsize, ysize, i;
float divisor, offset; float divisor, offset;
PyObject* kernel = NULL; PyObject* kernel = NULL;
if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize, if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize,
@ -835,8 +835,12 @@ _filter(ImagingObject* self, PyObject* args)
return ImagingError_ValueError("bad kernel size"); return ImagingError_ValueError("bad kernel size");
} }
for (i = 0; i < kernelsize; ++i) {
kerneldata[i] /= divisor;
}
imOut = PyImagingNew( imOut = PyImagingNew(
ImagingFilter(self->image, xsize, ysize, kerneldata, offset, divisor) ImagingFilter(self->image, xsize, ysize, kerneldata, offset)
); );
free(kerneldata); free(kerneldata);
@ -2198,26 +2202,45 @@ textwidth(ImagingFontObject* self, const unsigned char* text)
} }
void _font_text_asBytes(PyObject* encoded_string, unsigned char** text){ void _font_text_asBytes(PyObject* encoded_string, unsigned char** text){
/* Allocates *text, returns a 'new reference'. Caller is required to free */
PyObject* bytes = NULL; PyObject* bytes = NULL;
Py_ssize_t len = 0;
char *buffer;
*text = NULL; *text = NULL;
if (PyUnicode_CheckExact(encoded_string)){ if (PyUnicode_CheckExact(encoded_string)){
bytes = PyUnicode_AsLatin1String(encoded_string); bytes = PyUnicode_AsLatin1String(encoded_string);
PyBytes_AsStringAndSize(bytes, &buffer, &len);
} else if (PyBytes_Check(encoded_string)) { } else if (PyBytes_Check(encoded_string)) {
bytes = encoded_string; PyBytes_AsStringAndSize(encoded_string, &buffer, &len);
} }
if (bytes) {
*text = (unsigned char*)PyBytes_AsString(bytes); if (len) {
*text = calloc(len,1);
if (*text) {
memcpy(*text, buffer, len);
}
if(bytes) {
Py_DECREF(bytes);
}
return; return;
} }
#if PY_VERSION_HEX < 0x03000000 #if PY_VERSION_HEX < 0x03000000
/* likely case here is py2.x with an ordinary string. /* likely case here is py2.x with an ordinary string.
but this isn't defined in Py3.x */ but this isn't defined in Py3.x */
if (PyString_Check(encoded_string)) { if (PyString_Check(encoded_string)) {
*text = (unsigned char *)PyString_AsString(encoded_string); PyString_AsStringAndSize(encoded_string, &buffer, &len);
*text = calloc(len,1);
if (*text) {
memcpy(*text, buffer, len);
}
return;
} }
#endif #endif
} }
@ -2248,6 +2271,7 @@ _font_getmask(ImagingFontObject* self, PyObject* args)
im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize); im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize);
if (!im) { if (!im) {
free(text);
return NULL; return NULL;
} }
@ -2273,9 +2297,11 @@ _font_getmask(ImagingFontObject* self, PyObject* args)
x = x + glyph->dx; x = x + glyph->dx;
b = b + glyph->dy; b = b + glyph->dy;
} }
free(text);
return PyImagingNew(im); return PyImagingNew(im);
failed: failed:
free(text);
ImagingDelete(im); ImagingDelete(im);
return NULL; return NULL;
} }
@ -2285,6 +2311,7 @@ _font_getsize(ImagingFontObject* self, PyObject* args)
{ {
unsigned char* text; unsigned char* text;
PyObject* encoded_string; PyObject* encoded_string;
PyObject* val;
if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string)) if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string))
return NULL; return NULL;
@ -2294,7 +2321,9 @@ _font_getsize(ImagingFontObject* self, PyObject* args)
return NULL; return NULL;
} }
return Py_BuildValue("ii", textwidth(self, text), self->ysize); val = Py_BuildValue("ii", textwidth(self, text), self->ysize);
free(text);
return val;
} }
static struct PyMethodDef _font_methods[] = { static struct PyMethodDef _font_methods[] = {

View File

@ -225,6 +225,11 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
if (PyUnicode_Check(string)) { if (PyUnicode_Check(string)) {
Py_UNICODE *text = PyUnicode_AS_UNICODE(string); Py_UNICODE *text = PyUnicode_AS_UNICODE(string);
Py_ssize_t size = PyUnicode_GET_SIZE(string); Py_ssize_t size = PyUnicode_GET_SIZE(string);
if (! size) {
/* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */
goto failed;
}
if (!raqm_set_text(rq, (const uint32_t *)(text), size)) { if (!raqm_set_text(rq, (const uint32_t *)(text), size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed"); PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
goto failed; goto failed;
@ -234,6 +239,9 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
else if (PyString_Check(string)) { else if (PyString_Check(string)) {
char *text = PyString_AS_STRING(string); char *text = PyString_AS_STRING(string);
int size = PyString_GET_SIZE(string); int size = PyString_GET_SIZE(string);
if (! size) {
goto failed;
}
if (!raqm_set_text_utf8(rq, text, size)) { if (!raqm_set_text_utf8(rq, text, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed"); PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
goto failed; goto failed;
@ -450,6 +458,10 @@ font_getsize(FontObject* self, PyObject* args)
y_max = y_min = 0; y_max = y_min = 0;
count = text_layout(string, self, dir, features, &glyph_info, 0); count = text_layout(string, self, dir, features, &glyph_info, 0);
if (PyErr_Occurred()) {
return NULL;
}
for (x = i = 0; i < count; i++) { for (x = i = 0; i < count; i++) {
int index, error; int index, error;
@ -497,6 +509,11 @@ font_getsize(FontObject* self, PyObject* args)
FT_Done_Glyph(glyph); FT_Done_Glyph(glyph);
} }
if (glyph_info) {
PyMem_Free(glyph_info);
glyph_info = NULL;
}
if (face) { if (face) {
/* left bearing */ /* left bearing */
@ -584,9 +601,12 @@ font_render(FontObject* self, PyObject* args)
glyph_info = NULL; glyph_info = NULL;
count = text_layout(string, self, dir, features, &glyph_info, mask); count = text_layout(string, self, dir, features, &glyph_info, mask);
if (count == 0) { if (PyErr_Occurred()) {
return NULL; return NULL;
} }
if (count == 0) {
Py_RETURN_NONE;
}
im = (Imaging) id; im = (Imaging) id;
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */ /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */

View File

@ -452,84 +452,9 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
void* data; void* data;
PyObject* result; PyObject* result;
int verbose = 0; /* debugging; will be removed in future versions */
if (!PyArg_ParseTuple(args, "|i", &verbose))
return NULL;
clip = OpenClipboard(NULL); clip = OpenClipboard(NULL);
/* FIXME: check error status */ /* FIXME: check error status */
if (verbose) {
UINT format = EnumClipboardFormats(0);
char buffer[200];
char* result;
while (format != 0) {
if (GetClipboardFormatName(format, buffer, sizeof buffer) > 0)
result = buffer;
else
switch (format) {
case CF_BITMAP:
result = "CF_BITMAP";
break;
case CF_DIB:
result = "CF_DIB";
break;
case CF_DIF:
result = "CF_DIF";
break;
case CF_ENHMETAFILE:
result = "CF_ENHMETAFILE";
break;
case CF_HDROP:
result = "CF_HDROP";
break;
case CF_LOCALE:
result = "CF_LOCALE";
break;
case CF_METAFILEPICT:
result = "CF_METAFILEPICT";
break;
case CF_OEMTEXT:
result = "CF_OEMTEXT";
break;
case CF_OWNERDISPLAY:
result = "CF_OWNERDISPLAY";
break;
case CF_PALETTE:
result = "CF_PALETTE";
break;
case CF_PENDATA:
result = "CF_PENDATA";
break;
case CF_RIFF:
result = "CF_RIFF";
break;
case CF_SYLK:
result = "CF_SYLK";
break;
case CF_TEXT:
result = "CF_TEXT";
break;
case CF_WAVE:
result = "CF_WAVE";
break;
case CF_TIFF:
result = "CF_TIFF";
break;
case CF_UNICODETEXT:
result = "CF_UNICODETEXT";
break;
default:
sprintf(buffer, "[%d]", format);
result = buffer;
break;
}
printf("%s (%d)\n", result, format);
format = EnumClipboardFormats(format);
}
}
handle = GetClipboardData(CF_DIB); handle = GetClipboardData(CF_DIB);
if (!handle) { if (!handle) {
/* FIXME: add CF_HDROP support to allow cut-and-paste from /* FIXME: add CF_HDROP support to allow cut-and-paste from
@ -542,14 +467,6 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
size = GlobalSize(handle); size = GlobalSize(handle);
data = GlobalLock(handle); data = GlobalLock(handle);
#if 0
/* calculate proper size for string formats */
if (format == CF_TEXT || format == CF_OEMTEXT)
size = strlen(data);
else if (format == CF_UNICODETEXT)
size = wcslen(data) * 2;
#endif
result = PyBytes_FromStringAndSize(data, size); result = PyBytes_FromStringAndSize(data, size);
GlobalUnlock(handle); GlobalUnlock(handle);

View File

@ -178,8 +178,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**sizes** **sizes**
A list of sizes including in this ico file; these are a 2-tuple, A list of sizes including in this ico file; these are a 2-tuple,
``(width, height)``; Default to ``[(16, 16), (24, 24), (32, 32), (48, 48), ``(width, height)``; Default to ``[(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (255, 255)]``. Any sizes bigger than the original (64, 64), (128, 128), (256, 256)]``. Any sizes bigger than the original
size or 255 will be ignored. size or 256 will be ignored.
IM IM
^^ ^^

View File

@ -262,6 +262,7 @@ The easiest way to install external libraries is via `Homebrew
$ brew install libtiff libjpeg webp little-cms2 $ brew install libtiff libjpeg webp little-cms2
To install libraqm on macOS use Homebrew to install its dependencies:: To install libraqm on macOS use Homebrew to install its dependencies::
$ brew install freetype harfbuzz fribidi $ brew install freetype harfbuzz fribidi
Then see ``depends/install_raqm_cmake.sh`` to install libraqm. Then see ``depends/install_raqm_cmake.sh`` to install libraqm.

View File

@ -265,7 +265,7 @@ can be easily displayed in a chromaticity diagram, for example).
Calculates the white point temperature (see the LCMS documentation Calculates the white point temperature (see the LCMS documentation
for more information). for more information).
:type: :py:class:`float` or `None` :type: :py:class:`float` or ``None``
.. py:attribute:: viewing_condition .. py:attribute:: viewing_condition

View File

@ -6,4 +6,49 @@ Get One Channel From Image
New method :py:meth:`PIL.Image.Image.getchannel` added. New method :py:meth:`PIL.Image.Image.getchannel` added.
It returns single channel by index or name. For example, It returns single channel by index or name. For example,
``image.getchannel("A")`` will return alpha channel as seperate image. ``image.getchannel("A")`` will return alpha channel as separate image.
``getchannel`` should work up to 6 times faster than ``image.split()[0]``
in previous Pillow versions.
Partial Resampling
==================
Added new argument ``box`` for :py:meth:`PIL.Image.Image.resize`. This
argument defines a source rectangle from within the source image to be
resized. This is very similar to the ``image.crop(box).resize(size)``
sequence except that ``box`` can be specified with subpixel accuracy.
Loading 16-bit TIFF Images
==========================
Pillow now can read 16-bit multichannel TIFF files including files
with alpha transparency. The image data is truncated to 8-bit
precision.
Performance
===========
This release contains several performance improvements:
* Many memory bandwidth-bounded operations such as crop, image allocation,
conversion, split into bands and merging from bands are up to 2x faster.
* Upscaling of multichannel images (such as RGB) is accelerated by 5-10%
* JPEG loading is accelerated up to 15% and JPEG saving up to 20% when
using a recent version of libjpeg-turbo.
Core Image API Changes
======================
These are internal functions that should not have been used by user
code, but they were accessible from the python layer.
Debugging code within ``Image.core.grabclipboard`` was removed. It had been
marked as ``will be removed in future versions`` since PIL. When enabled, it
identified the format of the clipboard data.
The ``PIL.Image.core.copy`` and ``PIL.Image.Image.im.copy2`` methods
have been removed.

View File

@ -44,6 +44,8 @@
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
#define L(rgb)\ #define L(rgb)\
((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114) ((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114)
#define L24(rgb)\
((rgb)[0]*19595 + (rgb)[1]*38470 + (rgb)[2]*7471)
#ifndef round #ifndef round
double round(double x) { double round(double x) {
@ -210,7 +212,7 @@ rgb2l(UINT8* out, const UINT8* in, int xsize)
int x; int x;
for (x = 0; x < xsize; x++, in += 4) for (x = 0; x < xsize; x++, in += 4)
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
*out++ = L(in) / 1000; *out++ = L24(in) >> 16;
} }
static void static void
@ -219,7 +221,7 @@ rgb2la(UINT8* out, const UINT8* in, int xsize)
int x; int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) { for (x = 0; x < xsize; x++, in += 4, out += 4) {
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
out[0] = out[1] = out[2] = L(in) / 1000; out[0] = out[1] = out[2] = L24(in) >> 16;
out[3] = 255; out[3] = 255;
} }
} }
@ -230,7 +232,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize)
int x; int x;
INT32* out = (INT32*) out_; INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++, in += 4) for (x = 0; x < xsize; x++, in += 4)
*out++ = L(in) / 1000; *out++ = L24(in) >> 16;
} }
static void static void
@ -419,7 +421,7 @@ rgba2la(UINT8* out, const UINT8* in, int xsize)
int x; int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) { for (x = 0; x < xsize; x++, in += 4, out += 4) {
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
out[0] = out[1] = out[2] = L(in) / 1000; out[0] = out[1] = out[2] = L24(in) >> 16;
out[3] = in[3]; out[3] = in[3];
} }
} }

View File

@ -26,6 +26,23 @@
#include "Imaging.h" #include "Imaging.h"
#ifdef WORDS_BIGENDIAN
#define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24))
#else
#define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24))
#endif
static inline UINT8 clip8(float in)
{
if (in <= 0.0)
return 0;
if (in >= 255.0)
return 255;
return (UINT8) in;
}
Imaging Imaging
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
{ {
@ -36,9 +53,8 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
if (xmargin < 0 && ymargin < 0) if (xmargin < 0 && ymargin < 0)
return (Imaging) ImagingError_ValueError("bad kernel size"); return (Imaging) ImagingError_ValueError("bad kernel size");
imOut = ImagingNew( imOut = ImagingNewDirty(
imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin);
);
if (!imOut) if (!imOut)
return NULL; return NULL;
@ -74,16 +90,259 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
return imOut; return imOut;
} }
/* This is work around bug in GCC prior 4.9 in 64 bit mode.
GCC generates code with partial dependency which 3 times slower.
See: http://stackoverflow.com/a/26588074/253146 */
#if defined(__x86_64__) && defined(__SSE__) && ! defined(__NO_INLINE__) && \
! defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900)
static float __attribute__((always_inline)) inline i2f(int v) {
float x;
__asm__("xorps %0, %0; cvtsi2ss %1, %0" : "=X"(x) : "r"(v) );
return x;
}
#else
static float inline i2f(int v) { return (float) v; }
#endif
void
ImagingFilter3x3(Imaging imOut, Imaging im, const float* kernel,
float offset)
{
#define KERNEL1x3(in0, x, kernel, d) ( \
i2f((UINT8) in0[x-d]) * (kernel)[0] + \
i2f((UINT8) in0[x]) * (kernel)[1] + \
i2f((UINT8) in0[x+d]) * (kernel)[2])
int x = 0, y = 0;
memcpy(imOut->image[0], im->image[0], im->linesize);
if (im->bands == 1) {
// Add one time for rounding
offset += 0.5;
for (y = 1; y < im->ysize-1; y++) {
UINT8* in_1 = (UINT8*) im->image[y-1];
UINT8* in0 = (UINT8*) im->image[y];
UINT8* in1 = (UINT8*) im->image[y+1];
UINT8* out = (UINT8*) imOut->image[y];
out[0] = in0[0];
for (x = 1; x < im->xsize-1; x++) {
float ss = offset;
ss += KERNEL1x3(in1, x, &kernel[0], 1);
ss += KERNEL1x3(in0, x, &kernel[3], 1);
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
out[x] = clip8(ss);
}
out[x] = in0[x];
}
} else {
// Add one time for rounding
offset += 0.5;
for (y = 1; y < im->ysize-1; y++) {
UINT8* in_1 = (UINT8*) im->image[y-1];
UINT8* in0 = (UINT8*) im->image[y];
UINT8* in1 = (UINT8*) im->image[y+1];
UINT32* out = (UINT32*) imOut->image[y];
out[0] = ((UINT32*) in0)[0];
if (im->bands == 2) {
for (x = 1; x < im->xsize-1; x++) {
float ss0 = offset;
float ss3 = offset;
ss0 += KERNEL1x3(in1, x*4+0, &kernel[0], 4);
ss3 += KERNEL1x3(in1, x*4+3, &kernel[0], 4);
ss0 += KERNEL1x3(in0, x*4+0, &kernel[3], 4);
ss3 += KERNEL1x3(in0, x*4+3, &kernel[3], 4);
ss0 += KERNEL1x3(in_1, x*4+0, &kernel[6], 4);
ss3 += KERNEL1x3(in_1, x*4+3, &kernel[6], 4);
out[x] = MAKE_UINT32(clip8(ss0), 0, 0, clip8(ss3));
}
} else if (im->bands == 3) {
for (x = 1; x < im->xsize-1; x++) {
float ss0 = offset;
float ss1 = offset;
float ss2 = offset;
ss0 += KERNEL1x3(in1, x*4+0, &kernel[0], 4);
ss1 += KERNEL1x3(in1, x*4+1, &kernel[0], 4);
ss2 += KERNEL1x3(in1, x*4+2, &kernel[0], 4);
ss0 += KERNEL1x3(in0, x*4+0, &kernel[3], 4);
ss1 += KERNEL1x3(in0, x*4+1, &kernel[3], 4);
ss2 += KERNEL1x3(in0, x*4+2, &kernel[3], 4);
ss0 += KERNEL1x3(in_1, x*4+0, &kernel[6], 4);
ss1 += KERNEL1x3(in_1, x*4+1, &kernel[6], 4);
ss2 += KERNEL1x3(in_1, x*4+2, &kernel[6], 4);
out[x] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), 0);
}
} else if (im->bands == 4) {
for (x = 1; x < im->xsize-1; x++) {
float ss0 = offset;
float ss1 = offset;
float ss2 = offset;
float ss3 = offset;
ss0 += KERNEL1x3(in1, x*4+0, &kernel[0], 4);
ss1 += KERNEL1x3(in1, x*4+1, &kernel[0], 4);
ss2 += KERNEL1x3(in1, x*4+2, &kernel[0], 4);
ss3 += KERNEL1x3(in1, x*4+3, &kernel[0], 4);
ss0 += KERNEL1x3(in0, x*4+0, &kernel[3], 4);
ss1 += KERNEL1x3(in0, x*4+1, &kernel[3], 4);
ss2 += KERNEL1x3(in0, x*4+2, &kernel[3], 4);
ss3 += KERNEL1x3(in0, x*4+3, &kernel[3], 4);
ss0 += KERNEL1x3(in_1, x*4+0, &kernel[6], 4);
ss1 += KERNEL1x3(in_1, x*4+1, &kernel[6], 4);
ss2 += KERNEL1x3(in_1, x*4+2, &kernel[6], 4);
ss3 += KERNEL1x3(in_1, x*4+3, &kernel[6], 4);
out[x] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3));
}
}
out[x] = ((UINT32*) in0)[x];
}
}
memcpy(imOut->image[y], im->image[y], im->linesize);
}
void
ImagingFilter5x5(Imaging imOut, Imaging im, const float* kernel,
float offset)
{
#define KERNEL1x5(in0, x, kernel, d) ( \
i2f((UINT8) in0[x-d-d]) * (kernel)[0] + \
i2f((UINT8) in0[x-d]) * (kernel)[1] + \
i2f((UINT8) in0[x]) * (kernel)[2] + \
i2f((UINT8) in0[x+d]) * (kernel)[3] + \
i2f((UINT8) in0[x+d+d]) * (kernel)[4])
int x = 0, y = 0;
memcpy(imOut->image[0], im->image[0], im->linesize);
memcpy(imOut->image[1], im->image[1], im->linesize);
if (im->bands == 1) {
// Add one time for rounding
offset += 0.5;
for (y = 2; y < im->ysize-2; y++) {
UINT8* in_2 = (UINT8*) im->image[y-2];
UINT8* in_1 = (UINT8*) im->image[y-1];
UINT8* in0 = (UINT8*) im->image[y];
UINT8* in1 = (UINT8*) im->image[y+1];
UINT8* in2 = (UINT8*) im->image[y+2];
UINT8* out = (UINT8*) imOut->image[y];
out[0] = in0[0];
out[1] = in0[1];
for (x = 2; x < im->xsize-2; x++) {
float ss = offset;
ss += KERNEL1x5(in2, x, &kernel[0], 1);
ss += KERNEL1x5(in1, x, &kernel[5], 1);
ss += KERNEL1x5(in0, x, &kernel[10], 1);
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
out[x] = clip8(ss);
}
out[x+0] = in0[x+0];
out[x+1] = in0[x+1];
}
} else {
// Add one time for rounding
offset += 0.5;
for (y = 2; y < im->ysize-2; y++) {
UINT8* in_2 = (UINT8*) im->image[y-2];
UINT8* in_1 = (UINT8*) im->image[y-1];
UINT8* in0 = (UINT8*) im->image[y];
UINT8* in1 = (UINT8*) im->image[y+1];
UINT8* in2 = (UINT8*) im->image[y+2];
UINT32* out = (UINT32*) imOut->image[y];
out[0] = ((UINT32*) in0)[0];
out[1] = ((UINT32*) in0)[1];
if (im->bands == 2) {
for (x = 2; x < im->xsize-2; x++) {
float ss0 = offset;
float ss3 = offset;
ss0 += KERNEL1x5(in2, x*4+0, &kernel[0], 4);
ss3 += KERNEL1x5(in2, x*4+3, &kernel[0], 4);
ss0 += KERNEL1x5(in1, x*4+0, &kernel[5], 4);
ss3 += KERNEL1x5(in1, x*4+3, &kernel[5], 4);
ss0 += KERNEL1x5(in0, x*4+0, &kernel[10], 4);
ss3 += KERNEL1x5(in0, x*4+3, &kernel[10], 4);
ss0 += KERNEL1x5(in_1, x*4+0, &kernel[15], 4);
ss3 += KERNEL1x5(in_1, x*4+3, &kernel[15], 4);
ss0 += KERNEL1x5(in_2, x*4+0, &kernel[20], 4);
ss3 += KERNEL1x5(in_2, x*4+3, &kernel[20], 4);
out[x] = MAKE_UINT32(clip8(ss0), 0, 0, clip8(ss3));
}
} else if (im->bands == 3) {
for (x = 2; x < im->xsize-2; x++) {
float ss0 = offset;
float ss1 = offset;
float ss2 = offset;
ss0 += KERNEL1x5(in2, x*4+0, &kernel[0], 4);
ss1 += KERNEL1x5(in2, x*4+1, &kernel[0], 4);
ss2 += KERNEL1x5(in2, x*4+2, &kernel[0], 4);
ss0 += KERNEL1x5(in1, x*4+0, &kernel[5], 4);
ss1 += KERNEL1x5(in1, x*4+1, &kernel[5], 4);
ss2 += KERNEL1x5(in1, x*4+2, &kernel[5], 4);
ss0 += KERNEL1x5(in0, x*4+0, &kernel[10], 4);
ss1 += KERNEL1x5(in0, x*4+1, &kernel[10], 4);
ss2 += KERNEL1x5(in0, x*4+2, &kernel[10], 4);
ss0 += KERNEL1x5(in_1, x*4+0, &kernel[15], 4);
ss1 += KERNEL1x5(in_1, x*4+1, &kernel[15], 4);
ss2 += KERNEL1x5(in_1, x*4+2, &kernel[15], 4);
ss0 += KERNEL1x5(in_2, x*4+0, &kernel[20], 4);
ss1 += KERNEL1x5(in_2, x*4+1, &kernel[20], 4);
ss2 += KERNEL1x5(in_2, x*4+2, &kernel[20], 4);
out[x] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), 0);
}
} else if (im->bands == 4) {
for (x = 2; x < im->xsize-2; x++) {
float ss0 = offset;
float ss1 = offset;
float ss2 = offset;
float ss3 = offset;
ss0 += KERNEL1x5(in2, x*4+0, &kernel[0], 4);
ss1 += KERNEL1x5(in2, x*4+1, &kernel[0], 4);
ss2 += KERNEL1x5(in2, x*4+2, &kernel[0], 4);
ss3 += KERNEL1x5(in2, x*4+3, &kernel[0], 4);
ss0 += KERNEL1x5(in1, x*4+0, &kernel[5], 4);
ss1 += KERNEL1x5(in1, x*4+1, &kernel[5], 4);
ss2 += KERNEL1x5(in1, x*4+2, &kernel[5], 4);
ss3 += KERNEL1x5(in1, x*4+3, &kernel[5], 4);
ss0 += KERNEL1x5(in0, x*4+0, &kernel[10], 4);
ss1 += KERNEL1x5(in0, x*4+1, &kernel[10], 4);
ss2 += KERNEL1x5(in0, x*4+2, &kernel[10], 4);
ss3 += KERNEL1x5(in0, x*4+3, &kernel[10], 4);
ss0 += KERNEL1x5(in_1, x*4+0, &kernel[15], 4);
ss1 += KERNEL1x5(in_1, x*4+1, &kernel[15], 4);
ss2 += KERNEL1x5(in_1, x*4+2, &kernel[15], 4);
ss3 += KERNEL1x5(in_1, x*4+3, &kernel[15], 4);
ss0 += KERNEL1x5(in_2, x*4+0, &kernel[20], 4);
ss1 += KERNEL1x5(in_2, x*4+1, &kernel[20], 4);
ss2 += KERNEL1x5(in_2, x*4+2, &kernel[20], 4);
ss3 += KERNEL1x5(in_2, x*4+3, &kernel[20], 4);
out[x] = MAKE_UINT32(
clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3));
}
}
out[x] = ((UINT32*) in0)[x];
out[x+1] = ((UINT32*) in0)[x+1];
}
}
memcpy(imOut->image[y], im->image[y], im->linesize);
memcpy(imOut->image[y+1], im->image[y+1], im->linesize);
}
Imaging Imaging
ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel, ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
FLOAT32 offset, FLOAT32 divisor) FLOAT32 offset)
{ {
Imaging imOut; Imaging imOut;
int x, y;
FLOAT32 sum;
ImagingSectionCookie cookie; ImagingSectionCookie cookie;
if (!im || strcmp(im->mode, "L") != 0) if ( ! im || im->type != IMAGING_TYPE_UINT8)
return (Imaging) ImagingError_ModeError(); return (Imaging) ImagingError_ModeError();
if (im->xsize < xsize || im->ysize < ysize) if (im->xsize < xsize || im->ysize < ysize)
@ -92,95 +351,17 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
if ((xsize != 3 && xsize != 5) || xsize != ysize) if ((xsize != 3 && xsize != 5) || xsize != ysize)
return (Imaging) ImagingError_ValueError("bad kernel size"); return (Imaging) ImagingError_ValueError("bad kernel size");
imOut = ImagingNew(im->mode, im->xsize, im->ysize); imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize);
if (!imOut) if (!imOut)
return NULL; return NULL;
// Add one time for rounding
offset += 0.5;
/* brute force kernel implementations */
#define KERNEL3x3(image, kernel, d) ( \
(int) image[y+1][x-d] * kernel[0] + \
(int) image[y+1][x] * kernel[1] + \
(int) image[y+1][x+d] * kernel[2] + \
(int) image[y][x-d] * kernel[3] + \
(int) image[y][x] * kernel[4] + \
(int) image[y][x+d] * kernel[5] + \
(int) image[y-1][x-d] * kernel[6] + \
(int) image[y-1][x] * kernel[7] + \
(int) image[y-1][x+d] * kernel[8])
#define KERNEL5x5(image, kernel, d) ( \
(int) image[y+2][x-d-d] * kernel[0] + \
(int) image[y+2][x-d] * kernel[1] + \
(int) image[y+2][x] * kernel[2] + \
(int) image[y+2][x+d] * kernel[3] + \
(int) image[y+2][x+d+d] * kernel[4] + \
(int) image[y+1][x-d-d] * kernel[5] + \
(int) image[y+1][x-d] * kernel[6] + \
(int) image[y+1][x] * kernel[7] + \
(int) image[y+1][x+d] * kernel[8] + \
(int) image[y+1][x+d+d] * kernel[9] + \
(int) image[y][x-d-d] * kernel[10] + \
(int) image[y][x-d] * kernel[11] + \
(int) image[y][x] * kernel[12] + \
(int) image[y][x+d] * kernel[13] + \
(int) image[y][x+d+d] * kernel[14] + \
(int) image[y-1][x-d-d] * kernel[15] + \
(int) image[y-1][x-d] * kernel[16] + \
(int) image[y-1][x] * kernel[17] + \
(int) image[y-1][x+d] * kernel[18] + \
(int) image[y-1][x+d+d] * kernel[19] + \
(int) image[y-2][x-d-d] * kernel[20] + \
(int) image[y-2][x-d] * kernel[21] + \
(int) image[y-2][x] * kernel[22] + \
(int) image[y-2][x+d] * kernel[23] + \
(int) image[y-2][x+d+d] * kernel[24])
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
if (xsize == 3) { if (xsize == 3) {
/* 3x3 kernel. */ /* 3x3 kernel. */
for (x = 0; x < im->xsize; x++) ImagingFilter3x3(imOut, im, kernel, offset);
imOut->image[0][x] = im->image8[0][x];
for (y = 1; y < im->ysize-1; y++) {
imOut->image[y][0] = im->image8[y][0];
for (x = 1; x < im->xsize-1; x++) {
sum = KERNEL3x3(im->image8, kernel, 1) / divisor + offset;
if (sum <= 0)
imOut->image8[y][x] = 0;
else if (sum >= 255)
imOut->image8[y][x] = 255;
else
imOut->image8[y][x] = (UINT8) sum;
}
imOut->image8[y][x] = im->image8[y][x];
}
for (x = 0; x < im->xsize; x++)
imOut->image8[y][x] = im->image8[y][x];
} else { } else {
/* 5x5 kernel. */ /* 5x5 kernel. */
for (y = 0; y < 2; y++) ImagingFilter5x5(imOut, im, kernel, offset);
for (x = 0; x < im->xsize; x++)
imOut->image8[y][x] = im->image8[y][x];
for (; y < im->ysize-2; y++) {
for (x = 0; x < 2; x++)
imOut->image8[y][x] = im->image8[y][x];
for (; x < im->xsize-2; x++) {
sum = KERNEL5x5(im->image8, kernel, 1) / divisor + offset;
if (sum <= 0)
imOut->image8[y][x] = 0;
else if (sum >= 255)
imOut->image8[y][x] = 255;
else
imOut->image8[y][x] = (UINT8) sum;
}
for (; x < im->xsize; x++)
imOut->image8[y][x] = im->image8[y][x];
}
for (; y < im->ysize; y++)
for (x = 0; x < im->xsize; x++)
imOut->image8[y][x] = im->image8[y][x];
} }
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
return imOut; return imOut;

View File

@ -26,24 +26,30 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
ImagingCopyInfo(imOut, imIn); ImagingCopyInfo(imOut, imIn);
#define FLIP_HORIZ(image)\
for (y = 0; y < imIn->ysize; y++) {\
xr = imIn->xsize-1;\
for (x = 0; x < imIn->xsize; x++, xr--)\
imOut->image[y][x] = imIn->image[y][xr];\
}
ImagingSectionEnter(&cookie); ImagingSectionEnter(&cookie);
if (imIn->image8) if (imIn->image8) {
FLIP_HORIZ(image8) for (y = 0; y < imIn->ysize; y++) {
else UINT8* in = (UINT8*) imIn->image8[y];
FLIP_HORIZ(image32) UINT8* out = (UINT8*) imOut->image8[y];
x = 0;
xr = imIn->xsize-1;
for (; x < imIn->xsize; x++, xr--)
out[xr] = in[x];
}
} else {
for (y = 0; y < imIn->ysize; y++) {
UINT32* in = (UINT32*) imIn->image32[y];
UINT32* out = (UINT32*) imOut->image32[y];
x = 0;
xr = imIn->xsize-1;
for (; x < imIn->xsize; x++, xr--)
out[xr] = in[x];
}
}
ImagingSectionLeave(&cookie); ImagingSectionLeave(&cookie);
#undef FLIP_HORIZ
return imOut; return imOut;
} }

View File

@ -260,7 +260,7 @@ extern Imaging ImagingFillLinearGradient(const char* mode);
extern Imaging ImagingFillRadialGradient(const char* mode); extern Imaging ImagingFillRadialGradient(const char* mode);
extern Imaging ImagingFilter( extern Imaging ImagingFilter(
Imaging im, int xsize, int ysize, const FLOAT32* kernel, Imaging im, int xsize, int ysize, const FLOAT32* kernel,
FLOAT32 offset, FLOAT32 divisor); FLOAT32 offset);
extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn); extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn);
extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn); extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn);
extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius, extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius,