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:
fast_finish: true
include:
- python: "pypy-5.7.1"
- python: "pypy3.3-5.2-alpha1"
- python: "pypy"
- python: "pypy3"
- python: '3.6'
- python: '2.7'
- python: "2.7_with_system_site_packages" # For PyQt4

View File

@ -3,6 +3,46 @@ Changelog (Pillow)
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
[homm]

View File

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

View File

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

View File

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

View File

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

View File

@ -267,17 +267,11 @@ def _save(im, fp, filename):
raise IOError("Error creating Spider header")
# write the SPIDER header
try:
fp = open(filename, 'wb')
except:
raise IOError("Unable to open %s for writing" % filename)
fp.writelines(hdr)
rawmode = "F;32NF" # 32-bit native floating point
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
fp.close()
def _save_spider(im, fp, filename):
# 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:
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as TGA" % im.mode)
if check:
return check
if colormaptype:
colormapfirst, colormaplength, colormapentry = 0, 256, 24
else:
@ -182,6 +179,9 @@ def _save(im, fp, filename, check=0):
ImageFile._save(
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

View File

@ -153,7 +153,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
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")
_handler.save(im, fp, filename)

View File

@ -170,6 +170,41 @@ class PillowTestCase(unittest.TestCase):
return Image.open(outfile)
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

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
self.assert_warning(Image.DecompressionBombWarning,
lambda: Image.open(TEST_FILE))
Image.open, TEST_FILE)
class TestDecompressionCrop(PillowTestCase):
@ -52,7 +52,7 @@ class TestDecompressionCrop(PillowTestCase):
# same decompression bomb warnings on them.
box = (0, 0, self.src.width * 2, self.src.height * 2)
self.assert_warning(Image.DecompressionBombWarning,
lambda: self.src.crop(box))
self.src.crop, box)
if __name__ == '__main__':
unittest.main()

View File

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

View File

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

View File

@ -24,8 +24,7 @@ class TestFileBufrStub(PillowTestCase):
# Act / Assert
self.assertRaises(SyntaxError,
lambda:
BufrStubImagePlugin.BufrStubImageFile(invalid_file))
BufrStubImagePlugin.BufrStubImageFile, invalid_file)
def test_load(self):
# Arrange
@ -40,7 +39,7 @@ class TestFileBufrStub(PillowTestCase):
tmpfile = self.tempfile("temp.bufr")
# 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__':

View File

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

View File

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

View File

@ -55,7 +55,7 @@ class TestFileEps(PillowTestCase):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: EpsImagePlugin.EpsImageFile(invalid_file))
EpsImagePlugin.EpsImageFile, invalid_file)
def test_cmyk(self):
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')
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):
# issue 479
image1 = Image.open(file1)
@ -97,7 +107,7 @@ class TestFileEps(PillowTestCase):
def test_image_mode_not_supported(self):
im = hopper("RGBA")
tmpfile = self.tempfile('temp.eps')
self.assertRaises(ValueError, lambda: im.save(tmpfile))
self.assertRaises(ValueError, im.save, tmpfile)
def test_render_scale1(self):
# We need png support for these render test

View File

@ -24,8 +24,7 @@ class TestFileFitsStub(PillowTestCase):
# Act / Assert
self.assertRaises(SyntaxError,
lambda:
FitsStubImagePlugin.FITSStubImageFile(invalid_file))
FitsStubImagePlugin.FITSStubImageFile, invalid_file)
def test_load(self):
# Arrange
@ -41,10 +40,10 @@ class TestFileFitsStub(PillowTestCase):
dummy_filename = "dummy.filename"
# 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(
IOError,
lambda: FitsStubImagePlugin._save(im, dummy_fp, dummy_filename))
FitsStubImagePlugin._save, im, dummy_fp, dummy_filename)
if __name__ == '__main__':

View File

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

View File

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

View File

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

View File

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

View File

@ -10,13 +10,13 @@ class TestImage(PillowTestCase):
GimpPaletteFile(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:
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp))
self.assertRaises(SyntaxError, GimpPaletteFile, 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):
# Arrange

View File

@ -24,8 +24,7 @@ class TestFileGribStub(PillowTestCase):
# Act / Assert
self.assertRaises(SyntaxError,
lambda:
GribStubImagePlugin.GribStubImageFile(invalid_file))
GribStubImagePlugin.GribStubImageFile, invalid_file)
def test_load(self):
# Arrange
@ -40,7 +39,7 @@ class TestFileGribStub(PillowTestCase):
tmpfile = self.tempfile("temp.grib")
# 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__':

View File

@ -24,8 +24,7 @@ class TestFileHdf5Stub(PillowTestCase):
# Act / Assert
self.assertRaises(SyntaxError,
lambda:
Hdf5StubImagePlugin.HDF5StubImageFile(invalid_file))
Hdf5StubImagePlugin.HDF5StubImageFile, invalid_file)
def test_load(self):
# Arrange
@ -41,10 +40,10 @@ class TestFileHdf5Stub(PillowTestCase):
dummy_filename = "dummy.filename"
# 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(
IOError,
lambda: Hdf5StubImagePlugin._save(im, dummy_fp, dummy_filename))
Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename)
if __name__ == '__main__':

View File

@ -1,7 +1,8 @@
from helper import unittest, PillowTestCase
from PIL import Image
from PIL import Image, IcnsImagePlugin
import io
import sys
# sample icon file
@ -82,6 +83,23 @@ class TestFileIcns(PillowTestCase):
self.assertEqual(im2.mode, 'RGBA')
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__':
unittest.main()

View File

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

View File

@ -43,18 +43,27 @@ class TestFileIm(PillowTestCase):
self.assertLess(im.tell(), n_frames)
def test_roundtrip(self):
out = self.tempfile('temp.im')
im = hopper()
im.save(out)
reread = Image.open(out)
for mode in ["RGB", "P"]:
out = self.tempfile('temp.im')
im = hopper(mode)
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):
invalid_file = "Tests/images/flower.jpg"
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__':

View File

@ -314,7 +314,7 @@ class TestFileJpeg(PillowTestCase):
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
self.assertRaises(
TypeError, lambda: self.roundtrip(hopper(), subsampling="1:1:1"))
TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
def test_exif(self):
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")
# not a sequence
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))
self.assertRaises(Exception, self.roundtrip, im, qtables='a')
# sequence wrong length
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))
self.assertRaises(Exception, self.roundtrip, im, qtables=[])
# sequence wrong length
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
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))
self.assertRaises(Exception, self.roundtrip, im, qtables=[1])
# qtable entry has wrong number of items
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")
def test_load_djpeg(self):
@ -479,7 +479,7 @@ class TestFileJpeg(PillowTestCase):
# Act
# Shouldn't raise error
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
self.assertEqual(im.format, "JPEG")
@ -584,7 +584,7 @@ class TestFileCloseW32(PillowTestCase):
im = Image.open(tmpfile)
fp = im.fp
self.assertFalse(fp.closed)
self.assertRaises(Exception, lambda: os.remove(tmpfile))
self.assertRaises(Exception, os.remove, tmpfile)
im.load()
self.assertTrue(fp.closed)
# 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"
self.assertRaises(SyntaxError,
lambda:
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file))
Jpeg2KImagePlugin.Jpeg2KImageFile, invalid_file)
def test_bytesio(self):
with open('Tests/images/test-card-lossless.jp2', 'rb') as f:

View File

@ -362,10 +362,9 @@ class TestFileLibTiff(LibTiffTestCase):
im = hopper('RGB')
out = self.tempfile('temp.tif')
self.assertRaises(
IOError, lambda: im.save(out, compression='tiff_ccitt'))
self.assertRaises(IOError, lambda: im.save(out, compression='group3'))
self.assertRaises(IOError, lambda: im.save(out, compression='group4'))
self.assertRaises(IOError, im.save, out, compression='tiff_ccitt')
self.assertRaises(IOError, im.save, out, compression='group3')
self.assertRaises(IOError, im.save, out, compression='group4')
def test_fp_leak(self):
im = Image.open("Tests/images/hopper_g4_500.tif")
@ -373,10 +372,10 @@ class TestFileLibTiff(LibTiffTestCase):
os.fstat(fn)
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.
self.assertRaises(OSError, lambda: os.fstat(fn))
self.assertRaises(OSError, lambda: os.close(fn))
self.assertRaises(OSError, os.fstat, fn)
self.assertRaises(OSError, os.close, fn)
def test_multipage(self):
# issue #862

View File

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

View File

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

View File

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

View File

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

View File

@ -21,13 +21,14 @@ class TestFilePcx(PillowTestCase):
# Test an unsupported mode
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):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: PcxImagePlugin.PcxImageFile(invalid_file))
PcxImagePlugin.PcxImageFile, invalid_file)
def test_odd(self):
# 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")
outfile = self.tempfile("temp_LA.pdf")
self.assertRaises(ValueError, lambda: im.save(outfile))
self.assertRaises(ValueError, im.save, outfile)
def test_save_all(self):
# Single frame image

View File

@ -22,7 +22,7 @@ class TestFilePixar(PillowTestCase):
self.assertRaises(
SyntaxError,
lambda: PixarImagePlugin.PixarImageFile(invalid_file))
PixarImagePlugin.PixarImageFile, invalid_file)
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 io import BytesIO
@ -7,9 +7,6 @@ import sys
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
@ -87,14 +84,14 @@ class TestFilePng(PillowTestCase):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError,
lambda: PngImagePlugin.PngImageFile(invalid_file))
PngImagePlugin.PngImageFile, invalid_file)
def test_broken(self):
# Check reading of totally broken files. In this case, the test
# file was checked into Subversion as a text file.
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):
# Make sure PIL can read malformed tEXt chunks (@PIL152)
@ -424,7 +421,7 @@ class TestFilePng(PillowTestCase):
data = b'\x89' + fd.read()
pngfile = BytesIO(data)
self.assertRaises(IOError, lambda: Image.open(pngfile))
self.assertRaises(IOError, Image.open, pngfile)
def test_trns_rgb(self):
# 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))
chunks = []
fp = open(test_file, "rb")
fp.read(8)
png = PngImagePlugin.PngStream(fp)
while True:
cid, pos, length = png.read()
chunks.append(cid)
try:
s = png.call(cid, pos, length)
except EOFError:
break
png.crc(cid, s)
with open(test_file, "rb") as fp:
fp.read(8)
png = PngImagePlugin.PngStream(fp)
while True:
cid, pos, length = png.read()
chunks.append(cid)
try:
s = png.call(cid, pos, length)
except EOFError:
break
png.crc(cid, s)
# https://www.w3.org/TR/PNG/#5ChunkOrdering
# IHDR - shall be first
@ -531,28 +528,22 @@ class TestFilePng(PillowTestCase):
# pHYs - before 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")
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):
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
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):
with open('Tests/images/hopper.png', 'rb') as f:
DATA = BytesIO(f.read(16 * 1024))
@ -560,13 +551,13 @@ class TestTruncatedPngPLeaks(PillowTestCase):
ImageFile.LOAD_TRUNCATED_IMAGES = True
with Image.open(DATA) as im:
im.load()
start_mem = self._get_mem_usage()
def core():
with Image.open(DATA) as im:
im.load()
try:
for _ in range(ITERATIONS):
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')
self._test_leak(core)
finally:
ImageFile.LOAD_TRUNCATED_IMAGES = False

View File

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

View File

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

View File

@ -42,8 +42,7 @@ class TestFileSgi(PillowTestCase):
invalid_file = "Tests/images/flower.jpg"
self.assertRaises(ValueError,
lambda:
SgiImagePlugin.SgiImageFile(invalid_file))
SgiImagePlugin.SgiImageFile, invalid_file)
def test_write(self):
def roundtrip(img):
@ -62,14 +61,14 @@ class TestFileSgi(PillowTestCase):
im = hopper('LA')
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):
im = hopper('YCbCr')
im.mode = 'RGB'
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__':

View File

@ -4,6 +4,8 @@ from PIL import Image
from PIL import ImageSequence
from PIL import SpiderImagePlugin
import tempfile
TEST_FILE = "Tests/images/hopper.spider"
@ -30,6 +32,21 @@ class TestImageSpider(PillowTestCase):
self.assertEqual(im2.size, (128, 128))
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):
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
@ -84,12 +101,12 @@ class TestImageSpider(PillowTestCase):
def test_invalid_file(self):
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):
im = Image.open(TEST_FILE)
self.assertRaises(EOFError, lambda: im.seek(0))
self.assertRaises(EOFError, im.seek, 0)
def test_nonstack_dos(self):
im = Image.open(TEST_FILE)

View File

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

View File

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

View File

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

View File

@ -75,7 +75,8 @@ class TestFileWebp(PillowTestCase):
def test_write_unsupported_mode(self):
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):
self.assertRaises(TypeError, _webp.WebPEncode)

View File

@ -1,5 +1,7 @@
from helper import unittest, PillowTestCase, hopper
from PIL import Image
from PIL import WmfImagePlugin
class TestFileWmf(PillowTestCase):
@ -26,12 +28,29 @@ class TestFileWmf(PillowTestCase):
imref.load()
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):
im = hopper()
for ext in [".wmf", ".emf"]:
tmpfile = self.tempfile("temp"+ext)
self.assertRaises(IOError, lambda: im.save(tmpfile))
self.assertRaises(IOError, im.save, tmpfile)
if __name__ == '__main__':

View File

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

View File

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

View File

@ -9,15 +9,15 @@ class TestFontBdf(PillowTestCase):
def test_sanity(self):
test_file = open(filename, "rb")
font = BdfFontFile.BdfFontFile(test_file)
with open(filename, "rb") as test_file:
font = BdfFontFile.BdfFontFile(test_file)
self.assertIsInstance(font, FontFile.FontFile)
self.assertEqual(len([_f for _f in font.glyph if _f]), 190)
def test_invalid_file(self):
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__':

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")
def save_font(self):
test_file = open(fontname, "rb")
font = PcfFontFile.PcfFontFile(test_file)
with open(fontname, "rb") as test_file:
font = PcfFontFile.PcfFontFile(test_file)
self.assertIsInstance(font, FontFile.FontFile)
self.assertEqual(len([_f for _f in font.glyph if _f]), 192)
@ -32,7 +32,7 @@ class TestFontPcf(PillowTestCase):
def test_invalid_file(self):
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):

View File

@ -47,10 +47,9 @@ class TestImage(PillowTestCase):
self.assertEqual(im2.getcolors(), [(10000, 0)])
self.assertEqual(im3.getcolors(), [(10000, 0)])
self.assertRaises(ValueError, lambda: Image.new("X", (100, 100)))
self.assertRaises(ValueError, lambda: Image.new("", (100, 100)))
# self.assertRaises(
# MemoryError, lambda: Image.new("L", (1000000, 1000000)))
self.assertRaises(ValueError, Image.new, "X", (100, 100))
self.assertRaises(ValueError, Image.new, "", (100, 100))
# self.assertRaises(MemoryError, Image.new, "L", (1000000, 1000000))
def test_width_height(self):
im = Image.new("RGB", (1, 2))
@ -68,7 +67,10 @@ class TestImage(PillowTestCase):
else:
import io
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),
"pathlib only available in Python 3.4 or later")
@ -109,7 +111,7 @@ class TestImage(PillowTestCase):
def test_unknown_extension(self):
im = hopper()
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):
@ -273,6 +275,20 @@ class TestImage(PillowTestCase):
target.crop((32, 32, 96, 96)))
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):
# Arrange
Image._initialized = 0
@ -327,7 +343,7 @@ class TestImage(PillowTestCase):
# Act/Assert
self.assertRaises(
ValueError,
lambda: Image.effect_mandelbrot(size, extent, quality))
Image.effect_mandelbrot, size, extent, quality)
def test_effect_noise(self):
# Arrange
@ -388,7 +404,7 @@ class TestImage(PillowTestCase):
im = hopper()
# Act / Assert
self.assertRaises(NotImplementedError, lambda: im.offset(None))
self.assertRaises(NotImplementedError, im.offset, None)
def test_fromstring(self):
self.assertRaises(NotImplementedError, Image.fromstring)
@ -399,8 +415,7 @@ class TestImage(PillowTestCase):
# Act / Assert
self.assertRaises(ValueError,
lambda: Image.linear_gradient(wrong_mode))
return
Image.linear_gradient, wrong_mode)
def test_linear_gradient(self):
@ -425,8 +440,7 @@ class TestImage(PillowTestCase):
# Act / Assert
self.assertRaises(ValueError,
lambda: Image.radial_gradient(wrong_mode))
return
Image.radial_gradient, wrong_mode)
def test_radial_gradient(self):
@ -445,6 +459,11 @@ class TestImage(PillowTestCase):
target = Image.open(target_file).convert(mode)
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):
from PIL import ImagePalette
@ -496,10 +515,10 @@ class TestRegistry(PillowTestCase):
self.assertEqual(enc.args, ('RGB', 'args', 'extra'))
def test_encode_registry_fail(self):
self.assertRaises(IOError, lambda: Image._getencoder('RGB',
'DoesNotExist',
('args',),
extra=('extra',)))
self.assertRaises(IOError, Image._getencoder, 'RGB',
'DoesNotExist',
('args',),
extra=('extra',))
if __name__ == '__main__':
unittest.main()

View File

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

View File

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

View File

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

View File

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

View File

@ -6,17 +6,17 @@ class TestImagePoint(PillowTestCase):
def test_sanity(self):
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(lambda x: x)
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+1)
self.assertRaises(TypeError, lambda: 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)
self.assertRaises(TypeError, im.point, lambda x: x/1)
def test_16bit_lut(self):
""" 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'))
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__':

View File

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

View File

@ -39,7 +39,7 @@ class TestImageQuantize(PillowTestCase):
def test_rgba_quantize(self):
image = hopper('RGBA')
image.quantize()
self.assertRaises(Exception, lambda: image.quantize(method=0))
self.assertRaises(Exception, image.quantize, method=0)
def test_quantize(self):
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)
def test_passthrough(self):
"When no resize is required"
# When no resize is required
im = hopper()
for size, box in [
@ -478,7 +478,7 @@ class CoreResampleBoxTest(PillowTestCase):
raise
def test_no_passthrough(self):
"When resize is required"
# When resize is required
im = hopper()
for size, box in [
@ -498,7 +498,7 @@ class CoreResampleBoxTest(PillowTestCase):
raise
def test_skip_horizontal(self):
"Can skip resize in one dimension"
# Can skip resize for one dimension
im = hopper()
for flt in [Image.NEAREST, Image.BICUBIC]:
@ -519,7 +519,7 @@ class CoreResampleBoxTest(PillowTestCase):
raise
def test_skip_vertical(self):
"Can skip resize in one dimension"
# Can skip resize for one dimension
im = hopper()
for flt in [Image.NEAREST, Image.BICUBIC]:

View File

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

View File

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

View File

@ -144,20 +144,26 @@ class TestImageCms(PillowTestCase):
'IEC 61966-2.1 Default RGB colour space - sRGB')
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
self.assertRaises(
ImageCms.PyCMSError,
lambda: ImageCms.profileToProfile(hopper(), "foo", "bar"))
ImageCms.profileToProfile, hopper(), "foo", "bar")
self.assertRaises(
ImageCms.PyCMSError,
lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
ImageCms.buildTransform, "foo", "bar", "RGB", "RGB")
self.assertRaises(
ImageCms.PyCMSError,
lambda: ImageCms.getProfileName(None))
ImageCms.getProfileName, None)
self.skip_missing()
self.assertRaises(
ImageCms.PyCMSError,
lambda: ImageCms.isIntentSupported(SRGB, None, None))
ImageCms.isIntentSupported, SRGB, None, None)
def test_display_profile(self):
# try fetching the profile for the current display device
@ -167,6 +173,14 @@ class TestImageCms(PillowTestCase):
ImageCms.createProfile("LAB", 5000)
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):
i = Image.new('RGB', (10, 10), (128, 128, 128))

View File

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

View File

@ -69,7 +69,7 @@ class TestImageFile(PillowTestCase):
im1, im2 = roundtrip("JPEG") # lossy compression
self.assert_image(im1, im2.mode, im2.size)
self.assertRaises(IOError, lambda: roundtrip("PDF"))
self.assertRaises(IOError, roundtrip, "PDF")
def test_ico(self):
with open('Tests/images/python.ico', 'rb') as f:
@ -93,7 +93,7 @@ class TestImageFile(PillowTestCase):
self.assert_image_equal(im1, im2)
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):
with self.assertRaises(TypeError):
@ -182,7 +182,7 @@ class TestPyDecoder(PillowTestCase):
self.assertEqual(d.state.xsize, xsize)
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):
buf = BytesIO(b'\x00'*255)

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ class TestImageSequence(PillowTestCase):
self.assertEqual(index, 1)
self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0))
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
def test_iterator(self):
im = Image.open('Tests/images/multipage.tiff')
@ -30,7 +30,7 @@ class TestImageSequence(PillowTestCase):
for index in range(0, im.n_frames):
self.assertEqual(i[index], next(i))
self.assertRaises(IndexError, lambda: i[index+1])
self.assertRaises(StopIteration, lambda: next(i))
self.assertRaises(StopIteration, next, i)
def _test_multipage_tiff(self):
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 ImageShow
@ -10,12 +10,30 @@ class TestImageShow(PillowTestCase):
dir(Image)
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):
viewer = ImageShow.Viewer()
self.assertIsNone(viewer.get_format(None))
self.assertRaises(NotImplementedError, lambda: viewer.get_command(None))
self.assertRaises(NotImplementedError, viewer.get_command, None)
if __name__ == '__main__':

View File

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

View File

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

View File

@ -86,12 +86,12 @@ class TestNumpy(PillowTestCase):
self.assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE)
# Check 64-bit integer formats
self.assertRaises(TypeError, lambda: to_image(numpy.uint64))
self.assertRaises(TypeError, lambda: to_image(numpy.int64))
self.assertRaises(TypeError, to_image, numpy.uint64)
self.assertRaises(TypeError, to_image, numpy.int64)
# Check floating-point formats
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.float64), "F", TEST_IMAGE_SIZE)

View File

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

View File

@ -225,6 +225,11 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
if (PyUnicode_Check(string)) {
Py_UNICODE *text = PyUnicode_AS_UNICODE(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)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
goto failed;
@ -234,6 +239,9 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
else if (PyString_Check(string)) {
char *text = PyString_AS_STRING(string);
int size = PyString_GET_SIZE(string);
if (! size) {
goto failed;
}
if (!raqm_set_text_utf8(rq, text, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
goto failed;
@ -450,6 +458,10 @@ font_getsize(FontObject* self, PyObject* args)
y_max = y_min = 0;
count = text_layout(string, self, dir, features, &glyph_info, 0);
if (PyErr_Occurred()) {
return NULL;
}
for (x = i = 0; i < count; i++) {
int index, error;
@ -497,6 +509,11 @@ font_getsize(FontObject* self, PyObject* args)
FT_Done_Glyph(glyph);
}
if (glyph_info) {
PyMem_Free(glyph_info);
glyph_info = NULL;
}
if (face) {
/* left bearing */
@ -584,9 +601,12 @@ font_render(FontObject* self, PyObject* args)
glyph_info = NULL;
count = text_layout(string, self, dir, features, &glyph_info, mask);
if (count == 0) {
if (PyErr_Occurred()) {
return NULL;
}
if (count == 0) {
Py_RETURN_NONE;
}
im = (Imaging) id;
/* 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;
PyObject* result;
int verbose = 0; /* debugging; will be removed in future versions */
if (!PyArg_ParseTuple(args, "|i", &verbose))
return NULL;
clip = OpenClipboard(NULL);
/* 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);
if (!handle) {
/* FIXME: add CF_HDROP support to allow cut-and-paste from
@ -542,14 +467,6 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
size = GlobalSize(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);
GlobalUnlock(handle);

View File

@ -178,8 +178,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
**sizes**
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),
(64, 64), (128, 128), (255, 255)]``. Any sizes bigger than the original
size or 255 will be ignored.
(64, 64), (128, 128), (256, 256)]``. Any sizes bigger than the original
size or 256 will be ignored.
IM
^^

View File

@ -262,6 +262,7 @@ The easiest way to install external libraries is via `Homebrew
$ brew install libtiff libjpeg webp little-cms2
To install libraqm on macOS use Homebrew to install its dependencies::
$ brew install freetype harfbuzz fribidi
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
for more information).
:type: :py:class:`float` or `None`
:type: :py:class:`float` or ``None``
.. py:attribute:: viewing_condition

View File

@ -6,4 +6,49 @@ Get One Channel From Image
New method :py:meth:`PIL.Image.Image.getchannel` added.
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) */
#define L(rgb)\
((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
double round(double x) {
@ -210,7 +212,7 @@ rgb2l(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++, in += 4)
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
*out++ = L(in) / 1000;
*out++ = L24(in) >> 16;
}
static void
@ -219,7 +221,7 @@ rgb2la(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
/* 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;
}
}
@ -230,7 +232,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize)
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++, in += 4)
*out++ = L(in) / 1000;
*out++ = L24(in) >> 16;
}
static void
@ -419,7 +421,7 @@ rgba2la(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
/* 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];
}
}

View File

@ -26,6 +26,23 @@
#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
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)
return (Imaging) ImagingError_ValueError("bad kernel size");
imOut = ImagingNew(
imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin
);
imOut = ImagingNewDirty(
imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin);
if (!imOut)
return NULL;
@ -74,16 +90,259 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
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
ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
FLOAT32 offset, FLOAT32 divisor)
FLOAT32 offset)
{
Imaging imOut;
int x, y;
FLOAT32 sum;
ImagingSectionCookie cookie;
if (!im || strcmp(im->mode, "L") != 0)
if ( ! im || im->type != IMAGING_TYPE_UINT8)
return (Imaging) ImagingError_ModeError();
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)
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)
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);
if (xsize == 3) {
/* 3x3 kernel. */
for (x = 0; x < im->xsize; x++)
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];
ImagingFilter3x3(imOut, im, kernel, offset);
} else {
/* 5x5 kernel. */
for (y = 0; y < 2; y++)
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];
ImagingFilter5x5(imOut, im, kernel, offset);
}
ImagingSectionLeave(&cookie);
return imOut;

View File

@ -26,24 +26,30 @@ ImagingFlipLeftRight(Imaging imOut, Imaging 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);
if (imIn->image8)
FLIP_HORIZ(image8)
else
FLIP_HORIZ(image32)
if (imIn->image8) {
for (y = 0; y < imIn->ysize; y++) {
UINT8* in = (UINT8*) imIn->image8[y];
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);
#undef FLIP_HORIZ
return imOut;
}

View File

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