mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-14 03:21:44 +03:00
Merge branch 'master' into alpha-composite-box
This commit is contained in:
commit
5bc72dbc10
|
@ -10,8 +10,8 @@ notifications:
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
include:
|
include:
|
||||||
- python: "pypy-5.7.1"
|
- python: "pypy"
|
||||||
- python: "pypy3.3-5.2-alpha1"
|
- python: "pypy3"
|
||||||
- python: '3.6'
|
- python: '3.6'
|
||||||
- python: '2.7'
|
- python: '2.7'
|
||||||
- python: "2.7_with_system_site_packages" # For PyQt4
|
- python: "2.7_with_system_site_packages" # For PyQt4
|
||||||
|
|
40
CHANGES.rst
40
CHANGES.rst
|
@ -3,6 +3,46 @@ Changelog (Pillow)
|
||||||
|
|
||||||
4.3.0 (unreleased)
|
4.3.0 (unreleased)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
- Faster filter operations for Kernel, Gaussian, and Unsharp Mask filters #2679
|
||||||
|
[homm]
|
||||||
|
|
||||||
|
- EPS: Add showpage to force rendering of some EPS images #2636
|
||||||
|
[kaplun]
|
||||||
|
|
||||||
|
- DOC: Fix type of palette parameter in Image.quantize. #2703
|
||||||
|
[kkopachev]
|
||||||
|
|
||||||
|
- DOC: Fix Ico docs to match code #2712
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Added file pointer save to SpiderImagePlugin #2647
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Add targa version 2 footer #2713
|
||||||
|
[jhultgre]
|
||||||
|
|
||||||
|
- Removed redundant lines #2714
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Travis CI: Use default pypy/pypy3 #2721
|
||||||
|
[hugovk]
|
||||||
|
|
||||||
|
- Fix for SystemError when rendering an empty string, added in 4.2.0 #2706
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Fix for memory leaks in font handling added in 4.2.0 #2634
|
||||||
|
[wiredfool]
|
||||||
|
|
||||||
|
- Tests: cleanup, more tests. Fixed WMF save handler #2689
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Removed debugging interface for Image.core.grabclipboard #2708
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
|
- Doc syntax fix #2710
|
||||||
|
[radarhere]
|
||||||
|
|
||||||
- Faster packing and unpacking for RGB, LA, and related storage modes #2693
|
- Faster packing and unpacking for RGB, LA, and related storage modes #2693
|
||||||
[homm]
|
[homm]
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,7 @@ def Ghostscript(tile, size, fp, scale=1):
|
||||||
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
|
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
|
||||||
# adjust for image origin
|
# adjust for image origin
|
||||||
"-f", infile, # input file
|
"-f", infile, # input file
|
||||||
|
"-c", "showpage", # showpage (see: https://bugs.ghostscript.com/show_bug.cgi?id=698272)
|
||||||
]
|
]
|
||||||
|
|
||||||
if gs_windows_binary is not None:
|
if gs_windows_binary is not None:
|
||||||
|
|
13
PIL/Image.py
13
PIL/Image.py
|
@ -992,7 +992,7 @@ class Image(object):
|
||||||
2 = fast octree
|
2 = fast octree
|
||||||
3 = libimagequant
|
3 = libimagequant
|
||||||
:param kmeans: Integer
|
:param kmeans: Integer
|
||||||
:param palette: Quantize to the :py:class:`PIL.ImagingPalette` palette.
|
:param palette: Quantize to the palette of given :py:class:`PIL.Image.Image`.
|
||||||
:returns: A new image
|
:returns: A new image
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -1114,6 +1114,8 @@ class Image(object):
|
||||||
:param filter: Filter kernel.
|
:param filter: Filter kernel.
|
||||||
:returns: An :py:class:`~PIL.Image.Image` object. """
|
:returns: An :py:class:`~PIL.Image.Image` object. """
|
||||||
|
|
||||||
|
from . import ImageFilter
|
||||||
|
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
if isinstance(filter, collections.Callable):
|
if isinstance(filter, collections.Callable):
|
||||||
|
@ -1122,9 +1124,10 @@ class Image(object):
|
||||||
raise TypeError("filter argument should be ImageFilter.Filter " +
|
raise TypeError("filter argument should be ImageFilter.Filter " +
|
||||||
"instance or class")
|
"instance or class")
|
||||||
|
|
||||||
if self.im.bands == 1:
|
multiband = isinstance(filter, ImageFilter.MultibandFilter)
|
||||||
|
if self.im.bands == 1 or multiband:
|
||||||
return self._new(filter.filter(self.im))
|
return self._new(filter.filter(self.im))
|
||||||
# fix to handle multiband images since _imaging doesn't
|
|
||||||
ims = []
|
ims = []
|
||||||
for c in range(self.im.bands):
|
for c in range(self.im.bands):
|
||||||
ims.append(self._new(filter.filter(self.im.getband(c))))
|
ims.append(self._new(filter.filter(self.im.getband(c))))
|
||||||
|
@ -1494,14 +1497,13 @@ class Image(object):
|
||||||
mode = getmodebase(self.mode) + "A"
|
mode = getmodebase(self.mode) + "A"
|
||||||
try:
|
try:
|
||||||
self.im.setmode(mode)
|
self.im.setmode(mode)
|
||||||
self.pyaccess = None
|
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
# do things the hard way
|
# do things the hard way
|
||||||
im = self.im.convert(mode)
|
im = self.im.convert(mode)
|
||||||
if im.mode not in ("LA", "RGBA"):
|
if im.mode not in ("LA", "RGBA"):
|
||||||
raise ValueError # sanity check
|
raise ValueError # sanity check
|
||||||
self.im = im
|
self.im = im
|
||||||
self.pyaccess = None
|
self.pyaccess = None
|
||||||
self.mode = self.im.mode
|
self.mode = self.im.mode
|
||||||
except (KeyError, ValueError):
|
except (KeyError, ValueError):
|
||||||
raise ValueError("illegal image mode")
|
raise ValueError("illegal image mode")
|
||||||
|
@ -1602,7 +1604,6 @@ class Image(object):
|
||||||
self.load()
|
self.load()
|
||||||
if self.readonly:
|
if self.readonly:
|
||||||
self._copy()
|
self._copy()
|
||||||
self.pyaccess = None
|
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
if self.pyaccess:
|
if self.pyaccess:
|
||||||
|
|
|
@ -22,7 +22,11 @@ class Filter(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Kernel(Filter):
|
class MultibandFilter(Filter):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Kernel(MultibandFilter):
|
||||||
"""
|
"""
|
||||||
Create a convolution kernel. The current version only
|
Create a convolution kernel. The current version only
|
||||||
supports 3x3 and 5x5 integer and floating point kernels.
|
supports 3x3 and 5x5 integer and floating point kernels.
|
||||||
|
@ -142,7 +146,7 @@ class ModeFilter(Filter):
|
||||||
return image.modefilter(self.size)
|
return image.modefilter(self.size)
|
||||||
|
|
||||||
|
|
||||||
class GaussianBlur(Filter):
|
class GaussianBlur(MultibandFilter):
|
||||||
"""Gaussian blur filter.
|
"""Gaussian blur filter.
|
||||||
|
|
||||||
:param radius: Blur radius.
|
:param radius: Blur radius.
|
||||||
|
@ -156,7 +160,7 @@ class GaussianBlur(Filter):
|
||||||
return image.gaussian_blur(self.radius)
|
return image.gaussian_blur(self.radius)
|
||||||
|
|
||||||
|
|
||||||
class UnsharpMask(Filter):
|
class UnsharpMask(MultibandFilter):
|
||||||
"""Unsharp mask filter.
|
"""Unsharp mask filter.
|
||||||
|
|
||||||
See Wikipedia's entry on `digital unsharp masking`_ for an explanation of
|
See Wikipedia's entry on `digital unsharp masking`_ for an explanation of
|
||||||
|
|
|
@ -72,8 +72,7 @@ def grabclipboard():
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
return im
|
return im
|
||||||
else:
|
else:
|
||||||
debug = 0 # temporary interface
|
data = Image.core.grabclipboard()
|
||||||
data = Image.core.grabclipboard(debug)
|
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
from . import BmpImagePlugin
|
from . import BmpImagePlugin
|
||||||
import io
|
import io
|
||||||
|
|
|
@ -267,17 +267,11 @@ def _save(im, fp, filename):
|
||||||
raise IOError("Error creating Spider header")
|
raise IOError("Error creating Spider header")
|
||||||
|
|
||||||
# write the SPIDER header
|
# write the SPIDER header
|
||||||
try:
|
|
||||||
fp = open(filename, 'wb')
|
|
||||||
except:
|
|
||||||
raise IOError("Unable to open %s for writing" % filename)
|
|
||||||
fp.writelines(hdr)
|
fp.writelines(hdr)
|
||||||
|
|
||||||
rawmode = "F;32NF" # 32-bit native floating point
|
rawmode = "F;32NF" # 32-bit native floating point
|
||||||
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
|
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
|
||||||
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
|
|
||||||
def _save_spider(im, fp, filename):
|
def _save_spider(im, fp, filename):
|
||||||
# get the filename extension and register it with Image
|
# get the filename extension and register it with Image
|
||||||
|
|
|
@ -139,16 +139,13 @@ SAVE = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename, check=0):
|
def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
|
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise IOError("cannot write mode %s as TGA" % im.mode)
|
raise IOError("cannot write mode %s as TGA" % im.mode)
|
||||||
|
|
||||||
if check:
|
|
||||||
return check
|
|
||||||
|
|
||||||
if colormaptype:
|
if colormaptype:
|
||||||
colormapfirst, colormaplength, colormapentry = 0, 256, 24
|
colormapfirst, colormaplength, colormapentry = 0, 256, 24
|
||||||
else:
|
else:
|
||||||
|
@ -182,6 +179,9 @@ def _save(im, fp, filename, check=0):
|
||||||
ImageFile._save(
|
ImageFile._save(
|
||||||
im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))])
|
im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))])
|
||||||
|
|
||||||
|
# write targa version 2 footer
|
||||||
|
fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000")
|
||||||
|
|
||||||
#
|
#
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Registry
|
# Registry
|
||||||
|
|
|
@ -153,7 +153,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
|
||||||
|
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
def _save(im, fp, filename):
|
||||||
if _handler is None or not hasattr("_handler", "save"):
|
if _handler is None or not hasattr(_handler, "save"):
|
||||||
raise IOError("WMF save handler not installed")
|
raise IOError("WMF save handler not installed")
|
||||||
_handler.save(im, fp, filename)
|
_handler.save(im, fp, filename)
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,41 @@ class PillowTestCase(unittest.TestCase):
|
||||||
return Image.open(outfile)
|
return Image.open(outfile)
|
||||||
raise IOError()
|
raise IOError()
|
||||||
|
|
||||||
|
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
|
||||||
|
class PillowLeakTestCase(PillowTestCase):
|
||||||
|
# requires unix/osx
|
||||||
|
iterations = 100 # count
|
||||||
|
mem_limit = 512 # k
|
||||||
|
|
||||||
|
def _get_mem_usage(self):
|
||||||
|
"""
|
||||||
|
Gets the RUSAGE memory usage, returns in K. Encapsulates the difference
|
||||||
|
between OSX and Linux rss reporting
|
||||||
|
|
||||||
|
:returns; memory usage in kilobytes
|
||||||
|
"""
|
||||||
|
|
||||||
|
from resource import getpagesize, getrusage, RUSAGE_SELF
|
||||||
|
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
# man 2 getrusage:
|
||||||
|
# ru_maxrss the maximum resident set size utilized (in bytes).
|
||||||
|
return mem / 1024 # Kb
|
||||||
|
else:
|
||||||
|
# linux
|
||||||
|
# man 2 getrusage
|
||||||
|
# ru_maxrss (since Linux 2.6.32)
|
||||||
|
# This is the maximum resident set size used (in kilobytes).
|
||||||
|
return mem # Kb
|
||||||
|
|
||||||
|
def _test_leak(self, core):
|
||||||
|
start_mem = self._get_mem_usage()
|
||||||
|
for cycle in range(self.iterations):
|
||||||
|
core()
|
||||||
|
mem = (self._get_mem_usage() - start_mem)
|
||||||
|
self.assertLess(mem, self.mem_limit,
|
||||||
|
msg='memory usage limit exceeded in iteration %d' % cycle)
|
||||||
|
|
||||||
|
|
||||||
# helpers
|
# helpers
|
||||||
|
|
||||||
|
|
BIN
Tests/images/reqd_showpage.eps
Normal file
BIN
Tests/images/reqd_showpage.eps
Normal file
Binary file not shown.
BIN
Tests/images/reqd_showpage.png
Normal file
BIN
Tests/images/reqd_showpage.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
|
@ -36,7 +36,7 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assert_warning(Image.DecompressionBombWarning,
|
self.assert_warning(Image.DecompressionBombWarning,
|
||||||
lambda: Image.open(TEST_FILE))
|
Image.open, TEST_FILE)
|
||||||
|
|
||||||
class TestDecompressionCrop(PillowTestCase):
|
class TestDecompressionCrop(PillowTestCase):
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class TestDecompressionCrop(PillowTestCase):
|
||||||
# same decompression bomb warnings on them.
|
# same decompression bomb warnings on them.
|
||||||
box = (0, 0, self.src.width * 2, self.src.height * 2)
|
box = (0, 0, self.src.width * 2, self.src.height * 2)
|
||||||
self.assert_warning(Image.DecompressionBombWarning,
|
self.assert_warning(Image.DecompressionBombWarning,
|
||||||
lambda: self.src.crop(box))
|
self.src.crop, box)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -51,13 +51,13 @@ class TestFeatures(PillowTestCase):
|
||||||
# Arrange
|
# Arrange
|
||||||
codec = "unsupported_codec"
|
codec = "unsupported_codec"
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError, lambda: features.check_codec(codec))
|
self.assertRaises(ValueError, features.check_codec, codec)
|
||||||
|
|
||||||
def test_unsupported_module(self):
|
def test_unsupported_module(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
module = "unsupported_module"
|
module = "unsupported_module"
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError, lambda: features.check_module(module))
|
self.assertRaises(ValueError, features.check_module, module)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -28,7 +28,7 @@ class TestFileBmp(PillowTestCase):
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: BmpImagePlugin.BmpImageFile(fp))
|
BmpImagePlugin.BmpImageFile, fp)
|
||||||
|
|
||||||
def test_save_to_bytes(self):
|
def test_save_to_bytes(self):
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
|
|
|
@ -24,8 +24,7 @@ class TestFileBufrStub(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
BufrStubImagePlugin.BufrStubImageFile, invalid_file)
|
||||||
BufrStubImagePlugin.BufrStubImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -40,7 +39,7 @@ class TestFileBufrStub(PillowTestCase):
|
||||||
tmpfile = self.tempfile("temp.bufr")
|
tmpfile = self.tempfile("temp.bufr")
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, lambda: im.save(tmpfile))
|
self.assertRaises(IOError, im.save, tmpfile)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -21,7 +21,7 @@ class TestFileCur(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: CurImagePlugin.CurImageFile(invalid_file))
|
CurImagePlugin.CurImageFile, invalid_file)
|
||||||
|
|
||||||
no_cursors_file = "Tests/images/no_cursors.cur"
|
no_cursors_file = "Tests/images/no_cursors.cur"
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class TestFileDcx(PillowTestCase):
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: DcxImagePlugin.DcxImageFile(fp))
|
DcxImagePlugin.DcxImageFile, fp)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -58,7 +58,7 @@ class TestFileDcx(PillowTestCase):
|
||||||
frame = 999 # too big on purpose
|
frame = 999 # too big on purpose
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(EOFError, lambda: im.seek(frame))
|
self.assertRaises(EOFError, im.seek, frame)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -55,7 +55,7 @@ class TestFileEps(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: EpsImagePlugin.EpsImageFile(invalid_file))
|
EpsImagePlugin.EpsImageFile, invalid_file)
|
||||||
|
|
||||||
def test_cmyk(self):
|
def test_cmyk(self):
|
||||||
cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps")
|
cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps")
|
||||||
|
@ -71,6 +71,16 @@ class TestFileEps(PillowTestCase):
|
||||||
target = Image.open('Tests/images/pil_sample_rgb.jpg')
|
target = Image.open('Tests/images/pil_sample_rgb.jpg')
|
||||||
self.assert_image_similar(cmyk_image, target, 10)
|
self.assert_image_similar(cmyk_image, target, 10)
|
||||||
|
|
||||||
|
def test_showpage(self):
|
||||||
|
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||||
|
plot_image = Image.open("Tests/images/reqd_showpage.eps")
|
||||||
|
target = Image.open("Tests/images/reqd_showpage.png")
|
||||||
|
|
||||||
|
#should not crash/hang
|
||||||
|
plot_image.load()
|
||||||
|
# fonts could be slightly different
|
||||||
|
self.assert_image_similar(plot_image, target, 2)
|
||||||
|
|
||||||
def test_file_object(self):
|
def test_file_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
image1 = Image.open(file1)
|
||||||
|
@ -97,7 +107,7 @@ class TestFileEps(PillowTestCase):
|
||||||
def test_image_mode_not_supported(self):
|
def test_image_mode_not_supported(self):
|
||||||
im = hopper("RGBA")
|
im = hopper("RGBA")
|
||||||
tmpfile = self.tempfile('temp.eps')
|
tmpfile = self.tempfile('temp.eps')
|
||||||
self.assertRaises(ValueError, lambda: im.save(tmpfile))
|
self.assertRaises(ValueError, im.save, tmpfile)
|
||||||
|
|
||||||
def test_render_scale1(self):
|
def test_render_scale1(self):
|
||||||
# We need png support for these render test
|
# We need png support for these render test
|
||||||
|
|
|
@ -24,8 +24,7 @@ class TestFileFitsStub(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
FitsStubImagePlugin.FITSStubImageFile, invalid_file)
|
||||||
FitsStubImagePlugin.FITSStubImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -41,10 +40,10 @@ class TestFileFitsStub(PillowTestCase):
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, lambda: im.save(dummy_filename))
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError,
|
IOError,
|
||||||
lambda: FitsStubImagePlugin._save(im, dummy_fp, dummy_filename))
|
FitsStubImagePlugin._save, im, dummy_fp, dummy_filename)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -41,7 +41,7 @@ class TestFileFli(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: FliImagePlugin.FliImageFile(invalid_file))
|
FliImagePlugin.FliImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(static_test_file)
|
im = Image.open(static_test_file)
|
||||||
|
@ -71,7 +71,7 @@ class TestFileFli(PillowTestCase):
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
# Test seek past end of file
|
# Test seek past end of file
|
||||||
self.assertRaises(EOFError, lambda: im.seek(2))
|
self.assertRaises(EOFError, im.seek, 2)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(animated_test_file)
|
im = Image.open(animated_test_file)
|
||||||
|
|
|
@ -9,12 +9,12 @@ class TestFileFpx(PillowTestCase):
|
||||||
# Test an invalid OLE file
|
# Test an invalid OLE file
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: FpxImagePlugin.FpxImageFile(invalid_file))
|
FpxImagePlugin.FpxImageFile, invalid_file)
|
||||||
|
|
||||||
# Test a valid OLE file, but not an FPX file
|
# Test a valid OLE file, but not an FPX file
|
||||||
ole_file = "Tests/images/test-ole-file.doc"
|
ole_file = "Tests/images/test-ole-file.doc"
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: FpxImagePlugin.FpxImageFile(ole_file))
|
FpxImagePlugin.FpxImageFile, ole_file)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -9,7 +9,7 @@ class TestFileGbr(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: GbrImagePlugin.GbrImageFile(invalid_file))
|
GbrImagePlugin.GbrImageFile, invalid_file)
|
||||||
|
|
||||||
def test_gbr_file(self):
|
def test_gbr_file(self):
|
||||||
im = Image.open('Tests/images/gbr.gbr')
|
im = Image.open('Tests/images/gbr.gbr')
|
||||||
|
|
|
@ -31,7 +31,7 @@ class TestFileGif(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: GifImagePlugin.GifImageFile(invalid_file))
|
GifImagePlugin.GifImageFile, invalid_file)
|
||||||
|
|
||||||
def test_optimize(self):
|
def test_optimize(self):
|
||||||
def test_grayscale(optimize):
|
def test_grayscale(optimize):
|
||||||
|
@ -375,29 +375,31 @@ class TestFileGif(PillowTestCase):
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
out = self.tempfile('temp.gif')
|
out = self.tempfile('temp.gif')
|
||||||
|
|
||||||
|
def assertVersionAfterSave(im, version):
|
||||||
|
im.save(out)
|
||||||
|
reread = Image.open(out)
|
||||||
|
self.assertEqual(reread.info["version"], version)
|
||||||
|
|
||||||
# Test that GIF87a is used by default
|
# Test that GIF87a is used by default
|
||||||
im = Image.new('L', (100, 100), '#000')
|
im = Image.new('L', (100, 100), '#000')
|
||||||
im.save(out)
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
reread = Image.open(out)
|
|
||||||
self.assertEqual(reread.info["version"], b"GIF87a")
|
# Test setting the version to 89a
|
||||||
|
im = Image.new('L', (100, 100), '#000')
|
||||||
|
im.info["version"] = b"89a"
|
||||||
|
assertVersionAfterSave(im, b"GIF89a")
|
||||||
|
|
||||||
# Test that adding a GIF89a feature changes the version
|
# Test that adding a GIF89a feature changes the version
|
||||||
im.info["transparency"] = 1
|
im.info["transparency"] = 1
|
||||||
im.save(out)
|
assertVersionAfterSave(im, b"GIF89a")
|
||||||
reread = Image.open(out)
|
|
||||||
self.assertEqual(reread.info["version"], b"GIF89a")
|
|
||||||
|
|
||||||
# Test that a GIF87a image is also saved in that format
|
# Test that a GIF87a image is also saved in that format
|
||||||
im = Image.open("Tests/images/test.colors.gif")
|
im = Image.open("Tests/images/test.colors.gif")
|
||||||
im.save(out)
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
reread = Image.open(out)
|
|
||||||
self.assertEqual(reread.info["version"], b"GIF87a")
|
|
||||||
|
|
||||||
# Test that a GIF89a image is also saved in that format
|
# Test that a GIF89a image is also saved in that format
|
||||||
im.info["version"] = b"GIF89a"
|
im.info["version"] = b"GIF89a"
|
||||||
im.save(out)
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
reread = Image.open(out)
|
|
||||||
self.assertEqual(reread.info["version"], b"GIF87a")
|
|
||||||
|
|
||||||
def test_append_images(self):
|
def test_append_images(self):
|
||||||
out = self.tempfile('temp.gif')
|
out = self.tempfile('temp.gif')
|
||||||
|
|
|
@ -10,13 +10,13 @@ class TestImage(PillowTestCase):
|
||||||
GimpPaletteFile(fp)
|
GimpPaletteFile(fp)
|
||||||
|
|
||||||
with open('Tests/images/hopper.jpg', 'rb') as fp:
|
with open('Tests/images/hopper.jpg', 'rb') as fp:
|
||||||
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp))
|
self.assertRaises(SyntaxError, GimpPaletteFile, fp)
|
||||||
|
|
||||||
with open('Tests/images/bad_palette_file.gpl', 'rb') as fp:
|
with open('Tests/images/bad_palette_file.gpl', 'rb') as fp:
|
||||||
self.assertRaises(SyntaxError, lambda: GimpPaletteFile(fp))
|
self.assertRaises(SyntaxError, GimpPaletteFile, fp)
|
||||||
|
|
||||||
with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp:
|
with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp:
|
||||||
self.assertRaises(ValueError, lambda: GimpPaletteFile(fp))
|
self.assertRaises(ValueError, GimpPaletteFile, fp)
|
||||||
|
|
||||||
def test_get_palette(self):
|
def test_get_palette(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -24,8 +24,7 @@ class TestFileGribStub(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
GribStubImagePlugin.GribStubImageFile, invalid_file)
|
||||||
GribStubImagePlugin.GribStubImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -40,7 +39,7 @@ class TestFileGribStub(PillowTestCase):
|
||||||
tmpfile = self.tempfile("temp.grib")
|
tmpfile = self.tempfile("temp.grib")
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, lambda: im.save(tmpfile))
|
self.assertRaises(IOError, im.save, tmpfile)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -24,8 +24,7 @@ class TestFileHdf5Stub(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
Hdf5StubImagePlugin.HDF5StubImageFile, invalid_file)
|
||||||
Hdf5StubImagePlugin.HDF5StubImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -41,10 +40,10 @@ class TestFileHdf5Stub(PillowTestCase):
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, lambda: im.save(dummy_filename))
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError,
|
IOError,
|
||||||
lambda: Hdf5StubImagePlugin._save(im, dummy_fp, dummy_filename))
|
Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from helper import unittest, PillowTestCase
|
from helper import unittest, PillowTestCase
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image, IcnsImagePlugin
|
||||||
|
|
||||||
|
import io
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# sample icon file
|
# sample icon file
|
||||||
|
@ -82,6 +83,23 @@ class TestFileIcns(PillowTestCase):
|
||||||
self.assertEqual(im2.mode, 'RGBA')
|
self.assertEqual(im2.mode, 'RGBA')
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
|
def test_getimage(self):
|
||||||
|
with open(TEST_FILE, 'rb') as fp:
|
||||||
|
icns_file = IcnsImagePlugin.IcnsFile(fp)
|
||||||
|
|
||||||
|
im = icns_file.getimage()
|
||||||
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (1024, 1024))
|
||||||
|
|
||||||
|
im = icns_file.getimage((512, 512))
|
||||||
|
self.assertEqual(im.mode, "RGBA")
|
||||||
|
self.assertEqual(im.size, (512, 512))
|
||||||
|
|
||||||
|
def test_not_an_icns_file(self):
|
||||||
|
with io.BytesIO(b'invalid\n') as fp:
|
||||||
|
self.assertRaises(SyntaxError,
|
||||||
|
IcnsImagePlugin.IcnsFile, fp)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -18,7 +18,7 @@ class TestFileIco(PillowTestCase):
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: IcoImagePlugin.IcoImageFile(fp))
|
IcoImagePlugin.IcoImageFile, fp)
|
||||||
|
|
||||||
def test_save_to_bytes(self):
|
def test_save_to_bytes(self):
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
|
|
|
@ -43,18 +43,27 @@ class TestFileIm(PillowTestCase):
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
def test_roundtrip(self):
|
def test_roundtrip(self):
|
||||||
out = self.tempfile('temp.im')
|
for mode in ["RGB", "P"]:
|
||||||
im = hopper()
|
out = self.tempfile('temp.im')
|
||||||
im.save(out)
|
im = hopper(mode)
|
||||||
reread = Image.open(out)
|
im.save(out)
|
||||||
|
reread = Image.open(out)
|
||||||
|
|
||||||
self.assert_image_equal(reread, im)
|
self.assert_image_equal(reread, im)
|
||||||
|
|
||||||
|
def test_save_unsupported_mode(self):
|
||||||
|
out = self.tempfile('temp.im')
|
||||||
|
im = hopper("HSV")
|
||||||
|
self.assertRaises(ValueError, im.save, out)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: ImImagePlugin.ImImageFile(invalid_file))
|
ImImagePlugin.ImImageFile, invalid_file)
|
||||||
|
|
||||||
|
def test_number(self):
|
||||||
|
self.assertEqual(1.2, ImImagePlugin.number("1.2"))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -314,7 +314,7 @@ class TestFileJpeg(PillowTestCase):
|
||||||
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
|
self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1))
|
||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
TypeError, lambda: self.roundtrip(hopper(), subsampling="1:1:1"))
|
TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
||||||
|
@ -423,18 +423,18 @@ class TestFileJpeg(PillowTestCase):
|
||||||
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
|
self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg")
|
||||||
|
|
||||||
# not a sequence
|
# not a sequence
|
||||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables='a'))
|
self.assertRaises(Exception, self.roundtrip, im, qtables='a')
|
||||||
# sequence wrong length
|
# sequence wrong length
|
||||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[]))
|
self.assertRaises(Exception, self.roundtrip, im, qtables=[])
|
||||||
# sequence wrong length
|
# sequence wrong length
|
||||||
self.assertRaises(Exception,
|
self.assertRaises(Exception,
|
||||||
lambda: self.roundtrip(im, qtables=[1, 2, 3, 4, 5]))
|
self.roundtrip, im, qtables=[1, 2, 3, 4, 5])
|
||||||
|
|
||||||
# qtable entry not a sequence
|
# qtable entry not a sequence
|
||||||
self.assertRaises(Exception, lambda: self.roundtrip(im, qtables=[1]))
|
self.assertRaises(Exception, self.roundtrip, im, qtables=[1])
|
||||||
# qtable entry has wrong number of items
|
# qtable entry has wrong number of items
|
||||||
self.assertRaises(Exception,
|
self.assertRaises(Exception,
|
||||||
lambda: self.roundtrip(im, qtables=[[1, 2, 3, 4]]))
|
self.roundtrip, im, qtables=[[1, 2, 3, 4]])
|
||||||
|
|
||||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||||
def test_load_djpeg(self):
|
def test_load_djpeg(self):
|
||||||
|
@ -479,7 +479,7 @@ class TestFileJpeg(PillowTestCase):
|
||||||
# Act
|
# Act
|
||||||
# Shouldn't raise error
|
# Shouldn't raise error
|
||||||
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
||||||
im = self.assert_warning(UserWarning, lambda: Image.open(fn))
|
im = self.assert_warning(UserWarning, Image.open, fn)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "JPEG")
|
self.assertEqual(im.format, "JPEG")
|
||||||
|
@ -584,7 +584,7 @@ class TestFileCloseW32(PillowTestCase):
|
||||||
im = Image.open(tmpfile)
|
im = Image.open(tmpfile)
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
self.assertFalse(fp.closed)
|
self.assertFalse(fp.closed)
|
||||||
self.assertRaises(Exception, lambda: os.remove(tmpfile))
|
self.assertRaises(Exception, os.remove, tmpfile)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertTrue(fp.closed)
|
self.assertTrue(fp.closed)
|
||||||
# this should not fail, as load should have closed the file.
|
# this should not fail, as load should have closed the file.
|
||||||
|
|
|
@ -44,8 +44,7 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
Jpeg2KImagePlugin.Jpeg2KImageFile, invalid_file)
|
||||||
Jpeg2KImagePlugin.Jpeg2KImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_bytesio(self):
|
def test_bytesio(self):
|
||||||
with open('Tests/images/test-card-lossless.jp2', 'rb') as f:
|
with open('Tests/images/test-card-lossless.jp2', 'rb') as f:
|
||||||
|
|
|
@ -362,10 +362,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
im = hopper('RGB')
|
im = hopper('RGB')
|
||||||
out = self.tempfile('temp.tif')
|
out = self.tempfile('temp.tif')
|
||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(IOError, im.save, out, compression='tiff_ccitt')
|
||||||
IOError, lambda: im.save(out, compression='tiff_ccitt'))
|
self.assertRaises(IOError, im.save, out, compression='group3')
|
||||||
self.assertRaises(IOError, lambda: im.save(out, compression='group3'))
|
self.assertRaises(IOError, im.save, out, compression='group4')
|
||||||
self.assertRaises(IOError, lambda: im.save(out, compression='group4'))
|
|
||||||
|
|
||||||
def test_fp_leak(self):
|
def test_fp_leak(self):
|
||||||
im = Image.open("Tests/images/hopper_g4_500.tif")
|
im = Image.open("Tests/images/hopper_g4_500.tif")
|
||||||
|
@ -373,10 +372,10 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
im.load() # this should close it.
|
im.load() # this should close it.
|
||||||
self.assertRaises(OSError, lambda: os.fstat(fn))
|
self.assertRaises(OSError, os.fstat, fn)
|
||||||
im = None # this should force even more closed.
|
im = None # this should force even more closed.
|
||||||
self.assertRaises(OSError, lambda: os.fstat(fn))
|
self.assertRaises(OSError, os.fstat, fn)
|
||||||
self.assertRaises(OSError, lambda: os.close(fn))
|
self.assertRaises(OSError, os.close, fn)
|
||||||
|
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
|
|
|
@ -9,8 +9,7 @@ class TestFileMcIdas(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
McIdasImagePlugin.McIdasImageFile, invalid_file)
|
||||||
McIdasImagePlugin.McIdasImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_valid_file(self):
|
def test_valid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -42,19 +42,19 @@ class TestFileMic(PillowTestCase):
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
self.assertRaises(EOFError, lambda: im.seek(99))
|
self.assertRaises(EOFError, im.seek, 99)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Test an invalid OLE file
|
# Test an invalid OLE file
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: MicImagePlugin.MicImageFile(invalid_file))
|
MicImagePlugin.MicImageFile, invalid_file)
|
||||||
|
|
||||||
# Test a valid OLE file, but not a MIC file
|
# Test a valid OLE file, but not a MIC file
|
||||||
ole_file = "Tests/images/test-ole-file.doc"
|
ole_file = "Tests/images/test-ole-file.doc"
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: MicImagePlugin.MicImageFile(ole_file))
|
MicImagePlugin.MicImageFile, ole_file)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -26,7 +26,7 @@ class TestFileMsp(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: MspImagePlugin.MspImageFile(invalid_file))
|
MspImagePlugin.MspImageFile, invalid_file)
|
||||||
|
|
||||||
def test_bad_checksum(self):
|
def test_bad_checksum(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -35,7 +35,7 @@ class TestFileMsp(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: MspImagePlugin.MspImageFile(bad_checksum))
|
MspImagePlugin.MspImageFile, bad_checksum)
|
||||||
|
|
||||||
def test_open_windows_v1(self):
|
def test_open_windows_v1(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -77,7 +77,7 @@ class TestFileMsp(PillowTestCase):
|
||||||
filename = self.tempfile("temp.msp")
|
filename = self.tempfile("temp.msp")
|
||||||
|
|
||||||
# Act/Assert
|
# Act/Assert
|
||||||
self.assertRaises(IOError, lambda: im.save(filename))
|
self.assertRaises(IOError, im.save, filename)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -51,7 +51,7 @@ class TestFilePalm(PillowTestCase):
|
||||||
mode = "RGB"
|
mode = "RGB"
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(IOError, lambda: self.helper_save_as_palm(mode))
|
self.assertRaises(IOError, self.helper_save_as_palm, mode)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -21,13 +21,14 @@ class TestFilePcx(PillowTestCase):
|
||||||
|
|
||||||
# Test an unsupported mode
|
# Test an unsupported mode
|
||||||
f = self.tempfile("temp.pcx")
|
f = self.tempfile("temp.pcx")
|
||||||
self.assertRaises(ValueError, lambda: hopper("RGBA").save(f))
|
im = hopper("RGBA")
|
||||||
|
self.assertRaises(ValueError, im.save, f)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: PcxImagePlugin.PcxImageFile(invalid_file))
|
PcxImagePlugin.PcxImageFile, invalid_file)
|
||||||
|
|
||||||
def test_odd(self):
|
def test_odd(self):
|
||||||
# see issue #523, odd sized images should have a stride that's even.
|
# see issue #523, odd sized images should have a stride that's even.
|
||||||
|
|
|
@ -59,7 +59,7 @@ class TestFilePdf(PillowTestCase):
|
||||||
im = hopper("LA")
|
im = hopper("LA")
|
||||||
outfile = self.tempfile("temp_LA.pdf")
|
outfile = self.tempfile("temp_LA.pdf")
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: im.save(outfile))
|
self.assertRaises(ValueError, im.save, outfile)
|
||||||
|
|
||||||
def test_save_all(self):
|
def test_save_all(self):
|
||||||
# Single frame image
|
# Single frame image
|
||||||
|
|
|
@ -22,7 +22,7 @@ class TestFilePixar(PillowTestCase):
|
||||||
|
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
SyntaxError,
|
SyntaxError,
|
||||||
lambda: PixarImagePlugin.PixarImageFile(invalid_file))
|
PixarImagePlugin.PixarImageFile, invalid_file)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, PillowLeakTestCase, hopper
|
||||||
from PIL import Image, ImageFile, PngImagePlugin
|
from PIL import Image, ImageFile, PngImagePlugin
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
@ -7,9 +7,6 @@ import sys
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
# For Truncated phng memory leak
|
|
||||||
MEM_LIMIT = 2 # max increase in MB
|
|
||||||
ITERATIONS = 100 # Leak is 56k/iteration, this will leak 5.6megs
|
|
||||||
|
|
||||||
# sample png stream
|
# sample png stream
|
||||||
|
|
||||||
|
@ -87,14 +84,14 @@ class TestFilePng(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: PngImagePlugin.PngImageFile(invalid_file))
|
PngImagePlugin.PngImageFile, invalid_file)
|
||||||
|
|
||||||
def test_broken(self):
|
def test_broken(self):
|
||||||
# Check reading of totally broken files. In this case, the test
|
# Check reading of totally broken files. In this case, the test
|
||||||
# file was checked into Subversion as a text file.
|
# file was checked into Subversion as a text file.
|
||||||
|
|
||||||
test_file = "Tests/images/broken.png"
|
test_file = "Tests/images/broken.png"
|
||||||
self.assertRaises(IOError, lambda: Image.open(test_file))
|
self.assertRaises(IOError, Image.open, test_file)
|
||||||
|
|
||||||
def test_bad_text(self):
|
def test_bad_text(self):
|
||||||
# Make sure PIL can read malformed tEXt chunks (@PIL152)
|
# Make sure PIL can read malformed tEXt chunks (@PIL152)
|
||||||
|
@ -424,7 +421,7 @@ class TestFilePng(PillowTestCase):
|
||||||
data = b'\x89' + fd.read()
|
data = b'\x89' + fd.read()
|
||||||
|
|
||||||
pngfile = BytesIO(data)
|
pngfile = BytesIO(data)
|
||||||
self.assertRaises(IOError, lambda: Image.open(pngfile))
|
self.assertRaises(IOError, Image.open, pngfile)
|
||||||
|
|
||||||
def test_trns_rgb(self):
|
def test_trns_rgb(self):
|
||||||
# Check writing and reading of tRNS chunks for RGB images.
|
# Check writing and reading of tRNS chunks for RGB images.
|
||||||
|
@ -505,17 +502,17 @@ class TestFilePng(PillowTestCase):
|
||||||
im.convert("P").save(test_file, dpi=(100, 100))
|
im.convert("P").save(test_file, dpi=(100, 100))
|
||||||
|
|
||||||
chunks = []
|
chunks = []
|
||||||
fp = open(test_file, "rb")
|
with open(test_file, "rb") as fp:
|
||||||
fp.read(8)
|
fp.read(8)
|
||||||
png = PngImagePlugin.PngStream(fp)
|
png = PngImagePlugin.PngStream(fp)
|
||||||
while True:
|
while True:
|
||||||
cid, pos, length = png.read()
|
cid, pos, length = png.read()
|
||||||
chunks.append(cid)
|
chunks.append(cid)
|
||||||
try:
|
try:
|
||||||
s = png.call(cid, pos, length)
|
s = png.call(cid, pos, length)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
break
|
break
|
||||||
png.crc(cid, s)
|
png.crc(cid, s)
|
||||||
|
|
||||||
# https://www.w3.org/TR/PNG/#5ChunkOrdering
|
# https://www.w3.org/TR/PNG/#5ChunkOrdering
|
||||||
# IHDR - shall be first
|
# IHDR - shall be first
|
||||||
|
@ -531,28 +528,22 @@ class TestFilePng(PillowTestCase):
|
||||||
# pHYs - before IDAT
|
# pHYs - before IDAT
|
||||||
self.assertLess(chunks.index(b"pHYs"), chunks.index(b"IDAT"))
|
self.assertLess(chunks.index(b"pHYs"), chunks.index(b"IDAT"))
|
||||||
|
|
||||||
|
def test_getchunks(self):
|
||||||
|
im = hopper()
|
||||||
|
|
||||||
|
chunks = PngImagePlugin.getchunks(im)
|
||||||
|
self.assertEqual(len(chunks), 3)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
|
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or MacOS")
|
||||||
class TestTruncatedPngPLeaks(PillowTestCase):
|
class TestTruncatedPngPLeaks(PillowLeakTestCase):
|
||||||
|
mem_limit = 2*1024 # max increase in K
|
||||||
|
iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
|
if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
def _get_mem_usage(self):
|
|
||||||
from resource import getpagesize, getrusage, RUSAGE_SELF
|
|
||||||
mem = getrusage(RUSAGE_SELF).ru_maxrss
|
|
||||||
if sys.platform == 'darwin':
|
|
||||||
# man 2 getrusage:
|
|
||||||
# ru_maxrss the maximum resident set size utilized (in bytes).
|
|
||||||
return mem / 1024 / 1024 # megs
|
|
||||||
else:
|
|
||||||
# linux
|
|
||||||
# man 2 getrusage
|
|
||||||
# ru_maxrss (since Linux 2.6.32)
|
|
||||||
# This is the maximum resident set size used (in kilobytes).
|
|
||||||
return mem / 1024 # megs
|
|
||||||
|
|
||||||
def test_leak_load(self):
|
def test_leak_load(self):
|
||||||
with open('Tests/images/hopper.png', 'rb') as f:
|
with open('Tests/images/hopper.png', 'rb') as f:
|
||||||
DATA = BytesIO(f.read(16 * 1024))
|
DATA = BytesIO(f.read(16 * 1024))
|
||||||
|
@ -560,13 +551,13 @@ class TestTruncatedPngPLeaks(PillowTestCase):
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||||
with Image.open(DATA) as im:
|
with Image.open(DATA) as im:
|
||||||
im.load()
|
im.load()
|
||||||
start_mem = self._get_mem_usage()
|
|
||||||
|
def core():
|
||||||
|
with Image.open(DATA) as im:
|
||||||
|
im.load()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for _ in range(ITERATIONS):
|
self._test_leak(core)
|
||||||
with Image.open(DATA) as im:
|
|
||||||
im.load()
|
|
||||||
mem = (self._get_mem_usage() - start_mem)
|
|
||||||
self.assertLess(mem, MEM_LIMIT, msg='memory usage limit exceeded')
|
|
||||||
finally:
|
finally:
|
||||||
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
ImageFile.LOAD_TRUNCATED_IMAGES = False
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ class TestFilePpm(PillowTestCase):
|
||||||
with open(path, 'w') as f:
|
with open(path, 'w') as f:
|
||||||
f.write('P6')
|
f.write('P6')
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: Image.open(path))
|
self.assertRaises(ValueError, Image.open, path)
|
||||||
|
|
||||||
def test_neg_ppm(self):
|
def test_neg_ppm(self):
|
||||||
# Storage.c accepted negative values for xsize, ysize. the
|
# Storage.c accepted negative values for xsize, ysize. the
|
||||||
|
|
|
@ -21,7 +21,7 @@ class TestImagePsd(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: PsdImagePlugin.PsdImageFile(invalid_file))
|
PsdImagePlugin.PsdImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
im = Image.open("Tests/images/hopper_merged.psd")
|
||||||
|
@ -66,7 +66,7 @@ class TestImagePsd(PillowTestCase):
|
||||||
def test_seek_eoferror(self):
|
def test_seek_eoferror(self):
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
|
|
||||||
self.assertRaises(EOFError, lambda: im.seek(-1))
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
|
|
||||||
def test_icc_profile(self):
|
def test_icc_profile(self):
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
|
|
|
@ -42,8 +42,7 @@ class TestFileSgi(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
lambda:
|
SgiImagePlugin.SgiImageFile, invalid_file)
|
||||||
SgiImagePlugin.SgiImageFile(invalid_file))
|
|
||||||
|
|
||||||
def test_write(self):
|
def test_write(self):
|
||||||
def roundtrip(img):
|
def roundtrip(img):
|
||||||
|
@ -62,14 +61,14 @@ class TestFileSgi(PillowTestCase):
|
||||||
im = hopper('LA')
|
im = hopper('LA')
|
||||||
out = self.tempfile('temp.sgi')
|
out = self.tempfile('temp.sgi')
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: im.save(out, format='sgi'))
|
self.assertRaises(ValueError, im.save, out, format='sgi')
|
||||||
|
|
||||||
def test_incorrect_number_of_bands(self):
|
def test_incorrect_number_of_bands(self):
|
||||||
im = hopper('YCbCr')
|
im = hopper('YCbCr')
|
||||||
im.mode = 'RGB'
|
im.mode = 'RGB'
|
||||||
out = self.tempfile('temp.sgi')
|
out = self.tempfile('temp.sgi')
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: im.save(out, format='sgi'))
|
self.assertRaises(ValueError, im.save, out, format='sgi')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -4,6 +4,8 @@ from PIL import Image
|
||||||
from PIL import ImageSequence
|
from PIL import ImageSequence
|
||||||
from PIL import SpiderImagePlugin
|
from PIL import SpiderImagePlugin
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.spider"
|
TEST_FILE = "Tests/images/hopper.spider"
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,6 +32,21 @@ class TestImageSpider(PillowTestCase):
|
||||||
self.assertEqual(im2.size, (128, 128))
|
self.assertEqual(im2.size, (128, 128))
|
||||||
self.assertEqual(im2.format, "SPIDER")
|
self.assertEqual(im2.format, "SPIDER")
|
||||||
|
|
||||||
|
def test_tempfile(self):
|
||||||
|
# Arrange
|
||||||
|
im = hopper()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
with tempfile.TemporaryFile() as fp:
|
||||||
|
im.save(fp, "SPIDER")
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
fp.seek(0)
|
||||||
|
reloaded = Image.open(fp)
|
||||||
|
self.assertEqual(reloaded.mode, "F")
|
||||||
|
self.assertEqual(reloaded.size, (128, 128))
|
||||||
|
self.assertEqual(reloaded.format, "SPIDER")
|
||||||
|
|
||||||
def test_isSpiderImage(self):
|
def test_isSpiderImage(self):
|
||||||
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
|
self.assertTrue(SpiderImagePlugin.isSpiderImage(TEST_FILE))
|
||||||
|
|
||||||
|
@ -84,12 +101,12 @@ class TestImageSpider(PillowTestCase):
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/invalid.spider"
|
invalid_file = "Tests/images/invalid.spider"
|
||||||
|
|
||||||
self.assertRaises(IOError, lambda: Image.open(invalid_file))
|
self.assertRaises(IOError, Image.open, invalid_file)
|
||||||
|
|
||||||
def test_nonstack_file(self):
|
def test_nonstack_file(self):
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
|
|
||||||
self.assertRaises(EOFError, lambda: im.seek(0))
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
|
|
||||||
def test_nonstack_dos(self):
|
def test_nonstack_dos(self):
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
|
|
|
@ -24,7 +24,7 @@ class TestFileSun(PillowTestCase):
|
||||||
|
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: SunImagePlugin.SunImageFile(invalid_file))
|
SunImagePlugin.SunImageFile, invalid_file)
|
||||||
|
|
||||||
def test_im1(self):
|
def test_im1(self):
|
||||||
im = Image.open('Tests/images/sunraster.im1')
|
im = Image.open('Tests/images/sunraster.im1')
|
||||||
|
|
|
@ -85,6 +85,10 @@ class TestFileTiff(PillowTestCase):
|
||||||
im = Image.open("Tests/images/copyleft.tiff")
|
im = Image.open("Tests/images/copyleft.tiff")
|
||||||
self.assertEqual(im.mode, 'RGB')
|
self.assertEqual(im.mode, 'RGB')
|
||||||
|
|
||||||
|
def test_set_legacy_api(self):
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
ImageFileDirectory_v2.legacy_api = None
|
||||||
|
|
||||||
def test_xyres_tiff(self):
|
def test_xyres_tiff(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
im = Image.open(filename)
|
||||||
|
@ -140,11 +144,11 @@ class TestFileTiff(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: TiffImagePlugin.TiffImageFile(invalid_file))
|
TiffImagePlugin.TiffImageFile, invalid_file)
|
||||||
|
|
||||||
TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0")
|
TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0")
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: TiffImagePlugin.TiffImageFile(invalid_file))
|
TiffImagePlugin.TiffImageFile, invalid_file)
|
||||||
TiffImagePlugin.PREFIXES.pop()
|
TiffImagePlugin.PREFIXES.pop()
|
||||||
|
|
||||||
def test_bad_exif(self):
|
def test_bad_exif(self):
|
||||||
|
@ -163,7 +167,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
def test_save_unsupported_mode(self):
|
def test_save_unsupported_mode(self):
|
||||||
im = hopper("HSV")
|
im = hopper("HSV")
|
||||||
outfile = self.tempfile("temp.tif")
|
outfile = self.tempfile("temp.tif")
|
||||||
self.assertRaises(IOError, lambda: im.save(outfile))
|
self.assertRaises(IOError, im.save, outfile)
|
||||||
|
|
||||||
def test_little_endian(self):
|
def test_little_endian(self):
|
||||||
im = Image.open('Tests/images/16bit.cropped.tif')
|
im = Image.open('Tests/images/16bit.cropped.tif')
|
||||||
|
@ -340,7 +344,7 @@ class TestFileTiff(PillowTestCase):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
im = Image.open(filename)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
self.assertRaises(EOFError, lambda: im.seek(1))
|
self.assertRaises(EOFError, im.seek, 1)
|
||||||
|
|
||||||
def test__limit_rational_int(self):
|
def test__limit_rational_int(self):
|
||||||
from PIL.TiffImagePlugin import _limit_rational
|
from PIL.TiffImagePlugin import _limit_rational
|
||||||
|
@ -495,12 +499,12 @@ class TestFileTiff(PillowTestCase):
|
||||||
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
with Image.open("Tests/images/uint16_1_4660.tif") as im:
|
||||||
im.save(tmpfile)
|
im.save(tmpfile)
|
||||||
|
|
||||||
f = open(tmpfile, 'rb')
|
with open(tmpfile, 'rb') as f:
|
||||||
im = Image.open(f)
|
im = Image.open(f)
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
self.assertFalse(fp.closed)
|
self.assertFalse(fp.closed)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertFalse(fp.closed)
|
self.assertFalse(fp.closed)
|
||||||
|
|
||||||
@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only")
|
@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only")
|
||||||
class TestFileTiffW32(PillowTestCase):
|
class TestFileTiffW32(PillowTestCase):
|
||||||
|
@ -515,7 +519,7 @@ class TestFileTiffW32(PillowTestCase):
|
||||||
im = Image.open(tmpfile)
|
im = Image.open(tmpfile)
|
||||||
fp = im.fp
|
fp = im.fp
|
||||||
self.assertFalse(fp.closed)
|
self.assertFalse(fp.closed)
|
||||||
self.assertRaises(Exception, lambda: os.remove(tmpfile))
|
self.assertRaises(Exception, os.remove, tmpfile)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertTrue(fp.closed)
|
self.assertTrue(fp.closed)
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
head = f.read(8)
|
head = f.read(8)
|
||||||
info = TiffImagePlugin.ImageFileDirectory(head)
|
info = TiffImagePlugin.ImageFileDirectory(head)
|
||||||
try:
|
try:
|
||||||
self.assert_warning(UserWarning, lambda: info.load(f))
|
self.assert_warning(UserWarning, info.load, f)
|
||||||
except struct.error:
|
except struct.error:
|
||||||
self.fail("Should not be struct errors there.")
|
self.fail("Should not be struct errors there.")
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,8 @@ class TestFileWebp(PillowTestCase):
|
||||||
def test_write_unsupported_mode(self):
|
def test_write_unsupported_mode(self):
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
|
|
||||||
self.assertRaises(IOError, lambda: hopper("L").save(temp_file))
|
im = hopper("L")
|
||||||
|
self.assertRaises(IOError, im.save, temp_file)
|
||||||
|
|
||||||
def test_WebPEncode_with_invalid_args(self):
|
def test_WebPEncode_with_invalid_args(self):
|
||||||
self.assertRaises(TypeError, _webp.WebPEncode)
|
self.assertRaises(TypeError, _webp.WebPEncode)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from helper import unittest, PillowTestCase, hopper
|
from helper import unittest, PillowTestCase, hopper
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from PIL import WmfImagePlugin
|
||||||
|
|
||||||
|
|
||||||
class TestFileWmf(PillowTestCase):
|
class TestFileWmf(PillowTestCase):
|
||||||
|
@ -26,12 +28,29 @@ class TestFileWmf(PillowTestCase):
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 2.0)
|
self.assert_image_similar(im, imref, 2.0)
|
||||||
|
|
||||||
|
def test_register_handler(self):
|
||||||
|
class TestHandler:
|
||||||
|
methodCalled = False
|
||||||
|
|
||||||
|
def save(self, im, fp, filename):
|
||||||
|
self.methodCalled = True
|
||||||
|
handler = TestHandler()
|
||||||
|
WmfImagePlugin.register_handler(handler)
|
||||||
|
|
||||||
|
im = hopper()
|
||||||
|
tmpfile = self.tempfile("temp.wmf")
|
||||||
|
im.save(tmpfile)
|
||||||
|
self.assertTrue(handler.methodCalled)
|
||||||
|
|
||||||
|
# Restore the state before this test
|
||||||
|
WmfImagePlugin.register_handler(None)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
for ext in [".wmf", ".emf"]:
|
for ext in [".wmf", ".emf"]:
|
||||||
tmpfile = self.tempfile("temp"+ext)
|
tmpfile = self.tempfile("temp"+ext)
|
||||||
self.assertRaises(IOError, lambda: im.save(tmpfile))
|
self.assertRaises(IOError, im.save, tmpfile)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -21,7 +21,7 @@ class TestFileXpm(PillowTestCase):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda: XpmImagePlugin.XpmImageFile(invalid_file))
|
XpmImagePlugin.XpmImageFile, invalid_file)
|
||||||
|
|
||||||
def test_load_read(self):
|
def test_load_read(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -25,8 +25,7 @@ class TestFileXVThumb(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
XVThumbImagePlugin.XVThumbImageFile, bad_file)
|
||||||
XVThumbImagePlugin.XVThumbImageFile(bad_file))
|
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -34,8 +33,7 @@ class TestFileXVThumb(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(SyntaxError,
|
self.assertRaises(SyntaxError,
|
||||||
lambda:
|
XVThumbImagePlugin.XVThumbImageFile, invalid_file)
|
||||||
XVThumbImagePlugin.XVThumbImageFile(invalid_file))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -9,15 +9,15 @@ class TestFontBdf(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
test_file = open(filename, "rb")
|
with open(filename, "rb") as test_file:
|
||||||
font = BdfFontFile.BdfFontFile(test_file)
|
font = BdfFontFile.BdfFontFile(test_file)
|
||||||
|
|
||||||
self.assertIsInstance(font, FontFile.FontFile)
|
self.assertIsInstance(font, FontFile.FontFile)
|
||||||
self.assertEqual(len([_f for _f in font.glyph if _f]), 190)
|
self.assertEqual(len([_f for _f in font.glyph if _f]), 190)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
self.assertRaises(SyntaxError, lambda: BdfFontFile.BdfFontFile(fp))
|
self.assertRaises(SyntaxError, BdfFontFile.BdfFontFile, fp)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
34
Tests/test_font_leaks.py
Normal file
34
Tests/test_font_leaks.py
Normal 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()
|
|
@ -17,8 +17,8 @@ class TestFontPcf(PillowTestCase):
|
||||||
self.skipTest("zlib support not available")
|
self.skipTest("zlib support not available")
|
||||||
|
|
||||||
def save_font(self):
|
def save_font(self):
|
||||||
test_file = open(fontname, "rb")
|
with open(fontname, "rb") as test_file:
|
||||||
font = PcfFontFile.PcfFontFile(test_file)
|
font = PcfFontFile.PcfFontFile(test_file)
|
||||||
self.assertIsInstance(font, FontFile.FontFile)
|
self.assertIsInstance(font, FontFile.FontFile)
|
||||||
self.assertEqual(len([_f for _f in font.glyph if _f]), 192)
|
self.assertEqual(len([_f for _f in font.glyph if _f]), 192)
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class TestFontPcf(PillowTestCase):
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
with open("Tests/images/flower.jpg", "rb") as fp:
|
with open("Tests/images/flower.jpg", "rb") as fp:
|
||||||
self.assertRaises(SyntaxError, lambda: PcfFontFile.PcfFontFile(fp))
|
self.assertRaises(SyntaxError, PcfFontFile.PcfFontFile, fp)
|
||||||
|
|
||||||
def xtest_draw(self):
|
def xtest_draw(self):
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,9 @@ class TestImage(PillowTestCase):
|
||||||
self.assertEqual(im2.getcolors(), [(10000, 0)])
|
self.assertEqual(im2.getcolors(), [(10000, 0)])
|
||||||
self.assertEqual(im3.getcolors(), [(10000, 0)])
|
self.assertEqual(im3.getcolors(), [(10000, 0)])
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: Image.new("X", (100, 100)))
|
self.assertRaises(ValueError, Image.new, "X", (100, 100))
|
||||||
self.assertRaises(ValueError, lambda: Image.new("", (100, 100)))
|
self.assertRaises(ValueError, Image.new, "", (100, 100))
|
||||||
# self.assertRaises(
|
# self.assertRaises(MemoryError, Image.new, "L", (1000000, 1000000))
|
||||||
# MemoryError, lambda: Image.new("L", (1000000, 1000000)))
|
|
||||||
|
|
||||||
def test_width_height(self):
|
def test_width_height(self):
|
||||||
im = Image.new("RGB", (1, 2))
|
im = Image.new("RGB", (1, 2))
|
||||||
|
@ -68,7 +67,10 @@ class TestImage(PillowTestCase):
|
||||||
else:
|
else:
|
||||||
import io
|
import io
|
||||||
im = io.BytesIO(b'')
|
im = io.BytesIO(b'')
|
||||||
self.assertRaises(IOError, lambda: Image.open(im))
|
self.assertRaises(IOError, Image.open, im)
|
||||||
|
|
||||||
|
def test_bad_mode(self):
|
||||||
|
self.assertRaises(ValueError, Image.open, "filename", "bad mode")
|
||||||
|
|
||||||
@unittest.skipIf(sys.version_info < (3, 4),
|
@unittest.skipIf(sys.version_info < (3, 4),
|
||||||
"pathlib only available in Python 3.4 or later")
|
"pathlib only available in Python 3.4 or later")
|
||||||
|
@ -109,7 +111,7 @@ class TestImage(PillowTestCase):
|
||||||
def test_unknown_extension(self):
|
def test_unknown_extension(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
temp_file = self.tempfile("temp.unknown")
|
temp_file = self.tempfile("temp.unknown")
|
||||||
self.assertRaises(ValueError, lambda: im.save(temp_file))
|
self.assertRaises(ValueError, im.save, temp_file)
|
||||||
|
|
||||||
def test_internals(self):
|
def test_internals(self):
|
||||||
|
|
||||||
|
@ -273,6 +275,20 @@ class TestImage(PillowTestCase):
|
||||||
target.crop((32, 32, 96, 96)))
|
target.crop((32, 32, 96, 96)))
|
||||||
self.assertEqual(source.size, (128, 128))
|
self.assertEqual(source.size, (128, 128))
|
||||||
|
|
||||||
|
# errors
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
source.alpha_composite, over, "invalid source")
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
source.alpha_composite, over, (0, 0), "invalid destination")
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
source.alpha_composite, over, (0))
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
source.alpha_composite, over, (0, 0), (0))
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
source.alpha_composite, over, (0, -1))
|
||||||
|
self.assertRaises(ValueError,
|
||||||
|
source.alpha_composite, over, (0, 0), (0, -1))
|
||||||
|
|
||||||
def test_registered_extensions_uninitialized(self):
|
def test_registered_extensions_uninitialized(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
Image._initialized = 0
|
Image._initialized = 0
|
||||||
|
@ -327,7 +343,7 @@ class TestImage(PillowTestCase):
|
||||||
# Act/Assert
|
# Act/Assert
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ValueError,
|
ValueError,
|
||||||
lambda: Image.effect_mandelbrot(size, extent, quality))
|
Image.effect_mandelbrot, size, extent, quality)
|
||||||
|
|
||||||
def test_effect_noise(self):
|
def test_effect_noise(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -388,7 +404,7 @@ class TestImage(PillowTestCase):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(NotImplementedError, lambda: im.offset(None))
|
self.assertRaises(NotImplementedError, im.offset, None)
|
||||||
|
|
||||||
def test_fromstring(self):
|
def test_fromstring(self):
|
||||||
self.assertRaises(NotImplementedError, Image.fromstring)
|
self.assertRaises(NotImplementedError, Image.fromstring)
|
||||||
|
@ -399,8 +415,7 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
lambda: Image.linear_gradient(wrong_mode))
|
Image.linear_gradient, wrong_mode)
|
||||||
return
|
|
||||||
|
|
||||||
def test_linear_gradient(self):
|
def test_linear_gradient(self):
|
||||||
|
|
||||||
|
@ -425,8 +440,7 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
lambda: Image.radial_gradient(wrong_mode))
|
Image.radial_gradient, wrong_mode)
|
||||||
return
|
|
||||||
|
|
||||||
def test_radial_gradient(self):
|
def test_radial_gradient(self):
|
||||||
|
|
||||||
|
@ -445,6 +459,11 @@ class TestImage(PillowTestCase):
|
||||||
target = Image.open(target_file).convert(mode)
|
target = Image.open(target_file).convert(mode)
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
|
def test_remap_palette(self):
|
||||||
|
# Test illegal image mode
|
||||||
|
im = hopper()
|
||||||
|
self.assertRaises(ValueError, im.remap_palette, None)
|
||||||
|
|
||||||
|
|
||||||
def test__new(self):
|
def test__new(self):
|
||||||
from PIL import ImagePalette
|
from PIL import ImagePalette
|
||||||
|
@ -496,10 +515,10 @@ class TestRegistry(PillowTestCase):
|
||||||
self.assertEqual(enc.args, ('RGB', 'args', 'extra'))
|
self.assertEqual(enc.args, ('RGB', 'args', 'extra'))
|
||||||
|
|
||||||
def test_encode_registry_fail(self):
|
def test_encode_registry_fail(self):
|
||||||
self.assertRaises(IOError, lambda: Image._getencoder('RGB',
|
self.assertRaises(IOError, Image._getencoder, 'RGB',
|
||||||
'DoesNotExist',
|
'DoesNotExist',
|
||||||
('args',),
|
('args',),
|
||||||
extra=('extra',)))
|
extra=('extra',))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -107,7 +107,7 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
p = self.assert_warning(
|
p = self.assert_warning(
|
||||||
UserWarning,
|
UserWarning,
|
||||||
lambda: im.convert('P', palette=Image.ADAPTIVE))
|
im.convert, 'P', palette=Image.ADAPTIVE)
|
||||||
self.assertNotIn('transparency', p.info)
|
self.assertNotIn('transparency', p.info)
|
||||||
p.save(f)
|
p.save(f)
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
p = self.assert_warning(
|
p = self.assert_warning(
|
||||||
UserWarning,
|
UserWarning,
|
||||||
lambda: im.convert('P', palette=Image.ADAPTIVE))
|
im.convert, 'P', palette=Image.ADAPTIVE)
|
||||||
self.assertNotIn('transparency', p.info)
|
self.assertNotIn('transparency', p.info)
|
||||||
p.save(f)
|
p.save(f)
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
lambda: im.convert(mode='CMYK', matrix=matrix))
|
im.convert, mode='CMYK', matrix=matrix)
|
||||||
|
|
||||||
def test_matrix_wrong_mode(self):
|
def test_matrix_wrong_mode(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -168,7 +168,7 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
lambda: im.convert(mode='L', matrix=matrix))
|
im.convert, mode='L', matrix=matrix)
|
||||||
|
|
||||||
def test_matrix_xyz(self):
|
def test_matrix_xyz(self):
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class TestImageFilter(PillowTestCase):
|
||||||
filter(ImageFilter.UnsharpMask)
|
filter(ImageFilter.UnsharpMask)
|
||||||
filter(ImageFilter.UnsharpMask(10))
|
filter(ImageFilter.UnsharpMask(10))
|
||||||
|
|
||||||
self.assertRaises(TypeError, lambda: filter("hello"))
|
self.assertRaises(TypeError, filter, "hello")
|
||||||
|
|
||||||
def test_crash(self):
|
def test_crash(self):
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class TestImageFilter(PillowTestCase):
|
||||||
|
|
||||||
self.assertEqual(rankfilter("1"), (0, 4, 8))
|
self.assertEqual(rankfilter("1"), (0, 4, 8))
|
||||||
self.assertEqual(rankfilter("L"), (0, 4, 8))
|
self.assertEqual(rankfilter("L"), (0, 4, 8))
|
||||||
self.assertRaises(ValueError, lambda: rankfilter("P"))
|
self.assertRaises(ValueError, rankfilter, "P")
|
||||||
self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0)))
|
self.assertEqual(rankfilter("RGB"), ((0, 0, 0), (4, 0, 0), (8, 0, 0)))
|
||||||
self.assertEqual(rankfilter("I"), (0, 4, 8))
|
self.assertEqual(rankfilter("I"), (0, 4, 8))
|
||||||
self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0))
|
self.assertEqual(rankfilter("F"), (0.0, 4.0, 8.0))
|
||||||
|
@ -95,26 +95,38 @@ class TestImageFilter(PillowTestCase):
|
||||||
self.assertEqual(rankfilter.rank, 2)
|
self.assertEqual(rankfilter.rank, 2)
|
||||||
|
|
||||||
def test_consistency_3x3(self):
|
def test_consistency_3x3(self):
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
source = Image.open("Tests/images/hopper.bmp")
|
||||||
emboss = im.filter(ImageFilter.Kernel((3, 3),
|
reference = Image.open("Tests/images/hopper_emboss.bmp")
|
||||||
(-1, -1, 0,
|
kernel = ImageFilter.Kernel((3, 3),
|
||||||
-1, 0, 1,
|
(-1, -1, 0,
|
||||||
0, 1, 1), .3))
|
-1, 0, 1,
|
||||||
|
0, 1, 1), .3)
|
||||||
|
source = source.split() * 2
|
||||||
|
reference = reference.split() * 2
|
||||||
|
|
||||||
self.assert_image_equal(
|
for mode in ['L', 'LA', 'RGB', 'CMYK']:
|
||||||
emboss, Image.open("Tests/images/hopper_emboss.bmp"))
|
self.assert_image_equal(
|
||||||
|
Image.merge(mode, source[:len(mode)]).filter(kernel),
|
||||||
|
Image.merge(mode, reference[:len(mode)]),
|
||||||
|
)
|
||||||
|
|
||||||
def test_consistency_5x5(self):
|
def test_consistency_5x5(self):
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
source = Image.open("Tests/images/hopper.bmp")
|
||||||
emboss = im.filter(ImageFilter.Kernel((5, 5),
|
reference = Image.open("Tests/images/hopper_emboss_more.bmp")
|
||||||
(-1, -1, -1, -1, 0,
|
kernel = ImageFilter.Kernel((5, 5),
|
||||||
-1, -1, -1, 0, 1,
|
(-1, -1, -1, -1, 0,
|
||||||
-1, -1, 0, 1, 1,
|
-1, -1, -1, 0, 1,
|
||||||
-1, 0, 1, 1, 1,
|
-1, -1, 0, 1, 1,
|
||||||
0, 1, 1, 1, 1), 0.3))
|
-1, 0, 1, 1, 1,
|
||||||
|
0, 1, 1, 1, 1), 0.3)
|
||||||
|
source = source.split() * 2
|
||||||
|
reference = reference.split() * 2
|
||||||
|
|
||||||
self.assert_image_equal(
|
for mode in ['L', 'LA', 'RGB', 'CMYK']:
|
||||||
emboss, Image.open("Tests/images/hopper_emboss_more.bmp"))
|
self.assert_image_equal(
|
||||||
|
Image.merge(mode, source[:len(mode)]).filter(kernel),
|
||||||
|
Image.merge(mode, reference[:len(mode)]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -11,6 +11,9 @@ class TestImageFromBytes(PillowTestCase):
|
||||||
|
|
||||||
self.assert_image_equal(im1, im2)
|
self.assert_image_equal(im1, im2)
|
||||||
|
|
||||||
|
def test_not_implemented(self):
|
||||||
|
self.assertRaises(NotImplementedError, Image.fromstring)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -19,7 +19,7 @@ class TestImageLoad(PillowTestCase):
|
||||||
im = Image.open("Tests/images/hopper.gif")
|
im = Image.open("Tests/images/hopper.gif")
|
||||||
im.close()
|
im.close()
|
||||||
self.assertRaises(ValueError, im.load)
|
self.assertRaises(ValueError, im.load)
|
||||||
self.assertRaises(ValueError, lambda: im.getpixel((0, 0)))
|
self.assertRaises(ValueError, im.getpixel, (0, 0))
|
||||||
|
|
||||||
def test_contextmanager(self):
|
def test_contextmanager(self):
|
||||||
fn = None
|
fn = None
|
||||||
|
@ -27,7 +27,7 @@ class TestImageLoad(PillowTestCase):
|
||||||
fn = im.fp.fileno()
|
fn = im.fp.fileno()
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
|
|
||||||
self.assertRaises(OSError, lambda: os.fstat(fn))
|
self.assertRaises(OSError, os.fstat, fn)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -6,17 +6,17 @@ class TestImagePoint(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: im.point(list(range(256))))
|
self.assertRaises(ValueError, im.point, list(range(256)))
|
||||||
im.point(list(range(256))*3)
|
im.point(list(range(256))*3)
|
||||||
im.point(lambda x: x)
|
im.point(lambda x: x)
|
||||||
|
|
||||||
im = im.convert("I")
|
im = im.convert("I")
|
||||||
self.assertRaises(ValueError, lambda: im.point(list(range(256))))
|
self.assertRaises(ValueError, im.point, list(range(256)))
|
||||||
im.point(lambda x: x*1)
|
im.point(lambda x: x*1)
|
||||||
im.point(lambda x: x+1)
|
im.point(lambda x: x+1)
|
||||||
im.point(lambda x: x*1+1)
|
im.point(lambda x: x*1+1)
|
||||||
self.assertRaises(TypeError, lambda: im.point(lambda x: x-1))
|
self.assertRaises(TypeError, im.point, lambda x: x-1)
|
||||||
self.assertRaises(TypeError, lambda: im.point(lambda x: x/1))
|
self.assertRaises(TypeError, im.point, lambda x: x/1)
|
||||||
|
|
||||||
def test_16bit_lut(self):
|
def test_16bit_lut(self):
|
||||||
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
""" Tests for 16 bit -> 8 bit lut for converting I->L images
|
||||||
|
@ -36,7 +36,8 @@ class TestImagePoint(PillowTestCase):
|
||||||
self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L'))
|
self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L'))
|
||||||
|
|
||||||
def test_f_mode(self):
|
def test_f_mode(self):
|
||||||
self.assertRaises(ValueError, lambda: hopper('F').point(None))
|
im = hopper('F')
|
||||||
|
self.assertRaises(ValueError, im.point, None)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -13,14 +13,14 @@ class TestImagePutPalette(PillowTestCase):
|
||||||
if p:
|
if p:
|
||||||
return im.mode, p[:10]
|
return im.mode, p[:10]
|
||||||
return im.mode
|
return im.mode
|
||||||
self.assertRaises(ValueError, lambda: palette("1"))
|
self.assertRaises(ValueError, palette, "1")
|
||||||
self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
self.assertEqual(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
||||||
self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
self.assertEqual(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
|
||||||
self.assertRaises(ValueError, lambda: palette("I"))
|
self.assertRaises(ValueError, palette, "I")
|
||||||
self.assertRaises(ValueError, lambda: palette("F"))
|
self.assertRaises(ValueError, palette, "F")
|
||||||
self.assertRaises(ValueError, lambda: palette("RGB"))
|
self.assertRaises(ValueError, palette, "RGB")
|
||||||
self.assertRaises(ValueError, lambda: palette("RGBA"))
|
self.assertRaises(ValueError, palette, "RGBA")
|
||||||
self.assertRaises(ValueError, lambda: palette("YCbCr"))
|
self.assertRaises(ValueError, palette, "YCbCr")
|
||||||
|
|
||||||
def test_imagepalette(self):
|
def test_imagepalette(self):
|
||||||
im = hopper("P")
|
im = hopper("P")
|
||||||
|
|
|
@ -39,7 +39,7 @@ class TestImageQuantize(PillowTestCase):
|
||||||
def test_rgba_quantize(self):
|
def test_rgba_quantize(self):
|
||||||
image = hopper('RGBA')
|
image = hopper('RGBA')
|
||||||
image.quantize()
|
image.quantize()
|
||||||
self.assertRaises(Exception, lambda: image.quantize(method=0))
|
self.assertRaises(Exception, image.quantize, method=0)
|
||||||
|
|
||||||
def test_quantize(self):
|
def test_quantize(self):
|
||||||
image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB')
|
image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB')
|
||||||
|
|
|
@ -460,7 +460,7 @@ class CoreResampleBoxTest(PillowTestCase):
|
||||||
self.assert_image_similar(cropped, with_box, 0.4)
|
self.assert_image_similar(cropped, with_box, 0.4)
|
||||||
|
|
||||||
def test_passthrough(self):
|
def test_passthrough(self):
|
||||||
"When no resize is required"
|
# When no resize is required
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
for size, box in [
|
for size, box in [
|
||||||
|
@ -478,7 +478,7 @@ class CoreResampleBoxTest(PillowTestCase):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def test_no_passthrough(self):
|
def test_no_passthrough(self):
|
||||||
"When resize is required"
|
# When resize is required
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
for size, box in [
|
for size, box in [
|
||||||
|
@ -498,7 +498,7 @@ class CoreResampleBoxTest(PillowTestCase):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def test_skip_horizontal(self):
|
def test_skip_horizontal(self):
|
||||||
"Can skip resize in one dimension"
|
# Can skip resize for one dimension
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
for flt in [Image.NEAREST, Image.BICUBIC]:
|
for flt in [Image.NEAREST, Image.BICUBIC]:
|
||||||
|
@ -519,7 +519,7 @@ class CoreResampleBoxTest(PillowTestCase):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def test_skip_vertical(self):
|
def test_skip_vertical(self):
|
||||||
"Can skip resize in one dimension"
|
# Can skip resize for one dimension
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
||||||
for flt in [Image.NEAREST, Image.BICUBIC]:
|
for flt in [Image.NEAREST, Image.BICUBIC]:
|
||||||
|
|
|
@ -112,6 +112,10 @@ class TestImageResize(PillowTestCase):
|
||||||
resize(mode, (112, 103))
|
resize(mode, (112, 103))
|
||||||
resize(mode, (188, 214))
|
resize(mode, (188, 214))
|
||||||
|
|
||||||
|
# Test unknown resampling filter
|
||||||
|
im = hopper()
|
||||||
|
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -141,8 +141,8 @@ class TestImageTransform(PillowTestCase):
|
||||||
self.test_mesh()
|
self.test_mesh()
|
||||||
|
|
||||||
def test_missing_method_data(self):
|
def test_missing_method_data(self):
|
||||||
self.assertRaises(ValueError, lambda:
|
im = hopper()
|
||||||
hopper().transform((100, 100), None))
|
self.assertRaises(ValueError, im.transform, (100, 100), None)
|
||||||
|
|
||||||
|
|
||||||
class TestImageTransformAffine(PillowTestCase):
|
class TestImageTransformAffine(PillowTestCase):
|
||||||
|
|
|
@ -144,20 +144,26 @@ class TestImageCms(PillowTestCase):
|
||||||
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
'IEC 61966-2.1 Default RGB colour space - sRGB')
|
||||||
|
|
||||||
def test_exceptions(self):
|
def test_exceptions(self):
|
||||||
|
# Test mode mismatch
|
||||||
|
psRGB = ImageCms.createProfile("sRGB")
|
||||||
|
pLab = ImageCms.createProfile("LAB")
|
||||||
|
t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB")
|
||||||
|
self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA"))
|
||||||
|
|
||||||
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ImageCms.PyCMSError,
|
ImageCms.PyCMSError,
|
||||||
lambda: ImageCms.profileToProfile(hopper(), "foo", "bar"))
|
ImageCms.profileToProfile, hopper(), "foo", "bar")
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ImageCms.PyCMSError,
|
ImageCms.PyCMSError,
|
||||||
lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB"))
|
ImageCms.buildTransform, "foo", "bar", "RGB", "RGB")
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ImageCms.PyCMSError,
|
ImageCms.PyCMSError,
|
||||||
lambda: ImageCms.getProfileName(None))
|
ImageCms.getProfileName, None)
|
||||||
self.skip_missing()
|
self.skip_missing()
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ImageCms.PyCMSError,
|
ImageCms.PyCMSError,
|
||||||
lambda: ImageCms.isIntentSupported(SRGB, None, None))
|
ImageCms.isIntentSupported, SRGB, None, None)
|
||||||
|
|
||||||
def test_display_profile(self):
|
def test_display_profile(self):
|
||||||
# try fetching the profile for the current display device
|
# try fetching the profile for the current display device
|
||||||
|
@ -167,6 +173,14 @@ class TestImageCms(PillowTestCase):
|
||||||
ImageCms.createProfile("LAB", 5000)
|
ImageCms.createProfile("LAB", 5000)
|
||||||
ImageCms.createProfile("LAB", 6500)
|
ImageCms.createProfile("LAB", 6500)
|
||||||
|
|
||||||
|
def test_unsupported_color_space(self):
|
||||||
|
self.assertRaises(ImageCms.PyCMSError,
|
||||||
|
ImageCms.createProfile, "unsupported")
|
||||||
|
|
||||||
|
def test_invalid_color_temperature(self):
|
||||||
|
self.assertRaises(ImageCms.PyCMSError,
|
||||||
|
ImageCms.createProfile, "LAB", "invalid")
|
||||||
|
|
||||||
def test_simple_lab(self):
|
def test_simple_lab(self):
|
||||||
i = Image.new('RGB', (10, 10), (128, 128, 128))
|
i = Image.new('RGB', (10, 10), (128, 128, 128))
|
||||||
|
|
||||||
|
|
|
@ -51,13 +51,11 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.line(((0, 0)), fill=(0, 0, 0))
|
draw.line(((0, 0)), fill=(0, 0, 0))
|
||||||
del draw
|
|
||||||
|
|
||||||
def test_mode_mismatch(self):
|
def test_mode_mismatch(self):
|
||||||
im = hopper("RGB").copy()
|
im = hopper("RGB").copy()
|
||||||
|
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError, ImageDraw.ImageDraw, im, mode="L")
|
||||||
lambda: ImageDraw.ImageDraw(im, mode="L"))
|
|
||||||
|
|
||||||
def helper_arc(self, bbox, start, end):
|
def helper_arc(self, bbox, start, end):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -66,7 +64,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(bbox, start, end)
|
draw.arc(bbox, start, end)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(
|
||||||
|
@ -89,7 +86,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, start=start, end=end)
|
draw.arc(BBOX1, start=start, end=end)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -105,7 +101,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.arc(BBOX1, start=start, end=end)
|
draw.arc(BBOX1, start=start, end=end)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(
|
||||||
|
@ -119,7 +114,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.bitmap((10, 10), small)
|
draw.bitmap((10, 10), small)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -133,7 +127,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
draw.chord(bbox, start, end, fill="red", outline="yellow")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(im, Image.open(expected), 1)
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
|
@ -156,7 +149,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(bbox, fill="green", outline="blue")
|
draw.ellipse(bbox, fill="green", outline="blue")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(im, Image.open(expected), 1)
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
|
@ -176,7 +168,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.ellipse(((0, 0), (W-1, H)), fill="white")
|
draw.ellipse(((0, 0), (W-1, H)), fill="white")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(
|
||||||
|
@ -189,7 +180,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line(points, fill="yellow", width=2)
|
draw.line(points, fill="yellow", width=2)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -217,7 +207,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
s.line(x0, y0)
|
s.line(x0, y0)
|
||||||
|
|
||||||
draw.shape(s, fill=1)
|
draw.shape(s, fill=1)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -239,7 +228,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
s.line(x0, y0)
|
s.line(x0, y0)
|
||||||
|
|
||||||
draw.shape(s, outline="blue")
|
draw.shape(s, outline="blue")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -252,7 +240,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
draw.pieslice(bbox, start, end, fill="white", outline="blue")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(
|
self.assert_image_similar(
|
||||||
|
@ -273,7 +260,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.point(points, fill="yellow")
|
draw.point(points, fill="yellow")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -292,7 +278,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.polygon(points, fill="red", outline="blue")
|
draw.polygon(points, fill="red", outline="blue")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -316,7 +301,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.polygon(KITE_POINTS, fill="blue", outline="yellow")
|
draw.polygon(KITE_POINTS, fill="blue", outline="yellow")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(im, Image.open(expected))
|
self.assert_image_equal(im, Image.open(expected))
|
||||||
|
@ -328,7 +312,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.rectangle(bbox, fill="black", outline="green")
|
draw.rectangle(bbox, fill="black", outline="green")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -350,7 +333,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.rectangle(bbox, fill="orange")
|
draw.rectangle(bbox, fill="orange")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(im, Image.open(expected), 1)
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
|
@ -377,7 +359,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
# Test that filling outside the image does not change the image
|
# Test that filling outside the image does not change the image
|
||||||
ImageDraw.floodfill(im, (W, H), red)
|
ImageDraw.floodfill(im, (W, H), red)
|
||||||
self.assert_image_equal(im, im_floodfill)
|
self.assert_image_equal(im, im_floodfill)
|
||||||
del draw
|
|
||||||
|
|
||||||
@unittest.skipIf(hasattr(sys, 'pypy_version_info'),
|
@unittest.skipIf(hasattr(sys, 'pypy_version_info'),
|
||||||
"Causes fatal RPython error on PyPy")
|
"Causes fatal RPython error on PyPy")
|
||||||
|
@ -394,7 +375,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
ImageDraw.floodfill(
|
ImageDraw.floodfill(
|
||||||
im, centre_point, ImageColor.getrgb("red"),
|
im, centre_point, ImageColor.getrgb("red"),
|
||||||
border=ImageColor.getrgb("black"))
|
border=ImageColor.getrgb("black"))
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -414,7 +394,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
ImageDraw.floodfill(
|
ImageDraw.floodfill(
|
||||||
im, centre_point, ImageColor.getrgb("red"),
|
im, centre_point, ImageColor.getrgb("red"),
|
||||||
thresh=30)
|
thresh=30)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(
|
self.assert_image_equal(
|
||||||
|
@ -577,7 +556,6 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line([(50, 50), (50, 50)], width=3)
|
draw.line([(50, 50), (50, 50)], width=3)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_similar(im, Image.open(expected), 1)
|
self.assert_image_similar(im, Image.open(expected), 1)
|
||||||
|
|
|
@ -69,7 +69,7 @@ class TestImageFile(PillowTestCase):
|
||||||
im1, im2 = roundtrip("JPEG") # lossy compression
|
im1, im2 = roundtrip("JPEG") # lossy compression
|
||||||
self.assert_image(im1, im2.mode, im2.size)
|
self.assert_image(im1, im2.mode, im2.size)
|
||||||
|
|
||||||
self.assertRaises(IOError, lambda: roundtrip("PDF"))
|
self.assertRaises(IOError, roundtrip, "PDF")
|
||||||
|
|
||||||
def test_ico(self):
|
def test_ico(self):
|
||||||
with open('Tests/images/python.ico', 'rb') as f:
|
with open('Tests/images/python.ico', 'rb') as f:
|
||||||
|
@ -93,7 +93,7 @@ class TestImageFile(PillowTestCase):
|
||||||
self.assert_image_equal(im1, im2)
|
self.assert_image_equal(im1, im2)
|
||||||
|
|
||||||
def test_raise_ioerror(self):
|
def test_raise_ioerror(self):
|
||||||
self.assertRaises(IOError, lambda: ImageFile.raise_ioerror(1))
|
self.assertRaises(IOError, ImageFile.raise_ioerror, 1)
|
||||||
|
|
||||||
def test_raise_typeerror(self):
|
def test_raise_typeerror(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
|
@ -182,7 +182,7 @@ class TestPyDecoder(PillowTestCase):
|
||||||
self.assertEqual(d.state.xsize, xsize)
|
self.assertEqual(d.state.xsize, xsize)
|
||||||
self.assertEqual(d.state.ysize, ysize)
|
self.assertEqual(d.state.ysize, ysize)
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: d.set_as_raw(b'\x00'))
|
self.assertRaises(ValueError, d.set_as_raw, b'\x00')
|
||||||
|
|
||||||
def test_extents_none(self):
|
def test_extents_none(self):
|
||||||
buf = BytesIO(b'\x00'*255)
|
buf = BytesIO(b'\x00'*255)
|
||||||
|
|
|
@ -106,7 +106,7 @@ class TestImageFont(PillowTestCase):
|
||||||
# Usage note: making two fonts from the same buffer fails.
|
# Usage note: making two fonts from the same buffer fails.
|
||||||
# shared_bytes = self._font_as_bytes()
|
# shared_bytes = self._font_as_bytes()
|
||||||
# self._render(shared_bytes)
|
# self._render(shared_bytes)
|
||||||
# self.assertRaises(Exception, lambda: _render(shared_bytes))
|
# self.assertRaises(Exception, _render, shared_bytes)
|
||||||
|
|
||||||
def test_font_with_open_file(self):
|
def test_font_with_open_file(self):
|
||||||
with open(FONT_PATH, 'rb') as f:
|
with open(FONT_PATH, 'rb') as f:
|
||||||
|
@ -141,7 +141,6 @@ class TestImageFont(PillowTestCase):
|
||||||
size = draw.textsize(txt, ttf)
|
size = draw.textsize(txt, ttf)
|
||||||
draw.text((10, 10), txt, font=ttf)
|
draw.text((10, 10), txt, font=ttf)
|
||||||
draw.rectangle((10, 10, 10 + size[0], 10 + size[1]))
|
draw.rectangle((10, 10, 10 + size[0], 10 + size[1]))
|
||||||
del draw
|
|
||||||
|
|
||||||
target = 'Tests/images/rectangle_surrounding_text.png'
|
target = 'Tests/images/rectangle_surrounding_text.png'
|
||||||
target_img = Image.open(target)
|
target_img = Image.open(target)
|
||||||
|
@ -188,7 +187,6 @@ class TestImageFont(PillowTestCase):
|
||||||
draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None,
|
draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None,
|
||||||
spacing=4, align="left")
|
spacing=4, align="left")
|
||||||
draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left")
|
draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left")
|
||||||
del draw
|
|
||||||
|
|
||||||
# Test align center and right
|
# Test align center and right
|
||||||
for align, ext in {"center": "_center",
|
for align, ext in {"center": "_center",
|
||||||
|
@ -196,7 +194,6 @@ class TestImageFont(PillowTestCase):
|
||||||
im = Image.new(mode='RGB', size=(300, 100))
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align)
|
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align)
|
||||||
del draw
|
|
||||||
|
|
||||||
target = 'Tests/images/multiline_text'+ext+'.png'
|
target = 'Tests/images/multiline_text'+ext+'.png'
|
||||||
target_img = Image.open(target)
|
target_img = Image.open(target)
|
||||||
|
@ -211,9 +208,9 @@ class TestImageFont(PillowTestCase):
|
||||||
|
|
||||||
# Act/Assert
|
# Act/Assert
|
||||||
self.assertRaises(AssertionError,
|
self.assertRaises(AssertionError,
|
||||||
lambda: draw.multiline_text((0, 0), TEST_TEXT,
|
draw.multiline_text, (0, 0), TEST_TEXT,
|
||||||
font=ttf,
|
font=ttf,
|
||||||
align="unknown"))
|
align="unknown")
|
||||||
|
|
||||||
def test_draw_align(self):
|
def test_draw_align(self):
|
||||||
im = Image.new('RGB', (300, 100), 'white')
|
im = Image.new('RGB', (300, 100), 'white')
|
||||||
|
@ -221,7 +218,6 @@ class TestImageFont(PillowTestCase):
|
||||||
ttf = self.get_font()
|
ttf = self.get_font()
|
||||||
line = "some text"
|
line = "some text"
|
||||||
draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left')
|
draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left')
|
||||||
del draw
|
|
||||||
|
|
||||||
def test_multiline_size(self):
|
def test_multiline_size(self):
|
||||||
ttf = self.get_font()
|
ttf = self.get_font()
|
||||||
|
@ -236,7 +232,6 @@ class TestImageFont(PillowTestCase):
|
||||||
# to multiline_textsize()
|
# to multiline_textsize()
|
||||||
draw.textsize(TEST_TEXT, font=ttf, spacing=4)
|
draw.textsize(TEST_TEXT, font=ttf, spacing=4)
|
||||||
draw.textsize(TEST_TEXT, ttf, 4)
|
draw.textsize(TEST_TEXT, ttf, 4)
|
||||||
del draw
|
|
||||||
|
|
||||||
def test_multiline_width(self):
|
def test_multiline_width(self):
|
||||||
ttf = self.get_font()
|
ttf = self.get_font()
|
||||||
|
@ -246,7 +241,6 @@ class TestImageFont(PillowTestCase):
|
||||||
self.assertEqual(draw.textsize("longest line", font=ttf)[0],
|
self.assertEqual(draw.textsize("longest line", font=ttf)[0],
|
||||||
draw.multiline_textsize("longest line\nline",
|
draw.multiline_textsize("longest line\nline",
|
||||||
font=ttf)[0])
|
font=ttf)[0])
|
||||||
del draw
|
|
||||||
|
|
||||||
def test_multiline_spacing(self):
|
def test_multiline_spacing(self):
|
||||||
ttf = self.get_font()
|
ttf = self.get_font()
|
||||||
|
@ -254,7 +248,6 @@ class TestImageFont(PillowTestCase):
|
||||||
im = Image.new(mode='RGB', size=(300, 100))
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10)
|
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10)
|
||||||
del draw
|
|
||||||
|
|
||||||
target = 'Tests/images/multiline_text_spacing.png'
|
target = 'Tests/images/multiline_text_spacing.png'
|
||||||
target_img = Image.open(target)
|
target_img = Image.open(target)
|
||||||
|
@ -279,7 +272,6 @@ class TestImageFont(PillowTestCase):
|
||||||
# Rotated font
|
# Rotated font
|
||||||
draw.font = transposed_font
|
draw.font = transposed_font
|
||||||
box_size_b = draw.textsize(word)
|
box_size_b = draw.textsize(word)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Check (w,h) of box a is (h,w) of box b
|
# Check (w,h) of box a is (h,w) of box b
|
||||||
self.assertEqual(box_size_a[0], box_size_b[1])
|
self.assertEqual(box_size_a[0], box_size_b[1])
|
||||||
|
@ -302,7 +294,6 @@ class TestImageFont(PillowTestCase):
|
||||||
# Rotated font
|
# Rotated font
|
||||||
draw.font = transposed_font
|
draw.font = transposed_font
|
||||||
box_size_b = draw.textsize(word)
|
box_size_b = draw.textsize(word)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Check boxes a and b are same size
|
# Check boxes a and b are same size
|
||||||
self.assertEqual(box_size_a, box_size_b)
|
self.assertEqual(box_size_a, box_size_b)
|
||||||
|
@ -384,7 +375,7 @@ class TestImageFont(PillowTestCase):
|
||||||
filename = "somefilenamethatdoesntexist.ttf"
|
filename = "somefilenamethatdoesntexist.ttf"
|
||||||
|
|
||||||
# Act/Assert
|
# Act/Assert
|
||||||
self.assertRaises(IOError, lambda: ImageFont.load_path(filename))
|
self.assertRaises(IOError, ImageFont.load_path, filename)
|
||||||
|
|
||||||
def test_default_font(self):
|
def test_default_font(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -398,16 +389,29 @@ class TestImageFont(PillowTestCase):
|
||||||
# Act
|
# Act
|
||||||
default_font = ImageFont.load_default()
|
default_font = ImageFont.load_default()
|
||||||
draw.text((10, 10), txt, font=default_font)
|
draw.text((10, 10), txt, font=default_font)
|
||||||
del draw
|
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assert_image_equal(im, target_img)
|
self.assert_image_equal(im, target_img)
|
||||||
|
|
||||||
def test_getsize_empty(self):
|
def test_getsize_empty(self):
|
||||||
|
# issue #2614
|
||||||
font = self.get_font()
|
font = self.get_font()
|
||||||
# should not crash.
|
# should not crash.
|
||||||
self.assertEqual((0, 0), font.getsize(''))
|
self.assertEqual((0, 0), font.getsize(''))
|
||||||
|
|
||||||
|
def test_render_empty(self):
|
||||||
|
# issue 2666
|
||||||
|
font = self.get_font()
|
||||||
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
|
target = im.copy()
|
||||||
|
draw = ImageDraw.Draw(im)
|
||||||
|
#should not crash here.
|
||||||
|
draw.text((10, 10), '', font=font)
|
||||||
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _test_fake_loading_font(self, path_to_fake, fontname):
|
def _test_fake_loading_font(self, path_to_fake, fontname):
|
||||||
# Make a copy of FreeTypeFont so we can patch the original
|
# Make a copy of FreeTypeFont so we can patch the original
|
||||||
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
|
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
|
||||||
|
|
|
@ -81,9 +81,9 @@ class MorphTests(PillowTestCase):
|
||||||
|
|
||||||
def test_no_operator_loaded(self):
|
def test_no_operator_loaded(self):
|
||||||
mop = ImageMorph.MorphOp()
|
mop = ImageMorph.MorphOp()
|
||||||
self.assertRaises(Exception, lambda: mop.apply(None))
|
self.assertRaises(Exception, mop.apply, None)
|
||||||
self.assertRaises(Exception, lambda: mop.match(None))
|
self.assertRaises(Exception, mop.match, None)
|
||||||
self.assertRaises(Exception, lambda: mop.save_lut(None))
|
self.assertRaises(Exception, mop.save_lut, None)
|
||||||
|
|
||||||
# Test the named patterns
|
# Test the named patterns
|
||||||
def test_erosion8(self):
|
def test_erosion8(self):
|
||||||
|
@ -214,9 +214,9 @@ class MorphTests(PillowTestCase):
|
||||||
im = hopper('RGB')
|
im = hopper('RGB')
|
||||||
mop = ImageMorph.MorphOp(op_name="erosion8")
|
mop = ImageMorph.MorphOp(op_name="erosion8")
|
||||||
|
|
||||||
self.assertRaises(Exception, lambda: mop.apply(im))
|
self.assertRaises(Exception, mop.apply, im)
|
||||||
self.assertRaises(Exception, lambda: mop.match(im))
|
self.assertRaises(Exception, mop.match, im)
|
||||||
self.assertRaises(Exception, lambda: mop.get_on_pixels(im))
|
self.assertRaises(Exception, mop.get_on_pixels, im)
|
||||||
|
|
||||||
def test_add_patterns(self):
|
def test_add_patterns(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -240,7 +240,7 @@ class MorphTests(PillowTestCase):
|
||||||
def test_unknown_pattern(self):
|
def test_unknown_pattern(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
Exception,
|
Exception,
|
||||||
lambda: ImageMorph.LutBuilder(op_name='unknown'))
|
ImageMorph.LutBuilder, op_name='unknown')
|
||||||
|
|
||||||
def test_pattern_syntax_error(self):
|
def test_pattern_syntax_error(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -249,9 +249,7 @@ class MorphTests(PillowTestCase):
|
||||||
lb.add_patterns(new_patterns)
|
lb.add_patterns(new_patterns)
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(
|
self.assertRaises(Exception, lb.build_lut)
|
||||||
Exception,
|
|
||||||
lambda: lb.build_lut())
|
|
||||||
|
|
||||||
def test_load_invalid_mrl(self):
|
def test_load_invalid_mrl(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -259,7 +257,7 @@ class MorphTests(PillowTestCase):
|
||||||
mop = ImageMorph.MorphOp()
|
mop = ImageMorph.MorphOp()
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(Exception, lambda: mop.load_lut(invalid_mrl))
|
self.assertRaises(Exception, mop.load_lut, invalid_mrl)
|
||||||
|
|
||||||
def test_roundtrip_mrl(self):
|
def test_roundtrip_mrl(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -37,26 +37,26 @@ class TestImageOpsUsm(PillowTestCase):
|
||||||
def test_usm_formats(self):
|
def test_usm_formats(self):
|
||||||
|
|
||||||
usm = ImageOps.unsharp_mask
|
usm = ImageOps.unsharp_mask
|
||||||
self.assertRaises(ValueError, lambda: usm(im.convert("1")))
|
self.assertRaises(ValueError, usm, im.convert("1"))
|
||||||
usm(im.convert("L"))
|
usm(im.convert("L"))
|
||||||
self.assertRaises(ValueError, lambda: usm(im.convert("I")))
|
self.assertRaises(ValueError, usm, im.convert("I"))
|
||||||
self.assertRaises(ValueError, lambda: usm(im.convert("F")))
|
self.assertRaises(ValueError, usm, im.convert("F"))
|
||||||
usm(im.convert("RGB"))
|
usm(im.convert("RGB"))
|
||||||
usm(im.convert("RGBA"))
|
usm(im.convert("RGBA"))
|
||||||
usm(im.convert("CMYK"))
|
usm(im.convert("CMYK"))
|
||||||
self.assertRaises(ValueError, lambda: usm(im.convert("YCbCr")))
|
self.assertRaises(ValueError, usm, im.convert("YCbCr"))
|
||||||
|
|
||||||
def test_blur_formats(self):
|
def test_blur_formats(self):
|
||||||
|
|
||||||
blur = ImageOps.gaussian_blur
|
blur = ImageOps.gaussian_blur
|
||||||
self.assertRaises(ValueError, lambda: blur(im.convert("1")))
|
self.assertRaises(ValueError, blur, im.convert("1"))
|
||||||
blur(im.convert("L"))
|
blur(im.convert("L"))
|
||||||
self.assertRaises(ValueError, lambda: blur(im.convert("I")))
|
self.assertRaises(ValueError, blur, im.convert("I"))
|
||||||
self.assertRaises(ValueError, lambda: blur(im.convert("F")))
|
self.assertRaises(ValueError, blur, im.convert("F"))
|
||||||
blur(im.convert("RGB"))
|
blur(im.convert("RGB"))
|
||||||
blur(im.convert("RGBA"))
|
blur(im.convert("RGBA"))
|
||||||
blur(im.convert("CMYK"))
|
blur(im.convert("CMYK"))
|
||||||
self.assertRaises(ValueError, lambda: blur(im.convert("YCbCr")))
|
self.assertRaises(ValueError, blur, im.convert("YCbCr"))
|
||||||
|
|
||||||
def test_usm_accuracy(self):
|
def test_usm_accuracy(self):
|
||||||
|
|
||||||
|
|
|
@ -2,56 +2,54 @@ from helper import unittest, PillowTestCase
|
||||||
|
|
||||||
from PIL import ImagePalette, Image
|
from PIL import ImagePalette, Image
|
||||||
|
|
||||||
ImagePalette = ImagePalette.ImagePalette
|
|
||||||
|
|
||||||
|
|
||||||
class TestImagePalette(PillowTestCase):
|
class TestImagePalette(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
ImagePalette("RGB", list(range(256))*3)
|
ImagePalette.ImagePalette("RGB", list(range(256))*3)
|
||||||
self.assertRaises(
|
self.assertRaises(ValueError,
|
||||||
ValueError, lambda: ImagePalette("RGB", list(range(256))*2))
|
ImagePalette.ImagePalette, "RGB", list(range(256))*2)
|
||||||
|
|
||||||
def test_getcolor(self):
|
def test_getcolor(self):
|
||||||
|
|
||||||
palette = ImagePalette()
|
palette = ImagePalette.ImagePalette()
|
||||||
|
|
||||||
test_map = {}
|
test_map = {}
|
||||||
for i in range(256):
|
for i in range(256):
|
||||||
test_map[palette.getcolor((i, i, i))] = i
|
test_map[palette.getcolor((i, i, i))] = i
|
||||||
|
|
||||||
self.assertEqual(len(test_map), 256)
|
self.assertEqual(len(test_map), 256)
|
||||||
self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3)))
|
self.assertRaises(ValueError, palette.getcolor, (1, 2, 3))
|
||||||
|
|
||||||
|
# Test unknown color specifier
|
||||||
|
self.assertRaises(ValueError, palette.getcolor, "unknown")
|
||||||
|
|
||||||
def test_file(self):
|
def test_file(self):
|
||||||
|
|
||||||
palette = ImagePalette("RGB", list(range(256))*3)
|
palette = ImagePalette.ImagePalette("RGB", list(range(256))*3)
|
||||||
|
|
||||||
f = self.tempfile("temp.lut")
|
f = self.tempfile("temp.lut")
|
||||||
|
|
||||||
palette.save(f)
|
palette.save(f)
|
||||||
|
|
||||||
from PIL.ImagePalette import load, raw
|
p = ImagePalette.load(f)
|
||||||
|
|
||||||
p = load(f)
|
|
||||||
|
|
||||||
# load returns raw palette information
|
# load returns raw palette information
|
||||||
self.assertEqual(len(p[0]), 768)
|
self.assertEqual(len(p[0]), 768)
|
||||||
self.assertEqual(p[1], "RGB")
|
self.assertEqual(p[1], "RGB")
|
||||||
|
|
||||||
p = raw(p[1], p[0])
|
p = ImagePalette.raw(p[1], p[0])
|
||||||
self.assertIsInstance(p, ImagePalette)
|
self.assertIsInstance(p, ImagePalette.ImagePalette)
|
||||||
self.assertEqual(p.palette, palette.tobytes())
|
self.assertEqual(p.palette, palette.tobytes())
|
||||||
|
|
||||||
def test_make_linear_lut(self):
|
def test_make_linear_lut(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
from PIL.ImagePalette import make_linear_lut
|
|
||||||
black = 0
|
black = 0
|
||||||
white = 255
|
white = 255
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
lut = make_linear_lut(black, white)
|
lut = ImagePalette.make_linear_lut(black, white)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(lut, list)
|
self.assertIsInstance(lut, list)
|
||||||
|
@ -63,22 +61,19 @@ class TestImagePalette(PillowTestCase):
|
||||||
def test_make_linear_lut_not_yet_implemented(self):
|
def test_make_linear_lut_not_yet_implemented(self):
|
||||||
# Update after FIXME
|
# Update after FIXME
|
||||||
# Arrange
|
# Arrange
|
||||||
from PIL.ImagePalette import make_linear_lut
|
|
||||||
black = 1
|
black = 1
|
||||||
white = 255
|
white = 255
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
self.assertRaises(
|
self.assertRaises(NotImplementedError,
|
||||||
NotImplementedError,
|
ImagePalette.make_linear_lut, black, white)
|
||||||
lambda: make_linear_lut(black, white))
|
|
||||||
|
|
||||||
def test_make_gamma_lut(self):
|
def test_make_gamma_lut(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
from PIL.ImagePalette import make_gamma_lut
|
|
||||||
exp = 5
|
exp = 5
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
lut = make_gamma_lut(exp)
|
lut = ImagePalette.make_gamma_lut(exp)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(lut, list)
|
self.assertIsInstance(lut, list)
|
||||||
|
@ -92,19 +87,18 @@ class TestImagePalette(PillowTestCase):
|
||||||
|
|
||||||
def test_rawmode_valueerrors(self):
|
def test_rawmode_valueerrors(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
from PIL.ImagePalette import raw
|
palette = ImagePalette.raw("RGB", list(range(256))*3)
|
||||||
palette = raw("RGB", list(range(256))*3)
|
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(ValueError, palette.tobytes)
|
self.assertRaises(ValueError, palette.tobytes)
|
||||||
self.assertRaises(ValueError, lambda: palette.getcolor((1, 2, 3)))
|
self.assertRaises(ValueError, palette.getcolor, (1, 2, 3))
|
||||||
f = self.tempfile("temp.lut")
|
f = self.tempfile("temp.lut")
|
||||||
self.assertRaises(ValueError, lambda: palette.save(f))
|
self.assertRaises(ValueError, palette.save, f)
|
||||||
|
|
||||||
def test_getdata(self):
|
def test_getdata(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
data_in = list(range(256))*3
|
data_in = list(range(256))*3
|
||||||
palette = ImagePalette("RGB", data_in)
|
palette = ImagePalette.ImagePalette("RGB", data_in)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
mode, data_out = palette.getdata()
|
mode, data_out = palette.getdata()
|
||||||
|
@ -114,9 +108,8 @@ class TestImagePalette(PillowTestCase):
|
||||||
|
|
||||||
def test_rawmode_getdata(self):
|
def test_rawmode_getdata(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
from PIL.ImagePalette import raw
|
|
||||||
data_in = list(range(256))*3
|
data_in = list(range(256))*3
|
||||||
palette = raw("RGB", data_in)
|
palette = ImagePalette.raw("RGB", data_in)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
rawmode, data_out = palette.getdata()
|
rawmode, data_out = palette.getdata()
|
||||||
|
@ -138,6 +131,10 @@ class TestImagePalette(PillowTestCase):
|
||||||
|
|
||||||
self.assert_image_equal(img, reloaded)
|
self.assert_image_equal(img, reloaded)
|
||||||
|
|
||||||
|
def test_invalid_palette(self):
|
||||||
|
self.assertRaises(IOError,
|
||||||
|
ImagePalette.load, "Tests/images/hopper.jpg")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -22,7 +22,7 @@ class TestImageSequence(PillowTestCase):
|
||||||
|
|
||||||
self.assertEqual(index, 1)
|
self.assertEqual(index, 1)
|
||||||
|
|
||||||
self.assertRaises(AttributeError, lambda: ImageSequence.Iterator(0))
|
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
|
||||||
|
|
||||||
def test_iterator(self):
|
def test_iterator(self):
|
||||||
im = Image.open('Tests/images/multipage.tiff')
|
im = Image.open('Tests/images/multipage.tiff')
|
||||||
|
@ -30,7 +30,7 @@ class TestImageSequence(PillowTestCase):
|
||||||
for index in range(0, im.n_frames):
|
for index in range(0, im.n_frames):
|
||||||
self.assertEqual(i[index], next(i))
|
self.assertEqual(i[index], next(i))
|
||||||
self.assertRaises(IndexError, lambda: i[index+1])
|
self.assertRaises(IndexError, lambda: i[index+1])
|
||||||
self.assertRaises(StopIteration, lambda: next(i))
|
self.assertRaises(StopIteration, next, i)
|
||||||
|
|
||||||
def _test_multipage_tiff(self):
|
def _test_multipage_tiff(self):
|
||||||
im = Image.open('Tests/images/multipage.tiff')
|
im = Image.open('Tests/images/multipage.tiff')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from helper import unittest, PillowTestCase
|
from helper import unittest, PillowTestCase, hopper
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL import ImageShow
|
from PIL import ImageShow
|
||||||
|
@ -10,12 +10,30 @@ class TestImageShow(PillowTestCase):
|
||||||
dir(Image)
|
dir(Image)
|
||||||
dir(ImageShow)
|
dir(ImageShow)
|
||||||
|
|
||||||
|
def test_register(self):
|
||||||
|
# Test registering a viewer that is not a class
|
||||||
|
ImageShow.register("not a class")
|
||||||
|
|
||||||
|
def test_show(self):
|
||||||
|
class TestViewer:
|
||||||
|
methodCalled = False
|
||||||
|
|
||||||
|
def show(self, image, title=None, **options):
|
||||||
|
self.methodCalled = True
|
||||||
|
return True
|
||||||
|
viewer = TestViewer()
|
||||||
|
ImageShow.register(viewer, -1)
|
||||||
|
|
||||||
|
im = hopper()
|
||||||
|
self.assertTrue(ImageShow.show(im))
|
||||||
|
self.assertTrue(viewer.methodCalled)
|
||||||
|
|
||||||
def test_viewer(self):
|
def test_viewer(self):
|
||||||
viewer = ImageShow.Viewer()
|
viewer = ImageShow.Viewer()
|
||||||
|
|
||||||
self.assertIsNone(viewer.get_format(None))
|
self.assertIsNone(viewer.get_format(None))
|
||||||
|
|
||||||
self.assertRaises(NotImplementedError, lambda: viewer.get_command(None))
|
self.assertRaises(NotImplementedError, viewer.get_command, None)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -26,7 +26,7 @@ class TestImageStat(PillowTestCase):
|
||||||
|
|
||||||
self.assertRaises(AttributeError, lambda: st.spam)
|
self.assertRaises(AttributeError, lambda: st.spam)
|
||||||
|
|
||||||
self.assertRaises(TypeError, lambda: ImageStat.Stat(1))
|
self.assertRaises(TypeError, ImageStat.Stat, 1)
|
||||||
|
|
||||||
def test_hopper(self):
|
def test_hopper(self):
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@ class TestLibImage(PillowTestCase):
|
||||||
im.im.setmode("RGB")
|
im.im.setmode("RGB")
|
||||||
self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3))
|
self.assertEqual(im.im.getpixel((0, 0)), (1, 2, 3))
|
||||||
|
|
||||||
self.assertRaises(ValueError, lambda: im.im.setmode("L"))
|
self.assertRaises(ValueError, im.im.setmode, "L")
|
||||||
self.assertRaises(ValueError, lambda: im.im.setmode("RGBABCDE"))
|
self.assertRaises(ValueError, im.im.setmode, "RGBABCDE")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -86,12 +86,12 @@ class TestNumpy(PillowTestCase):
|
||||||
self.assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE)
|
self.assert_image(to_image(numpy.int32), "I", TEST_IMAGE_SIZE)
|
||||||
|
|
||||||
# Check 64-bit integer formats
|
# Check 64-bit integer formats
|
||||||
self.assertRaises(TypeError, lambda: to_image(numpy.uint64))
|
self.assertRaises(TypeError, to_image, numpy.uint64)
|
||||||
self.assertRaises(TypeError, lambda: to_image(numpy.int64))
|
self.assertRaises(TypeError, to_image, numpy.int64)
|
||||||
|
|
||||||
# Check floating-point formats
|
# Check floating-point formats
|
||||||
self.assert_image(to_image(numpy.float), "F", TEST_IMAGE_SIZE)
|
self.assert_image(to_image(numpy.float), "F", TEST_IMAGE_SIZE)
|
||||||
self.assertRaises(TypeError, lambda: to_image(numpy.float16))
|
self.assertRaises(TypeError, to_image, numpy.float16)
|
||||||
self.assert_image(to_image(numpy.float32), "F", TEST_IMAGE_SIZE)
|
self.assert_image(to_image(numpy.float32), "F", TEST_IMAGE_SIZE)
|
||||||
self.assert_image(to_image(numpy.float64), "F", TEST_IMAGE_SIZE)
|
self.assert_image(to_image(numpy.float64), "F", TEST_IMAGE_SIZE)
|
||||||
|
|
||||||
|
|
43
_imaging.c
43
_imaging.c
|
@ -819,7 +819,7 @@ _filter(ImagingObject* self, PyObject* args)
|
||||||
Py_ssize_t kernelsize;
|
Py_ssize_t kernelsize;
|
||||||
FLOAT32* kerneldata;
|
FLOAT32* kerneldata;
|
||||||
|
|
||||||
int xsize, ysize;
|
int xsize, ysize, i;
|
||||||
float divisor, offset;
|
float divisor, offset;
|
||||||
PyObject* kernel = NULL;
|
PyObject* kernel = NULL;
|
||||||
if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize,
|
if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize,
|
||||||
|
@ -835,8 +835,12 @@ _filter(ImagingObject* self, PyObject* args)
|
||||||
return ImagingError_ValueError("bad kernel size");
|
return ImagingError_ValueError("bad kernel size");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < kernelsize; ++i) {
|
||||||
|
kerneldata[i] /= divisor;
|
||||||
|
}
|
||||||
|
|
||||||
imOut = PyImagingNew(
|
imOut = PyImagingNew(
|
||||||
ImagingFilter(self->image, xsize, ysize, kerneldata, offset, divisor)
|
ImagingFilter(self->image, xsize, ysize, kerneldata, offset)
|
||||||
);
|
);
|
||||||
|
|
||||||
free(kerneldata);
|
free(kerneldata);
|
||||||
|
@ -2198,26 +2202,45 @@ textwidth(ImagingFontObject* self, const unsigned char* text)
|
||||||
}
|
}
|
||||||
|
|
||||||
void _font_text_asBytes(PyObject* encoded_string, unsigned char** text){
|
void _font_text_asBytes(PyObject* encoded_string, unsigned char** text){
|
||||||
|
/* Allocates *text, returns a 'new reference'. Caller is required to free */
|
||||||
|
|
||||||
PyObject* bytes = NULL;
|
PyObject* bytes = NULL;
|
||||||
|
Py_ssize_t len = 0;
|
||||||
|
char *buffer;
|
||||||
|
|
||||||
*text = NULL;
|
*text = NULL;
|
||||||
|
|
||||||
if (PyUnicode_CheckExact(encoded_string)){
|
if (PyUnicode_CheckExact(encoded_string)){
|
||||||
bytes = PyUnicode_AsLatin1String(encoded_string);
|
bytes = PyUnicode_AsLatin1String(encoded_string);
|
||||||
|
PyBytes_AsStringAndSize(bytes, &buffer, &len);
|
||||||
} else if (PyBytes_Check(encoded_string)) {
|
} else if (PyBytes_Check(encoded_string)) {
|
||||||
bytes = encoded_string;
|
PyBytes_AsStringAndSize(encoded_string, &buffer, &len);
|
||||||
}
|
}
|
||||||
if (bytes) {
|
|
||||||
*text = (unsigned char*)PyBytes_AsString(bytes);
|
if (len) {
|
||||||
|
*text = calloc(len,1);
|
||||||
|
if (*text) {
|
||||||
|
memcpy(*text, buffer, len);
|
||||||
|
}
|
||||||
|
if(bytes) {
|
||||||
|
Py_DECREF(bytes);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if PY_VERSION_HEX < 0x03000000
|
#if PY_VERSION_HEX < 0x03000000
|
||||||
/* likely case here is py2.x with an ordinary string.
|
/* likely case here is py2.x with an ordinary string.
|
||||||
but this isn't defined in Py3.x */
|
but this isn't defined in Py3.x */
|
||||||
if (PyString_Check(encoded_string)) {
|
if (PyString_Check(encoded_string)) {
|
||||||
*text = (unsigned char *)PyString_AsString(encoded_string);
|
PyString_AsStringAndSize(encoded_string, &buffer, &len);
|
||||||
|
*text = calloc(len,1);
|
||||||
|
if (*text) {
|
||||||
|
memcpy(*text, buffer, len);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2248,6 +2271,7 @@ _font_getmask(ImagingFontObject* self, PyObject* args)
|
||||||
|
|
||||||
im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize);
|
im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize);
|
||||||
if (!im) {
|
if (!im) {
|
||||||
|
free(text);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2273,9 +2297,11 @@ _font_getmask(ImagingFontObject* self, PyObject* args)
|
||||||
x = x + glyph->dx;
|
x = x + glyph->dx;
|
||||||
b = b + glyph->dy;
|
b = b + glyph->dy;
|
||||||
}
|
}
|
||||||
|
free(text);
|
||||||
return PyImagingNew(im);
|
return PyImagingNew(im);
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
|
free(text);
|
||||||
ImagingDelete(im);
|
ImagingDelete(im);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -2285,6 +2311,7 @@ _font_getsize(ImagingFontObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
unsigned char* text;
|
unsigned char* text;
|
||||||
PyObject* encoded_string;
|
PyObject* encoded_string;
|
||||||
|
PyObject* val;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string))
|
if (!PyArg_ParseTuple(args, "O:getsize", &encoded_string))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2294,7 +2321,9 @@ _font_getsize(ImagingFontObject* self, PyObject* args)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Py_BuildValue("ii", textwidth(self, text), self->ysize);
|
val = Py_BuildValue("ii", textwidth(self, text), self->ysize);
|
||||||
|
free(text);
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct PyMethodDef _font_methods[] = {
|
static struct PyMethodDef _font_methods[] = {
|
||||||
|
|
22
_imagingft.c
22
_imagingft.c
|
@ -225,6 +225,11 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
||||||
if (PyUnicode_Check(string)) {
|
if (PyUnicode_Check(string)) {
|
||||||
Py_UNICODE *text = PyUnicode_AS_UNICODE(string);
|
Py_UNICODE *text = PyUnicode_AS_UNICODE(string);
|
||||||
Py_ssize_t size = PyUnicode_GET_SIZE(string);
|
Py_ssize_t size = PyUnicode_GET_SIZE(string);
|
||||||
|
if (! size) {
|
||||||
|
/* return 0 and clean up, no glyphs==no size,
|
||||||
|
and raqm fails with empty strings */
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
if (!raqm_set_text(rq, (const uint32_t *)(text), size)) {
|
if (!raqm_set_text(rq, (const uint32_t *)(text), size)) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
|
@ -234,6 +239,9 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
||||||
else if (PyString_Check(string)) {
|
else if (PyString_Check(string)) {
|
||||||
char *text = PyString_AS_STRING(string);
|
char *text = PyString_AS_STRING(string);
|
||||||
int size = PyString_GET_SIZE(string);
|
int size = PyString_GET_SIZE(string);
|
||||||
|
if (! size) {
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
if (!raqm_set_text_utf8(rq, text, size)) {
|
if (!raqm_set_text_utf8(rq, text, size)) {
|
||||||
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
|
PyErr_SetString(PyExc_ValueError, "raqm_set_text_utf8() failed");
|
||||||
goto failed;
|
goto failed;
|
||||||
|
@ -450,6 +458,10 @@ font_getsize(FontObject* self, PyObject* args)
|
||||||
y_max = y_min = 0;
|
y_max = y_min = 0;
|
||||||
|
|
||||||
count = text_layout(string, self, dir, features, &glyph_info, 0);
|
count = text_layout(string, self, dir, features, &glyph_info, 0);
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
for (x = i = 0; i < count; i++) {
|
for (x = i = 0; i < count; i++) {
|
||||||
int index, error;
|
int index, error;
|
||||||
|
@ -497,6 +509,11 @@ font_getsize(FontObject* self, PyObject* args)
|
||||||
FT_Done_Glyph(glyph);
|
FT_Done_Glyph(glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (glyph_info) {
|
||||||
|
PyMem_Free(glyph_info);
|
||||||
|
glyph_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (face) {
|
if (face) {
|
||||||
|
|
||||||
/* left bearing */
|
/* left bearing */
|
||||||
|
@ -584,9 +601,12 @@ font_render(FontObject* self, PyObject* args)
|
||||||
|
|
||||||
glyph_info = NULL;
|
glyph_info = NULL;
|
||||||
count = text_layout(string, self, dir, features, &glyph_info, mask);
|
count = text_layout(string, self, dir, features, &glyph_info, mask);
|
||||||
if (count == 0) {
|
if (PyErr_Occurred()) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (count == 0) {
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
im = (Imaging) id;
|
im = (Imaging) id;
|
||||||
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
|
/* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */
|
||||||
|
|
83
display.c
83
display.c
|
@ -452,84 +452,9 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
|
||||||
void* data;
|
void* data;
|
||||||
PyObject* result;
|
PyObject* result;
|
||||||
|
|
||||||
int verbose = 0; /* debugging; will be removed in future versions */
|
|
||||||
if (!PyArg_ParseTuple(args, "|i", &verbose))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
|
|
||||||
clip = OpenClipboard(NULL);
|
clip = OpenClipboard(NULL);
|
||||||
/* FIXME: check error status */
|
/* FIXME: check error status */
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
UINT format = EnumClipboardFormats(0);
|
|
||||||
char buffer[200];
|
|
||||||
char* result;
|
|
||||||
while (format != 0) {
|
|
||||||
if (GetClipboardFormatName(format, buffer, sizeof buffer) > 0)
|
|
||||||
result = buffer;
|
|
||||||
else
|
|
||||||
switch (format) {
|
|
||||||
case CF_BITMAP:
|
|
||||||
result = "CF_BITMAP";
|
|
||||||
break;
|
|
||||||
case CF_DIB:
|
|
||||||
result = "CF_DIB";
|
|
||||||
break;
|
|
||||||
case CF_DIF:
|
|
||||||
result = "CF_DIF";
|
|
||||||
break;
|
|
||||||
case CF_ENHMETAFILE:
|
|
||||||
result = "CF_ENHMETAFILE";
|
|
||||||
break;
|
|
||||||
case CF_HDROP:
|
|
||||||
result = "CF_HDROP";
|
|
||||||
break;
|
|
||||||
case CF_LOCALE:
|
|
||||||
result = "CF_LOCALE";
|
|
||||||
break;
|
|
||||||
case CF_METAFILEPICT:
|
|
||||||
result = "CF_METAFILEPICT";
|
|
||||||
break;
|
|
||||||
case CF_OEMTEXT:
|
|
||||||
result = "CF_OEMTEXT";
|
|
||||||
break;
|
|
||||||
case CF_OWNERDISPLAY:
|
|
||||||
result = "CF_OWNERDISPLAY";
|
|
||||||
break;
|
|
||||||
case CF_PALETTE:
|
|
||||||
result = "CF_PALETTE";
|
|
||||||
break;
|
|
||||||
case CF_PENDATA:
|
|
||||||
result = "CF_PENDATA";
|
|
||||||
break;
|
|
||||||
case CF_RIFF:
|
|
||||||
result = "CF_RIFF";
|
|
||||||
break;
|
|
||||||
case CF_SYLK:
|
|
||||||
result = "CF_SYLK";
|
|
||||||
break;
|
|
||||||
case CF_TEXT:
|
|
||||||
result = "CF_TEXT";
|
|
||||||
break;
|
|
||||||
case CF_WAVE:
|
|
||||||
result = "CF_WAVE";
|
|
||||||
break;
|
|
||||||
case CF_TIFF:
|
|
||||||
result = "CF_TIFF";
|
|
||||||
break;
|
|
||||||
case CF_UNICODETEXT:
|
|
||||||
result = "CF_UNICODETEXT";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sprintf(buffer, "[%d]", format);
|
|
||||||
result = buffer;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf("%s (%d)\n", result, format);
|
|
||||||
format = EnumClipboardFormats(format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handle = GetClipboardData(CF_DIB);
|
handle = GetClipboardData(CF_DIB);
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
/* FIXME: add CF_HDROP support to allow cut-and-paste from
|
/* FIXME: add CF_HDROP support to allow cut-and-paste from
|
||||||
|
@ -542,14 +467,6 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
|
||||||
size = GlobalSize(handle);
|
size = GlobalSize(handle);
|
||||||
data = GlobalLock(handle);
|
data = GlobalLock(handle);
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* calculate proper size for string formats */
|
|
||||||
if (format == CF_TEXT || format == CF_OEMTEXT)
|
|
||||||
size = strlen(data);
|
|
||||||
else if (format == CF_UNICODETEXT)
|
|
||||||
size = wcslen(data) * 2;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
result = PyBytes_FromStringAndSize(data, size);
|
result = PyBytes_FromStringAndSize(data, size);
|
||||||
|
|
||||||
GlobalUnlock(handle);
|
GlobalUnlock(handle);
|
||||||
|
|
|
@ -178,8 +178,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
**sizes**
|
**sizes**
|
||||||
A list of sizes including in this ico file; these are a 2-tuple,
|
A list of sizes including in this ico file; these are a 2-tuple,
|
||||||
``(width, height)``; Default to ``[(16, 16), (24, 24), (32, 32), (48, 48),
|
``(width, height)``; Default to ``[(16, 16), (24, 24), (32, 32), (48, 48),
|
||||||
(64, 64), (128, 128), (255, 255)]``. Any sizes bigger than the original
|
(64, 64), (128, 128), (256, 256)]``. Any sizes bigger than the original
|
||||||
size or 255 will be ignored.
|
size or 256 will be ignored.
|
||||||
|
|
||||||
IM
|
IM
|
||||||
^^
|
^^
|
||||||
|
|
|
@ -262,6 +262,7 @@ The easiest way to install external libraries is via `Homebrew
|
||||||
$ brew install libtiff libjpeg webp little-cms2
|
$ brew install libtiff libjpeg webp little-cms2
|
||||||
|
|
||||||
To install libraqm on macOS use Homebrew to install its dependencies::
|
To install libraqm on macOS use Homebrew to install its dependencies::
|
||||||
|
|
||||||
$ brew install freetype harfbuzz fribidi
|
$ brew install freetype harfbuzz fribidi
|
||||||
|
|
||||||
Then see ``depends/install_raqm_cmake.sh`` to install libraqm.
|
Then see ``depends/install_raqm_cmake.sh`` to install libraqm.
|
||||||
|
|
|
@ -265,7 +265,7 @@ can be easily displayed in a chromaticity diagram, for example).
|
||||||
Calculates the white point temperature (see the LCMS documentation
|
Calculates the white point temperature (see the LCMS documentation
|
||||||
for more information).
|
for more information).
|
||||||
|
|
||||||
:type: :py:class:`float` or `None`
|
:type: :py:class:`float` or ``None``
|
||||||
|
|
||||||
.. py:attribute:: viewing_condition
|
.. py:attribute:: viewing_condition
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,49 @@ Get One Channel From Image
|
||||||
|
|
||||||
New method :py:meth:`PIL.Image.Image.getchannel` added.
|
New method :py:meth:`PIL.Image.Image.getchannel` added.
|
||||||
It returns single channel by index or name. For example,
|
It returns single channel by index or name. For example,
|
||||||
``image.getchannel("A")`` will return alpha channel as seperate image.
|
``image.getchannel("A")`` will return alpha channel as separate image.
|
||||||
|
``getchannel`` should work up to 6 times faster than ``image.split()[0]``
|
||||||
|
in previous Pillow versions.
|
||||||
|
|
||||||
|
|
||||||
|
Partial Resampling
|
||||||
|
==================
|
||||||
|
|
||||||
|
Added new argument ``box`` for :py:meth:`PIL.Image.Image.resize`. This
|
||||||
|
argument defines a source rectangle from within the source image to be
|
||||||
|
resized. This is very similar to the ``image.crop(box).resize(size)``
|
||||||
|
sequence except that ``box`` can be specified with subpixel accuracy.
|
||||||
|
|
||||||
|
|
||||||
|
Loading 16-bit TIFF Images
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Pillow now can read 16-bit multichannel TIFF files including files
|
||||||
|
with alpha transparency. The image data is truncated to 8-bit
|
||||||
|
precision.
|
||||||
|
|
||||||
|
|
||||||
|
Performance
|
||||||
|
===========
|
||||||
|
|
||||||
|
This release contains several performance improvements:
|
||||||
|
|
||||||
|
* Many memory bandwidth-bounded operations such as crop, image allocation,
|
||||||
|
conversion, split into bands and merging from bands are up to 2x faster.
|
||||||
|
* Upscaling of multichannel images (such as RGB) is accelerated by 5-10%
|
||||||
|
* JPEG loading is accelerated up to 15% and JPEG saving up to 20% when
|
||||||
|
using a recent version of libjpeg-turbo.
|
||||||
|
|
||||||
|
|
||||||
|
Core Image API Changes
|
||||||
|
======================
|
||||||
|
|
||||||
|
These are internal functions that should not have been used by user
|
||||||
|
code, but they were accessible from the python layer.
|
||||||
|
|
||||||
|
Debugging code within ``Image.core.grabclipboard`` was removed. It had been
|
||||||
|
marked as ``will be removed in future versions`` since PIL. When enabled, it
|
||||||
|
identified the format of the clipboard data.
|
||||||
|
|
||||||
|
The ``PIL.Image.core.copy`` and ``PIL.Image.Image.im.copy2`` methods
|
||||||
|
have been removed.
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
||||||
#define L(rgb)\
|
#define L(rgb)\
|
||||||
((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114)
|
((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114)
|
||||||
|
#define L24(rgb)\
|
||||||
|
((rgb)[0]*19595 + (rgb)[1]*38470 + (rgb)[2]*7471)
|
||||||
|
|
||||||
#ifndef round
|
#ifndef round
|
||||||
double round(double x) {
|
double round(double x) {
|
||||||
|
@ -210,7 +212,7 @@ rgb2l(UINT8* out, const UINT8* in, int xsize)
|
||||||
int x;
|
int x;
|
||||||
for (x = 0; x < xsize; x++, in += 4)
|
for (x = 0; x < xsize; x++, in += 4)
|
||||||
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
||||||
*out++ = L(in) / 1000;
|
*out++ = L24(in) >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -219,7 +221,7 @@ rgb2la(UINT8* out, const UINT8* in, int xsize)
|
||||||
int x;
|
int x;
|
||||||
for (x = 0; x < xsize; x++, in += 4, out += 4) {
|
for (x = 0; x < xsize; x++, in += 4, out += 4) {
|
||||||
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
||||||
out[0] = out[1] = out[2] = L(in) / 1000;
|
out[0] = out[1] = out[2] = L24(in) >> 16;
|
||||||
out[3] = 255;
|
out[3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +232,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize)
|
||||||
int x;
|
int x;
|
||||||
INT32* out = (INT32*) out_;
|
INT32* out = (INT32*) out_;
|
||||||
for (x = 0; x < xsize; x++, in += 4)
|
for (x = 0; x < xsize; x++, in += 4)
|
||||||
*out++ = L(in) / 1000;
|
*out++ = L24(in) >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -419,7 +421,7 @@ rgba2la(UINT8* out, const UINT8* in, int xsize)
|
||||||
int x;
|
int x;
|
||||||
for (x = 0; x < xsize; x++, in += 4, out += 4) {
|
for (x = 0; x < xsize; x++, in += 4, out += 4) {
|
||||||
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
|
||||||
out[0] = out[1] = out[2] = L(in) / 1000;
|
out[0] = out[1] = out[2] = L24(in) >> 16;
|
||||||
out[3] = in[3];
|
out[3] = in[3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,23 @@
|
||||||
|
|
||||||
#include "Imaging.h"
|
#include "Imaging.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
#define MAKE_UINT32(u0, u1, u2, u3) (u3 | (u2<<8) | (u1<<16) | (u0<<24))
|
||||||
|
#else
|
||||||
|
#define MAKE_UINT32(u0, u1, u2, u3) (u0 | (u1<<8) | (u2<<16) | (u3<<24))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static inline UINT8 clip8(float in)
|
||||||
|
{
|
||||||
|
if (in <= 0.0)
|
||||||
|
return 0;
|
||||||
|
if (in >= 255.0)
|
||||||
|
return 255;
|
||||||
|
return (UINT8) in;
|
||||||
|
}
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
|
ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
|
||||||
{
|
{
|
||||||
|
@ -36,9 +53,8 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
|
||||||
if (xmargin < 0 && ymargin < 0)
|
if (xmargin < 0 && ymargin < 0)
|
||||||
return (Imaging) ImagingError_ValueError("bad kernel size");
|
return (Imaging) ImagingError_ValueError("bad kernel size");
|
||||||
|
|
||||||
imOut = ImagingNew(
|
imOut = ImagingNewDirty(
|
||||||
imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin
|
imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin);
|
||||||
);
|
|
||||||
if (!imOut)
|
if (!imOut)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -74,16 +90,259 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
|
||||||
return imOut;
|
return imOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This is work around bug in GCC prior 4.9 in 64 bit mode.
|
||||||
|
GCC generates code with partial dependency which 3 times slower.
|
||||||
|
See: http://stackoverflow.com/a/26588074/253146 */
|
||||||
|
#if defined(__x86_64__) && defined(__SSE__) && ! defined(__NO_INLINE__) && \
|
||||||
|
! defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900)
|
||||||
|
static float __attribute__((always_inline)) inline i2f(int v) {
|
||||||
|
float x;
|
||||||
|
__asm__("xorps %0, %0; cvtsi2ss %1, %0" : "=X"(x) : "r"(v) );
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static float inline i2f(int v) { return (float) v; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ImagingFilter3x3(Imaging imOut, Imaging im, const float* kernel,
|
||||||
|
float offset)
|
||||||
|
{
|
||||||
|
#define KERNEL1x3(in0, x, kernel, d) ( \
|
||||||
|
i2f((UINT8) in0[x-d]) * (kernel)[0] + \
|
||||||
|
i2f((UINT8) in0[x]) * (kernel)[1] + \
|
||||||
|
i2f((UINT8) in0[x+d]) * (kernel)[2])
|
||||||
|
|
||||||
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
memcpy(imOut->image[0], im->image[0], im->linesize);
|
||||||
|
if (im->bands == 1) {
|
||||||
|
// Add one time for rounding
|
||||||
|
offset += 0.5;
|
||||||
|
for (y = 1; y < im->ysize-1; y++) {
|
||||||
|
UINT8* in_1 = (UINT8*) im->image[y-1];
|
||||||
|
UINT8* in0 = (UINT8*) im->image[y];
|
||||||
|
UINT8* in1 = (UINT8*) im->image[y+1];
|
||||||
|
UINT8* out = (UINT8*) imOut->image[y];
|
||||||
|
|
||||||
|
out[0] = in0[0];
|
||||||
|
for (x = 1; x < im->xsize-1; x++) {
|
||||||
|
float ss = offset;
|
||||||
|
ss += KERNEL1x3(in1, x, &kernel[0], 1);
|
||||||
|
ss += KERNEL1x3(in0, x, &kernel[3], 1);
|
||||||
|
ss += KERNEL1x3(in_1, x, &kernel[6], 1);
|
||||||
|
out[x] = clip8(ss);
|
||||||
|
}
|
||||||
|
out[x] = in0[x];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add one time for rounding
|
||||||
|
offset += 0.5;
|
||||||
|
for (y = 1; y < im->ysize-1; y++) {
|
||||||
|
UINT8* in_1 = (UINT8*) im->image[y-1];
|
||||||
|
UINT8* in0 = (UINT8*) im->image[y];
|
||||||
|
UINT8* in1 = (UINT8*) im->image[y+1];
|
||||||
|
UINT32* out = (UINT32*) imOut->image[y];
|
||||||
|
|
||||||
|
out[0] = ((UINT32*) in0)[0];
|
||||||
|
if (im->bands == 2) {
|
||||||
|
for (x = 1; x < im->xsize-1; x++) {
|
||||||
|
float ss0 = offset;
|
||||||
|
float ss3 = offset;
|
||||||
|
ss0 += KERNEL1x3(in1, x*4+0, &kernel[0], 4);
|
||||||
|
ss3 += KERNEL1x3(in1, x*4+3, &kernel[0], 4);
|
||||||
|
ss0 += KERNEL1x3(in0, x*4+0, &kernel[3], 4);
|
||||||
|
ss3 += KERNEL1x3(in0, x*4+3, &kernel[3], 4);
|
||||||
|
ss0 += KERNEL1x3(in_1, x*4+0, &kernel[6], 4);
|
||||||
|
ss3 += KERNEL1x3(in_1, x*4+3, &kernel[6], 4);
|
||||||
|
out[x] = MAKE_UINT32(clip8(ss0), 0, 0, clip8(ss3));
|
||||||
|
}
|
||||||
|
} else if (im->bands == 3) {
|
||||||
|
for (x = 1; x < im->xsize-1; x++) {
|
||||||
|
float ss0 = offset;
|
||||||
|
float ss1 = offset;
|
||||||
|
float ss2 = offset;
|
||||||
|
ss0 += KERNEL1x3(in1, x*4+0, &kernel[0], 4);
|
||||||
|
ss1 += KERNEL1x3(in1, x*4+1, &kernel[0], 4);
|
||||||
|
ss2 += KERNEL1x3(in1, x*4+2, &kernel[0], 4);
|
||||||
|
ss0 += KERNEL1x3(in0, x*4+0, &kernel[3], 4);
|
||||||
|
ss1 += KERNEL1x3(in0, x*4+1, &kernel[3], 4);
|
||||||
|
ss2 += KERNEL1x3(in0, x*4+2, &kernel[3], 4);
|
||||||
|
ss0 += KERNEL1x3(in_1, x*4+0, &kernel[6], 4);
|
||||||
|
ss1 += KERNEL1x3(in_1, x*4+1, &kernel[6], 4);
|
||||||
|
ss2 += KERNEL1x3(in_1, x*4+2, &kernel[6], 4);
|
||||||
|
out[x] = MAKE_UINT32(
|
||||||
|
clip8(ss0), clip8(ss1), clip8(ss2), 0);
|
||||||
|
}
|
||||||
|
} else if (im->bands == 4) {
|
||||||
|
for (x = 1; x < im->xsize-1; x++) {
|
||||||
|
float ss0 = offset;
|
||||||
|
float ss1 = offset;
|
||||||
|
float ss2 = offset;
|
||||||
|
float ss3 = offset;
|
||||||
|
ss0 += KERNEL1x3(in1, x*4+0, &kernel[0], 4);
|
||||||
|
ss1 += KERNEL1x3(in1, x*4+1, &kernel[0], 4);
|
||||||
|
ss2 += KERNEL1x3(in1, x*4+2, &kernel[0], 4);
|
||||||
|
ss3 += KERNEL1x3(in1, x*4+3, &kernel[0], 4);
|
||||||
|
ss0 += KERNEL1x3(in0, x*4+0, &kernel[3], 4);
|
||||||
|
ss1 += KERNEL1x3(in0, x*4+1, &kernel[3], 4);
|
||||||
|
ss2 += KERNEL1x3(in0, x*4+2, &kernel[3], 4);
|
||||||
|
ss3 += KERNEL1x3(in0, x*4+3, &kernel[3], 4);
|
||||||
|
ss0 += KERNEL1x3(in_1, x*4+0, &kernel[6], 4);
|
||||||
|
ss1 += KERNEL1x3(in_1, x*4+1, &kernel[6], 4);
|
||||||
|
ss2 += KERNEL1x3(in_1, x*4+2, &kernel[6], 4);
|
||||||
|
ss3 += KERNEL1x3(in_1, x*4+3, &kernel[6], 4);
|
||||||
|
out[x] = MAKE_UINT32(
|
||||||
|
clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out[x] = ((UINT32*) in0)[x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(imOut->image[y], im->image[y], im->linesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ImagingFilter5x5(Imaging imOut, Imaging im, const float* kernel,
|
||||||
|
float offset)
|
||||||
|
{
|
||||||
|
#define KERNEL1x5(in0, x, kernel, d) ( \
|
||||||
|
i2f((UINT8) in0[x-d-d]) * (kernel)[0] + \
|
||||||
|
i2f((UINT8) in0[x-d]) * (kernel)[1] + \
|
||||||
|
i2f((UINT8) in0[x]) * (kernel)[2] + \
|
||||||
|
i2f((UINT8) in0[x+d]) * (kernel)[3] + \
|
||||||
|
i2f((UINT8) in0[x+d+d]) * (kernel)[4])
|
||||||
|
|
||||||
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
memcpy(imOut->image[0], im->image[0], im->linesize);
|
||||||
|
memcpy(imOut->image[1], im->image[1], im->linesize);
|
||||||
|
if (im->bands == 1) {
|
||||||
|
// Add one time for rounding
|
||||||
|
offset += 0.5;
|
||||||
|
for (y = 2; y < im->ysize-2; y++) {
|
||||||
|
UINT8* in_2 = (UINT8*) im->image[y-2];
|
||||||
|
UINT8* in_1 = (UINT8*) im->image[y-1];
|
||||||
|
UINT8* in0 = (UINT8*) im->image[y];
|
||||||
|
UINT8* in1 = (UINT8*) im->image[y+1];
|
||||||
|
UINT8* in2 = (UINT8*) im->image[y+2];
|
||||||
|
UINT8* out = (UINT8*) imOut->image[y];
|
||||||
|
|
||||||
|
out[0] = in0[0];
|
||||||
|
out[1] = in0[1];
|
||||||
|
for (x = 2; x < im->xsize-2; x++) {
|
||||||
|
float ss = offset;
|
||||||
|
ss += KERNEL1x5(in2, x, &kernel[0], 1);
|
||||||
|
ss += KERNEL1x5(in1, x, &kernel[5], 1);
|
||||||
|
ss += KERNEL1x5(in0, x, &kernel[10], 1);
|
||||||
|
ss += KERNEL1x5(in_1, x, &kernel[15], 1);
|
||||||
|
ss += KERNEL1x5(in_2, x, &kernel[20], 1);
|
||||||
|
out[x] = clip8(ss);
|
||||||
|
}
|
||||||
|
out[x+0] = in0[x+0];
|
||||||
|
out[x+1] = in0[x+1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add one time for rounding
|
||||||
|
offset += 0.5;
|
||||||
|
for (y = 2; y < im->ysize-2; y++) {
|
||||||
|
UINT8* in_2 = (UINT8*) im->image[y-2];
|
||||||
|
UINT8* in_1 = (UINT8*) im->image[y-1];
|
||||||
|
UINT8* in0 = (UINT8*) im->image[y];
|
||||||
|
UINT8* in1 = (UINT8*) im->image[y+1];
|
||||||
|
UINT8* in2 = (UINT8*) im->image[y+2];
|
||||||
|
UINT32* out = (UINT32*) imOut->image[y];
|
||||||
|
|
||||||
|
out[0] = ((UINT32*) in0)[0];
|
||||||
|
out[1] = ((UINT32*) in0)[1];
|
||||||
|
if (im->bands == 2) {
|
||||||
|
for (x = 2; x < im->xsize-2; x++) {
|
||||||
|
float ss0 = offset;
|
||||||
|
float ss3 = offset;
|
||||||
|
ss0 += KERNEL1x5(in2, x*4+0, &kernel[0], 4);
|
||||||
|
ss3 += KERNEL1x5(in2, x*4+3, &kernel[0], 4);
|
||||||
|
ss0 += KERNEL1x5(in1, x*4+0, &kernel[5], 4);
|
||||||
|
ss3 += KERNEL1x5(in1, x*4+3, &kernel[5], 4);
|
||||||
|
ss0 += KERNEL1x5(in0, x*4+0, &kernel[10], 4);
|
||||||
|
ss3 += KERNEL1x5(in0, x*4+3, &kernel[10], 4);
|
||||||
|
ss0 += KERNEL1x5(in_1, x*4+0, &kernel[15], 4);
|
||||||
|
ss3 += KERNEL1x5(in_1, x*4+3, &kernel[15], 4);
|
||||||
|
ss0 += KERNEL1x5(in_2, x*4+0, &kernel[20], 4);
|
||||||
|
ss3 += KERNEL1x5(in_2, x*4+3, &kernel[20], 4);
|
||||||
|
out[x] = MAKE_UINT32(clip8(ss0), 0, 0, clip8(ss3));
|
||||||
|
}
|
||||||
|
} else if (im->bands == 3) {
|
||||||
|
for (x = 2; x < im->xsize-2; x++) {
|
||||||
|
float ss0 = offset;
|
||||||
|
float ss1 = offset;
|
||||||
|
float ss2 = offset;
|
||||||
|
ss0 += KERNEL1x5(in2, x*4+0, &kernel[0], 4);
|
||||||
|
ss1 += KERNEL1x5(in2, x*4+1, &kernel[0], 4);
|
||||||
|
ss2 += KERNEL1x5(in2, x*4+2, &kernel[0], 4);
|
||||||
|
ss0 += KERNEL1x5(in1, x*4+0, &kernel[5], 4);
|
||||||
|
ss1 += KERNEL1x5(in1, x*4+1, &kernel[5], 4);
|
||||||
|
ss2 += KERNEL1x5(in1, x*4+2, &kernel[5], 4);
|
||||||
|
ss0 += KERNEL1x5(in0, x*4+0, &kernel[10], 4);
|
||||||
|
ss1 += KERNEL1x5(in0, x*4+1, &kernel[10], 4);
|
||||||
|
ss2 += KERNEL1x5(in0, x*4+2, &kernel[10], 4);
|
||||||
|
ss0 += KERNEL1x5(in_1, x*4+0, &kernel[15], 4);
|
||||||
|
ss1 += KERNEL1x5(in_1, x*4+1, &kernel[15], 4);
|
||||||
|
ss2 += KERNEL1x5(in_1, x*4+2, &kernel[15], 4);
|
||||||
|
ss0 += KERNEL1x5(in_2, x*4+0, &kernel[20], 4);
|
||||||
|
ss1 += KERNEL1x5(in_2, x*4+1, &kernel[20], 4);
|
||||||
|
ss2 += KERNEL1x5(in_2, x*4+2, &kernel[20], 4);
|
||||||
|
out[x] = MAKE_UINT32(
|
||||||
|
clip8(ss0), clip8(ss1), clip8(ss2), 0);
|
||||||
|
}
|
||||||
|
} else if (im->bands == 4) {
|
||||||
|
for (x = 2; x < im->xsize-2; x++) {
|
||||||
|
float ss0 = offset;
|
||||||
|
float ss1 = offset;
|
||||||
|
float ss2 = offset;
|
||||||
|
float ss3 = offset;
|
||||||
|
ss0 += KERNEL1x5(in2, x*4+0, &kernel[0], 4);
|
||||||
|
ss1 += KERNEL1x5(in2, x*4+1, &kernel[0], 4);
|
||||||
|
ss2 += KERNEL1x5(in2, x*4+2, &kernel[0], 4);
|
||||||
|
ss3 += KERNEL1x5(in2, x*4+3, &kernel[0], 4);
|
||||||
|
ss0 += KERNEL1x5(in1, x*4+0, &kernel[5], 4);
|
||||||
|
ss1 += KERNEL1x5(in1, x*4+1, &kernel[5], 4);
|
||||||
|
ss2 += KERNEL1x5(in1, x*4+2, &kernel[5], 4);
|
||||||
|
ss3 += KERNEL1x5(in1, x*4+3, &kernel[5], 4);
|
||||||
|
ss0 += KERNEL1x5(in0, x*4+0, &kernel[10], 4);
|
||||||
|
ss1 += KERNEL1x5(in0, x*4+1, &kernel[10], 4);
|
||||||
|
ss2 += KERNEL1x5(in0, x*4+2, &kernel[10], 4);
|
||||||
|
ss3 += KERNEL1x5(in0, x*4+3, &kernel[10], 4);
|
||||||
|
ss0 += KERNEL1x5(in_1, x*4+0, &kernel[15], 4);
|
||||||
|
ss1 += KERNEL1x5(in_1, x*4+1, &kernel[15], 4);
|
||||||
|
ss2 += KERNEL1x5(in_1, x*4+2, &kernel[15], 4);
|
||||||
|
ss3 += KERNEL1x5(in_1, x*4+3, &kernel[15], 4);
|
||||||
|
ss0 += KERNEL1x5(in_2, x*4+0, &kernel[20], 4);
|
||||||
|
ss1 += KERNEL1x5(in_2, x*4+1, &kernel[20], 4);
|
||||||
|
ss2 += KERNEL1x5(in_2, x*4+2, &kernel[20], 4);
|
||||||
|
ss3 += KERNEL1x5(in_2, x*4+3, &kernel[20], 4);
|
||||||
|
out[x] = MAKE_UINT32(
|
||||||
|
clip8(ss0), clip8(ss1), clip8(ss2), clip8(ss3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out[x] = ((UINT32*) in0)[x];
|
||||||
|
out[x+1] = ((UINT32*) in0)[x+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memcpy(imOut->image[y], im->image[y], im->linesize);
|
||||||
|
memcpy(imOut->image[y+1], im->image[y+1], im->linesize);
|
||||||
|
}
|
||||||
|
|
||||||
Imaging
|
Imaging
|
||||||
ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
|
ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
|
||||||
FLOAT32 offset, FLOAT32 divisor)
|
FLOAT32 offset)
|
||||||
{
|
{
|
||||||
Imaging imOut;
|
Imaging imOut;
|
||||||
int x, y;
|
|
||||||
FLOAT32 sum;
|
|
||||||
ImagingSectionCookie cookie;
|
ImagingSectionCookie cookie;
|
||||||
|
|
||||||
if (!im || strcmp(im->mode, "L") != 0)
|
if ( ! im || im->type != IMAGING_TYPE_UINT8)
|
||||||
return (Imaging) ImagingError_ModeError();
|
return (Imaging) ImagingError_ModeError();
|
||||||
|
|
||||||
if (im->xsize < xsize || im->ysize < ysize)
|
if (im->xsize < xsize || im->ysize < ysize)
|
||||||
|
@ -92,95 +351,17 @@ ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
|
||||||
if ((xsize != 3 && xsize != 5) || xsize != ysize)
|
if ((xsize != 3 && xsize != 5) || xsize != ysize)
|
||||||
return (Imaging) ImagingError_ValueError("bad kernel size");
|
return (Imaging) ImagingError_ValueError("bad kernel size");
|
||||||
|
|
||||||
imOut = ImagingNew(im->mode, im->xsize, im->ysize);
|
imOut = ImagingNewDirty(im->mode, im->xsize, im->ysize);
|
||||||
if (!imOut)
|
if (!imOut)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// Add one time for rounding
|
|
||||||
offset += 0.5;
|
|
||||||
|
|
||||||
/* brute force kernel implementations */
|
|
||||||
#define KERNEL3x3(image, kernel, d) ( \
|
|
||||||
(int) image[y+1][x-d] * kernel[0] + \
|
|
||||||
(int) image[y+1][x] * kernel[1] + \
|
|
||||||
(int) image[y+1][x+d] * kernel[2] + \
|
|
||||||
(int) image[y][x-d] * kernel[3] + \
|
|
||||||
(int) image[y][x] * kernel[4] + \
|
|
||||||
(int) image[y][x+d] * kernel[5] + \
|
|
||||||
(int) image[y-1][x-d] * kernel[6] + \
|
|
||||||
(int) image[y-1][x] * kernel[7] + \
|
|
||||||
(int) image[y-1][x+d] * kernel[8])
|
|
||||||
|
|
||||||
#define KERNEL5x5(image, kernel, d) ( \
|
|
||||||
(int) image[y+2][x-d-d] * kernel[0] + \
|
|
||||||
(int) image[y+2][x-d] * kernel[1] + \
|
|
||||||
(int) image[y+2][x] * kernel[2] + \
|
|
||||||
(int) image[y+2][x+d] * kernel[3] + \
|
|
||||||
(int) image[y+2][x+d+d] * kernel[4] + \
|
|
||||||
(int) image[y+1][x-d-d] * kernel[5] + \
|
|
||||||
(int) image[y+1][x-d] * kernel[6] + \
|
|
||||||
(int) image[y+1][x] * kernel[7] + \
|
|
||||||
(int) image[y+1][x+d] * kernel[8] + \
|
|
||||||
(int) image[y+1][x+d+d] * kernel[9] + \
|
|
||||||
(int) image[y][x-d-d] * kernel[10] + \
|
|
||||||
(int) image[y][x-d] * kernel[11] + \
|
|
||||||
(int) image[y][x] * kernel[12] + \
|
|
||||||
(int) image[y][x+d] * kernel[13] + \
|
|
||||||
(int) image[y][x+d+d] * kernel[14] + \
|
|
||||||
(int) image[y-1][x-d-d] * kernel[15] + \
|
|
||||||
(int) image[y-1][x-d] * kernel[16] + \
|
|
||||||
(int) image[y-1][x] * kernel[17] + \
|
|
||||||
(int) image[y-1][x+d] * kernel[18] + \
|
|
||||||
(int) image[y-1][x+d+d] * kernel[19] + \
|
|
||||||
(int) image[y-2][x-d-d] * kernel[20] + \
|
|
||||||
(int) image[y-2][x-d] * kernel[21] + \
|
|
||||||
(int) image[y-2][x] * kernel[22] + \
|
|
||||||
(int) image[y-2][x+d] * kernel[23] + \
|
|
||||||
(int) image[y-2][x+d+d] * kernel[24])
|
|
||||||
|
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
if (xsize == 3) {
|
if (xsize == 3) {
|
||||||
/* 3x3 kernel. */
|
/* 3x3 kernel. */
|
||||||
for (x = 0; x < im->xsize; x++)
|
ImagingFilter3x3(imOut, im, kernel, offset);
|
||||||
imOut->image[0][x] = im->image8[0][x];
|
|
||||||
for (y = 1; y < im->ysize-1; y++) {
|
|
||||||
imOut->image[y][0] = im->image8[y][0];
|
|
||||||
for (x = 1; x < im->xsize-1; x++) {
|
|
||||||
sum = KERNEL3x3(im->image8, kernel, 1) / divisor + offset;
|
|
||||||
if (sum <= 0)
|
|
||||||
imOut->image8[y][x] = 0;
|
|
||||||
else if (sum >= 255)
|
|
||||||
imOut->image8[y][x] = 255;
|
|
||||||
else
|
|
||||||
imOut->image8[y][x] = (UINT8) sum;
|
|
||||||
}
|
|
||||||
imOut->image8[y][x] = im->image8[y][x];
|
|
||||||
}
|
|
||||||
for (x = 0; x < im->xsize; x++)
|
|
||||||
imOut->image8[y][x] = im->image8[y][x];
|
|
||||||
} else {
|
} else {
|
||||||
/* 5x5 kernel. */
|
/* 5x5 kernel. */
|
||||||
for (y = 0; y < 2; y++)
|
ImagingFilter5x5(imOut, im, kernel, offset);
|
||||||
for (x = 0; x < im->xsize; x++)
|
|
||||||
imOut->image8[y][x] = im->image8[y][x];
|
|
||||||
for (; y < im->ysize-2; y++) {
|
|
||||||
for (x = 0; x < 2; x++)
|
|
||||||
imOut->image8[y][x] = im->image8[y][x];
|
|
||||||
for (; x < im->xsize-2; x++) {
|
|
||||||
sum = KERNEL5x5(im->image8, kernel, 1) / divisor + offset;
|
|
||||||
if (sum <= 0)
|
|
||||||
imOut->image8[y][x] = 0;
|
|
||||||
else if (sum >= 255)
|
|
||||||
imOut->image8[y][x] = 255;
|
|
||||||
else
|
|
||||||
imOut->image8[y][x] = (UINT8) sum;
|
|
||||||
}
|
|
||||||
for (; x < im->xsize; x++)
|
|
||||||
imOut->image8[y][x] = im->image8[y][x];
|
|
||||||
}
|
|
||||||
for (; y < im->ysize; y++)
|
|
||||||
for (x = 0; x < im->xsize; x++)
|
|
||||||
imOut->image8[y][x] = im->image8[y][x];
|
|
||||||
}
|
}
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
return imOut;
|
return imOut;
|
||||||
|
|
|
@ -26,24 +26,30 @@ ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
|
||||||
|
|
||||||
ImagingCopyInfo(imOut, imIn);
|
ImagingCopyInfo(imOut, imIn);
|
||||||
|
|
||||||
#define FLIP_HORIZ(image)\
|
|
||||||
for (y = 0; y < imIn->ysize; y++) {\
|
|
||||||
xr = imIn->xsize-1;\
|
|
||||||
for (x = 0; x < imIn->xsize; x++, xr--)\
|
|
||||||
imOut->image[y][x] = imIn->image[y][xr];\
|
|
||||||
}
|
|
||||||
|
|
||||||
ImagingSectionEnter(&cookie);
|
ImagingSectionEnter(&cookie);
|
||||||
|
|
||||||
if (imIn->image8)
|
if (imIn->image8) {
|
||||||
FLIP_HORIZ(image8)
|
for (y = 0; y < imIn->ysize; y++) {
|
||||||
else
|
UINT8* in = (UINT8*) imIn->image8[y];
|
||||||
FLIP_HORIZ(image32)
|
UINT8* out = (UINT8*) imOut->image8[y];
|
||||||
|
x = 0;
|
||||||
|
xr = imIn->xsize-1;
|
||||||
|
for (; x < imIn->xsize; x++, xr--)
|
||||||
|
out[xr] = in[x];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < imIn->ysize; y++) {
|
||||||
|
UINT32* in = (UINT32*) imIn->image32[y];
|
||||||
|
UINT32* out = (UINT32*) imOut->image32[y];
|
||||||
|
x = 0;
|
||||||
|
xr = imIn->xsize-1;
|
||||||
|
for (; x < imIn->xsize; x++, xr--)
|
||||||
|
out[xr] = in[x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImagingSectionLeave(&cookie);
|
ImagingSectionLeave(&cookie);
|
||||||
|
|
||||||
#undef FLIP_HORIZ
|
|
||||||
|
|
||||||
return imOut;
|
return imOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,7 +260,7 @@ extern Imaging ImagingFillLinearGradient(const char* mode);
|
||||||
extern Imaging ImagingFillRadialGradient(const char* mode);
|
extern Imaging ImagingFillRadialGradient(const char* mode);
|
||||||
extern Imaging ImagingFilter(
|
extern Imaging ImagingFilter(
|
||||||
Imaging im, int xsize, int ysize, const FLOAT32* kernel,
|
Imaging im, int xsize, int ysize, const FLOAT32* kernel,
|
||||||
FLOAT32 offset, FLOAT32 divisor);
|
FLOAT32 offset);
|
||||||
extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn);
|
extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn);
|
||||||
extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn);
|
extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn);
|
||||||
extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius,
|
extern Imaging ImagingGaussianBlur(Imaging imOut, Imaging imIn, float radius,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user